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
endclass 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
endDelegated 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
endform = 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.planNested 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
endNested 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
endAuto 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
endBefore/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
endUsing in forms
Using main_model will delegate id, to_param, persisted? and new_record? to the model. Allowing you to use it in forms.
class SignUpForm
include MiniForm::Model
main_model :user
def initialize
@user = User.new(account: @account)
end
end<% form_for SignUpForm.new %>
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
endform = 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