Search code examples
ruby-on-railsrubymodelrake-task

Calculation in model failing for rake task


I have a method/action/piece of code (I always get a bit lost on the terminology of rails) set up in my model to update the amount remaining in a budget when I input a new advert.

class Budget < ActiveRecord::Base
belongs_to :client
has_many :adverts
before_save :update_budget

validates :name, :amount, :client_id, presence: true

def update_budget
    self.amount_remaining = self.amount - self.amount_spent
end
end

This works fine when updating my budgets or entering a new advert. Everything works as it should. However, I have written a rake task to write a list of recurring budgets to the budget table (so I can schedule it monthly). However, when I run this task from the terminal it runs into an error.

The rake task.

desc "Recurring Updates"
task :recurring_budgets => :environment do
    @recurring_budgets = RecurringBudget.all
    @recurring_budgets.each do |recurring_budget|
        Budget.create(name: recurring_budget.name, amount: recurring_budget.amount, client_id: recurring_budget.client_id)
    end
end

The error.

$ rake recurring_budgets
rake aborted!
TypeError: nil can't be coerced into Fixnum
/Users/tomgamon/projects/adsman/app/models/budget.rb:9:in `-'
/Users/tomgamon/projects/adsman/app/models/budget.rb:9:in `update_budget'
/Users/tomgamon/projects/adsman/lib/tasks/recurring_budgets.rake:6:in `block (2 levels) in <top (required)>'
/Users/tomgamon/projects/adsman/lib/tasks/recurring_budgets.rake:5:in `block in <top (required)>'
Tasks: TOP => recurring_budgets
(See full trace by running task with --trace)

Worth noting is when I remove the method from my model, the task writes to the budgets table fine.

Anyone have any ideas?


Solution

  • This is due to you were trying to minus nil from amount

    update_budget called in before_save callback - and here you calculating amount_remaining with subtracting amount_spent from amount in your case amount_spent is nil, so when you try subtracting nil value from something then you will get this error - TypeError: nil can't be coerced into Fixnum

    There are two solutions for this -

    1 - if amount_spent is nil you have to use 0

    def update_budget
        self.amount_remaining = self.amount - (self.amount_spent || 0)
    end
    

    OR

    2 - you can set default values for amount_remaining, amountand amount_spent as 0(Zero) in migration.

    It will be better to set default values for such fields instead of checking nil values in code.