Problem with overriding rails accessor methods

Hello all,

I just encountered something that is not what I would expect. I overrode an accessor method in my model class and within this method I did a "super" call which failed. Of couse my real method would be doing more but here is the basic code that shows the problem:

class Word < ActiveRecord::Base   belongs_to :verse   belongs_to :grammar_tense_code

def grammar_tense_code       obj = super end end

In my view I call: <td><%= word.grammar_tense_code.descr %></td>

The error message I get is: "super: no superclass method `grammar_tense_code'".

I realize there is no real 'grammar_tense_code' method and that rails handles this for me. BUT, I would have expected the message to be passed up the chain and handled by Rails active record code which would read the database. This is typical oo behavior. Does anybody know what is happening here and how to work around it?

(Incidentally, if I remove this method everything works fine. Rails retrieves the object from the database and gives me it's "descr" attribute.)

Thanks in advance, Paul

Active Record uses method_missing to dynamically read and write attributes. Use read_attribute(‘grammar_tense_code’) within your method rather than super.

jeremy

Hello Paul,     Urm, I think you may have a slight misunderstanding here of Rails, ORM's and possibly Database's ;> (joking). If you want a 'Word' to be a child of 'grammar_tense_code' then you should do

    class Word < grammar_tense_code (yes yes, classes should begin with upper case, so sue me :wink:

    As it is, the 'super' class of Word is ActiveRecord::Base. The only thing that 'grammar_tense_code' does is to denote a foreign key or 'relationship'. There is no implication that grammar_tense_code is in the dispatch chain at all. I could understand that you think belongs_to act's as a 'mixin', but, its [belongs_to] there almost there to help active record find the relationships at the database level. There is no 'importing' or the objects method into this object. I honestly can't think of a single ORM (WebWare/SeaSide/etc) that does this. Dare I ask, is this a typical 'java'-ism ? (NO OFFENSE meant here, jst wondering if this comes from a different technology that I don't use :wink:

    Satori ?     Regards     Stef

Paul Corcoran wrote:

Hi Stef,

I think my example probably obscured my real question. The Word database table has a "grammar_tense_code_id" column that is a key to the grammar_tense_codes lookup table which has "id" and "descr" columns.

However, take the following line of Ruby code:

     descr = word.grammar_tense_code.descr.

Assuming word is an instance of Word, the grammar_tense_code message is sent to the word object and the descr message is sent to the object from that. This incidentally works just fine for me. That is, rails reads the database row from the grammar_tense_codes table and returns to me the "descr".

However, being a rails app, my Word class does not define a grammar_tense_code method and so when the grammer_tense_code message gets sent, Ruby passes it up the superclass chain which eventually generates a method_missing exception. Note that this is all Ruby up to this point, no rails stuff involved. But the method_missing exception must trigger a rails handler that then kicks in and retrieves the database row.

Now for the real question: How is that process different than if I code my own grammar_tense_code method and then do a "super"? The method is still missing in the super class and should still trigger a method_missing exception.

UNLESS, these are two different exceptions and rails has not taken into account the exception generated by the missing super class method. This might be the whole issue.

Sorry for the rambling but I come from a Smalltalk background where this situation simply generates a #doesNotUnderstand message that gets sent to the object no matter what kind of message send it is: simple send or super.

I am going to investigate further but at this point I am inclined to think the rails framework isn't dealing with the exception generated by a missing method on a "super" call.

Thanks, Paul

Hi Jeremy,

I think you are correct. However, read_attribute('grammar_tense_code') just returns to me the value of that attribute rather than triggering the rails code that would read it from the database. So it just returns nil in my case. Do you happen to know if there is a way to trigger the database read that sets the attribute to begin with?

Thanks, Paul

Hello Paul,     Okay, I follow what your thinking, I hope :slight_smile: The problem comes from that the 'belongs_to' probably creates methods to -delegate- over to the appropriate class/table So, a belongs_to :grammar will send forward any methods to grammar (grammer.correct?). The problem is that this delegation means that the delegated class is NOT in the ancestral/hiearchy for your class, and it probably creates methods for a getter/setter named the same as the delegated class.

    So, if you override the methods which are created by the belongs_to, by creating your own class method called 'grammar', then its going to take precedence over the auto-created delegation. Your effectively 'masking' the auto-delegation. This coupled with 'super' only working on objects in your hierarchy means method_missing :wink:

    Perhaps a diagram may help.

             ---- Active Record Object --              > >             Word -> Delegates -> Grammar                 So, calling 'super' on Word won't find any methods in Grammar, because they are only related by delegation.. does this make sense ?

    Usually, calling a method the -exact- same name as a delegated class is a bad idea :wink:

    Regards     Stef (ps. I reserve the right to be wrong, in the past, now, and in the future)

Paul Corcoran wrote:

Try:

class Word < ActiveRecord::Base

belongs_to :verse

belongs_to :grammar_tense_code

alias :real_grammar_tense_code :grammar_tense_code

def grammar_tense_code

obj = real_grammar_tense_code

end

end

belongs_to adds the `grammar_tense_code’ method to Word. You’re then (almost immediately) overriding it. To get what you want you need to alias the original method then reference it inside your new method.

ar/lib/active_record/associations.rb will lead the way to wealth and power.

Did he really mean to make a question about associations, though - or just about mapped columns in general?

I have to say I find the way AR works quite counter-intuitive.

What I'd expect is to have method_missing() implemented in the super class and fully overridable by subclasses, but instead AR:Base decides to fudge with its children.

There are probably some sound technical reasons for this, though. I take it there's no viable way for AR::Base.method_missing() to reach/inspect the caller when called (indirectly) from a subclass?

Isak

Did he really mean to make a question about associations, though - or just about mapped columns in general?

For mapped columns, Sir Kemper's read_attribute trick works.

What I'd expect is to have method_missing() implemented in the super class and fully overridable by subclasses, but instead AR:Base decides to fudge with its children.

Association collection methods are not method_missing tricks. They are methods defined on your model when belongs_to, has_many, etc is declared. Thus, no super.

As for the mapped columns, they work with super() the way you'd expect. But we are not talking about mapped columns in the parent's example, we are talking about collection methods.

However, check it:

class Story < ActiveRecord::Base    def title      "My title is: " + super    end end

>> story = Story.new(:title => "Railzzzz!") => #<Story:0x266c820 @attributes={"title"=>"Railzzzz!"}, @new_record=true> >> story.save => true >> story.title => "My title is: Railzzzz!"

> What I'd expect is to have method_missing() implemented in the super > class and fully overridable by subclasses, but instead AR:Base decides > to fudge with its children.

As for the mapped columns, they work with super() the way you'd expect.

Wow, indeed it does. I tried to get this working earlier, and jumped to the conclusion that AR was extremely weird when i couldn't.. Thanks.

Isak

Chris, Isak,

Thanks fellas. I fully understand what is going on now.

Paul