I'm using Reform
gem. Let's say I have some form to which I pass two dates as two separate attributes in params. Now in form I want to instantiate another attribute (which is also existing model attribute) called interval
to calculate interval between these two dates and then save it to model.
# some_form.rb
class SomeForm < Reform::Form
property: :starts_on
property: :ends_on
property: :interval
end
What is the best way to populate interval based on incoming dates, validate it and save it on model?
I tried different things, from overriding validate
method and using populator
/prepopulator
options, but to no avail.
In best scenario, I would assume it would look like this:
class SomeForm < Reform::Form
property: :starts_on
property: :ends_on
property: :interval, populate: :calculate_interval
end
def calcute_interval
(starts_on..ends_on).count
end
populator
and prepopulator
assume that the relevant properties are present in the incoming data. In your case, interval
is not.
Do you want to validate the interval
value? If not, calculate interval
in an Operation
step and don't involve reform
.
If you want validation, your options are:
a populator
on either timestamp:
# some_form.rb
class SomeForm < Reform::Form
property :starts_on
property :ends_on, populator: ->(model:, fragment:, **) do
self.interval = fragment - starts_on
self.ends_on = fragment
end
property :interval, readable: false
validation do
required(:starts_on).filled(:time?)
required(:ends_on).filled(:time?)
required(:interval).filled(gt?: 2.hours) # or whatever value you like
end
end
a custom predicate+rule combo:
# some_form.rb
class SomeForm < Reform::Form
property :starts_on
property :ends_on
# NOTE: uses dry-validation v0.13 syntax
validation do
configure do
config.messages_file = "/path/to/your/error_messages.yml"
def gt?(interval, starts_on, ends_on)
ends_on - starts_on > interval
end
end
end
required(:starts_on).filled(:time?)
required(:ends_on).filled(:time?)
rule(ends_on: [:ends_on, :starts_on]) do |ends_on, starts_on|
ends_on.gt?(starts_on, 2.hours) # or whatever value you like
end
end
end
# error_messages.yml
en:
errors:
rules:
ends_on:
gt?: "must be greater than starts_on by %{interval} seconds"