ActiveRecord::Base descendant "loses" the "id" method?

All,

I have a set of AR::Base descendant objects that I’ve stored in an
array.

At some point in my application, I attempt to process these objects, and
part of that processing requires calling their “id” methods to get their
primary key value.

Given the following code, where the contents of
@datagrid.displayable_objects is a bunch of AR objects:

@datagrid.displayable_objects.each do |obj|
puts “Inspect object: #{obj.inspect}”
puts “Object class: #{obj.class}”
puts “Object superclass: #{obj.class.superclass}”
puts “Object methods include id: #{obj.methods.include?(‘id’)}”
puts “Object is a ActiveRecord::Base:
#{obj.is_a?(ActiveRecord::Base)}”
puts “Object id: #{obj.id}”
end

the first set of displayed output is the following:

Inspect object: #<Target:0xfc052b0 @attributes={“Date Code 3”=>nil,
“Merge20”=>nil, “Merge19”=>nil, “Merge8”=>nil, “Status”=>“G”,
“Contact”=>"Mr. David Bleasdell ", “DataSetID”=>345, “Merge10”=>nil,
“Merge9”=>nil, “Zip”=>nil, “Fax Number”=>“239-643-6733”,
“cityCode”=>nil, “CallType”=>nil, “Merge11”=>nil, “Merge12”=>nil,
“Merge1”=>nil, “Ad1”=>nil, “Error Code 1”=>nil, “Merge13”=>nil,
“Merge2”=>nil, “Ad2”=>nil, “UniqueID”=>64867, “ConnectTime”=>nil, “Error
Code 2”=>nil, “Merge14”=>nil, “Merge3”=>nil, “Error Code 3”=>nil,
“Merge15”=>“[email protected]”, “Merge4”=>nil, “CountryCode”=>nil,
“Merge16”=>nil, “Merge5”=>nil, “Company”=>“S.T. Aviation L.L.C.”,
“Job”=>nil, “Merge17”=>nil, “Merge6”=>nil, “Date Code 1”=>nil,
“Consecutive Failures”=>nil, “Pages Sent”=>nil, “Date Entered”=>nil,
“Merge18”=>nil, “Merge7”=>nil, “Date Code 2”=>nil}>
Object class: Target
Object superclass: ActiveRecord::Base
Object methods include id: false
Object is a ActiveRecord::Base: true

How can an object that self-identifies as ActiveRecord::Base not have
the id method?

Wes G. wrote:
In particular, notice these two lines of output:

Object superclass: ActiveRecord::Base
Object methods include id: false

Wes

Hi

How can an object that self-identifies as ActiveRecord::Base not have the id method?

I’m going to be maybe too obvious here, but according to Occam that
would be the right solution :wink:

Maybe your table has a custom key field? I’m going to risk and say that
in this piece of output there is a “UniqueID” that looks suspicious to
me.

“Merge2”=>nil, “Ad2”=>nil, “UniqueID”=>64867, “ConnectTime”=>nil,

Of course it could be a totally different issue, but i thought it was
worth to give it a try. Have you tried to do “puts Target.primary_key”
just in case?

Regards,

Javier R.

Estamos de estreno… si necesitas llevar el control de tus gastos
visita http://www.gastosgem.com !!Es gratis!!

Javier,

I do, in fact, have a custom primary key.

However, here is the code for the ActiveRecord::Base “id” method - note
the comment:

A model instance’s primary key is always available as model.id

whether you name it the default ‘id’ or set it to something else.

def id
attr_name = self.class.primary_key
column = column_for_attribute(attr_name)
define_read_method(:id, attr_name, column) if
self.class.generate_read_methods
read_attribute(attr_name)
end

Basically, I am looking at an array of ids, doing a find on each one and
then turning around and asking for the id back and not getting it. I
realize, of course, that a workaround would be to do
obj.send(name_of_custom_primary_key).

It’s the weirdest thing I’ve seen since I’ve started Rails development.

Wes

And then, look at this:

Code:

