bymannan
bymannan

Reputation: 1383

Rails how to improve if record exists?

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

Answers (3)

hawk
hawk

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

Joe
Joe

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

Holger Just
Holger Just

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

Related Questions