David S
David S

Reputation: 335

Why do I get "undefined method for nil:NilClass" on an object that clearly exists?

controller

def show
  @dailies = Daily.where(store_id: params[:store]).order(created_at: :asc).by_month_year(params[:month],params[:year])
  @services = Service.all.order(id: :asc)
end

view

<%= daily_data_for(params[:store], params[:month], date.day, params[:year], 'starting_safe') %>

helper

def daily_data_for(store, month, day, year, field)
  Daily.where(store_id: store, month: month, day: day, year: year).first().send(field)
end

I'm getting the error undefined method 'starting_safe' for nil:NilClass when trying to display ActiveRecord object attribute in the view. I'm a but confused as it shouldn't be nil, because if I remove .send(field) from the helper, I get #<Daily:0x00007fea597aad50>. So it's returning an object. So then I inspect the Activerecord Object in the view:

daily_data_for(params[:store], params[:month], date.day, params[:year], 'starting_safe').inspect

Which displays #<Daily id: 1030, store_id: 1, created_at: "2018-10-01 18:20:07", updated_at: "2018-10-01 18:20:07", starting_safe: 0.1e1, ending_safe: 0.1e1, total_in: 0.1e1, total_out: 0.1e1, sum: 0.1e1, starting_drawer: 0.1e1, ending_drawer: 0.1e1, over_short: 0.1e1, year: "2018", month: "10", day: "1">

It's getting the right record and returning it, but then I get undefined method of nil class when I try to access starting_safe.

Why am I unable to display starting_safe in the view? I'd like to be using the instance variable @dailies in the helper instead of querying the database again, but I get the same errors. I've tried in the console and everything works fine. I'm a bit lost as to what's going on here.

Upvotes: 3

Views: 4118

Answers (2)

lacostenycoder
lacostenycoder

Reputation: 11216

The error undefined method 'starting_safe' for nil:NilClass is specifically telling you that you can't call starting_safe on nil. Since your helper method calls a scope .where method, calling .first on that doesn't insure that it won't be an empty activerecord relation object. If it is empty, then calling .first on it will return nil. So you might wanna just use safe navigation to be safe.

def daily_data_for(store, month, day, year, field)
  Daily.where(store_id: store, month: month, day: day, year: year).first&.send(field)
end

Or if you happen to be on ruby version < 2.3 just use a .try

Daily.where(store_id: store, month: month, day: day, year: year).first.try(:send, field)
#try will only run if first is not nil

Upvotes: 0

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230336

I'm pretty sure that the record you inspect (Daily with id=1030) and the failing [non-existing] record are two different records. Here's how you can find out. Amend your method like this, temporarily:

def daily_data_for(store, month, day, year, field)
  daily = Daily.where(store_id: store, month: month, day: day, year: year).first
  raise "Daily not found for #{store.inspect}, #{month.inspect}, #{day.inspect}, #{year.inspect}" unless daily
  daily.send(field)
end

Now the error message should be much more revealing.

Upvotes: 2

Related Questions