In a previous question I asked how I could show the contents of a Dictionary in a GUI. I started from this idea to build a GUI with a slightly better look and feel. It mainly consists of RectangleMorphs glued together in columns and rows (cfr. the accepted answer in my previous question).
The problem now is that I would like my table to be updated when elements are added/removed/edited in my dictionary. I managed to write some Morph that consists of columns of CellMorphs, which inherit from RectangleMorph and have model and message as instance variables with the following update message:
update
" update the contents of this cell "
| value |
self removeAllMorphs.
(model = nil or: message = nil)
ifTrue: [ value := '' ]
ifFalse: [ value := model perform: message ].
self addMorph: value asMorph.
As can be seen, the CellMorph is a container for a Morph containing the actual content of the cell. This works great for displaying the size of the dictionary for instance:
d := Dictionary new.
d at: 'foo' put: 100.
d at: 'bar' put: 200.
cell := CellMorph new
model: d;
message: #size;
color: Color white.
cell openInWorld.
d at: 'boo' put: 300. " cell will be updated "
but I don't seem to get something similar working for the contents of the dictionary, because I can't find a way to access single keys or values with a message. The only solution I can think of is to create new columns with new cells every time, but this is so expensive and I can't imagine that this is a good idea...
Therefore my question:
Is there a way to update my Morph displaying the dictionary without creating billions of my CellMorphs or should I forget about my idea and rather work with rows of CellMorphs for instance in order to group the entries in the dictionary?
for completeness: the model: message in CellMorph looks like:
model: newModel
"change the model behind this cell"
model ifNotNil: [ model removeDependent: self ].
newModel ifNotNil: [newModel addDependent: self].
model := newModel.
self update.
update: aParameter does nothing more than call update. and I also added self changed. in all messages of Dictionary that I want the interface to be notified of (at: put:, removeKey:, etc.).
In the instance variable named 'message' you could have a Message object, instead of having only the selector.
An instance of Message has the receiver, selector and arguments. So, you could configure it with the dictionary keys sorted asArray in the receiver, the selector #at: and an index, to get a specific key. Accessing the value would be getting the value at: that key in the dictionary.
I think that a Message is not executed with object perform: message, you should check. message perform should work because it already has the receiver.
In any case, this complexity may show that having only (one) model and (one) message is not enough to get the model in th granularity you want, and you can possibly specialize a bit more, using the knowledge that the model is a dictionary. For instance, having an instance variable for key or for keyIndex.
Some side notes about the code:
(model = nil or: message = nil)
has comparisons with nil, that can be replaced by #isNil message or, if you want to stick with equality, use the faster == to compare identity, since nil is unique.
#or: is used to get the benefits of partial evaluation (the argument is evaluated only if the receiver is false). But that only works if you have a block as argument, otherwise the expression is evaluated before, to get the argument for the message in the stack.
Related
I need to get some values for a hash but, I don't know how can I do it!
My data:
data = {
name: "Pedro Álvares",
age: 35,
sensitive_data: {
cpf_cnpj=>27046645678,
fantasy_name: "I have the power"
}
}
I search for ruby methods and find the method values_at, but this method gets only the first_data like name, age.
If I use:
name = data.values_at(:name)
the field returned is Pedro Álvares
but if I try use:
fantasy_name = data.values_at(:fantasy_name)
the data returned is nil.
How can I get the field without using data[:sensitive_data][:fantasy_name]?
Because you know the correct structure of the hash you should write:
data[:sensitive_data][:fantasy_name]
#=> "I have the power"
You should not use dig here. Why? Suppose you accidently wrote
data.dig(:sesnitive_data, :fantasy_name)
The would return nil (because data has no key :sesnitive). Depending on the context the error might not surface until sometime later, making debugging more difficult than is necessary.
By contrast, if you wrote
data[:sesnitive_data][:fantasy_name]
data[:sesnitive_data] would return nil (because data has no key :sesnitive_data) and then nil[:sesnitive_data] would raise an exception, informing you that nil has no method []. That is precisely what you want to happen: you want to be notified of the error immediately, and have the reason for it it pinpointed, so you can easily correct your code.
Hash#dig, Array#dig and Struct#dig (which call each other) have their uses (when you do not know the structures of objects in advance--a hash's keys, for example), but those methods should not be used when an object's structure is known.
You could get the nested value by dig method:
data.dig(:sensitive_data, :fantasy_name) # => "I have the power"
I have the following scenario:
# Keeps track of all `HomeMadeObject` objects
obj_list = []
# A 2-dimensional matrix of either `nil` or `HomeMadeObject` elements
matrix = ...
# I then add an object like so.
obj = HomeMadeObject.new
matrix[y][x] = obj
obj_list.push(obj)
# Later on, I need to remove 'that same object' from the `matrix`.
matrix[y][x] = nil
When I set matrix[y][x] to nil, this will also affect the same object in the obj_list, making it also nil in the obj_list.
puts obj_list[0] # nil
What I'd like to do is to change the matrix[y][x] pointer's location (or the reference) so that the matrix[y][x] points to the nil object. In that way, obj_list points to all the correct objects and the matrix cells can address different locations without overriding a HomeMadeObject object by nil (creating nil elements in obj_list).
Edit
I would like to update the objects in obj_list and see the updates in the matrix (so I need to have some kind of a reference here to the original object). But When a HomeMadeObject isn't needed anymore in the matrix, I want to remove it only from the matrix.
All you have to understand here is, In Ruby all variables and constants store references to objects. That’s why one can’t copy the content by assigning one variable to another variable. Variables of type Object in Java or pointers to objects in C++ are good to think of. However, you can’t change the value of each pointer itself.
So use dup method when you are pushing into the object list, your problem is solved.
obj_list.push(obj.dup)
So I found this quiz on a website that I was excited to solve with my newly acquired Ruby skills (CodeAcademy, not quite finished yet).
What I want to do is make an array with 100 entries, all set to "open". Then, I planned to create a method containing a for loop that iterates through every nth entry of the array and changes it to either "open" or "closed", based on what it was before. In the for loop, n should be increased from 1 to 100.
What I have so far is this:
change_state = Proc.new { |element| element == "open" ? element = "closed" : element = "open" }
def janitor(array,n)
for i in 1..n
array.each { |element| if array.index(element) % i == 0 then element.change_state end }
end
end
lockers = [*1..100]
lockers = lockers.map{ |element| element = "closed" }
result = janitor(lockers,100)
When trying to execute I receive an error saying:
undefined method `change_state' for "closed":String (NoMethodError)
Anybody an idea what is wrong here? I kinda think I'm calling the "change_state" proc incorrectly on the current array element.
If you know the quiz, no spoilers please!
As you have implemented change_state, it is not a method of any class, and definitely not one attached to any of the individual elements of the array, despite you using the same variable name element. So you cannot call it as element.change_state.
Instead, it is a variable pointing to a Proc object.
To call the code in a Proc object, you would use the call method, and syntax like proc_obj.call( params ) - in your case change_state.call( element )
If you just drop in that change, your error message will change to:
NameError: undefined local variable or method `change_state' for main:Object
That's because the change_state variable is not in scope inside the method, in order to be called. There are lots of ways to make it available. One option would be to pass it in as a parameter, so your definition for janitor becomes
def janitor(array,n,state_proc)
(use the variable name state_proc inside your routine instead of change_state - I am suggesting you change the name to avoid confusing yourself)
You could then call it like this:
result = janitor(lockers,100,change_state)
Although your example does not really need this structure, this is one way in which Ruby code can provide a generic "outer" function - working through the elements of an array, say - and have the user of that code provide a small internal custom part of it. A more common way to achieve the same result as your example is to use a Ruby block and the yield method, but Procs also have their uses, because you can treat them like data as well as code - so you can pass them around, put them into hashes or arrays to decide which one to call etc.
There may be other issues to address in your code, but this is the cause of the error message in the question.
I have seen when coding in LINQ that when a value is assigned to a field sometimes is in this way Table["Field"] and any others like this Table.Field but can somebody explain me what's the difference please?
For example when modifying a field:
var ttAbccode_xRow =
(from ttAbccode_Row in ds.ABCCode select ttAbccode_Row).FirstOrDefault();
if (ttAbccode_xRow != null) {
ttAbccode_xRow["PI"] = 3.1416;
}
or
if (ttAbccode_xRow != null) {
ttAbccode_xRow.PI = 3.1416;
}
Accessing field via indexer (square brackets) returns object data type. That means that your compiler cannot detect data types incompatibility. You could assing for example string value (eg. "abcd") and you won't get error at design time, but as late as at runtime.
Second method (if available in your result set) is much more safe. Your property will have proper data type hence compiler will detect data types incompatibility at design time.
If I had both access methods available I would always prefer second one. It is less error prone.
I have a model MyModel with a method to return a specific record (see logic below).
def self.find_future_rec #note2
rec = find(rand(MyModel.count)+1) #note1
while rec.nil? | (rec.expdate<Date.today)
rec = find(rand(MyModel.count)+1)
end
return rec
end
Every record of MyModel class has a variable expdate of Date class. (I know this is a horrible way to find a record, this is more for my own edification and also some test code.)
This method will iterate through several undesirable records before finding an appropriate record, but the record returned is always the one found at note 1, the first record queried. Is there some lazy assignment thing going on here?
I had to add self at note2 to be able to call the method in a static context. Is this the correct interpretation?
The find method never returns nil when you give it a single ID to find: if it can't find the record you're asking for, it raises an ActiveRecord::RecordNotFound exception. So, rec is always non-nil and rec.nil? is always false. That means that your loop is really like this:
while rec.expdate < Date.today
If your loop is always returning the rec from #note1 then you're never entering the while loop at all and you're always getting a desirable MyModel on the first try.
Other points to consider:
Sometimes things get deleted so Model.count + 1 is not necessarily the maximum ID.
find raises an exception to indicate failure so you need to rescue ActiveRecord::RecordNotFound rather than check for nil.
You are using | which is a boolean OR. Try using or or ||.
Adding self to make a method a class method ("static" is a decent approximation) is indeed one of the correct ways to approach this.