AnthonyGalli.com
AnthonyGalli.com

Reputation: 2866

before_save if attribute.present?

before_save :date_started_sets_deadline, if date_started.present?

I don't want this before_save to run if :date_started == nil. I've tried various versions of the above line so not sure if I have to change that or the method itself.

def date_started_sets_deadline
  if self.date_started > Date.tomorrow
    self.deadline = self.date_started
  end
end

I'm trying to avoid the error NoMethodError (undefined method '>' for nil:NilClass): app/models/challenge.rb:35:in 'date_started_sets_deadline' whenever a user tries to create a challenge without a date_started

Upvotes: 3

Views: 4732

Answers (4)

pgolm
pgolm

Reputation: 46

You can check the conditions and change the data inside the callback method.

before_save :date_started_sets_deadline

def date_started_sets_deadline
  if date_started.present? && self.date_started > Date.tomorrow
   self.deadline = self.date_started
  end
end

But i think it's a good idea to split this responsibilities. For example you can move the check inside another method.

before_save :set_deadline, if: :set_deadline?

def set_deadline?
  date_started.present? && date_started > Date.tomorrow
end

def set_deadline
  self.deadline = date_started
end

For simple conditions it's very hard to find good method names or it feels a little bit over engineered, so you can check it directly at callback definition with procs and lambdas. But you should use it carefully.

before_save :set_deadline, if: ->() { date_started.present? }

def set_deadline
  self.deadline = date_started if self.date_started > Date.tomorrow
end

Upvotes: 0

smathy
smathy

Reputation: 27961

Another answer mentions providing the symbol to be called, I would recommend creating your own method to be that symbol so that you can include all the conditions you're interested in, ie.

before_save :set_deadline, if: :starts_after_tomorrow?

def starts_after_tomorrow?
  date_started? && date_started > Date.tomorrow
end

def set_deadline
  self.deadline = date_started
end

I think that's much cleaner than having the logic repeated in your before_save and setter.

Upvotes: 1

Dharam Gollapudi
Dharam Gollapudi

Reputation: 6438

Change the before_save statement to the following:

before_save :date_started_sets_deadline, if: :date_started?

If you provide a symbol to the if, then rails evaluates it in the context of the instance. By adding ?, it is a auto generated method which is essentially same as date_started.present?.

Also, if the date_started is required within the date_started_sets_deadline implementation, I would also add the check explicitly instead of solely depending on adding the if condition on the callback logic.

def date_started_sets_deadline
  if self.date_started.present? && (self.date_started > Date.tomorrow)
    self.deadline = self.date_started
  end
end

Refer to Using :if and :unless with a Symbol for more info.

Upvotes: 5

Zaid Qureshi
Zaid Qureshi

Reputation: 1213

One way to do it will be putting a conditional if in side the method.

def date_started_sets_deadline
  if self.date_started != nil 
    if self.date_started > Date.tomorrow
     self.deadline = self.date_started
    end
  end
end

EDIT

Try this, I checked it on a personal project.

before_save :date_started_sets_deadline, if: self.date_started.present?

Upvotes: 1

Related Questions