public
Description: A Rails plugin to improve forms. Includes a better FormBuilder and DRYness for form fields.
Homepage: http://shadowfiend.posterous.com/
Clone URL: git://github.com/Shadowfiend/awesome_fields.git
commit  4b52e1516edd21bed7e1e0c0f175b9c642648bae
tree    56c973f7a0522f332ce9226ca17b12c1e0d72160
parent  3ad3dc49b65b70278176d6b4418aff485269a235
awesome_fields / README.rdoc
c3b39ff4 » shadowfiend 2008-10-19 Switched around README and ... 1 = Awesome Fields
2 Awesome fields is a plugin that adds an extension to the built-in Rails
3 FormBuilder in the form of a new method, <tt>FormBuilder#field</tt>. This method
4 magically determines what type of field to display for the corresponding method.
5 It also adds a new form builder, +LinedBuilder+, and related methods, such as
6 lined_form_for and lined_fields_for. Note that JS-related methods are not
7 provided, since generally speaking I don't think Rails's JS helpers should be
8 used (look into a library like LowPro instead).
9
10 == Overview of the +field+ method
11
12 So let's say you had a model MyModel with a migration that looked oddly like:
13 create_table(:my_model) do |t|
14 t.column 'magic', :string
15 t.column 'number', :integer
16 t.column 'hyperdate', :date
17 t.column 'supertime', :time
18 t.column 'incredidatetime', :datetime
19 end
20
21 Then let's say we did this bit of magic:
22 <% form_for :model, @model, :action => 'superplace' do |f| %>
23 <%= f.field :magic %>
24 <%= f.field :number %>
25 <%= f.field :hyperdate %>
26 <%= f.field :supertime %>
27 <%= f.field :incredidatetime %>
28 <% end %>
29 In this situation, you would get two plain text boxes, one date selector, one
30 time selector, and one datetime selector.
31
32 What if the `magic' field was really long (i.e., we generated a column with type
33 :text instead of :string)? Then we'd probably prefer a textarea. In this
34 situation, we pass the <tt>:long</tt> option to the +field+ method:
35 <%= f.field :magic, :long => true %>
36 Different field types may deal with this option differently, and some may ignore
37 it entirely.
38
39
40 == Overview of the +LinedBuilder+
41
42 +LinedBuilder+ is a Rails form builder that adds support for labels and pretty
43 error handling to forms. +LinedBuilder+ wraps each input element created by a
44 call to a field helper in a div of CSS class +form_line+. It also gives it a
45 label (by default, this contains the humanized name of the field) and, if an
46 error exists, it provides a nested div within the +form_line+ of CSS class
47 +field_error+ and the div's class is changed to
48 <tt>form_line with_errors</tt>. More details are below.
49
50 A short example now. Assume there are three fields for a model -- `number',
51 `date', and `name'. `name' is blank but should not be, and so has an error on
52 it. The ERB code:
53
54 <% lined_form_for @model do |f| %>
55 <%= f.field :name %>
56 <%= f.text_field :number %>
57 <%= f.field :date %>
58 <% end %>
59
60 Will yield the HTML output:
61
62 <form ...>
63 <div class="form_line with_errors">
64 <div class="field_error">Name should not be blank.</div>
65 <label for="model_name">Name:</label>
66 <input type="text" name="model[name]" id="model_name" value="" />
67 </div>
68 <div class="form_line">
69 <label for="model_number">Number:</label>
70 <input type="text" name="model[number]" id="model_number" />
71 </div>
72 <div class="form_line">
73 <label for="model_date">Date:</label>
74 <!-- A date selector. -->
75 </div>
76 </form>
77
78 See the section on +LinedBuilder+ below for more details.
79
80 == The +field+ Method
81
82 The field method automatically takes care of generating a field of the
83 appropriate type for you in a form. It even takes care of a lot of the magic of
84 collection fields. Some details on the inner workings follow. See also the
85 documentation for the +AwesomeFieldHelpers+ for more information.
86
87 === Field Generation
88 There are two steps to generating the field: first, we determine what type of
89 field to generate, then we generate the actual HTML for it.
90
91 There are two ways we use to find the type of field to generate. First, we run
92 the method itself and look for what the class of the object returned is. If we
93 get a nil result from the method, then we use the actual database type as our
94 determiner.
95
96 To generate the actual HTML, we call a helper method. For example, if we had a
97 string field, then we would call +string_field+. Since there are already methods
98 for most fields, we simply alias the existing methods to the relevant ones.
99 Sometimes, the mapping isn't one-to-one. For example, in the case of strings,
100 sometimes we'll need to make the field a text area and other times just a simple
101 text field (see the situation where we're using the <tt>:long</tt> option
102 above). In these cases, we can implement the actual method and have it dispatch
103 to the relevant helper methods.
104
105 What if you want to specialize the behavior in one of your builders? Just
106 override the relevant methods! Then there's the other situation -- one where
107 maybe you've serialized an object to the database in YAML form, and you want to
108 display a field for it. Since the returned value from the method will be of your
109 class, we may be unable to resolve that class to an actual field type. In this
110 case, you can either alias in your own helper or implement the relevant method
111 in your own builder. The pattern of the methods called is simply the class name
112 underscored (literally, the result of
113 <tt>@object.send(method).class.underscore</tt>) followed by +_field+. So Strings
114 go to +string_field+, Dates to +date_field+, and DateTimes to +date_time_field+.
115
116 == Collection Fields
117 Basic fields are all well and good, but it'd be nice to be able to automatically
118 generate select boxes based on collection fields, too, and have them behave as
807b48c2 » shadowfiend 2008-10-19 Fixed one last, old instanc... 119 expected. Awesome fields makes this easier to a certain extent, as well. First of
c3b39ff4 » shadowfiend 2008-10-19 Switched around README and ... 120 all, if a field returns a value that responds to the +collect+ method, then
121 the field is assumed to be a collection field (an exception is made for Strings,
122 which do respond to +collect+, but are handled as regular text fields).
123
124 Then, we use +collection_select+ to generate the appropriate field (actually, we
125 use +select+, because +collection_select+ doesn't allow us to easily indicate
126 the multiple objects that should be preselected). By default, the +value_method+
d70c5452 » Shadowfiend 2008-11-11 Fixed a note in the main RE... 127 used to determine the value for the collection options is +to_param+. If a
c3b39ff4 » shadowfiend 2008-10-19 Switched around README and ... 128 +text_method+ is not specified, then the +name+ or +to_s+ methods are used (they
129 are attempted in that order).
130
131 For +select+, we also need an original collection -- basically, a list of all of
132 the possible values -- to use with the list of selected values. If the objects
133 inside the collection are AR objects, then we take their class and issue a
f3ecbc2b » shadowfiend 2008-10-19 Added support for reflectin... 134 <tt>find(:all)</tt> on it. If there are no available objects, then we attempt to
135 reflect on the association for the given method to determine the right class.
136 For example, with this call:
c3b39ff4 » shadowfiend 2008-10-19 Switched around README and ... 137
138 f.field :collection_method
139
2ebb2d71 » shadowfiend 2008-10-19 Added the ability to specif... 140 If +collection_method+ returned a list of Post objects (or a single Post
141 object), then we issue a <tt>Post::find(:all)</tt> to determine the full
f3ecbc2b » shadowfiend 2008-10-19 Added support for reflectin... 142 collection (of selected and unselected values). If an empty list is returned, or
143 nil, then we look up the association for the object on +collection_method+ and
144 try to invoke a <tt>find(:all)</tt> on that class. This guessing can be
145 overridden by passing the <tt>:collection</tt> option with the desired
146 collection.
2ebb2d71 » shadowfiend 2008-10-19 Added the ability to specif... 147
148 Note also that the set of selected items (or the single selected item) can be
149 overridden by providing a collection or object to use instead through the
150 <tt>:selected</tt> option. If <tt>:collection</tt> is not provided, the class of
151 this object or of the first item in the list will be used to run the find
152 metnioned above.
c3b39ff4 » shadowfiend 2008-10-19 Switched around README and ... 153
154 You can override both the method used for display text and that used for the
155 option value by passing the +text_method+ and +value_method+ options to the
156 +field+ helper. Further options and HTML options are passed on to
157 +select+, as usual.
158
159 === What About Collections of Non-ActiveRecord Objects?
160 Unfortunately, this convenience comes at a slight price when it comes to
161 collections of non-AR objects. If you want to use +field+ with its collection
162 field magic and you've got, say, integers in the collection, then you'll have to
163 do a bit of extra work and specify both the value and text methods, like so:
164
165 f.field :numbers, :value_method => :to_int, :text_method => :to_int
166
167 In this case, since we're getting numbers out, we just use the +to_int+ method
168 for both the value and the text.
169
170 === Notes
171 Since Rails will typecast date and datetime fields to the Ruby +Time+ class,
172 these typically won't be recognized appropriately. Currently, the +time_field+
173 method is implemented to double-check with the database the type that needs to
174 be used. If you implement accessors for the relevant fields to return objects
175 of the appropriate type (see http://www.railsweenie.com/forums/1/topics/936 for
176 an example of how you could do this), then the appropriate methods will be
177 called without the extra level of indirection. Sadly, this specialization means
178 that any Time fields will always have their type checked twice.
179
180 Also, the presence of <tt>FormBuilder#field</tt> does NOT in any way mean the
181 other helpers are not available for use. If the decisions +field+ makes don't
182 float your boat, feel free to put in a direct call to the regular helpers. The
183 purpose of +field+ is to try and reduce the repetition (it's a already a string,
184 why say it again?), not to get in your way. If it's getting in your way, you
185 probably need a different solution.
186
187 == The +LinedBuilder+ Class
188
189 +LinedBuilder+ is meant to take the pain out of creating forms whose fields fit
190 on multiple lines and whose fields have labels and correlated errors. By
191 default, Rails's +FormBuilder+ doesn't do this. This builder provides those
192 capabilities transparently.
193
194 Because this builder is a self-contained class, the detailed documentation for
195 it is in the <tt>AwesomeFields::LinedBuilder</tt> documentation. Go get it!
196
197 == License and Such
198
199 +awesome_fields+ is Copyright (c) 2008 Antonio Salazar Cardozo, released under
200 the MIT license.