Reputation: 2442
I'm using ActiveSupport::Concern to dry up some code that is included on my AR classes. I have a module for calculating the lower wilson bound on data:
module CalculateWilsonBound
extend ActiveSupport::Concern
included do
class_attribute :wilson_ratings
class_attribute :wilson_positive_ratings
end
def calculate_wilson_lower_bound
number_of_ratings = self.class.wilson_ratings.call(self)
...
end
end
After I've included it onto an object, I want to provide two class level methods (wilson_ratings, wilson_positive_ratings) which define blocks which will return respective counts.
From the AR objects point of view:
class Influence < ActiveRecord::Base
include CalculateWilsonBound
wilson_ratings { |model| model.votes }
wilson_positive_ratings { |model| model.positive_votes }
This does not cause any runtime errors, but when I got to access the class attribute:
number_of_ratings = self.class.wilson_ratings.call(self)
It's nil.
First of all, am I organising the code in a that makes sense, secondaly, why is the class attribute nil?
Upvotes: 1
Views: 4486
Reputation: 1390
I believe you will need to do:
class Influence < ActiveRecord::Base
include CalculateWilsonBound
self.wilson_ratings = Proc.new { |model| model.votes }
self.wilson_positive_ratings = Proc.new { |model| model.positive_votes }
end
At the moment you have 2 problems.
When trying to assign a class attribute in the context of a class definition Rails will not realise that you are referring to the class attribute unless you use self.
You need to assign the class attribute, rather than pass a block into it. As your code reads now it looks like you are calling a method called wilson_ratings
and passing a block into it.
As to whether your code is sensible, it is starting to smell a little funny to me. I much prefer the service class pattern where possible (see http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/) - it prevents you from having to mess around with class_attributes and other potentially hairy concepts.
Upvotes: 2
Reputation: 10018
If you want to make class attribute callable, you can assign it a proc
self.wilson_ratings = proc{ |model| model.votes }
Upvotes: 0