MiniForm
Helpers for dealing with form objects and nested forms.
Installation
Add this line to your application's Gemfile:
gem 'mini_form'
And then execute:
$ bundle
Or install it yourself as:
$ gem install mini_form
Usage
class ProductForm
include MiniForm::Model
attributes :id, :name, :price, :description
validates :name, :price, :description, presence: true
# called after successful validations in update
def perform
@id = ExternalService.create(attributes)
end
end
class ProductsController < ApplicationController
def create
@product = ProductForm.new
if @product.update(product_params)
redirect_to product_path(@product.id)
else
render :edit
end
end
private
def product_params
params.require(:product).permit(:name, :price, :description)
end
end
Delegated attributes
Attributes can be delegated to a sub object.
class SignUpForm
include MiniForm::Model
attr_reader :account, :user
attributes :name, :email, delegate: :user
attributes :company_name, :plan, delegate: :account
validates :name, :email, :company_name, :plan, presence: true
def initialize
@account = Account.new
@user = User.new account: @account
end
def perform
user.save!
account.save!
end
end
form = SignUpForm.new
form.name = 'name' # => form.user.name = 'name'
form.name # => form.user.name
form.plan = 'free' # => form.account.plan = 'free'
form.plan # => form.account.plan
Nested validator
mini_form/nested validator runs validations on the given model and copies errors to the form object.
class SignUpForm
include MiniForm::Model
attr_reader :account, :user
attributes :name, :email, delegate: :user
attributes :company_name, :plan, delegate: :account
validates :account, :user, 'mini_form/nested' => true
def initialize
@account = Account.new
@user = User.new account: @account
end
def perform
account.save!
user.save!
end
end
Nested models
Combines delegated attributes and nested validation into a single call.
class SignUpForm
include MiniForm::Model
model :user, attributes: %i(name email)
model :account, attributes: %i(company_name plan)
def initialize
@account = Account.new
@user = User.new account: @account
end
def perform
account.save!
user.save!
end
end
Auto saving nested models
Most of the time perform is just calling save!. We can avoid this by using model's save option.
class SignUpForm
include MiniForm::Model
model :user, attributes: %i(name email), save: true
model :account, attributes: %i(company_name plan), save: true
def initialize
@account = Account.new
@user = User.new account: @account
end
end
Before/after callbacks
class SignUpForm
include MiniForm::Model
# ... code
before_update :run_before_update
after_update :run_after_update
private
def run_before_update
# ...
end
def run_after_update
# ...
end
# alternatively you can overwrite "before_update"
def before_update
end
# alternatively you can overwrite "after_update"
def after_update
end
end
Delegating model attributes
class SignUpForm
include MiniForm::Model
model :user, attributes: %i(name email), read: %i(id)
def initialize
@user = User.new account: @account
end
end
form = SignUpForm.new
form.update! form_params
form.id # => delegates to `user.id`
form.id = 42 # => raises `NoMethodError`
Methods
| Method | Description |
|---|---|
| .model | Defines a sub object for the form |
| .attributes | Defines an attribute, it can delegate to sub object |
| .attribute_names | Returns list of attribute names |
| #initialize | Meant to be overwritten. By defaults calls `attributes=` |
| #attributes= | Sets values of all attributes |
| #attributes | Returns all attributes of the form |
| #update | Sets attributes, calls validations, saves models and `perform` |
| #update! | Calls `update`. If validation fails, it raises an error |
| #perform | Meant to be overwritten. Doesn't do anything by default |
| #before_update | Meant to be overwritten. |
| #after_update | Meant to be overwritten. |
| #before_assignment | Meant to be overwritten. |
| #after_assignment | Meant to be overwritten. |
| #transaction | If ActiveRecord is available, wraps `perform` in transaction. |
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Run the tests (
rake) - Create new Pull Request