Skip to content

Instantly share code, notes, and snippets.

@alexch
Created September 18, 2012 21:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexch/3745884 to your computer and use it in GitHub Desktop.
Save alexch/3745884 to your computer and use it in GitHub Desktop.
ActiveRecord create should fail when passed a non-integer for an integer field
demonstration of https://github.com/rails/rails/issues/7690 in a vanilla rails 3.2.8 app
TL;DR: Foo.create(user_id: bob) sets foo's user id to 1, no matter bob's actual id
sesame:rails_bug [ruby-1.9.3-p194] alex$ cat app/models/user.rb
class User < ActiveRecord::Base
attr_accessible :name
has_many :articles
end
sesame:rails_bug [ruby-1.9.3-p194] alex$ cat app/models/article.rb
class Article < ActiveRecord::Base
attr_accessible :body, :user_id
has_many :likes
end
sesame:rails_bug [ruby-1.9.3-p194] alex$ cat app/models/like.rb
class Like < ActiveRecord::Base
attr_accessible :article_id, :user_id
belongs_to :user
belongs_to :article
end
sesame:rails_bug [ruby-1.9.3-p194] alex$ rails c
Loading development environment (Rails 3.2.8)
>> User.all
User Load (0.1ms) SELECT "users".* FROM "users"
=> []
>> alice = User.create(name: "Alice")
(0.1ms) begin transaction
SQL (6.9ms) INSERT INTO "users" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", Tue, 18 Sep 2012 20:35:56 UTC +00:00], ["name", "Alice"], ["updated_at", Tue, 18 Sep 2012 20:35:56 UTC +00:00]]
(0.7ms) commit transaction
=> #<User id: 1, name: "Alice", created_at: "2012-09-18 20:35:56", updated_at: "2012-09-18 20:35:56">
>> bob = User.create(name: "Bob")
(0.1ms) begin transaction
SQL (0.9ms) INSERT INTO "users" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", Tue, 18 Sep 2012 20:36:01 UTC +00:00], ["name", "Bob"], ["updated_at", Tue, 18 Sep 2012 20:36:01 UTC +00:00]]
(2.0ms) commit transaction
=> #<User id: 2, name: "Bob", created_at: "2012-09-18 20:36:01", updated_at: "2012-09-18 20:36:01">
>> article = alice.articles.create!(body: "i like cheese")
(0.1ms) begin transaction
SQL (27.3ms) INSERT INTO "articles" ("body", "created_at", "updated_at", "user_id") VALUES (?, ?, ?, ?) [["body", "i like cheese"], ["created_at", Tue, 18 Sep 2012 20:37:34 UTC +00:00], ["updated_at", Tue, 18 Sep 2012 20:37:34 UTC +00:00], ["user_id", 1]]
(1.0ms) commit transaction
=> #<Article id: 1, user_id: 1, body: "i like cheese", created_at: "2012-09-18 20:37:34", updated_at: "2012-09-18 20:37:34">
>> article.likes.create!(user_id: bob)
(0.1ms) begin transaction
SQL (0.8ms) INSERT INTO "likes" ("article_id", "created_at", "updated_at", "user_id") VALUES (?, ?, ?, ?) [["article_id", 1], ["created_at", Tue, 18 Sep 2012 20:37:52 UTC +00:00], ["updated_at", Tue, 18 Sep 2012 20:37:52 UTC +00:00], ["user_id", 1]]
(0.9ms) commit transaction
=> #<Like id: 1, user_id: 1, article_id: 1, created_at: "2012-09-18 20:37:52", updated_at: "2012-09-18 20:37:52">
*** note that bob's id is 2 but the user_id of the Like is 1 ***
>> article.likes.create(user_id: bob)
(0.1ms) begin transaction
SQL (0.6ms) INSERT INTO "likes" ("article_id", "created_at", "updated_at", "user_id") VALUES (?, ?, ?, ?) [["article_id", 1], ["created_at", Tue, 18 Sep 2012 20:38:26 UTC +00:00], ["updated_at", Tue, 18 Sep 2012 20:38:26 UTC +00:00], ["user_id", 1]]
(2.4ms) commit transaction
=> #<Like id: 2, user_id: 1, article_id: 1, created_at: "2012-09-18 20:38:26", updated_at: "2012-09-18 20:38:26">
*** same thing with no ! ***
>> Like.create!(article_id: 1, user_id: bob)
(0.1ms) begin transaction
SQL (7.9ms) INSERT INTO "likes" ("article_id", "created_at", "updated_at", "user_id") VALUES (?, ?, ?, ?) [["article_id", 1], ["created_at", Tue, 18 Sep 2012 20:43:05 UTC +00:00], ["updated_at", Tue, 18 Sep 2012 20:43:05 UTC +00:00], ["user_id", 1]]
(0.9ms) commit transaction
=> #<Like id: 3, user_id: 1, article_id: 1, created_at: "2012-09-18 20:43:05", updated_at: "2012-09-18 20:43:05">
*** same thing not going through the association ***
*** bob's id is 2, but the created object's user_id is 1 ***
>> Like.create!(article_id: 1, user_id: "foo")
(0.1ms) begin transaction
SQL (2.5ms) INSERT INTO "likes" ("article_id", "created_at", "updated_at", "user_id") VALUES (?, ?, ?, ?) [["article_id", 1], ["created_at", Tue, 18 Sep 2012 20:45:40 UTC +00:00], ["updated_at", Tue, 18 Sep 2012 20:45:40 UTC +00:00], ["user_id", 0]]
(0.8ms) commit transaction
=> #<Like id: 4, user_id: 0, article_id: 1, created_at: "2012-09-18 20:45:40", updated_at: "2012-09-18 20:45:40">
*** the "id" of a string is 0 ***
>> Like.create!(article_id: 1, user_id: :foo)
(0.1ms) begin transaction
SQL (0.6ms) INSERT INTO "likes" ("article_id", "created_at", "updated_at", "user_id") VALUES (?, ?, ?, ?) [["article_id", 1], ["created_at", Tue, 18 Sep 2012 20:45:56 UTC +00:00], ["updated_at", Tue, 18 Sep 2012 20:45:56 UTC +00:00], ["user_id", 1]]
(2.2ms) commit transaction
=> #<Like id: 5, user_id: 1, article_id: 1, created_at: "2012-09-18 20:45:56", updated_at: "2012-09-18 20:45:56">
*** the "id" of a symbol is 1 ***
>> Like.create!(article_id: 1, user_id: 9)
(0.1ms) begin transaction
SQL (0.5ms) INSERT INTO "likes" ("article_id", "created_at", "updated_at", "user_id") VALUES (?, ?, ?, ?) [["article_id", 1], ["created_at", Tue, 18 Sep 2012 20:46:15 UTC +00:00], ["updated_at", Tue, 18 Sep 2012 20:46:15 UTC +00:00], ["user_id", 9]]
(2.0ms) commit transaction
=> #<Like id: 6, user_id: 9, article_id: 1, created_at: "2012-09-18 20:46:15", updated_at: "2012-09-18 20:46:15">
*** this is fine since we're passing in an integer (even though there's no user 9) ***
>> bob.to_i
NoMethodError: undefined method `to_i' for #<User:0x007f82ca25de48>
...
>> bob.to_id
NoMethodError: undefined method `to_id' for #<User:0x007f82ca25de48>
...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment