What is happening is that you are getting the object’s object_id instead of it’s saved id. To see what I am talking about, load up irb and make an object, say:
a = “A String”
then type:
a.id
It will say this is deprecated, and that you should use object_id instead, but it will also show you the object_id. These numbers vary greatly, but the one returned to you seems to fit right in with this.
However, I am not sure why this is happening in your specific case. Hopefully this will at least help you look into the problem further.
irb(main):034:0> class A
irb(main):035:1> p self
irb(main):036:1> def g
irb(main):037:2>
p self
irb(main):038:2> end
irb(main):039:1> end
A
=> nil
irb(main):043:0> a=A.new
=>
#<A:0x2c59570>
irb(main):044:0> a.g
#<A:0x2c59570>
=> nil
Now that I look at your code, I think it's a context issue. Consider this, when you call a class method, what is the definition of self? Out of curiosity, why do you need to be so specific in your finder_sql? Wouldn't it be better to do something along the lines of
app/models/member.rb
class Member < ActiveRecord::Base
has_many :my_friends, :class_name => "Friend", :foreign_key =>
"member_id"
has_many :friends, :through => :my_friends, :source => :member
end
app/models/friend.rb
class Friend < ActiveRecord::Base
belongs_to :member
end
db/migrate/001_create_members.rb
class CreateMembers < ActiveRecord::Migration
def self.up
create_table :members do |t|
# t.column :name, :string
t.column :username, :string
end
end
def self.down
drop_table :members
end
end
db/migrate/002_create_friends.rb
class CreateFriends < ActiveRecord::Migration
def self.up
create_table :friends do |t|
# t.column :name, :string
t.column :member_id, :int
end
end
def self.down
drop_table :friends
end
end
ruby script/console
Member.new(:username => "edbond").save
=> true
Member.find(:first).friends
=>
will generate (from log/development.log):
Member Load (0.000361) SELECT members.* FROM members INNER JOIN
friends ON members.id = friends.member_id WHERE (friends.member_id = 1)
Yes you are on the right track... although I removed the difficult
logic to simplify the explanation of my problem.
Although at this point, I think I understand the context issue and why
it's not working, so I think I'll switch my strategy around and
eliminate the use of finder_sql.
has_many :friends, :class_name => "Member", :finder_sql => 'SELECT members.* FROM members, friends WHERE friends.member_id = #{self.id}'
Note the change from "-quotes to '-quotes. You want the #{self.id} to be evalulated later when the SQL is needed, not while you're defining the :finder_sql. (That is, when self is an instance of Member not the class itself.)
You could also have escaped the interpolation "SELECT ... \#{self.id}"
Do you really need :finder_sql in this case? If a member can really have more than one friend, wouldn't you need another table to hold the relationship (i.e., the friendships table)?
class Member < ActiveRecord::Base
has_many :friendships, :dependent => :destroy
has_many :friends, :through => :friendships
end
class Friendship < ActiveRecord::Base
belongs_to :member
belongs_to :friend, :class => 'Member', :foreign_key => :friend_id
end
has_many :friends, :class_name => "Member", :finder_sql => 'SELECT
members.* FROM members, friends WHERE friends.member_id = #{self.id}'
Note the change from "-quotes to '-quotes. You want the #{self.id}
to be evalulated later when the SQL is needed, not while you're
defining the :finder_sql. (That is, when self is an instance of
Member not the class itself.)
Thank you for pointing this out! I think this is quite a gotcha and
deserves a word or two in
documentation...