Reputation: 1383
I have this model:
class Device < ActiveRecord::Base
has_many :events
def last_event
events.last
end
end
As you can see, I have a method to get the last event for the device. Now, elsewhere in the Device model I have this method:
def place
self.last_event.place
end
Now, if I don't have any records in Events for this Device I get an error "undefined method `place' for nil:NilClass".
And so I added:
def place
self.last_event.place if self.last_event.present?
end
And this pattern repeated itself throughout the app, I had to add "if self.last_event.present?" so it won't crash in other places too.
I am sure there must be a better way to handle this kind of thing without the need to check if last_event is present everywhere?
Any suggestions?
Thanks,
Upvotes: 1
Views: 103
Reputation: 5408
In that case you can use delegates
delegate :last, to: events, allow_nil: true, prefix: :event
delegate :place, to: event_last, allow_nil: true
Upvotes: 0
Reputation: 300
Another option would be to have the method return a blank object which would respond to calls:
class Device < ActiveRecord::Base
has_many :events
def last_event
events.last || Event.new
end
def place
self.last_event.place
end
end
2.0.0p247 :001 > d = Device.new
=> #<Device id: nil, name: nil, created_at: nil, updated_at: nil>
2.0.0p247 :002 > d.place
=> nil
2.0.0p247 :003 > d.last_event
=> #<Event id: nil, device_id: nil, created_at: nil, updated_at: nil, place: nil>
The idea is that if a method always returns an object of the expected type, you never have to worry about subsequent calls encountering a nil object. Of course, this could have other implications - such as the need to determine if you have a valid object or a new one, but this can be checked later with:
2.0.0p247 :005 > d.last_event.new_record?
=> true
Upvotes: 0
Reputation: 55718
The try
method (an addition of ActiveSupport) allows exactly that. If called on a nil
object, it will just return nil
too. Thus, both of the following lines are equivalent:
self.last_event.try(:place)
# equivalent to
self.last_event.place if self.last_event
Upvotes: 3