Jump to content

The ultimate community for Ruby on Rails developers.


Photo

Why is the behaviour of ActiveRecord method_missing different between rails 3.2 and 4.0 ?

rails4 method_missing

  • Please log in to reply
4 replies to this topic

#1 starfry

starfry

    Signalman

  • Members
  • 11 posts

Posted 30 August 2013 - 03:15 PM

* I posted this on rubyonrailstalk on google but for some reason the post hasn't appeared. If it does, then apologies in advance for the double-post *

 

I have been working on updating a rails application to 4.0 and spent some time getting to the bottom of why it wasn't working.

What I found was, if a model has a method_missing definition then it is called instead of any accessors. This causes any model set-up to fail.

The following example demonstrates the problem:

class Item < ActiveRecord::Base
attr_accessible :name, :content
store :content

def method_missing(id, *args)
puts "method missing: #{id}"
end
end

In Rails 3.2.14:

$ rails console
Loading development environment (Rails 3.2.14)
2.0.0p247 :001 > x = Item.new(name: 'foo')
=> #<Item id: nil, name: "foo", content: {}, created_at: nil, updated_at: nil> 
2.0.0p247 :002 >

and in Rails 4.0.0:

$ rails console
Loading development environment (Rails 4.0.0)
2.0.0p247 :001 > x = Item.new(name: 'foo')
method missing: name=
=> #<Item id: nil, name: nil, content: {}, created_at: nil, updated_at: nil> 
2.0.0p247 :002 > 

In Rails 3.2.14, the name attribute is set to foo as is the intention. In Rails 4.0.0, however, see that method_missing is called and the attribute is not set.

I can work around this by manually invoking the upward method_missing chain:

def method_missing(id, *args) 
super
if respond_to? id
send(id,*args)
else
puts "method missing: #{id}"
end 
end 

I don't understand why this happens, or if I am doing something wrong. Would someone with more knowledge of the internals be able to explain this ?

 

 

*update* I have a model where there are also kind-of virtual attributes and I use method_missing to trap calls for these and handle them appropriately. In rails 3, I simply had something like this:

def method_missing(id, *args) 
  handle_virtual_attribute(id, *args)
end

I changed this to

def method_missing(id, *args)

  # Let rails define accessors and try again
  super
  if respond_to? id
    send(id,*args)
  else
    handle_virtual_attribute(id, *args)
  end
end

but the call to super doesn't return - the app borks with an error like:


undefined method `my_virtual_attribute' for #<MyModelClass:0xb5d629f4>

If I don't call super, the virtual attributes work but the real ones don't.

If I do call super, the virtual attributes don't work but the real ones do.

 

At this point I am stuck and would appreciate some pointers. I have an app the works in 3.2.14 that I can't get to work in 4 and I don't know what to do to get it to work.... can anyone shine a light on this please?


Edited by Jamie, 30 August 2013 - 03:33 PM.
Added code tags


#2 Kelli Shaver

Kelli Shaver

    Inspector

  • Administrators
  • 75 posts
  • LocationKentucky

Posted 01 September 2013 - 07:15 AM

Rails 4 uses strong parameters instead of attr_accessible 



#3 starfry

starfry

    Signalman

  • Members
  • 11 posts

Posted 01 September 2013 - 07:42 AM

It happens with strong parameters as well. Sorry about having attr_accessible in the example, it was from before I switched over to strong paramaters. But the problem is there without the protected attributes gem in place.



#4 katafrakt

katafrakt

    Signalman

  • Members
  • 16 posts
  • LocationKrak√≥w, PL

Posted 04 September 2013 - 08:35 AM

How many virtual attributes do you have (and are they some kind of finite list)? Wouldn't it be a better idea to just handle them explicitly with setters and getters?

 

You could also wrap your call to super with exception handling but this would be a dirty workaround ;)



#5 starfry

starfry

    Signalman

  • Members
  • 11 posts

Posted 04 September 2013 - 06:40 PM

the application is very data driven and the virtual attributes refer to data held in a store whose keys are not known up front. I could probably devise some restrictions and limit the scope of it so I could write accessors for the (now) limited set of valid virtual attributes. Just seems a shame when what is there is quite elegant and worked up until the upgrade. re super, yes I was considering handling the exception and will probably end up doing that in the absence of anything else.

 

I had hoped I'd find out what had changed, why and what the "proper" alternative is though. I've read many things about using method_missing in this way and I know rails itself uses it to achieve much of its goodness but I can't help feeling somewhat frustrated by the change between these versions and the lack of any information in the change log (or perhaps I am just looking in the wrong place...)







Also tagged with one or more of these keywords: rails4, method_missing

0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users