Reputation: 2485
Why do I get nil can't be coerced into BigDecimal
when I try to perform a calculation: here's the code:
model/drink.rb
class Drink < ActiveRecord::Base
belongs_to :menu
before_save :total_amount
def total_amount
self.total_amount = self.price * self.quantity
end
model/menu.rb
class Menu < ActiveRecord::Base
has_many :drinks, :dependent => :destroy
accepts_nested_attributes_for :drinks, :allow_destroy => true
#Validations
end
*Drink is the (nested)child model and Menu the parent model When I attempt to create a new drink the browser display following error message nil can't be coerced into BigDecimal app/models/drink.rb:7:in 'total-amount'
app/controllers/menus_controller.rb:47:in 'create'
app/controllers/menus_controller.rb:46:in 'create'
app/db/migration
class CreateDrinks < ActiveRecord::Migration
def change
create_table :drinks do |t|
t.string :name
t.decimal :quantity,:precision => 8, :scale => 2
t.decimal :price, :precision => 8, :scale => 2
t.decimal :vat, :precision => 8, :scale => 2
t.references :menu
t.timestamps
end
add_index :drinks, :menu_id
end
end
controllers/drinks_controller.rb
class DrinksController < ApplicationController
# GET /drinks
# GET /drinks.json
def index
@drinks = Drink.all
respond_to do |format|
format.html # index.html.erb
format.json { render :json => @drinks }
end
end
# GET /drinks/1
# GET /drinks/1.json
def show
@drink = Drink.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render :json => @drink }
end
end
# GET /drinks/new
# GET /drinks/new.json
def new
@drink = Drink.new
respond_to do |format|
format.html # new.html.erb
format.json { render :json => @drink }
end
end
# GET /drinks/1/edit
def edit
@drink = Drink.find(params[:id])
end
# POST /drinks
# POST /drinks.json
def create
@article = Drink.new(params[:drink])
respond_to do |format|
if @drink.save
format.html { redirect_to @drink, :notice => 'Drink was successfully created.' }
format.json { render :json => @drink, :status => :created, :location => @article }
else
format.html { render :action => "new" }
format.json { render :json => @drink.errors, :status => :unprocessable_entity }
end
end
end
# PUT /drinks/1
# PUT /drinks/1.json
def update
@drink = Drink.find(params[:id])
respond_to do |format|
if @drink.update_attributes(params[:drink])
format.html { redirect_to @drink, :notice => 'Drink was successfully updated.' }
format.json { head :ok }
else
format.html { render :action => "edit" }
format.json { render :json => @drink.errors, :status => :unprocessable_entity }
end
end
end
# DELETE /drinks/1
# DELETE /drinks/1.json
def destroy
@drink = Drink.find(params[:id])
@drink.destroy
respond_to do |format|
format.html { redirect_to drinks_url }
format.json { head :ok }
end
end
end
Please can anyone tell me what's wrong with the code?
Upvotes: 7
Views: 13934
Reputation: 12643
If you want nil to be evaluated as 0.0 then you can do something like this:
def total_amount
self.total_amount = self.price.to_s.to_d * self.quantity.to_s.to_d
end
Or explicitly check for nil
def total_amount
if self.price && self.quantity
self.total_amount = self.price * self.quantity
else
self.total_amount = "0.0".to_d
end
end
The problem is really that your record fields aren't set like you expect them to be. Do you need to use validations to make sure that the price
and quantity
fields are set?
class Drink
validates :price, :presence => true # Don't forget add DB validations, too :)
validates :quantity, :presence => true
end
That way you ensure that you don't get a nil value when calling #total_amount.
Upvotes: 21
Reputation: 434665
You're not setting the quantity
value anywhere so it is nil
and nil
cannot be coerced into a number for the multiplication.
Presumably your price
is a decimal(n,2)
(for some n
) in the database so self.price
is represented in Ruby as a BigDecimal object; that's why you're getting complaints about not being able to coerce to BigDecimal.
You can get a similar error out of irb
like this:
>> 11 * nil
TypeError: nil can't be coerced into Fixnum
from (irb):7:in `*'
Your self.price
is set though. If it wasn't then you'd get one of these:
NoMethodError: undefined method `*' for nil:NilClass
You might want to add
validates_presence_of :quantity, :price
(or possible stricter validations) to your model if you're requiring that they be set.
Upvotes: 2