I am developing a mentions system similar to the one in twitter. So I am dealing with three classes in this problem: Post, User and Mention. The mention object has a mentioned_id field (the mentioned user) and a post_id ( the post in which it happens).
I found some weird problems while writing tests, so I opened a console session, and this is what happens:
I create two users, two posts, and two mentions. If you notice, when I create the second mention (@mention2) the id of @mentioned_id is not the correct user’s id:
ruby-1.9.2-p180 :001 > @user1 = Factory(:user, :email => Factory.next(:email))
=> #<User id: 1, name: "Fulano", email: "person_number1@example.com", created_at: "2011-09-11 15:38:11", updated_at: "2011-09-11 15:38:11", encrypted_password: "ede4e8cc0440b8bd9fdc059e8496154715b6592690157aac618...", salt: "4bfae8fecee1629b7c290c2d5af5bd3bbeb38c4ce76546d7176...", admin: false>
ruby-1.9.2-p180 :002 > @user2 = Factory(:user, :email => Factory.next(:email))
=> #<User id: 2, name: "Fulano", email: "person_number2@example.com", created_at: "2011-09-11 15:39:06", updated_at: "2011-09-11 15:39:06", encrypted_password: "ddda10f48575f63724e1db3d3cf684f2c60380325236311e15d...", salt: "911121b8942dc57116dfd24383d96874c750bc5a21fa210288b...", admin: false>
ruby-1.9.2-p180 :003 > @post1 = Factory(:post, :user => @user1)
=> #<post id: 1, content: "Foo bar", user_id: 1, created_at: "2011-09-11 15:39:57", updated_at: "2011-09-11 15:39:57">
ruby-1.9.2-p180 :004 > @post2 = Factory(:post, :user => @user2)
=> #<post id: 2, content: "Foo bar", user_id: 2, created_at: "2011-09-11 15:40:37", updated_at: "2011-09-11 15:40:37">
ruby-1.9.2-p180 :005 > @mention1 = @post2.mentions.create :mentioned_id => @user1
=> #<Mention id: 1, post_id: 2, mentioned_id: 1, created_at: "2011-09-11 15:41:33", updated_at: "2011-09-11 15:41:33">
ruby-1.9.2-p180 :007 > @mention2 = @post1.mentions.create :mentioned_id => @user2
=> #<Mention id: 2, post_id: 1, mentioned_id: 1, created_at: "2011-09-11 15:42:16", updated_at: "2011-09-11 15:42:16">
Strangely enough, if I use @user2.id instead of just @user2 that last line, everything works fine:
ruby-1.9.2-p180 :008 > @mention2 = @post1.mentions.create :mentioned_id => @user2.id
=> #<Mention id: 3, post_id: 1, mentioned_id: 2, created_at: "2011-09-11 15:45:16", updated_at: "2011-09-11 15:45:16">
Can anyone please tell me what’s happening? I thought rails “magic” took care of getting the id when referencing an object. And why would it work with the first user but not with the second??
This is the code for my mention class:
class Mention < ActiveRecord::Base
attr_accessible :mentioned_id
belongs_to :mentioned, :class_name => "User"
belongs_to :post
validates :mentioned, :presence => true
validates :post, :presence => true
end
And this is the relevant portion of the code for my user class:
Class User < ActiveRecord::Base
…
has_many :posts, :dependent => :destroy
has_many :mentions, :dependent => :destroy, :foreign_key => 'mentioned_id'
…
This also happens in my spec test:
@user = Factory(:user, :email => Factory.next(:email))
@other_user = Factory(:user, :email => Factory.next(:email))
@post1 = Factory(:post, :user => @other_user)
@post2 = Factory(:post, :user => @other_user)
@post3 = Factory(:post, :user => @user)
@mention1 = @post1.mentions.create :mentioned_id => @user
@mention2 = @post2.mentions.create :mentioned_id => @user
@mention3 = @post3.mentions.create :mentioned_id => @other_user.id
The last line only works correctly if I use the .id method, but the previous two work correctly…
You’re trying to ask Rails to map an integer (
mentioned_id) to an object (@user). You’re doing it correctly on your@post1 = ...lines, where you’re assigning@other_userto:user, not:user_id. You need to do the same on your@mention1 = ...lines:However, with your code as it is, this won’t work either, because your
attr_accessorline is interfering.You can either get rid of the line altogether and the
:mentioned => @userpart will work, or you can change it like so, to allow assigning directly to:mentionedrather than:mentioned_id(yes, they really are the same thing, but it seems that Rails checksattr_accessibleand denies access to updating:mentionedbefore it realizes that:mentionedis really just:mentioned_idanyway).