Formed
Formed is the form object pattern you never knew you needed: uses ActiveModel under the hood, and supports associations just like ActiveRecord.
Contents
Usage
Formed form objects act just like the ActiveRecord models you started forcing into form helpers.
Basic form
class ProductForm < Formed::Base
acts_like_model :product
attribute :title
attribute :content, :text
end
With validations
Use all the validations your heart desires.
class PostForm < Formed::Base
acts_like_model :post
attribute :title
attribute :content, :text
validates :title, presence: true
validates :content, presence: true
end
Associations
Here's the big one:
class TicketForm < Formed::Base
acts_like_model :ticket
attribute :name
# automatically applies accepts_nested_attributes_for
has_many :ticket_prices, class_name: "TicketPriceForm"
validates :name, presence: true
end
class TicketPriceForm < Formed::Base
acts_like_model :ticket_price
attribute :price_in_cents, :integer
validates :price_in_cents, presence: true, numericality: { greater_than: 0 }
end
Context
Add context:
class OrganizationForm < Formed::Base
acts_like_model :organization
attribute :location_id, :integer
def location_id_options
context.organization.locations
end
end
form = OrganizationForm.new
form.with_context(organization: @organization)
Context gets passed down to all associations too.
class OrganizationForm < Formed::Base
acts_like_model :organization
has_many :users, class_name: "UserForm"
attribute :location_id, :integer
def location_id_options
context.organization.locations
end
end
form = OrganizationForm.new
form.with_context(organization: @organization)
user = form.users.new
user.context == form.context # true
Suggestions
Let forms be forms, not forms with actions
Forms should only know do one thing: represent a form and the form's state. Leave logic to its own.
If you use something like ActiveDuty, you could do this:
class MyCommand < ApplicationCommand
def initialize(form)
@form = form
end
def call
return broadcast(:invalid, form) unless @form.valid?
# ...
end
end
Contributing
By submitting a Pull Request, you disavow any rights or claims to any changes submitted to the Formed project and assign the copyright of those changes to joshmn.
If you cannot or do not want to reassign those rights (your employment contract for your employer may not allow this), you should not submit a PR. Open an issue and someone else can do the work.
This is a legal way of saying "If you submit a PR to us, that code becomes ours". 99.99% of the time that's what you intend anyways; we hope it doesn't scare you away from contributing.
Acknowledgements
This was heavily inspired by — and tries to be backwards compatible with — AndyPike's Rectify form pattern.