I've using a calendar gem that uses start_time
for the date (YYYY-MM-DD).
For example purposes I've got Meeting
, which has start_time
string and postponed
boolean attributes.
@meeting.start_time
must be at least one day in the future (the day after today), but you can't set a new start_time
(Meeting Date) if @meeting.postponed
, only if @meeting.postponed == false
Also, if @meeting.postponed
, cannot un postpone it without setting a new @meeting.start_time
(that is at least one day in the future).
I would like validations so when updating the Meeting (if @meeting
was previously postponed), I want to unselect the @meeting.postponed
checkbox and submit a new @meeting.start_date
in the form, and have it pass in the same submission.
if in the controller, something like:
@meeting.attributes = meeting_params
if @meeting.start_time != "" && @meeting.start_time != nil
if @meeting.start_time_changed?
if @meeting.posteponed
@error = "Can't change the Meeting Date if the Meeting is still postponed."
else
if @meeting.start_time.to_date <= Date.today
@error = "Meeting must be at least one day in the future."
end
end
else
if @meeting.postponed_changed? && @meeting.postponed == false
@error = "You can't un postpone the Meeting without setting a new Meeting Date."
end
end
else
@error = "Meeting Date cannot be blank."
end
How can I do this with validations?
UPDATE: As per max, this works for my specific scenario:
validates :start_time, presence: true
validate :start_time_must_be_in_future,
if: :start_time? # prevents a nil error
#unless: :postponed?,
validate :start_time_cannot_change,
if: :postponed?,
on: :update
validate :postponed_cannot_become_false_unless_a_new_start_time_is_submitted_that_is_not_in_the_past,
if: !:postponed? && :postponed_changed?,
on: :update
private
def start_time?
!start_time.nil? && start_time != ""
end
def start_time_must_be_in_future
errors.add(
:start_time,
"-- The date of the meeting cannot be in the past (must be either today or in the future)."
) if start_time.to_date < Date.today
end
def start_time_cannot_change
errors.add(
:start_time,
"-- Can't change the Meeing Date if the Event is still postponed."
) if start_time_changed? && start_time_must_be_in_future
end
def postponed_cannot_become_false_unless_a_new_start_time_is_submitted_that_is_not_in_the_past
errors.add(
:postponed,
"-- Can't un postpone a Meeting without setting a new Meeting Date that is either today or in the future. Please submit a new Meeting Date if you are updating the Meeting as no longer postponed."
) if postponed_changed? && !postponed? && !start_time_changed?
end
Instead of lumping all the buisness logic of your model into a single crazy tree of of conditional branches use individual validations for each aspect. I think you're actually overcomplicating it.
You can control when validations are fired with the on:
option and writing custom validators is as easy as creating a method and using validate :method_name
. If you want to create more complex validators create a validator class.
class Meeting
validates :start_time, presence: true
validate :start_time_must_be_in_future,
if: :start_time?, # prevents a nil error
unless: :postponed?,
on: :create
validate :start_time_cannot_change,
if: :postponed?
on: :update
private
def start_time?
!start_time.nil?
end
def start_time_must_be_in_future
errors.add(
:start_time,
"Meeting must be at least one day in the future."
) if start_time.to_date <= Date.today
end
def start_time_cannot_change
errors.add(
:start_time,
"Can't change the Meeting Date if the Meeting is still postponed."
) if will_save_change_to_start_date?
end
end
There are gems such as validates_timeliness that make temporal validations less onerous.