views:

44

answers:

1

I am seeing a very strange issue with a simple controller method. Either I am missing something fundamental or I am encountering a bug. My bet is on the former.

I have a Thing model with a ThingController.

A Thing has two variables, name and display, both strings.

ThingController (code below) has a method toggle_display, that toggles the contents of display between "on" and "off".

The problem is that when I call this action, Rails finds the correct Thing, but @thing.display is nil. When I check the database, the value in the 'display' column is correct.

The strange part is that when I uncomment the third line in the code below (i.e. access @thing.name before accessing @thing.display) then @thing.display is fine - it isn't nil and it has the value I would expect. It's as if @thing.display only gets initialized correctly after I access @thing.name.

Any idea why I would see this very strange behavior?

def toggle_display
  @thing = Thing.find(params[:id])

  # @thing.name

  if @thing.display
    @thing.toggle_display_in_model
    @thing.save
  end

  redirect_to things_url
end
+7  A: 

The problem is that there's already a method named "display" in Kernel which conflicts with ActiveRecord's magic.

ActiveRecord defines the methods corresponding to the database fields in method_missing. So until method_missing is called, the methods don't actually exist. When you call name on @thing, method_missing is called because there is no name method. However when you call display (without previously calling another method that does not exist), method_missing is not called because display is already defined in Kernel and this definition is executed. And since Kernel's display method returns nil, you get nil.

sepp2k
Wow, very good answer. I learned something from this.
Jaime Bellmyer
Thanks very much - this is very clear and helps me enormously. I should have thought about the possibility of some sort of conflict. Is there a simple way that one can get an exhaustive list of the names one should avoid. Or is there a best practice that I should adopt to force a call to method_missing?
Greg
You can get a list of all methods that exist on ActiveRecord objects by typing `ActiveRecord::Base.instance_methods` into script/console.
sepp2k