1
votes

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?

2

2 Answers

2
votes

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.

  1. 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.

  2. 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.

0
votes

If you want to make class attribute callable, you can assign it a proc

self.wilson_ratings = proc{ |model| model.votes }