Forme is a HTML forms library for ruby with the following goals:
-
Have no external dependencies
-
Have a simple API
-
Support forms both with and without related objects
-
Allow compiling down to different types of output
Forme is designed to make creating HTML forms easier. Flexibility and simplicity are primary objectives. The basic usage involves creating a Forme::Form
instance, and calling input
and tag
methods to return html strings for widgets, but it could also be used for serializing to other formats, or even as a DSL for a GUI application. # In order to be flexible, Forme stores tags in abstract form until output is requested. There are two separate abstract forms that Forme uses. One is Forme::Input
, and the other is Forme::Tag
. Forme::Input
is a high level abstract form, while Forme::Tag
is a low level abstract form. # The difference between Forme::Input
and Forme::Tag
is that Forme::Tag
directly represents the underlying html tag, containing a type, optional attributes, and children, while the Forme::Input
is more abstract and attempts to be user friendly. For example, these both compile by default to the same select tag:
f.input(:select, :options=>[['foo', 1]]) # or f.tag(:select, {}, [f.tag(:option, {:value=>1}, ['foo'])])
# The processing of high level Forme::Input
s into raw html data is broken down to the following steps (called transformers): #
-
Formatter
: converts aForme::Input
instance into aForme::Tag
instance (or array of them). -
ErrorHandler
: If theForme::Input
instance has a error, takes the formatted tag and marks it as having the error. -
Labeler
: If theForme::Input
instance has a label, takes the formatted output and labels it. -
Wrapper
: Takes the output of the formatter, labeler, and error_handler transformers, and wraps it in another tag (or just returns it unmodified). -
Serializer
: converts aForme::Tag
instance into an html string.
# Technically, only the Serializer
is necessary. The input
and tag
methods return Input
and Tag
objects. These objects both have to_s
defined to call the appropriate Serializer
with themselves. The Serializer
calls the appropriate Formatter
if it encounters an Input
instance, and attempts to serialize the output of that (which is usually a Tag
instance). It is up to the Formatter
to call the Labeler
and/or ErrorHandler
(if necessary) and the Wrapper
.
There is also an InputsWrapper
transformer, that is called by Forme::Form#inputs
. It’s used to wrap up a group of related options (in a fieldset by default). # The Forme::Form
object takes the 6 transformers as options (:formatter, :labeler, :error_handler, :wrapper, :inputs_wrapper, and :serializer), all of which should be objects responding to call
(so you can use +Proc+s) or be symbols registered with the library using Forme.register_transformer
: #
Forme.register_transformer(:wrapper, :p){|t| t.tag(:p, {}, t)}
# Most of the transformers can be overridden on a per instance basis by passing the appopriate option to input
or inputs
: #
f.input(:name, :wrapper=>:p)
gem install forme
- Demo Site
- RDoc
- Source
- IRC
-
irc.freenode.net/forme
- Google Group
- Bug Tracker
Without an object, Forme is a simple form builder:
f = Forme::Form.new f.open(:action=>'/foo', :method=>:post) # '<form action="/foo" method="post">' f.input(:textarea, :value=>'foo', :name=>'bar') # '<textarea name="bar">foo</textarea>' f.input(:text, :value=>'foo', :name=>'bar') # '<input name="bar" type="text" value="foo"/>' f.close # '</form>'
With an object, Form#input
calls forme_input
on the obj with the form, field, and options, which should return a Forme::Input
or Forme::Tag
instance. Also, in Form#initialize
, forme_config
is called on object with the form if the object responds to it, allowing customization of the entire form based on the object.
f = Forme::Form.new(obj) f.input(:field) # '<input id="obj_field" name="obj[field]" type="text" value="foo"/>'
If the object doesn’t respond to forme_input
, it falls back to creating text fields with the name and id set to the field name and the value set by calling the given method on the object (or using #[] if the object is a hash).
f = Forme::Form.new([:foo]) f.input(:first) # '<input id="first" name="first" type="text" value="foo"/>'
Forme comes with a DSL:
Forme.form(:action=>'/foo') do |f| f.input(:text, :name=>'bar') f.tag(:fieldset) do f.input(:textarea, :name=>'baz') end f.button('Update') end # <form action="/foo"> # <input name="bar" type="text"/> # <fieldset> # <textarea name="baz"></textarea> # </fieldset> # <input type="submit" value="Update"/> # </form>
You can wrap up multiple inputs with the :inputs
method:
Forme.form(:action=>'/foo') do |f| f.inputs([[:text, {:name=>'bar'}], [:textarea, {:name=>'baz'}]]) end # <form action="/foo"> # <fieldset class="inputs"> # <input name="bar" type="text"/> # <textarea name="baz"></textarea> # </fieldset> # </form>
You can even do everything in a single method call:
Forme.form({:action=>'/foo'}, :inputs=>[[:text, {:name=>'bar'}], [:textarea, {:name=>'baz'}]])
As shown above, the general way to create Forme::Form instances is via the Forme.form method. This method takes up to 3 arguments, and yields the Forme::Form object to the block (if given). Here are the argument styles that you can use for Forme.form.
- No args
-
Creates a
Form
object with no options and not associated to anobj
, and with no attributes in the opening tag. - 1 hash arg
-
Treated as opening form tag attributes, creating a
Form
object with no options. - 1 non-hash arg
-
Treated as the
Form
‘sobj
, with empty options and no attributes in the opening tag. - 2 hash args
-
First hash is opening attributes, second hash is
Form
options. - 1 non-hash arg, 1-2 hash args
-
First argument is
Form
‘s obj, second is opening attributes, third if provided isForm
’s options.
Examples:
# No arguments Forme.form # 1 hash argument (attributes) Forme.form(:action=>'/foo') # 1 non-hash argument (a reference object used when building the form) Forme.form(Album[1]) # 2 hash arguments (attributes, and options) Forme.form({:action=>'/foo'}, :values=>params) # 1 non-hash argument, 1-2 hash arguments (ref obj, attributes, options) Forme.form(Album[1], :action=>'/foo') Forme.form(Album[1], {:action=>'/foo'}, :values=>params)
If you want a Forme::Form instance where the reference object is a Hash, then you need to pass the hash object using the :obj option:
Forme.form({:action=>'/foo'}, :obj=>{:foo=>'bar'})
You can also create Forme::Form objects the normal ruby way using Forme::Form#new. The difference between Forme::Form#new and Forme.form is that Forme.form includes the enclosing <form> tag, where Forme::Form#new does not. Because of this, Forme::Form does not accept a hash of <form> tag attributes, so it has the following API:
# No arguments Forme::Form.new # 1 hash argument Forme::Form.new(:values=>params) # 1 non-hash argument Forme::Form.new(Album[1]) # 1 non-hash argument, 1-2 hash arguments Forme::Form.new(Album[1], :values=>params)
In addition to the input and inputs methods discussed above, Forme::Form instances also support the following methods.
If you create a Form via Forme::Forme#new, you can use the form method to create a form tag:
Forme::Form.new.form(:action=>'/foo')
This is what Forme.form uses internally to create the <form> tag
This adds a tag to the form. If a block is given, yields to the block, and tags and inputs inside the block are placed inside the tag. The first argument is the type of tag to create, and the second argument if given should be a hash of tag attributes. This allows you to nest inputs inside tags:
Forme.form do |f| f.tag(:span, :class="foo") do f.input(:text) end end
Which results in a form similar to the following:
<form> <span class="foo"> <input type="text"/> </span> </form>
This adds a submit input to the form:
f.button # <input type="submit"/>
It can be called with a string to provide a value for the button:
f.button('Search') # <input type="submit" value="Search"/>
It can be called with a hash to provide options for the submit input:
f.button(:value=>'Search', :class=>'btn') # <input class="btn" type="submit" value="Search"/>
This requires a block, and modifies the Form’s options inside the block, restoring the options when the block returns:
f.input(:text) # <input type="text"/> f.with_opts(:wrapper=>:li) do f.input(:text) end # <li><input type="text"/></li>
This supports most options you can provide to the Form, but not all.
This uses with_opts to change the Form’s object temporarily, but it yields the object to the block, and also supports appending to the existing namespaces:
Forme.form([:foo], :namespace=>'a') do |f| f.input(:first) # <input id="a_first" name="a[first]" type="text" value="foo"/> f.with_obj(['foobar'], 'b') do |o| f.input(:first, :size=>o.first.size) # <input id="a_b_first" name="a[b][first]" size="6" type="text" value="foobar"/> end end
This allows you to provide an object-yielding enumerable. Forme will call with_obj with each object in the enumerable. It yields each object as well as the index of the object in the enumerable, and includes the index in the namespace:
objectlist = ['foobar', ['good']] Forme.form([:foo], :namespace=>'a') do |f| f.each_obj(objectlist, 'b') do |o, i| f.input(:first, :size=>10+i) end # <input id="a_b_0_first" name="a[b][0][first]" size="10" type="text" value="foobar"/> # <input id="a_b_1_first" name="a[b][1][first]" size="11" type="text" value="good"/> end
Forme ships with a Sequel plugin (use Sequel::Model.plugin :forme
to enable), that makes Sequel::Model instances support the forme_config
and forme_input
methods and return customized inputs.
The Sequel support handles inputs based on database columns, and automatically handles labels and errors.
Forme.form(Album[1], :action=>'/foo') do |f| f.input :name f.input :copies_sold end
This will create a form similar to:
<form action="/foo" method="post"> <label>Name: <input id="album_name" name="album[name]" type="text" value="Rising Force"/></label> <label>Copies Sold: <input id="album_copies_sold" name="album[copies_sold]" type="number" value="100000"/></label> </form>
The Forme Sequel plugin also integerates with Sequel’s validation reflection support with the validation_class_methods
plugin that ships with Sequel. It will add pattern
and maxlength
attributes based on the format, numericality, and length validations.
In addition to the default Forme options, the Sequel support includes, for specific column types, these additional options to the #input method: .
- :as
-
Can be set to :select, :radio, or :checkbox. :select will use a select input with three options, a blank option, a true option, and a false option. :radio will use two radio inputs, one for true and one for false. :checkbox will use a single checkbox input. By default, uses :select if NULL values are allowed and the option is not required, and :checkbox otherwise.
- :false_label
-
The value to use for the false label, ‘No’ by default.
- :false_value
-
The value to use for the false input, ‘f’ by default.
- :true_label
-
The value to use for the true label, ‘Yes’ by default.
- :true_value
-
The value to use for the true input, ‘t’ by default.
- :as
-
Can be set to :textarea to use a textarea input. You can use the usual attributes hash or a stylesheet to
control the size of the textarea.
The Sequel support also handles associations, allowing you to change which objects are associated to the current object.
Forme.form(Album[1], :action=>'/foo') do |f| f.input :name f.input :artist f.input :tags, :as=>:checkbox end
This will create a form similar to:
<form action="/foo" method="post"> <label>Name: <input id="album_name" name="album[name]" type="text" value="Blue Hawaii"/></label> <label>Artist: <select id="album_artist_id" name="album[artist_id]"> <option selected="selected" value="1">Elvis Presley</option> <option value="2">The Beatles</option> <option value="3">The Monkeys</option> </select></label> <span class="label">Tags: <label><input checked="checked" id="album_tag_pks_1" name="album[tag_pks][]" type="checkbox" value="1"/> Rock and Roll</label> <label><input id="album_tag_pks_2" name="album[tag_pks][]" type="checkbox" value="2"/> Blues</label> <label><input id="album_tag_pks_3" name="album[tag_pks][]" type="checkbox" value="3"/> Country</label> </span> </form>
For one_to_many and many_to_many associations, you will probably want to use the association_pks
plugin that ships with Sequel.
association input options:
- :as
-
For many_to_one associations, set to :radio to use a series of radio buttons instead a select input. For one_to_many and many_to_many associations, set to :checkbox to use a series of checkboxes instead of a multiple select input.
- :dataset
-
If a Dataset, uses the dataset to retrieve the options. If a Proc or Method, calls the proc or method with the default dataset, and should return a modified dataset to use.
- :options
-
Specify the options to use for the input(s), instead of querying the database.
- :name_method
-
If a String or Symbol, treats it as a method name and calls it on each object returned by the dataset to get the text to use for the option. If not given, tries the following method names in order: :forme_name, :name, :title, :number. If given and not a String or Symbol, a callable object is assumed, and the value is called with each object and should return the text to use for the option.
The Sequel support includes a method called subform, which can handle nested_attributes:
Forme.form(Album[1], :action=>'/foo') do |f| f.input :name f.subform :artist do f.input :name end f.subform :tracks do f.input :number f.input :name end end
This adds an input for editing the artist’s name after the album’s inputs, as well as inputs for editing the number and name for all of the tracks in the album, creating a form similar to:
<form action="/foo" method="post"> <label>Name: <input id="album_name" name="album[name]" type="text" value="Blue Hawaii"/></label> <input id="album_artist_attributes_id" name="album[artist_attributes][id]" type="hidden" value="1"/> <fieldset class="inputs"><legend>Artist</legend> <label>Name: <input id="album_artist_attributes_name" name="album[artist_attributes][name]" type="text" value="Elvis Presley"/></label> </fieldset> <input id="album_tracks_attributes_0_id" name="album[tracks_attributes][0][id]" type="hidden" value="1"/> <fieldset class="inputs"><legend>Track #1</legend> <label>Number: <input id="album_tracks_attributes_0_number" name="album[tracks_attributes][0][number]" type="number" value="1"/></label> <label>Name: <input id="album_tracks_attributes_0_name" name="album[tracks_attributes][0][name]" type="text" value="Blue Hawaii"/></label> </fieldset> <input id="album_tracks_attributes_1_id" name="album[tracks_attributes][1][id]" type="hidden" value="2"/> <fieldset class="inputs"><legend>Track #2</legend> <label>Number: <input id="album_tracks_attributes_1_number" name="album[tracks_attributes][1][number]" type="number" value="2"/></label> <label>Name: <input id="album_tracks_attributes_1_name" name="album[tracks_attributes][1][name]" type="text" value="Almost Always True"/></label> </fieldset> </form>
Note: blank lines added for clarity; they would not appear in the actual output
subform options:
- :inputs
-
Automatically call
inputs
with the given values. Using this, it is not required to pass a block to the method, though it will still work if you do. - :legend
-
Overrides the default :legend used (which is based on the association name). You can also use a proc as the value, which will called with each associated object (and the position in the associated object already for *_to_many associations), and should return the legend string to use for that object.
- :grid
-
Sets up a table with one row per associated object, and one column per field.
- :labels
-
When using the :grid option, override the labels that would be created via the :inputs option. If you are not providing an :inputs option or are using a block with additional inputs, you should specify this option.
Forme ships with a Sinatra extension that you can get by require "forme/sinatra"
and using helpers Forme::Sinatra::ERB
in your Sinatra::Base subclass. It allows you to use the following API in your Sinatra ERB forms:
<% form(@obj, :action=>'/foo') do |f| %> <%= f.input(:field) %> <% f.tag(:fieldset) do %> <%= f.input(:field_two) %> <% end %> <% end %>
This example is for ERB/Erubis. Other Sinatra template libraries work differently and do not support this integration.
Forme ships with a Rails extension that you can get by require "forme/rails"
and using helpers Forme::Rails::ERB
in your controller. If allows you to use the following API in your Rails forms:
<%= forme(@obj, :action=>'/foo') do |f| %> <%= f.input(:field) %> <%= f.tag(:fieldset) do %> <%= f.input(:field_two) %> <% end %> <% end %>
This has been tested on Rails 3.2-4.1, but should hopefully work on Rails 3.0+.
All of these have external dependencies:
-
Rails built-in helpers
-
Formtastic
-
simple_form
-
padrino-helpers
Forme’s DSL draws a lot of inspiration from both Formtastic and simple_form.
These are the types and options supported by Forme::Input objects, usually created via Forme::Form#input:
These options are supported by all of the input types:
- :attr
-
The attributes hash to use for the given tag, takes precedence over other options that set attributes.
- :autofocus
-
Set the autofocus attribute if true
- :class
-
A class to use. Unlike other options, this is combined with the classes set in the :attr hash.
- :data
-
A hash of data-* attributes for the resulting tag. Keys in this hash will have attributes created with data- prepended to the attribute name.
- :disabled
-
Set the disabled attribute if true
- :error
-
Set an error message, invoking the error_handler
- :error_handler
-
Set a custom error_handler, overriding the form’s default
- :id
-
The id attribute to use
- :key
-
The base to use for the name and id attributes, based on the current namespace for the form.
- :label
-
Set a label, invoking the labeler
- :labeler
-
Set a custom labeler, overriding the form’s default
- :name
-
The name attribute to use
- :placeholder
-
The placeholder attribute to use
- :required
-
Set the required attribute if true
- :value
-
The value attribute to use for input tags, the content of the textarea for textarea tags, or the selected option(s) for select tags.
- :wrapper
-
Set a custom wrapper, overriding the form’s default
Creates an input tag with type checkbox, as well as a hidden input tag. Options:
- :checked
-
Mark the checkbox as checked.
- :hidden_value
-
The value to use for the hidden input tag.
- :no_hidden
-
Don’t create a hidden input tag.
Creates an input tag with type radio. Options:
- :checked
-
Mark the radio button as checked.
By default, creates an input tag with type date. With the :as=>:select option, creates 3 select boxes, one for the year, month, and day. Options:
- :as
-
When value is :select, uses 3 select boxes.
By default, creates an input tag with type datetime. With the :as=>:select option, creates 6 select boxes, one for the year, month, day, hour, minute, and second. Options:
- :as
-
When value is :select, uses 6 select boxes.
Creates a select tag, containing option tags specified by the :options option. Options:
- :add_blank
-
Add a blank option if true. If the value is a string, use it as the text content of the blank option. The default value can be set with Forme.default_add_blank_prompt, and defaults to the empty string.
- :multiple
-
Creates a multiple select box.
- :options
-
an array of options used for creating option tags. If the :text_method and :value_method are not given and the entry is an array, uses the first entry of the array as the text of the option, and the second entry of the array as the value of the option.
- :selected
-
The value that should be selected. Any options that are equal to this value (or included in this value if a multiple select box), are set to selected.
- :text_method
-
If set, each entry in the array has this option called on it to get the text of the object.
- :value
-
Same as :selected, but has lower priority.
- :value_method
-
If set (and :text_method is set), each entry in the array has this method called on it to get the value of the option.
Creates a set of checkbox inputs all using the same name. Supports the same options as the select type, except that the :multiple option is assumed to be true.
Creates a set of radio buttons all using the same name. Supports the same options as the select type.
Creates a textarea tag. Options:
- :cols
-
The number of columns in the text area.
- :rows
-
The number of rows in the text area.
Creates an input tag with the given type. This makes it easy to use inputs such as text and password, as well as newer HTML5 inputs such as number or email. Options:
- :size
-
Uses the size attribute on the tag
- :maxlength
-
Use the maxlength attribute on the tag
These are the options supported by Forme::Form object, mostly used to set the defaults for Inputs created via the form:
- :config
-
The configuration to use, which automatically sets defaults for the transformers to use.
- :error_handler
-
Sets the default error_handler for the form’s inputs
- :formatter
-
Sets the default formatter for the form’s inputs
- :hidden_tags
-
Sets the hidden tags to automatically add to this form, see below.
- :input_defaults
-
Sets the default options for each input type. This should be a hash with input type keys, where the values are the hash of default options to use for the input type.
- :inputs_wrapper
-
Sets the default inputs_wrapper for the form
- :labeler
-
Sets the default labeler for the form’s inputs
- :namespace
-
Sets the default namespace(s) to use for the form. Namespacing will automatically create namespaced name and id attributes for inputs that use the :key option.
- :obj
-
Sets the default
obj
for the form’s inputs. - :serializer
-
Sets the serializer for the form
- :values
-
The values from a previous form submission, used to set default values for inputs when the inputs use the :key option.
- :wrapper
-
Sets the default wrapper for the form’s inputs
For forms created by Forme.form, the following options are supported:
- :inputs
-
An array of inputs to create inside the form, before yielding to the block.
- :button
-
A button to add to the form, after yielding to the block.
This should be an array, where elements are one of the following types:
- String, Array, Forme::Tag
-
Added directly as a child of the form tag.
- Hash
-
Adds a hidden tag for each entry, with keys as the name of the hidden tag and values as the value of the hidden tag.
- Proc
-
Will be called with the form tag object, and should return an instance of one of the handled types (or nil to not add a tag).
Internally, Forme builds an abstract syntax tree of objects that represent the form. The abstract syntax tree goes through a series of transformations that convert it from high level abstract forms to low level abstract forms and finally to strings. Here are the main classes used by the library:
Forme::Form
-
main object
Forme::Input
-
high level abstract tag (a single
Input
could represent a select box with a bunch of options) Forme::Tag
-
low level abstract tag representing an html tag (there would be a separate
Tag
for each option in a select box)
The group of objects that perform the transformations to the abstract syntax trees are known as transformers. Transformers use a functional style, and all use a call
-based API, so you can use a Proc
for any custom transformer.
serializer
-
tags input/tag, returns string
formatter
-
takes input, returns tag
error_handler
-
takes tag and input, returns version of tag with errors noted
labeler
-
takes tag and input, returns labeled version of tag
wrapper
-
takes tag and input, returns wrapped version of tag
inputs_wrapper
-
takes form, options hash, and block, wrapping block in a tag
The serializer
is the base of the transformations. It turns Tag
instances into strings. If it comes across an Input
, it calls the formatter
on the Input
to turn it into a Tag
, and then serializes that Tag
. The formatter
first converts the Input
to a Tag
, and then calls the error_handler
if the :error
option is set and the labeler
if the :label
option is set. Finally, it calls the wrapper
to wrap the resulting tag before returning it.
The inputs_wrapper
is called by Forme::Form#inputs
and serves to wrap a bunch of related inputs.
Forme ships with a bunch of built-in transformers that you can use:
- :default
-
returns HTML strings
- :html_usa
-
returns HTML strings, formats dates and times in American format without timezones
- :text
-
returns plain text strings
- :default
-
turns Inputs into Tags
- :disabled
-
disables all resulting input tags
- :readonly
-
uses
span
tags for most values, good for printable versions of forms
- :default
-
modifies tag to add an error class and adds a span with the error message
- :default
-
uses implicit labels, where the tag is a child of the label tag
- :explicit
-
uses explicit labels with the for attribute, where tag is a sibling of the label tag
Both of these respect the following options:
- :label_position
-
Can be set to :before or :after to place the label before or after the the input.
- :label_attr
-
A hash of attributes to use for the label tag
- :default
-
returns tag without wrapping
- :div
-
wraps tag in div tag
- :fieldset_ol
-
same as :li, but also sets
inputs_wrapper
to :fieldset_ol - :li
-
wraps tag in li tag
- :ol
-
same as :li, but also sets
inputs_wrapper
to :ol - :p
-
wraps tag in p tag
- :span
-
wraps tag in span tag
- :table
-
same as :trtd, but also sets
inputs_wrapper
to :table - :td
-
wraps tag in a td tag
- :tr
-
same as :td, but also sets
inputs_wrapper
to :tr - :trtd
-
wraps tag in a tr tag with a td for the label and a td for the tag, useful for lining up inputs with the :explicit labeler without CSS
All of these except for :default respect the following options:
- :wrapper_attr
-
A hash of attributes to use for the wrapping tag.
- :default
-
uses a fieldset to wrap inputs
- :div
-
uses a div tag to wrap inputs
- :fieldset_ol
-
use both a fieldset and an ol tag to wrap inputs
- :ol
-
uses an ol tag to wrap inputs, useful with :li wrapper
- :table
-
uses a table tag to wrap inputs, useful with :trtd wrapper
- :tr
-
uses a tr tag to wrap inputs, useful with :td wrapper
All of these support the following options:
- :attr
-
A hash of attributes to use for the wrapping tag.
The :default, :fieldset_ol, and :table inputs_wrappers support the following options:
- :legend
-
A text description for the inputs, using the legend tag for fieldsets and the caption tag for a table.
- :legend_attr
-
A hash of attributes for the legend/caption tag.
The :table inputs_wrapper also supports the following options:
- :labels
-
An array of labels, used to setup a row of table headers with the labels.
You can associate a group of transformers into a configuration. This allows you to specify a single :config option when creating a Form
and have it automatically set all the related transformers.
There are a few configurations supported by default:
- :default
-
All
default
transformers - :formtastic
-
fieldset_ol
inputs_wrapper,li
wrapper,explicit
labeler
You can register and use your own configurations easily:
Forme.register_config(:mine, :wrapper=>:li, :inputs_wrapper=>:ol, :serializer=>:html_usa) Forme::Form.new(:config=>:mine)
If you want to, you can base your configuration on an existing configuration:
Forme.register_config(:yours, :base=>:mine, :inputs_wrapper=>:fieldset_ol)
You can mark a configuration as the default using:
Forme.default_config = :mine
Jeremy Evans <[email protected]>