Reputation: 55263
schema:
create_table "posts", force: true do |t|
t.string "title"
t.text "content"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
t.integer "total_stars"
t.integer "average_stars"
end
create_table "stars", force: true do |t|
t.integer "starable_id"
t.string "starable_type"
t.integer "user_id"
t.integer "number"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "stars", ["starable_id", "starable_type"], name: "index_stars_on_starable_id_and_starable_type"
create_table "users", force: true do |t|
t.string "name"
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
end
models:
class Post < ActiveRecord::Base
has_many :stars, :as => :starable, :dependent => :destroy
belongs_to :user
end
class Star < ActiveRecord::Base
before_create :add_to_total_stars
belongs_to :starable, :polymorphic => true
protected
def add_to_total_stars
if [Post].include?(starable.class)
self.starable.update_column(:total_stars, starable.total_stars + self.number)
end
end
end
class User < ActiveRecord::Base
has_many :posts, dependent: :destroy
has_many :votes, dependent: :destroy
end
So I tried creating a star in the Rails console like this:
post = Post.first
user = User.first
star = post.stars.build(number: 1)
star.user_id = user.id
And everything goes OK 'till here. But when I try to save it:
star.save
I get this error:
NoMethodError: undefined method
+' for nil:NilClass from /home/alex/rails/rating/app/models/star.rb:10:in
add_to_total_stars' from /home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activesupport-4.0.0/lib/active_support/callbacks.rb:377:in_run__956917800__create__callbacks' from /home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activesupport-4.0.0/lib/active_support/callbacks.rb:80:in
run_callbacks' from /home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-4.0.0/lib/active_record/callbacks.rb:303:increate_record' from /home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-4.0.0/lib/active_record/timestamp.rb:57:in
create_record' from /home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-4.0.0/lib/active_record/persistence.rb:466:increate_or_update' from /home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-4.0.0/lib/active_record/callbacks.rb:299:in
block in create_or_update' from /home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activesupport-4.0.0/lib/active_support/callbacks.rb:383:in_run__956917800__save__callbacks' from /home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activesupport-4.0.0/lib/active_support/callbacks.rb:80:in
run_callbacks'
What could be the cause?
(I'm using Rails 4)
Upvotes: 0
Views: 341
Reputation: 4737
It looks like the value of Post.first.total_stars
is nil. Going by your schema and model examples you allow null in the database and do not validate it's presence in ActiveRecord.
If it makes sense to default this value to 0, then you should set the default value in the schema.
So I would add the following to your schema:
create_table "posts", force: true do |t|
# ...
t.integer "total_stars", null: false, default: 0
end
And the following validation in your model:
class Post < ActiveRecord::Base
has_many :stars, :as => :starable, :dependent => :destroy
belongs_to :user
validates :total_stars, presence: true
end
As an aside, I would get rid of total_stars
altogether and let rails do this for you with the counter_cache
option instead. Here's the Railscast screencast to get you started.
Upvotes: 1
Reputation: 10630
you are getting that error because starable.total_stars
is nil in your callback method. you need to ensure that starable.total_stars
is set to 0 ro you can call to_i method on it (nil.to_i #=> 0
) to ensure that you have 0 if it is not initialized
Upvotes: 1