Hey all,
The Subject line may be a bad question, whether it’s possible to
invoke a class method on an array of objects. But look at the code
below, and unless I am misunderstanding, that’s what appears to be
going on, and yes the code does work:
class Dog
def total_caught
Cat.counters(:dog => self)[:caught]
end
class Cat
def self.counters(constraints = {})
source = constraints[:dog] ? constraints[:dog].cats : self
CatState.keys.map.each_with_object({}) { |k, h| h[k] =
source.count_of(k)
end
def self.count_of(state, options = {})
since = options.delete(:since)
options[:conditions] = ["#{state}_on > ?", since] if since
Cat.count("#{state}_on", options)
end
Basically, what’s going on here is when the total_caught method is
invoked, it calls the Cat class method of Cat, passing into the
argument list a hash key/value pair. We assign that hash to local
variable constraints, and then check if the key dog exists, if so we
get all associated cats to the dog. Hence, it is possible for the
source local variable to hold an array of cat objects, which is the
purpose of has_many and belongs_to. But then notice we call count_of
which is a class method, so if source holds an array of objects, how
is it possible to call the count_of class method of class Cat on it?
thanks for response
On Jul 19, 2011, at 4:18 PM, John M. wrote:
end
options[:conditions] = ["#{state}_on > ?", since] if since
purpose of has_many and belongs_to. But then notice we call count_of
which is a class method, so if source holds an array of objects, how
is it possible to call the count_of class method of class Cat on it?
thanks for response
If you have an array of objects, you can use map to pass a block to
each member of that array, and the result will be an array holding the
output of that block.
[‘foo’,‘bar’,‘baz’].map{|word| word.upcase } #-> [‘FOO’,‘BAR’,‘BAZ’]
So anything you want to put inside that block will work. If you need
multiple lines, just use do and end instead of the curly braces. If
you want to modify your objects in place, then I think you can use
map! instead, and that will return the original array with each of its
elements modified by the block.
Walter
On Jul 19, 9:18pm, John M. [email protected] wrote:
end
options[:conditions] = [“#{state}_on > ?”, since] if since
purpose of has_many and belongs_to. But then notice we call count_of
which is a class method, so if source holds an array of objects, how
is it possible to call the count_of class method of class Cat on it?
Assuming these are active record objects and that dog has_many cats,
then dog.cats isn’t actually an array, it’s an association proxy which
will forwards attempts to call class methods of Cat onto Cat, setting
the scope so that only the relevant cats will be found. If it was an
actual array you wouldn’t be able to do this.
Fred
Hi.
Try to do something like this:
CatState.keys.map.each_with_object({}) { |k, h| h[k]
= source.to_a.inject(0){|sum, a| sum += count_of(a)}
[1,2,3].inject(0) {|sum, a| sum += a }
as result => 6
On Jul 20, 2011, at 11:00 AM, Peter wrote:
Random note, you can use & and a symbol to send a message to each
element in an array. So for the above example, it would be more
concise to write
[‘foo’,‘bar’,‘baz’].map(&:upcase) #-> [‘FOO’,‘BAR’,‘BAZ’]
There are obviously still many times to use the block form, but I find
a lot can be shortened to this and it makes the code more readable.
I agree. I was showing the block case in order to allow for more
elaborate multi-step conversion.
Walter
On Jul 19, 3:45pm, Walter Lee D. [email protected] wrote:
If you have an array of objects, you can use map to pass a block to
each member of that array, and the result will be an array holding the
output of that block.
[‘foo’,‘bar’,‘baz’].map{|word| word.upcase } #-> [‘FOO’,‘BAR’,‘BAZ’]
Random note, you can use & and a symbol to send a message to each
element in an array. So for the above example, it would be more
concise to write
[‘foo’,‘bar’,‘baz’].map(&:upcase) #-> [‘FOO’,‘BAR’,‘BAZ’]
There are obviously still many times to use the block form, but I find
a lot can be shortened to this and it makes the code more readable.
\Peter