I have a active record, where I have a usecase: I want to use map in the ActiveRecord scopes. Following is my code:
class SupportedGeoIdAndLocation < ActiveRecord::Base
scope :supported_geo_ids, lambda { all.map(&:geo_id).uniq }
# also tried: scope :supported_geo_ids, lambda { select('distinct geo_id').map(&:geo_id).uniq }
scope :locations_for_geo_id, lambda { |geo_id| where(:geo_id => geo_id).map(&:location) }
end
but when I do SupportedGeoIdAndLocation.supported_geo_ids I get empty array, while SupportedGeoIdAndLocation.all.map(&:geo_id).uniq gives me desired result.
I am looking for a way so that this can be done using lambda in one go, not via chaining of different methods and completely avoiding class functions.
Looks like I am using scope in wrong way, Please suggest me what can be correct way.
version: padrino:0.10.5, ruby: ruby-1.9.3-p429, ActiveRecord: 3.1.6
scope should return an ActiveRecord::Relation, so scope could be chained with another scope or class methods etc.
If you want to get an array, use class method instead.
def self.supported_geo_ids
all.map(&:geo_id).uniq
end
After playing around with some similar code in one of my models I finally found this question (as I got the same error message (in Rails 3.2 that was)). The accepted answer states that ActiveRecord implicitly assumes scopes to be "chainable" (in ARel sense) which the array you return obviously is not.
So while you can do whatever you want in a class method you define yourself (as in the answer of @xdazz) you will have to adhere to ActiveRecord conventions if you insist on using scope (which at first glance just defines a class method with whatever lambda you provided it). There seems to be more to this than I could spot.
Update
After some more fiddling I think I got as close as it gets. You can write a scope that returns just supported geo ids, i.e. as follows:
class SupportedGeoIdAndLocation < ActiveRecord::Base
scope :supported_geo_ids, lambda { select("distinct(geo_id)") }
end
However that will return an ARel relation that can be turned into an array of SupportedGeoIdAndLocation objects with just the geo_id attribute set (and, sorry to disappoint you, it is a class method, even if it ends up being defined through the scope class method). So if you need an array of attributes you can turn it into one using the method you proposed in another class method:
class SupportedGeoIdAndLocation < ActiveRecord::Base
def self.geo_ids_array
supported_geo_ids.map(&:geo_id)
end
end
Note that there is no need to call uniq now as this has been taken care of by the distinct in the scope. Up from rails 4.0.2 ActiveRecord does support the distinct method by the way, so you won't have to resort to using a string if you can update your ActiveRecord gem as Padrino should not depend on it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With