Bureaucrat
Form handling for Ruby inspired by Django forms.
Description
Bureaucrat is a library for handling the processing, validation and rendering of HTML forms.
Structure of a Form
Form ----> valid?, errors/cleaned_data
______|________
/ | \
Field Field Field ----> clean
| | |
Widget Widget Widget ----> render
Form:
Collection of named Fields, handles global validation and the last pass of
data conversion.
After validation, a valid Form responds to cleaned_data
by returning a
hash of validated values and an invalid Form responds to errors
by
returning a hash of field_name => error_messages.
Field: Handles the validation and data conversion of each field belonging to the Form. Each Field is associated to a name on the parent Form.
Widget: Handles the rendering of a Form field. Each Field has two widgets associated, one for normal rendering, and another for hidden inputs rendering. Every type of Field has default Widgets defined, but they can be overriden on a per-Form basis.
Usage examples
require 'bureaucrat'
require 'bureaucrat/quickfields'
class MyForm < Bureaucrat::Forms::Form
extend Bureaucrat::Quickfields
string :nickname, max_length: 50
string :realname, required: false
email :email
integer :age, min_value: 0
boolean :newsletter, required: false
# Note: Bureaucrat doesn't define save
def save
user = User.create!(cleaned_data)
Mailer.deliver_confirmation_mail(user)
user
end
end
# A Form initialized without parameters is an unbound Form.
unbound_form = MyForm.new
unbound_form.valid? # => false
unbound_form.errors # => {}
unbound_form.cleaned_data # => nil
puts unbound_form.as_p
# Prints:
# <p><label for="id_nickname">Nickname:</label> <input type="text" name="nickname" id="id_nickname" maxlength="50" /></p>
# <p><label for="id_realname">Realname:</label> <input type="text" name="realname" id="id_realname" /></p>
# <p><label for="id_email">Email:</label> <input type="text" name="email" id="id_email" /></p>
# <p><label for="id_age">Age:</label> <input type="text" name="age" id="id_age" /></p>
# <p><label for="id_newsletter">Newsletter:</label> <input type="checkbox" name="newsletter" id="id_newsletter" /></p>
invalid_bound_form = MyForm.new(nickname: 'bureaucrat', email: 'badformat', age: '30')
invalid_bound_form.valid? # => false
invalid_bound_form.errors # {email: ["Enter a valid e-mail address."]}
invalid_bound_form.cleaned_data # => nil
puts invalid_bound_form.as_table
# Prints:
# <tr><th><label for="id_nickname">Nickname:</label></th><td><input type="text" value="bureaucrat" name="nickname" id="id_nickname" maxlength="50" /></td></tr>
# <tr><th><label for="id_realname">Realname:</label></th><td><ul class="errorlist"><li>This field is required</li></ul><input type="text" name="realname" id="id_realname" /></td></tr>
# <tr><th><label for="id_email">Email:</label></th><td><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul><input type="text" value="badformat" name="email" id="id_email" /></td></tr>
# <tr><th><label for="id_age">Age:</label></th><td><input type="text" value="30" name="age" id="id_age" /></td></tr>
# <tr><th><label for="id_newsletter">Newsletter:</label></th><td><input type="checkbox" name="newsletter" id="id_newsletter" /></td></tr>
valid_bound_form = MyForm.new(nickname: 'bureaucrat', email: '[email protected]', age: '30')
valid_bound_form.valid? # => true
valid_bound_form.errors # {}
valid_bound_form.cleaned_data # => {age: 30, newsletter: false, nickname: "bureaucrat", realname: "", :email = >"[email protected]"}
puts valid_bound_form.as_ul
# Prints:
# <li><label for="id_nickname">Nickname:</label> <input type="text" value="bureaucrat" name="nickname" id="id_nickname" maxlength="50" /></li>
# <li><ul class="errorlist"><li>This field is required</li></ul><label for="id_realname">Realname:</label> <input type="text" name="realname" id="id_realname" /></li>
# <li><label for="id_email">Email:</label> <input type="text" value="[email protected]" name="email" id="id_email" /></li>
# <li><label for="id_age">Age:</label> <input type="text" value="30" name="age" id="id_age" /></li>
# <li><label for="id_newsletter">Newsletter:</label> <input type="checkbox" name="newsletter" id="id_newsletter" /></li>
valid_bound_form.save # A new User is created and a confirmation mail is delivered
Examples of different ways of defining forms
require 'bureaucrat'
require 'bureaucrat/quickfields'
class MyForm < Bureaucrat::Forms::Form
include Bureaucrat::Fields
field :nickname, CharField.new(max_length: 50)
field :realname, CharField.new(required: false)
field :email, EmailField.new
field :age, IntegerField.new(min_value: 0)
field :newsletter, BooleanField.new(required: false)
end
class MyFormQuick < Bureaucrat::Forms::Form
extend Bureaucrat::Quickfields
string :nickname, max_length: 50
string :realname, required: false
email :email
integer :age, min_value: 0
boolean :newsletter, required: false
end
def inline_form
f = Class.new(Bureaucrat::Forms::Form)
f.extend(Bureaucrat::Quickfields)
yield f
f
end
form_maker = inline_form do |f|
f.string :nickname, max_length: 50
f.string :realname, required: false
f.email :email
f.integer :age, min_value: 0
f.boolean :newsletter, required: false
end