How to use Reform to prepopulate for featured objects?
Try this:
challenges_controller
def new
@form = ChallengeForm.new(Challenge.new)
respond_modal_with @form, location: root_path
end
def create
challenge = Challenge.new(challenge_params)
@form = ChallengeForm.new(challenge)
if params[:step] == '2'
@form.validate(user_id: current_user.id)
@form.save
redirect_to challenge
end
end
challenges/new.html.erb<%= simple_form_for @form, html: { data: { modal: true } }, url: 'your_challenge_create_path', method: :post do |f| %>
<%= f.text_field :action, placeholder: 'Enter a Custom Challenge' %><br>
Or choose a featured challenge:
<%= f.collection_radio_buttons :action, [['Run a Mile','Run a Mile'], ['Drink 16oz of Water','Drink 16oz of Water'], ['Take a Picture','Take a Picture'], ['1 Drink Max','1 Drink Max'], ['See Eiffel Tower','See Eiffel Tower'], ['Write a Book','Write a Book'], ['Skydive','Skydive'], ['Start a Business','Start a Business'], ['No Snooze','No Snooze'], ['Visit All 50 States','Visit All 50 States'], ['Talk to a Stranger','Talk to a Stranger'], ['Try a New Recipe','Try a New Recipe'], ['Media-fast','Media-fast']], :first, :last %>
<%= f.submit %>
<% end %>
challenges/create.html.erb<%= simple_form_for @form, html: { data: { modal: true } }, url: 'your_challenge_create_path', method: :post do |f| %>
<%= hidden_field_tag :step, 2 %>
Challenge: <%= f.text_field :action %>
Do For: <%= f.number_field :days_challenged %>
Do On: <%= f.collection_check_boxes :committed %>
<% end %>
I might be a bit off, but you get the point? How to prepopulate _form for featured objects?
You can use simple_form
with reform
. Reform will give you form object, where you can override methods that will populate your form.
Here is a watered-down example (you will have to adjust it to your case):
class ChallengeForm < Reform::Form
property :action
property :committed
property :days_challenged
model :challenge
def commited
super || action_to_commited_hash[model.action]
end
def days_challenged
super || action_to_days_challenged_hash[model.action]
end
def action_to_days_challenged_hash
{
'Run a Mile' => 30,
'Take a Picture' => 12
}
end
def action_to_commited_hash
{
'Run a Mile' => ['Mon', 'Wed', 'Fri'],
'Take a Picture' => ['Tu', 'Thu']
}
end
end
super
in the methods above will delegate to the model
. Note that you are overriding getter
methods, and it doesn't affect setters
(you can override setters too if you wanted to change form data before writing it).In your template, instead of
form_for @challenge
you will have:simple_form_for @form
It's a super common form library for Rails and I can't imagine not using it myself! Populating single property in Reform
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
enda 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"
Prepopulate Reform with GET vars
To follow the docs on Trailblazer GitHub:
class AlbumsController
def new
@form = AlbumForm.new(Album.new)
end
should be:class AlbumsController
def new
@form = AlbumForm.new(Album.new(language: params[:lang]))
end
Reform gem: use one model for multiple forms
Accidentally I've found an answer. The actual problem was my module name was Address what is an exactly name of one of my models. And it made some conflict obviously. So I've changed Address to AddressModule.
And my working setup will be:
class Checkout < Reform::Form
extend ::ActiveModel::Callbacks
#...
property :billing_address, populate_if_empty: Address, form: BillingAddress
property :shipping_address, populate_if_empty: Address, form: ShippingAddress
#...
end
class BillingAddress < Reform::Form
extend ::ActiveModel::Callbacks
include AddressModule
end
class ShippingAddress < Reform::Form
extend ::ActiveModel::Callbacks
include AddressModule
end
module AddressModule
include Reform::Form::Module
property :firstname
property :lastname
property :address1
property :address2
property :phone
property :city
property :zipcode
property :country_id
property :billing_address_for_id
property :shipping_address_for_id
validates :firstname,
:lastname,
:address1,
:phone,
:city,
:zipcode,
:country_id,
presence: true
# provided by phony_rails gem
# validates phone number to be correct and plausible
# without country accordance
validates :phone, phony_plausible: { ignore_record_country_code: true }
# provided by validates_zipcode gem
# validates zipcode to be correct due to country alpha2 code
validates :zipcode, zipcode: { country_code: :country_code }
end
Validation of a set value with Reform
According to reform documentation field can be validated with inclusion:
validates :status, inclusion: { in: %w(open closed) }
undefined method `persisted?' with Reform and Virtus model
It seems like the comment from @fanta helped, but I the long-term answer is that you should avoid using Virtus, especially since you are building a new project. Virtus is no longer supported by it's own team, they moved on to dry-rb ( dry-types and dry-validations)
If you need to mock a model - you can use dry-struct, OpenStruct, etc
Also Reform now has full support for dry-validation and dry-types, and it is going to be the way of the future ( thou AM is going to be supported till version 4)
Best of luck :-)
Related Topics
Sinatra on Nginx Configuration - What's Wrong
Browsing Ruby Code a La Smalltalk
Is There a Command Line Framework for Ruby
Ruby: How to Escape Url with Square Brackets [ and ]
Hw Impossibility: "Create a Rock Paper Scissors Program in Ruby Without Using Conditionals"
How to Set in a Middleware a Variable Accessible in All My Application
Problem with Vim's Ruby Plugin
Waiting for Ruby Child Pid to Exit
Gem Ransack Doesn't Return Any Results When Searched with Full Name
Skipping: Touch Associations When Saving an Activerecord Object
Sorting Hash of Hashes by Value (And Return The Hash, Not an Array)
Fastest Way to Skip Lines While Parsing Files in Ruby
How to Restrict Markdown Syntax in Ruby
Crontab Not Running Ruby Script
Accessing Microsoft Exchange Server from Ruby