puts “Displayable object ids:
#{@object_ids[@display_start_index@display_stop_index].inspect}”

@object_ids[@display_start_index@display_stop_index].collect {|obj_id|
@klass.find(obj_id)}.each do |obj|
puts “Inspect object: #{obj.inspect}”
puts “Object class: #{obj.class}”
puts “Object superclass: #{obj.class.superclass}”
puts “Object primary key: #{obj.class.primary_key}”
puts “Class methods include id: #{obj.class.methods.include?(‘id’)}”
puts “Object methods include id: #{obj.methods.include?(‘id’)}”
puts “Object is a ActiveRecord::Base:
#{obj.is_a?(ActiveRecord::Base)}”
puts “Object id: #{obj.id}”
end

Output:
Displayable object ids: [64860, 64861, 64862, 64863, 64866]
Inspect object: #<Target:0xe9d2400 @attributes={attributes removed for
brevity}>
Object class: Target
Object superclass: ActiveRecord::Base
Object primary key: UniqueID
Class methods include id: true
Object methods include id: false
Object is a ActiveRecord::Base: true

How can the class method list include “id” but the object method list
not include it? WTF??? I’m clearly dealing with an AR::Base
descendant here, which I just got by calling find on an id via a collect
block.

WTF?

WG

Wes G. wrote:

