0

Is there a short way for obj.update(attr1: "something") method to call method_missing instead of raising the error ActiveModel::UnknownAttributeError (unknown attribute 'attr1' for Obj.)?

I am thinking about simply rescuing it and then mimicking/calling the method_missing, but this way feels too bulky.

Gene D.
  • 311
  • 1
  • 15
  • what is the question here – Nithin May 23 '16 at 07:17
  • The question is: when you call `obj.update(attr1: "something")`, it raises error `ActiveModel::UnknownAttributeError (unknown attribute 'attr1' for Obj.)`. I want it to call `method_missing` instead, where I would specify the proper setter. – Gene D. May 23 '16 at 07:23
  • Hm. How could you call method_missing if method exists? – Yevgeniy Anfilofyev May 23 '16 at 07:28
  • It doesn't, there is no `attr1=` method on this model. Maybe my description is a bit vague. What I want to do is to create dynamic setters, so when `attr1` is undefined, it would go to `method_missing` where I would write the code to handle it. – Gene D. May 23 '16 at 07:30

3 Answers3

3

From the source code (Rails 4.2) it seems that ActiveRecord::UnknownAttributeError is raised when the model instance does not respond_to? to the given attribute setter. So what you have to do is define a method_missing as well as respond_to_missing? in the model for all your dynamic attributes. This is actually what you always should do when using method_missing anyway:

class Model < ActiveRecord::Base

  DYNAMIC_ATTRIBUTES = [:attr1, :attr2]

  def method_missing(method_name, *arguments, &block)
    if DYNAMIC_ATTRIBUTES.map { |attr| "#{attr}=" }.include?(method_name.to_s)
      puts "custom code for #{method_name} #{arguments.first.inspect}"
    else
      super
    end
  end

  def respond_to_missing?(method_name, include_private = false)
    DYNAMIC_ATTRIBUTES.map { |attr| "#{attr}=" }.include?(method_name.to_s) || super
  end

end

Test in Rails console:

Model.first.update(attr1: 'aa', attr2: 'bb')
# => custom code for attr1= "aa"
# => custom code for attr2= "bb"

Model.first.update(attr1: 'aa', attr2: 'bb', attr3: 'cc')
# => ActiveRecord::UnknownAttributeError: unknown attribute 'attr3' for Model.
Matouš Borák
  • 15,606
  • 1
  • 42
  • 53
0

Something like this?

  rescue_from ActiveModel::UnknownAttributeError do |exception|
    raise NoMethodError
    # or however you want to hanle
  end
Nithin
  • 3,679
  • 3
  • 30
  • 55
0

You can try this:

begin
  obj.update(attr1: "something")
rescue Exception => ex
  if  ex.class == ActiveRecord::UnknownAttributeError
    method_missing
  else
    raise ex
  end
end
Prity
  • 224
  • 1
  • 7