method_missing in a AR child only catching 1 of several call

Hello -

Why does a view including the following fragment:

     <% for column in User.content_columns %>        <td><%=h user.send(column.name) %></td>      <% end %>

and the User model having the following defined:

     def method_missing(meth_id, *args, &blk)        meth_name=meth_id.id2name        puts("Calling: #{meth_name}")        super      end

only result in the following output of one method call when there are several columns/attributes in the users table:

     Calling: name

Hi Aakash,

ActiveRecord::Base defines read methods lazily. That is, until you call a method to read an attribute, the method won't exist. Call a method to read an attribute, and it'll hit AR::B's method_missing hook, which defines all the read methods, and hence, subsequent calls to read methods won't trigger method_missing. You can witness the effect in script/console:

Thing.instance_methods(false)

=>

Thing.new.name

=> nil

Thing.instance_methods(false)

=> ["id?", "name?", "name", "value", "value?"]

I'm not sure what you're trying to do, but if you explain it, someone might be able to find something that works. :slight_smile: (Assuming this is a problem for you.)

Regards, George.

Have you looked around at the existing model-level authentication plugins? I think some do attribute-level authorization. e.g., [1].

If that doesn't fly for you, I'm not sure what the best way to do this is. Once you've generated the read methods, they're available via ModelName.read_methods. Perhaps you can override these. I don't think that's particularly watertight, though. #read_attribute, for example seems to pull things directly out of the @attributes hash inside the active record instances directly rather than going through the read methods. Perhaps you could hook into the # method on this hash, but that's getting pretty nasty!

class ActiveRecord::Base   def initialize_with_authorization_check(*args)     initialize_without_authorization_check(*args) do |*args|       class << @attributes         def (name)           # ... authorization check ...           super         end         def =(name, value)           # ... authorization check ...           super         end       end       yield(*args) if block_given?     end   end   alias initialize_without_authorization_check initialize   alias initialize initialize_with_authorization_check end

(Totally untested.)

[1] http://perens.com/FreeSoftware/ModelSecurity/

George.