Cristhian Boujon
Cristhian Boujon

Reputation: 4190

Overriding attribute accessors on Ruby on Rails 4

I have a Restriction model that represent a formula. The field formula is a string that at runtime is evaluated. This field can't be accessed from outside for security reasons so I'am trying to override the accessors.

class Restriction < ActiveRecord::Base
  belongs_to :indicator
  RESTRICTION_TYPES = {
    less_than: "IND<X", 
    greater_than: "X<IND", 
    between: "X<IND && IND<Y"
  }

  def evaluate(number)
    f = formula.gsub("IND", "#{number}")
    eval(f)
  end

  def create_formula(type_name, arg)
    if(type_name == :between)
      f = RESTRICTION_TYPES[:between].gsub("X", "#{arg[0]}").gsub("Y", "#{arg[1]}")
    else
      f = RESTRICTION_TYPES[type_name].gsub("X", "#{arg}")
    end
    formula = f
  end


  private
  def formula= f
    write_attribute(:formula, f)
  end

  def formula
    read_attribute(:formula)
  end

  def [](value)
    super[value]    
  end

  def []=(key, value)
    super[key] = value
  end

end

In rails console:

Loading development environment (Rails 4.0.0)
2.0.0p247 :001 > r = Restriction.new
 => #<Restriction id: nil, formula: nil, indicator_id: nil, created_at: nil, updated_at: nil, state: nil> 
2.0.0p247 :002 > r.create_formula(:between, [1,2])
 => "1<IND && IND<2" 
2.0.0p247 :003 > r.evaluate 1.5
NoMethodError: undefined method 'gsub' for nil:NilClass
2.0.0p247 :004 > r
 => #<Restriction id: nil, formula: nil> 

formula is not change the value. What am I doing wrong?

PS: You can see that I have also overriden [](value) and []=(key, value). This is due to my previous question

Upvotes: 0

Views: 716

Answers (1)

lawitschka
lawitschka

Reputation: 745

Rails internally relies on the hash like access methods for reading and writing attributes. By making them private you wanted to restrict the access of [] and []= to from within the object only. But you destroyed the method, because you are using super the wrong way. When calling super you are not getting the super object of this class, but the super method, the current method overrides. Thus change it like this and it should work:

def [](value)
  super(value)
end

def []=(key, value)
  super(key, value)
end

P.S.: Overriding a method for declaring it private is overkill in Ruby. You can simply declare it private with

private :[], []=

Upvotes: 1

Related Questions