I’ve tracked this all the way down into the instantiate method of
ActiveRecord::Base, and even there (which, if I’m not mistaken, is the
“end of the line”, these objects do not have a notion of the “id”
method. The objects are of the correct type, know that they are
AR::Base descendants, but do not have “id” in their method lists.

WTF?

WG

More explicitly showing that there is no “id” method available:

Code:
puts “Displayable object ids:
#{@object_ids[@display_start_index@display_stop_index].inspect}”

@object_ids[@display_start_index@display_stop_index].collect {|obj_id|
@klass.find(obj_id)}.each do |obj|
puts “Inspect object: #{obj.inspect}”
puts “Object class: #{obj.class}”
puts “Object superclass: #{obj.class.superclass}”
puts “Object primary key: #{obj.class.primary_key}”
puts “Class methods include id: #{obj.class.methods.include?(‘id’)}”
puts “Object methods include id: #{obj.methods.include?(‘id’)}”
puts “Object is a ActiveRecord::Base:
#{obj.is_a?(ActiveRecord::Base)}”
begin
puts obj.id
rescue
puts $!
end
end

Output:
Displayable object ids: [64860, 64861, 64862, 64863, 64866]
Inspect object: #<Target:0xe91e460 @attributes={removed for brevity}>
Object class: Target
Object superclass: ActiveRecord::Base
Object primary key: UniqueID
Class methods include id: true
Object methods include id: false
Object is a ActiveRecord::Base: true
undefined method `id’ for #Target:0xe91e460

Every single element in the id array behaves this way.

Wes

This totally doesn’t make sense.

How can an object allocated within a class not respond to a public
method within that class?

Even assuming that it’s possible that somehow these objects don’t have
access to AR::Base#id, wouldn’t they still respond to Object#id (it’s
not deprecated yet)?

I give up. Workaround, here I come…

WG

Just to be clear:

Here’s some code from the bottom of AR::Base instantiate:

object.instance_variable_set("@attributes", record)
puts “object has id: #{object.methods.include?(‘id’)}”
puts “object responds to id: #{object.respond_to?(‘id’)}”
puts “object class: #{object.class}”
puts “object is a AR: #{object.is_a?(ActiveRecord::Base)}”
object

Here’s some output from the objects that I find without access to the
“id” method:

object has id: false
object responds to id: false
object class: Target
object is a AR: true

WG

object.instance_variable_set(“@attributes”, record)
puts “object has id: #{object.methods.include?(‘id’)}”
puts “object responds to id: #{object.respond_to?(‘id’)}”
puts “object class: #{object.class}”
puts “object is a AR: #{object.is_a?(ActiveRecord::Base)}”
object

Just in case I created a model with a custom PK, and I run the same
code, and I got true in the three cases you are having a false, so I
definitely rule out the thing about the custom PK (anyway, after your
fist reply I already thought it was not because of that, but you know…
seeing is believing :wink: )

Guess your model is nothing fancy. I mean, my model for testing was just
class Apk < ActiveRecord::Base
set_primary_key :my_id
end

So, I don’t know… maybe you are using some mixins or any plugins that
make some (black) magic over AR:Base?.

Do you know what is even weirdest? I was thinking every object responds
to “id” (even if is deprecated and object_id should be used), so if it
weren’t a AR object and if the id method was not overwritten, It should
always respond to “id”

I’m trying here but I’m unable to replicate your problem :frowning:

good luck!

j


Estamos de estreno… si necesitas llevar el control de tus gastos
visita http://www.gastosgem.com !!Es gratis!!

Even after I implemented a workaround (to use
obj.send(obj.class.primary_key) instead of obj.id), now my objects that
are returned from find do not respond_to to any non-attribute methods.

I didn’t get into all of the details of how I’m calling to find these
objects. Basically I have an array of object ids that represent
potentially displayable objects in a datagrid.

If I load up the datagrid with the first set of objects, everything’s
fine. It is only when I attempt to page down (which automatically
causes a fetch of the next N objects), or when I attempt to “jump to” a
particular object in the datagrid list that I see this behavior. Both
of these behaviors simple present a different set of object IDs for
finding in an array.

Although I cannot understand after several hours how simply “sliding the
window” on the main object array could be causing this problem, I only
see this when the window is slid.

Sigh. Super fscking weird and super fscking annoying.

WG

javier ramirez wrote:

Do you know what is even weirdest? I was thinking every object responds
to “id” (even if is deprecated and object_id should be used), so if it
weren’t a AR object and if the id method was not overwritten, It should
always respond to “id”

Exactly.

Wes

Wes G. wrote:

By simply storing the datagrid object in the hash, hitting an action in
another controller, pulling the datagrid object out of the hash, and
doing the exact same operation on it, I can cause the AR finds to fail.

I can’t, however, do this within the same action.

WG

Some progress. I can now reproduce the error.

So I have this datagrid object which encapsulates an entire set of data,
including pulling data from the database when necessary.

This is achieved by storing an array of all of the object ids in the
datagrid. When a set of rows needs to be displayed, then I do finds on
those objects ids in order to pull those specific objects. The class of
the object is stored as a member in the datagrid object.

IF I store this datagrid object in the session, then when I scroll
through the grid, or jump to another area of the dataset (which will
cause additional finds to occur), everything’s fine, all of the find
calls behave the same way.

BUT storing this object in the session was not practical because
sometimes it can be pretty huge. So I moved to a custom hash storage (I
plan to move to memcached ASAP).

IF I store this datagrid object in a hash, then the subsequent finds
fail in the way that I’ve described above.

Of course, it still doesn’t make sense. I’m thinking that there might
be some issue with storing the class in my datagrid object and then
moving it into and out of a hash?

Why would the way that I’m storing the datagrid object make any
difference?

Wes

IF I store this datagrid object in a hash, then the subsequent finds
fail in the way that I’ve described above.

mmmm… well… being so and taking into account you said yesterday that
you can only access attribute variables, it looks as when the resultset
is not mapped to the AR object (not sure if the AR method for this was
called “instantiate”). For example, that’s what you get when you do a
ActiveRecord::Base.connection.select_all(). You get an array of hashes,
but no AR objects.

Of course this is not what’s really happening, because you were getting
“Target” as the class and it said it’s a AR::Base object… but still it
looks to me like that, as if you were getting a plain hashes array as a
result… why? no clue

Estamos de estreno… si necesitas llevar el control de tus gastos
visita http://www.gastosgem.com !!Es gratis!!

Wes G. wrote:
Last update. I can make this behavior happen by doing another action in
the same controller as well. So it seems to have something to do with
“crossing actions.”

WG