nofxx-formtastic 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.textile +51 -30
- data/generators/formtastic_stylesheets/templates/formtastic.css +51 -23
- data/lib/formtastic.rb +216 -72
- data/spec/formtastic_spec.rb +486 -146
- metadata +3 -3
data/README.textile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
h1. Formtastic
|
1
|
+
h1. Formtastic
|
2
2
|
|
3
3
|
Formtastic is a Rails FormBuilder DSL (with some other goodies) to make it far easier to create beautiful, semantically rich, syntactically awesome, readily stylable and wonderfully accessible HTML forms in your Rails applications.
|
4
4
|
|
@@ -89,8 +89,7 @@ And then add it as a dependency in your environment.rb file:
|
|
89
89
|
<pre>
|
90
90
|
config.gem "justinfrench-formtastic",
|
91
91
|
:lib => 'formtastic',
|
92
|
-
:source => 'http://gems.github.com'
|
93
|
-
:version => '0.1.4'
|
92
|
+
:source => 'http://gems.github.com'
|
94
93
|
</pre>
|
95
94
|
|
96
95
|
If you're a little more old school, install it as a plugin:
|
@@ -103,7 +102,7 @@ h2. Usage
|
|
103
102
|
|
104
103
|
Forms are really boring to code... you want to get onto the good stuff as fast as possible.
|
105
104
|
|
106
|
-
This renders a set of inputs (one for _most_ columns in the database table, and one for each ActiveRecord belongs_to
|
105
|
+
This renders a set of inputs (one for _most_ columns in the database table, and one for each ActiveRecord belongs_to association), followed by a submit button:
|
107
106
|
|
108
107
|
<pre>
|
109
108
|
<% semantic_form_for @user do |form| %>
|
@@ -138,7 +137,7 @@ If you want control over the input type Formtastic uses for each field, you can
|
|
138
137
|
<% end %>
|
139
138
|
</pre>
|
140
139
|
|
141
|
-
If you want to customize the label text, or render some hint text below the field, specify which fields are required/
|
140
|
+
If you want to customize the label text, or render some hint text below the field, specify which fields are required/optional, or break the form into two fieldsets, the DSL is pretty comprehensive:
|
142
141
|
|
143
142
|
<pre>
|
144
143
|
<% semantic_form_for @post do |form| %>
|
@@ -159,18 +158,6 @@ If you want to customize the label text, or render some hint text below the fiel
|
|
159
158
|
<% end %>
|
160
159
|
</pre>
|
161
160
|
|
162
|
-
If you want to customize html elements for any non button inputs and the li
|
163
|
-
wrapper you just need to specify the :input_html and :wrapper_html options hash.
|
164
|
-
|
165
|
-
<pre>
|
166
|
-
<% semantic_form_for @post do |form| %>
|
167
|
-
<%= form.input :title, :input_html => {:size => 60} %>
|
168
|
-
<%= form.input :body, :wrapper_html => { :class => 'body_wrapper' } %>
|
169
|
-
<%= form.input :created_at, :input_html => {:disabled => true} %>
|
170
|
-
<%= form.buttons %>
|
171
|
-
<% end %>
|
172
|
-
</pre>
|
173
|
-
|
174
161
|
To customize buttons, :button_html is available.
|
175
162
|
|
176
163
|
Nested forms (Rails 2.3) are also supported. You can do it in the Rails way:
|
@@ -178,29 +165,24 @@ Nested forms (Rails 2.3) are also supported. You can do it in the Rails way:
|
|
178
165
|
<pre>
|
179
166
|
<% semantic_form_for @post do |form| %>
|
180
167
|
<%= form.inputs :title, :body, :created_at %>
|
181
|
-
|
182
168
|
<% form.semantic_fields_for :author do |author| %>
|
183
169
|
<%= author.inputs :first_name, :last_name, :name => 'Author' %>
|
184
170
|
<% end %>
|
185
|
-
|
186
171
|
<%= form.buttons %>
|
187
172
|
<% end %>
|
188
173
|
</pre>
|
189
174
|
|
190
|
-
Or
|
175
|
+
Or the Formtastic way with the @:for@ option:
|
191
176
|
|
192
177
|
<pre>
|
193
178
|
<% semantic_form_for @post do |form| %>
|
194
179
|
<%= form.inputs :title, :body, :created_at %>
|
195
|
-
|
196
180
|
<%= form.inputs :first_name, :last_name, :for => :author, :name => "Author" %>
|
197
|
-
|
198
181
|
<%= form.buttons %>
|
199
182
|
<% end %>
|
200
183
|
</pre>
|
201
184
|
|
202
|
-
When working in has many association, you can even supply "%i" in your fieldset
|
203
|
-
name that it will be properly interpolated with the child index. For example:
|
185
|
+
When working in has many association, you can even supply "%i" in your fieldset name that it will be properly interpolated with the child index. For example:
|
204
186
|
|
205
187
|
<pre>
|
206
188
|
<% semantic_form_for @post do |form| %>
|
@@ -210,23 +192,60 @@ name that it will be properly interpolated with the child index. For example:
|
|
210
192
|
<% end %>
|
211
193
|
</pre>
|
212
194
|
|
213
|
-
|
214
|
-
|
195
|
+
|
196
|
+
Customize HTML attributes for any input using the @:input_html@ option. Typically his is used to disable the input, change the size of a text field, change the rows in a textarea, or even to add a special class to an input to attach special behavior like "autogrow":http://plugins.jquery.com/project/autogrow textareas:
|
197
|
+
|
198
|
+
<pre>
|
199
|
+
<% semantic_form_for @post do |form| %>
|
200
|
+
<%= form.input :title, :input_html => { :size => 60 } %>
|
201
|
+
<%= form.input :body, :input_html => { :class => 'autogrow' } %>
|
202
|
+
<%= form.input :created_at, :input_html => { :disabled => true } %>
|
203
|
+
<%= form.buttons %>
|
204
|
+
<% end %>
|
205
|
+
</pre>
|
206
|
+
|
207
|
+
The same can be done for buttons with the @:button_html@ option:
|
208
|
+
|
209
|
+
<pre>
|
210
|
+
<% semantic_form_for @post do |form| %>
|
211
|
+
...
|
212
|
+
<% form.buttons do %>
|
213
|
+
<%= form.commit_button :button_html => { :class => "primary" } %>
|
214
|
+
<% end %>
|
215
|
+
<% end %>
|
216
|
+
</pre>
|
217
|
+
|
218
|
+
Customize the HTML attributes for the @<li>@ wrapper around every input with the @:wrapper_html@ option hash. There's one special key in the hash (:class), which will actually _append_ your string of classes to the existing classes provided by Formtastic (like "required string error")
|
219
|
+
|
220
|
+
<pre>
|
221
|
+
<% semantic_form_for @post do |form| %>
|
222
|
+
<%= form.input :title, :wrapper_html => { :class => "important" } %>
|
223
|
+
<%= form.input :body %>
|
224
|
+
<%= form.input :description, :wrapper_html => { :style => "display:none;" } %>
|
225
|
+
...
|
226
|
+
<% end %>
|
227
|
+
</pre>
|
228
|
+
|
229
|
+
|
215
230
|
|
216
231
|
h2. The Available Inputs
|
217
232
|
|
218
233
|
* :select (a select menu) - default for ActiveRecord associations (belongs_to, has_many, has_and_belongs_to_many)
|
234
|
+
* :check_boxes (a set of check_box inputs) - alternative to :select has_many and has_and_belongs_to_many associations
|
219
235
|
* :radio (a set of radio inputs) - alternative to :select for ActiveRecord belongs_to associations
|
236
|
+
* :time_zone (a select input) - default for :string column types with 'time_zone' in the method name
|
220
237
|
* :password (a password input) - default for :string column types with 'password' in the method name
|
221
238
|
* :text (a textarea) - default for :text column types
|
222
239
|
* :date (a date select) - default for :date column types
|
223
240
|
* :datetime (a date and time select) - default for :datetime and :timestamp column types
|
224
241
|
* :time (a time select) - default for :time column types
|
225
242
|
* :boolean (a checkbox) - default for :boolean column types
|
226
|
-
* :boolean_select (a yes/no select box)
|
227
243
|
* :string (a text field) - default for :string column types
|
228
244
|
* :numeric (a text field, like string) - default for :integer, :float and :decimal column types
|
229
245
|
* :file (a file field) - default for paperclip or attachment_fu attributes
|
246
|
+
* :country (a select menu of country names) - default for :string columns named "country", requires a country_select plugin to be installed
|
247
|
+
* :hidden (a hidden field) - creates a hidden field (added for compatibility)
|
248
|
+
|
230
249
|
|
231
250
|
The documentation is pretty good for each of these (what it does, what the output is, what the options are, etc) so go check it out.
|
232
251
|
|
@@ -273,6 +292,9 @@ If you wish, put something like this in config/initializers/formtastic_config.rb
|
|
273
292
|
# the input, in the following order: hints, input and errors. You can
|
274
293
|
# customize it doing just as below:
|
275
294
|
Formtastic::SemanticFormBuilder.inline_order = [:hints, :input, :errors]
|
295
|
+
|
296
|
+
# Set the default "priority countries" to suit your user base when using :as => :country
|
297
|
+
Formtastic::SemanticFormBuilder.priority_countries = ["Australia", "New Zealand"]
|
276
298
|
</pre>
|
277
299
|
|
278
300
|
|
@@ -302,6 +324,7 @@ h2. Dependencies
|
|
302
324
|
There are none, but...
|
303
325
|
|
304
326
|
* if you have the "ValidationReflection":http://github.com/redinger/validation_reflection plugin is installed, you won't have to specify the :required option (it checks the validations on the model instead)
|
327
|
+
* if you want to use the :country input, you'll need to install the "iso-3166-country-select plugin":http://github.com/rails/iso-3166-country-select (or any other country_select plugin with the same API)
|
305
328
|
* rspec, rspec_hpricot_matchers and rcov gems (plus any of their own dependencies) are required for the test suite
|
306
329
|
|
307
330
|
|
@@ -331,10 +354,8 @@ A proof-of-concept (very much a work-in-progress) stylesheet is provided which y
|
|
331
354
|
|
332
355
|
h2. Contributors
|
333
356
|
|
334
|
-
Formtastic wouldn't be as awesome as it is today if it weren't for the wonderful contributions of these fine, fine coders.
|
357
|
+
Formtastic is maintained by "Justin French":http://justinfrench.com and "José Valim":http://github.com/josevalim, but it wouldn't be as awesome as it is today if it weren't for the wonderful contributions of these fine, fine coders.
|
335
358
|
|
336
|
-
* "Justin French":http://justinfrench.com
|
337
|
-
* "José Valim":http://github.com/josevalim
|
338
359
|
* "Jeff Smick":http://github.com/sprsquish
|
339
360
|
* "Tien Dung":http://github.com/tiendung
|
340
361
|
* "Mark Mansour":http://stateofflux.com
|
@@ -10,8 +10,10 @@ This stylesheet forms part of the Formtastic Rails Plugin
|
|
10
10
|
--------------------------------------------------------------------------------------------------*/
|
11
11
|
|
12
12
|
|
13
|
-
/* NORMALIZE AND RESET
|
13
|
+
/* NORMALIZE AND RESET
|
14
14
|
--------------------------------------------------------------------------------------------------*/
|
15
|
+
|
16
|
+
/* obviously inspired by Yahoo's reset.css, but scoped to just form.formtastic elements that we care about */
|
15
17
|
form.formtastic, form.formtastic ul, form.formtastic ol, form.formtastic li, form.formtastic fieldset, form.formtastic legend, form.formtastic input, form.formtastic textarea, form.formtastic select, form.formtastic p { margin:0; padding:0; }
|
16
18
|
form.formtastic fieldset { border:0; }
|
17
19
|
form.formtastic em, form.formtastic strong { font-style:normal; font-weight:normal; }
|
@@ -21,12 +23,16 @@ form.formtastic input, form.formtastic textarea, form.formtastic select { font-f
|
|
21
23
|
form.formtastic input, form.formtastic textarea, form.formtastic select { font-size:100%; }
|
22
24
|
form.formtastic legend { color:#000; }
|
23
25
|
|
26
|
+
/* Rails wraps error fields with divs */
|
27
|
+
div.fieldWithErrors { display:inline; margin:0; padding:0; }
|
28
|
+
|
24
29
|
|
25
30
|
/* FIELDSETS & LISTS
|
26
31
|
--------------------------------------------------------------------------------------------------*/
|
32
|
+
|
27
33
|
form.formtastic fieldset { }
|
28
|
-
form.formtastic fieldset.inputs { }
|
29
|
-
form.formtastic fieldset.buttons { padding
|
34
|
+
form.formtastic fieldset.inputs { border-top:1px solid #eee; }
|
35
|
+
form.formtastic fieldset.buttons { padding:10px; }
|
30
36
|
form.formtastic fieldset ol { }
|
31
37
|
|
32
38
|
/* clearfixing the fieldsets */
|
@@ -38,7 +44,8 @@ html[xmlns] form.formtastic fieldset { display: block; }
|
|
38
44
|
|
39
45
|
/* INPUT LIs
|
40
46
|
--------------------------------------------------------------------------------------------------*/
|
41
|
-
|
47
|
+
|
48
|
+
form.formtastic fieldset ol li { border-bottom:1px solid #eee; padding:5px; }
|
42
49
|
|
43
50
|
/* clearfixing the li's */
|
44
51
|
form.formtastic fieldset ol li { display: inline-block; }
|
@@ -53,12 +60,9 @@ form.formtastic fieldset ol li.error { }
|
|
53
60
|
|
54
61
|
/* LABELS
|
55
62
|
--------------------------------------------------------------------------------------------------*/
|
56
|
-
form.formtastic fieldset ol li label { display:block; width:25%; float:left; padding-top:.2em; }
|
57
|
-
form.formtastic fieldset ol li li label { line-height:100%; padding-top:0; }
|
58
|
-
form.formtastic fieldset ol li li label input { line-height:100%; vertical-align:middle; margin-top:-0.1em;}
|
59
63
|
|
60
64
|
|
61
|
-
/* NESTED FIELDSETS AND LEGENDS (radio and date/time inputs use nested fieldsets)
|
65
|
+
/* NESTED FIELDSETS AND LEGENDS (radio, check boxes and date/time inputs use nested fieldsets)
|
62
66
|
--------------------------------------------------------------------------------------------------*/
|
63
67
|
form.formtastic fieldset ol li fieldset { position:relative; }
|
64
68
|
form.formtastic fieldset ol li fieldset legend { position:absolute; width:25%; padding-top:0.1em; }
|
@@ -66,6 +70,7 @@ form.formtastic fieldset ol li fieldset legend span { position:absolute; }
|
|
66
70
|
form.formtastic fieldset ol li fieldset ol { float:left; width:74%; margin:0; padding:0 0 0 25%; }
|
67
71
|
form.formtastic fieldset ol li fieldset ol li { padding:0; border:0; }
|
68
72
|
|
73
|
+
|
69
74
|
/* INLINE HINTS
|
70
75
|
--------------------------------------------------------------------------------------------------*/
|
71
76
|
form.formtastic fieldset ol li p.inline-hints { color:#666; margin:0.5em 0 0 25%; }
|
@@ -73,42 +78,65 @@ form.formtastic fieldset ol li p.inline-hints { color:#666; margin:0.5em 0 0 25%
|
|
73
78
|
|
74
79
|
/* INLINE ERRORS
|
75
80
|
--------------------------------------------------------------------------------------------------*/
|
76
|
-
form.formtastic fieldset ol li p.inline-errors { color
|
77
|
-
form.formtastic fieldset ol li ul.errors { color
|
81
|
+
form.formtastic fieldset ol li p.inline-errors { color:red; margin:0.5em 0 0 25%; }
|
82
|
+
form.formtastic fieldset ol li ul.errors { color:red; margin:0.5em 0 0 25%; list-style:square; }
|
78
83
|
form.formtastic fieldset ol li ul.errors li { padding:0; border:none; display:list-item; }
|
79
84
|
|
80
|
-
|
81
85
|
/* STRING & NUMERIC OVERRIDES
|
82
86
|
--------------------------------------------------------------------------------------------------*/
|
83
87
|
form.formtastic fieldset ol li.string input { width:74%; }
|
84
88
|
form.formtastic fieldset ol li.numeric input { width:74%; }
|
85
89
|
|
86
|
-
|
87
90
|
/* TEXTAREA OVERRIDES
|
88
91
|
--------------------------------------------------------------------------------------------------*/
|
89
92
|
form.formtastic fieldset ol li.text textarea { width:74%; }
|
90
|
-
|
91
|
-
|
92
|
-
/* CHECKBOX OVERRIDES
|
93
|
-
--------------------------------------------------------------------------------------------------*/
|
94
93
|
form.formtastic fieldset ol li.boolean label { padding-left:25%; width:auto; }
|
95
94
|
form.formtastic fieldset ol li.boolean label input { margin:0 0.5em 0 0.2em; }
|
96
95
|
|
97
96
|
|
98
|
-
/* RADIO OVERRIDES
|
97
|
+
/* BELONGS_TO (RADIO) OVERRIDES
|
99
98
|
--------------------------------------------------------------------------------------------------*/
|
100
|
-
form.formtastic fieldset ol li.radio { }
|
101
|
-
form.formtastic fieldset ol li.radio fieldset
|
102
|
-
form.formtastic fieldset ol li.radio fieldset
|
99
|
+
form.formtastic fieldset ol li.radio fieldset { }
|
100
|
+
form.formtastic fieldset ol li.radio fieldset legend { display:block; float:left; width:25%; position:relative; }
|
101
|
+
form.formtastic fieldset ol li.radio fieldset legend span { }
|
102
|
+
form.formtastic fieldset ol li.radio fieldset ol { float:left; width:74%; }
|
103
|
+
form.formtastic fieldset ol li.radio fieldset ol li { padding:0; border:0; margin-bottom:.2em; }
|
103
104
|
form.formtastic fieldset ol li.radio fieldset ol li label { float:none; width:100%; }
|
104
|
-
form.formtastic fieldset ol li.radio fieldset ol li label input { margin-right:0.
|
105
|
+
form.formtastic fieldset ol li.radio fieldset ol li label input { margin-right:0.5em; }
|
106
|
+
|
107
|
+
|
108
|
+
/* CHECK BOXES (COLLECTION) OVERRIDES
|
109
|
+
--------------------------------------------------------------------------------------------------*/
|
110
|
+
form.formtastic fieldset ol li.check_boxes { }
|
111
|
+
form.formtastic fieldset ol li.check_boxes fieldset ol { margin-bottom:-0.6em; }
|
112
|
+
form.formtastic fieldset ol li.check_boxes fieldset ol li { margin:0.1em 0 0.5em 0; }
|
113
|
+
form.formtastic fieldset ol li.check_boxes fieldset ol li label { float:none; width:100%; }
|
114
|
+
form.formtastic fieldset ol li.check_boxes fieldset ol li label input { margin-right:0.2em; }
|
115
|
+
|
105
116
|
|
106
117
|
|
107
118
|
/* DATE & TIME OVERRIDES
|
108
119
|
--------------------------------------------------------------------------------------------------*/
|
120
|
+
|
121
|
+
form.formtastic fieldset ol li.date fieldset,
|
122
|
+
form.formtastic fieldset ol li.time fieldset,
|
123
|
+
form.formtastic fieldset ol li.datetime fieldset {}
|
124
|
+
|
125
|
+
form.formtastic fieldset ol li.date fieldset legend,
|
126
|
+
form.formtastic fieldset ol li.time fieldset legend,
|
127
|
+
form.formtastic fieldset ol li.datetime fieldset legend { display:block; float:left; width:25%; position:relative; }
|
128
|
+
|
129
|
+
form.formtastic fieldset ol li.date fieldset legend span,
|
130
|
+
form.formtastic fieldset ol li.time fieldset legend span,
|
131
|
+
form.formtastic fieldset ol li.datetime fieldset legend span { }
|
132
|
+
|
133
|
+
form.formtastic fieldset ol li.date fieldset ol,
|
134
|
+
form.formtastic fieldset ol li.time fieldset ol,
|
135
|
+
form.formtastic fieldset ol li.datetime fieldset ol { float:left; width:74%; margin:0; padding:0; }
|
136
|
+
|
109
137
|
form.formtastic fieldset ol li.date fieldset ol li,
|
110
138
|
form.formtastic fieldset ol li.time fieldset ol li,
|
111
|
-
form.formtastic fieldset ol li.datetime fieldset ol li { float:left; width:auto; margin:0 .3em 0 0; }
|
139
|
+
form.formtastic fieldset ol li.datetime fieldset ol li { float:left; width:auto; margin:0 .3em 0 0; padding:0; border:0; }
|
112
140
|
|
113
141
|
form.formtastic fieldset ol li.date fieldset ol li label,
|
114
142
|
form.formtastic fieldset ol li.time fieldset ol li label,
|
@@ -116,4 +144,4 @@ form.formtastic fieldset ol li.datetime fieldset ol li label { display:none; }
|
|
116
144
|
|
117
145
|
form.formtastic fieldset ol li.date fieldset ol li label input,
|
118
146
|
form.formtastic fieldset ol li.time fieldset ol li label input,
|
119
|
-
form.formtastic fieldset ol li.datetime fieldset ol li label input { display:inline; margin:0; padding:0; }
|
147
|
+
form.formtastic fieldset ol li.datetime fieldset ol li label input { display:inline; margin:0; padding:0; }
|
data/lib/formtastic.rb
CHANGED
@@ -18,13 +18,13 @@ module Formtastic #:nodoc:
|
|
18
18
|
@@collection_label_methods = %w[to_label display_name full_name name title username login value to_s]
|
19
19
|
@@inline_order = [ :input, :hints, :errors ]
|
20
20
|
@@file_methods = [ :file?, :public_filename ]
|
21
|
+
@@priority_countries = ["Australia", "Canada", "United Kingdom", "United States"]
|
21
22
|
|
22
23
|
cattr_accessor :default_text_field_size, :all_fields_required_by_default, :required_string,
|
23
24
|
:optional_string, :inline_errors, :label_str_method, :collection_label_methods,
|
24
|
-
:inline_order, :file_methods
|
25
|
+
:inline_order, :file_methods, :priority_countries
|
25
26
|
|
26
27
|
# Keeps simple mappings in a hash
|
27
|
-
#
|
28
28
|
INPUT_MAPPINGS = {
|
29
29
|
:string => :text_field,
|
30
30
|
:password => :password_field,
|
@@ -42,7 +42,7 @@ module Formtastic #:nodoc:
|
|
42
42
|
# Options:
|
43
43
|
#
|
44
44
|
# * :as - override the input type (eg force a :string to render as a :password field)
|
45
|
-
# * :label - use something other than the method name as the label
|
45
|
+
# * :label - use something other than the method name as the label text, when false no label is printed
|
46
46
|
# * :required - specify if the column is required (true) or not (false)
|
47
47
|
# * :hint - provide some text to hint or help the user provide the correct information for a field
|
48
48
|
# * :input_html - provide options that will be passed down to the generated input
|
@@ -55,8 +55,9 @@ module Formtastic #:nodoc:
|
|
55
55
|
# columns all map to a single numeric_input, for example).
|
56
56
|
#
|
57
57
|
# * :select (a select menu for associations) - default to association names
|
58
|
+
# * :check_boxes (a set of check_box inputs for associations) - alternative to :select has_many and has_and_belongs_to_many associations
|
59
|
+
# * :radio (a set of radio inputs for associations) - alternative to :select belongs_to associations
|
58
60
|
# * :time_zone (a select menu with time zones)
|
59
|
-
# * :radio (a set of radio inputs for associations) - default to association names
|
60
61
|
# * :password (a password input) - default for :string column types with 'password' in the method name
|
61
62
|
# * :text (a textarea) - default for :text column types
|
62
63
|
# * :date (a date select) - default for :date column types
|
@@ -65,6 +66,8 @@ module Formtastic #:nodoc:
|
|
65
66
|
# * :boolean (a checkbox) - default for :boolean column types (you can also have booleans as :select and :radio)
|
66
67
|
# * :string (a text field) - default for :string column types
|
67
68
|
# * :numeric (a text field, like string) - default for :integer, :float and :decimal column types
|
69
|
+
# * :country (a select menu of country names) - requires a country_select plugin to be installed
|
70
|
+
# * :hidden (a hidden field) - creates a hidden field (added for compatibility)
|
68
71
|
#
|
69
72
|
# Example:
|
70
73
|
#
|
@@ -78,7 +81,7 @@ module Formtastic #:nodoc:
|
|
78
81
|
# <% end %>
|
79
82
|
#
|
80
83
|
def input(method, options = {})
|
81
|
-
options[:required] = method_required?(method
|
84
|
+
options[:required] = method_required?(method) unless options.key?(:required)
|
82
85
|
options[:as] ||= default_input_type(method)
|
83
86
|
|
84
87
|
html_class = [ options[:as], (options[:required] ? :required : :optional) ]
|
@@ -308,6 +311,65 @@ module Formtastic #:nodoc:
|
|
308
311
|
fields_for(record_or_name_or_array, *args, &block)
|
309
312
|
end
|
310
313
|
|
314
|
+
# Generates the label for the input. It also accepts the same arguments as
|
315
|
+
# Rails label method. It has three options that are not supported by Rails
|
316
|
+
# label method:
|
317
|
+
#
|
318
|
+
# * :required - Appends an abbr tag if :required is true
|
319
|
+
# * :label - An alternative form to give the label content. Whenever label
|
320
|
+
# is false, a blank string is returned.
|
321
|
+
# * :as_span - When true returns a span tag with class label instead of a label element
|
322
|
+
#
|
323
|
+
# == Examples
|
324
|
+
#
|
325
|
+
# f.label :title # like in rails, except that it searches the label on I18n API too
|
326
|
+
#
|
327
|
+
# f.label :title, "Your post title"
|
328
|
+
# f.label :title, :label => "Your post title" # Added for formtastic API
|
329
|
+
#
|
330
|
+
# f.label :title, :required => true # Returns <label>Title<abbr title="required">*</abbr></label>
|
331
|
+
#
|
332
|
+
def label(method, options_or_text=nil, options=nil)
|
333
|
+
if options_or_text.is_a?(Hash)
|
334
|
+
return if options_or_text[:label] == false
|
335
|
+
|
336
|
+
options = options_or_text
|
337
|
+
text = options.delete(:label)
|
338
|
+
else
|
339
|
+
text = options_or_text
|
340
|
+
options ||= {}
|
341
|
+
end
|
342
|
+
|
343
|
+
text ||= humanized_attribute_name(method)
|
344
|
+
text << required_or_optional_string(options.delete(:required))
|
345
|
+
|
346
|
+
if options.delete(:as_span)
|
347
|
+
options[:class] ||= 'label'
|
348
|
+
template.content_tag(:span, text, options)
|
349
|
+
else
|
350
|
+
super(method, text, options)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
# Generates error messages for the given method. Errors can be shown as list
|
355
|
+
# or as sentence. If :none is set, no error is shown.
|
356
|
+
#
|
357
|
+
# This method is also aliased as errors_on, so you can call on your custom
|
358
|
+
# inputs as well:
|
359
|
+
#
|
360
|
+
# semantic_form_for :post do |f|
|
361
|
+
# f.text_field(:body)
|
362
|
+
# f.errors_on(:body)
|
363
|
+
# end
|
364
|
+
#
|
365
|
+
def inline_errors_for(method, options=nil) #:nodoc:
|
366
|
+
return nil unless @object && @object.respond_to?(:errors) && [:sentence, :list].include?(@@inline_errors)
|
367
|
+
|
368
|
+
errors = @object.errors.on(method.to_s)
|
369
|
+
send("error_#{@@inline_errors}", Array(errors)) unless errors.blank?
|
370
|
+
end
|
371
|
+
alias :errors_on :inline_errors_for
|
372
|
+
|
311
373
|
#
|
312
374
|
# Stolen from Attribute_fu (http://github.com/giraffesoft/attribute_fu)
|
313
375
|
# Rails 2.3 Patches from http://github.com/odadata/attribute_fu
|
@@ -454,9 +516,7 @@ module Formtastic #:nodoc:
|
|
454
516
|
# * if the :required option isn't provided, and the plugin isn't available, the value of the
|
455
517
|
# configuration option @@all_fields_required_by_default is used.
|
456
518
|
#
|
457
|
-
def method_required?(attribute
|
458
|
-
return required_option unless required_option.nil?
|
459
|
-
|
519
|
+
def method_required?(attribute) #:nodoc:
|
460
520
|
if @object && @object.class.respond_to?(:reflect_on_all_validations)
|
461
521
|
attribute_sym = attribute.to_s.sub(/_id$/, '').to_sym
|
462
522
|
|
@@ -472,13 +532,24 @@ module Formtastic #:nodoc:
|
|
472
532
|
# :textarea and :numeric). :select, :radio, :boolean and :datetime inputs
|
473
533
|
# are not handled by this method, since they need more detailed approach.
|
474
534
|
#
|
475
|
-
# If input_html is given as option, it's passed down to the input.
|
535
|
+
# If input_html is given as option, it's passed down to the input. The :size option is
|
536
|
+
# also passed in since it's so common to want to customize it.
|
476
537
|
#
|
477
538
|
def input_simple(type, method, options)
|
478
539
|
html_options = options.delete(:input_html) || {}
|
479
540
|
html_options = default_string_options(method).merge(html_options) if STRING_MAPPINGS.include?(type)
|
541
|
+
html_options[:size] = options.delete(:size) if options.has_key?(:size)
|
542
|
+
|
543
|
+
self.label(method, options.slice(:label, :required)) +
|
544
|
+
self.send(INPUT_MAPPINGS[type], method, html_options)
|
545
|
+
end
|
480
546
|
|
481
|
-
|
547
|
+
# Outputs a hidden field inside the wrapper, which should be hidden with CSS.
|
548
|
+
# Additionals options can be given and will be sent straight to hidden input
|
549
|
+
# element.
|
550
|
+
#
|
551
|
+
def hidden_input(method, options)
|
552
|
+
self.hidden_field(method, set_options(options))
|
482
553
|
end
|
483
554
|
|
484
555
|
# Outputs a label and a select box containing options from the parent
|
@@ -570,7 +641,7 @@ module Formtastic #:nodoc:
|
|
570
641
|
end
|
571
642
|
|
572
643
|
input_name = generate_association_input_name(method)
|
573
|
-
|
644
|
+
self.label(input_name, options.slice(:label, :required)) +
|
574
645
|
self.select(input_name, collection, set_options(options), html_options)
|
575
646
|
end
|
576
647
|
alias :boolean_select_input :select_input
|
@@ -585,7 +656,7 @@ module Formtastic #:nodoc:
|
|
585
656
|
def time_zone_input(method, options)
|
586
657
|
html_options = options.delete(:input_html) || {}
|
587
658
|
|
588
|
-
|
659
|
+
self.label(method, options.slice(:label, :required)) +
|
589
660
|
self.time_zone_select(method, options.delete(:priority_zones), set_options(options), html_options)
|
590
661
|
end
|
591
662
|
|
@@ -614,7 +685,7 @@ module Formtastic #:nodoc:
|
|
614
685
|
# You can customize the options available in the set by passing in a collection (Array) of
|
615
686
|
# ActiveRecord objects through the :collection option. If not provided, the choices are found
|
616
687
|
# by inferring the parent's class name from the method name and simply calling find(:all) on
|
617
|
-
# it (
|
688
|
+
# it (Author.find(:all) in the example above).
|
618
689
|
#
|
619
690
|
# Examples:
|
620
691
|
#
|
@@ -732,6 +803,7 @@ module Formtastic #:nodoc:
|
|
732
803
|
time_inputs << [:second] if options[:include_seconds]
|
733
804
|
|
734
805
|
list_items_capture = ""
|
806
|
+
hidden_fields_capture = ""
|
735
807
|
|
736
808
|
# Gets the datetime object. It can be a Fixnum, Date or Time, or nil.
|
737
809
|
datetime = @object ? @object.send(method) : nil
|
@@ -740,30 +812,137 @@ module Formtastic #:nodoc:
|
|
740
812
|
(inputs + time_inputs).each do |input|
|
741
813
|
html_id = generate_html_id(method, "#{position[input]}i")
|
742
814
|
field_name = "#{method}(#{position[input]}i)"
|
743
|
-
|
744
|
-
list_items_capture << if options["discard_#{input}".intern]
|
815
|
+
if options["discard_#{input}".intern]
|
745
816
|
break if time_inputs.include?(input)
|
746
|
-
|
817
|
+
|
747
818
|
hidden_value = datetime.respond_to?(input) ? datetime.send(input) : datetime
|
748
|
-
template.hidden_field_tag("#{@object_name}[#{field_name}]", (hidden_value || 1), :id => html_id)
|
819
|
+
hidden_fields_capture << template.hidden_field_tag("#{@object_name}[#{field_name}]", (hidden_value || 1), :id => html_id)
|
749
820
|
else
|
750
821
|
opts = set_options(options).merge(:prefix => @object_name, :field_name => field_name)
|
751
822
|
item_label_text = I18n.t(input.to_s, :default => input.to_s.humanize, :scope => [:datetime, :prompts])
|
752
823
|
|
753
|
-
template.content_tag(:li,
|
824
|
+
list_items_capture << template.content_tag(:li,
|
754
825
|
template.content_tag(:label, item_label_text, :for => html_id) +
|
755
826
|
template.send("select_#{input}".intern, datetime, opts, html_options.merge(:id => html_id))
|
756
827
|
)
|
757
828
|
end
|
758
829
|
end
|
759
830
|
|
760
|
-
field_set_and_list_wrapping_for_method(method, options, list_items_capture)
|
831
|
+
hidden_fields_capture + field_set_and_list_wrapping_for_method(method, options, list_items_capture)
|
761
832
|
end
|
762
833
|
|
834
|
+
|
835
|
+
# Outputs a fieldset containing a legend for the label text, and an ordered list (ol) of list
|
836
|
+
# items, one for each possible choice in the belongs_to association. Each li contains a
|
837
|
+
# label and a check_box input.
|
838
|
+
#
|
839
|
+
# This is an alternative for has many and has and belongs to many associations.
|
840
|
+
#
|
841
|
+
# Example:
|
842
|
+
#
|
843
|
+
# f.input :author, :as => :check_boxes
|
844
|
+
#
|
845
|
+
# Output:
|
846
|
+
#
|
847
|
+
# <fieldset>
|
848
|
+
# <legend><span>Authors</span></legend>
|
849
|
+
# <ol>
|
850
|
+
# <li>
|
851
|
+
# <input type="hidden" name="book[author_id][1]" value="">
|
852
|
+
# <label for="book_author_id_1"><input id="book_author_id_1" name="book[author_id][1]" type="checkbox" value="1" /> Justin French</label>
|
853
|
+
# </li>
|
854
|
+
# <li>
|
855
|
+
# <input type="hidden" name="book[author_id][2]" value="">
|
856
|
+
# <label for="book_author_id_2"><input id="book_author_id_2" name="book[owner_id][2]" type="checkbox" value="2" /> Kate French</label>
|
857
|
+
# </li>
|
858
|
+
# </ol>
|
859
|
+
# </fieldset>
|
860
|
+
#
|
861
|
+
# Notice that the value of the checkbox is the same as the id and the hidden
|
862
|
+
# field has empty value. You can override the hidden field value using the
|
863
|
+
# unchecked_value option.
|
864
|
+
#
|
865
|
+
# You can customize the options available in the set by passing in a collection (Array) of
|
866
|
+
# ActiveRecord objects through the :collection option. If not provided, the choices are found
|
867
|
+
# by inferring the parent's class name from the method name and simply calling find(:all) on
|
868
|
+
# it (Author.find(:all) in the example above).
|
869
|
+
#
|
870
|
+
# Examples:
|
871
|
+
#
|
872
|
+
# f.input :author, :as => :check_boxes, :collection => @authors
|
873
|
+
# f.input :author, :as => :check_boxes, :collection => Author.find(:all)
|
874
|
+
# f.input :author, :as => :check_boxes, :collection => [@justin, @kate]
|
875
|
+
#
|
876
|
+
# You can also customize the text label inside each option tag, by naming the correct method
|
877
|
+
# (:full_name, :display_name, :account_number, etc) to call on each object in the collection
|
878
|
+
# by passing in the :label_method option. By default the :label_method is whichever element of
|
879
|
+
# Formtastic::SemanticFormBuilder.collection_label_methods is found first.
|
880
|
+
#
|
881
|
+
# Examples:
|
882
|
+
#
|
883
|
+
# f.input :author, :as => :check_boxes, :label_method => :full_name
|
884
|
+
# f.input :author, :as => :check_boxes, :label_method => :display_name
|
885
|
+
# f.input :author, :as => :check_boxes, :label_method => :to_s
|
886
|
+
# f.input :author, :as => :check_boxes, :label_method => :label
|
887
|
+
#
|
888
|
+
# You can set :value_as_class => true if you want that LI wrappers contains
|
889
|
+
# a class with the wrapped checkbox input value.
|
890
|
+
#
|
891
|
+
def check_boxes_input(method, options)
|
892
|
+
collection = find_collection_for_column(method, options)
|
893
|
+
html_options = options.delete(:input_html) || {}
|
894
|
+
|
895
|
+
input_name = generate_association_input_name(method)
|
896
|
+
value_as_class = options.delete(:value_as_class)
|
897
|
+
unchecked_value = options.delete(:unchecked_value) || ''
|
898
|
+
html_options = { :name => "#{@object_name}[#{input_name}][]" }.merge(html_options)
|
899
|
+
|
900
|
+
list_item_content = collection.map do |c|
|
901
|
+
label = c.is_a?(Array) ? c.first : c
|
902
|
+
value = c.is_a?(Array) ? c.last : c
|
903
|
+
|
904
|
+
html_options.merge!(:id => generate_html_id(input_name, value.to_s.downcase))
|
905
|
+
|
906
|
+
li_content = template.content_tag(:label,
|
907
|
+
"#{self.check_box(input_name, html_options, value, unchecked_value)} #{label}",
|
908
|
+
:for => html_options[:id]
|
909
|
+
)
|
910
|
+
|
911
|
+
li_options = value_as_class ? { :class => value.to_s.downcase } : {}
|
912
|
+
template.content_tag(:li, li_content, li_options)
|
913
|
+
end
|
914
|
+
|
915
|
+
field_set_and_list_wrapping_for_method(method, options, list_item_content)
|
916
|
+
end
|
917
|
+
|
918
|
+
|
919
|
+
# Outputs a country select input, wrapping around a regular country_select helper.
|
920
|
+
# Rails doesn't come with a country_select helper by default any more, so you'll need to install
|
921
|
+
# the "official" plugin, or, if you wish, any other country_select plugin that behaves in the
|
922
|
+
# same way.
|
923
|
+
#
|
924
|
+
# The Rails plugin iso-3166-country-select plugin can be found "here":http://github.com/rails/iso-3166-country-select.
|
925
|
+
#
|
926
|
+
# By default, Formtastic includes a handfull of english-speaking countries as "priority counties",
|
927
|
+
# which you can change to suit your market and user base (see README for more info on config).
|
928
|
+
#
|
929
|
+
# Examples:
|
930
|
+
# f.input :location, :as => :country # use Formtastic::SemanticFormBuilder.priority_countries array for the priority countries
|
931
|
+
# f.input :location, :as => :country, :priority_countries => /Australia/ # set your own
|
932
|
+
#
|
933
|
+
def country_input(method, options)
|
934
|
+
raise "To use the :country input, please install a country_select plugin, like this one: http://github.com/rails/iso-3166-country-select" unless self.respond_to?(:country_select)
|
935
|
+
|
936
|
+
html_options = options.delete(:input_html) || {}
|
937
|
+
priority_countries = options.delete(:priority_countries) || @@priority_countries
|
938
|
+
|
939
|
+
self.label(method, options.slice(:label, :required)) +
|
940
|
+
self.country_select(method, priority_countries, set_options(options), html_options)
|
941
|
+
end
|
942
|
+
|
943
|
+
|
763
944
|
# Outputs a label containing a checkbox and the label text. The label defaults
|
764
945
|
# to the column name (method name) and can be altered with the :label option.
|
765
|
-
#
|
766
|
-
# Different from other inputs, :required options has no effect here and
|
767
946
|
# :checked_value and :unchecked_value options are also available.
|
768
947
|
#
|
769
948
|
def boolean_input(method, options)
|
@@ -772,10 +951,8 @@ module Formtastic #:nodoc:
|
|
772
951
|
input = self.check_box(method, set_options(options).merge(html_options),
|
773
952
|
options.delete(:checked_value) || '1', options.delete(:unchecked_value) || '0')
|
774
953
|
|
775
|
-
# Generate the label by hand because required or optional does not make sense here
|
776
954
|
label = options.delete(:label) || humanized_attribute_name(method)
|
777
|
-
|
778
|
-
self.label(method, input + label, options)
|
955
|
+
self.label(method, input + label, options.slice(:required))
|
779
956
|
end
|
780
957
|
|
781
958
|
# Generates an input for the given method using the type supplied with :as.
|
@@ -795,31 +972,6 @@ module Formtastic #:nodoc:
|
|
795
972
|
end
|
796
973
|
end
|
797
974
|
|
798
|
-
# Generates error messages for the given method. Errors can be shown as list
|
799
|
-
# or as sentence. If :none is set, no error is shown.
|
800
|
-
#
|
801
|
-
def inline_errors_for(method, options) #:nodoc:
|
802
|
-
return nil unless @object && @object.respond_to?(:errors) && [:sentence, :list].include?(@@inline_errors)
|
803
|
-
|
804
|
-
# Ruby 1.9: Strings are not Enumerable, ie no String#to_a
|
805
|
-
errors = @object.errors.on(method.to_s)
|
806
|
-
unless errors.respond_to?(:to_a)
|
807
|
-
errors = [errors]
|
808
|
-
else
|
809
|
-
errors = errors.to_a
|
810
|
-
end
|
811
|
-
|
812
|
-
# Ruby 1.9: Strings are not Enumerable, ie no String#to_a
|
813
|
-
errors = @object.errors.on(method.to_s)
|
814
|
-
unless errors.respond_to?(:to_a)
|
815
|
-
errors = [errors]
|
816
|
-
else
|
817
|
-
errors = errors.to_a
|
818
|
-
end
|
819
|
-
|
820
|
-
send("error_#{@@inline_errors}", errors) unless errors.empty?
|
821
|
-
end
|
822
|
-
|
823
975
|
# Generates hints for the given method using the text supplied in :hint.
|
824
976
|
#
|
825
977
|
def inline_hints_for(method, options) #:nodoc:
|
@@ -830,7 +982,7 @@ module Formtastic #:nodoc:
|
|
830
982
|
# Creates an error sentence by calling to_sentence on the errors array.
|
831
983
|
#
|
832
984
|
def error_sentence(errors) #:nodoc:
|
833
|
-
template.content_tag(:p, errors.to_sentence, :class => 'inline-errors')
|
985
|
+
template.content_tag(:p, errors.to_sentence.untaint, :class => 'inline-errors')
|
834
986
|
end
|
835
987
|
|
836
988
|
# Creates an error li list.
|
@@ -838,37 +990,28 @@ module Formtastic #:nodoc:
|
|
838
990
|
def error_list(errors) #:nodoc:
|
839
991
|
list_elements = []
|
840
992
|
errors.each do |error|
|
841
|
-
list_elements << template.content_tag(:li, error)
|
993
|
+
list_elements << template.content_tag(:li, error.untaint)
|
842
994
|
end
|
843
995
|
template.content_tag(:ul, list_elements.join("\n"), :class => 'errors')
|
844
996
|
end
|
845
997
|
|
846
|
-
# Generates the label for the input. Accepts the same options as Rails label
|
847
|
-
# method and a fourth option that allows the label to be generated as span
|
848
|
-
# with class label.
|
849
|
-
#
|
850
|
-
def input_label(method, text, options={}, as_span=false) #:nodoc:
|
851
|
-
text ||= humanized_attribute_name(method)
|
852
|
-
text << required_or_optional_string(options.delete(:required))
|
853
|
-
|
854
|
-
if as_span
|
855
|
-
options[:class] ||= 'label'
|
856
|
-
template.content_tag(:span, text, options)
|
857
|
-
else
|
858
|
-
self.label(method, text, options)
|
859
|
-
end
|
860
|
-
end
|
861
|
-
|
862
998
|
# Generates the required or optional string. If the value set is a proc,
|
863
999
|
# it evaluates the proc first.
|
864
1000
|
#
|
865
1001
|
def required_or_optional_string(required) #:nodoc:
|
866
|
-
string_or_proc = required
|
1002
|
+
string_or_proc = case required
|
1003
|
+
when true
|
1004
|
+
@@required_string
|
1005
|
+
when false
|
1006
|
+
@@optional_string
|
1007
|
+
else
|
1008
|
+
required
|
1009
|
+
end
|
867
1010
|
|
868
|
-
if string_or_proc.is_a?
|
1011
|
+
if string_or_proc.is_a?(Proc)
|
869
1012
|
string_or_proc.call
|
870
1013
|
else
|
871
|
-
string_or_proc
|
1014
|
+
string_or_proc.to_s
|
872
1015
|
end
|
873
1016
|
end
|
874
1017
|
|
@@ -904,7 +1047,7 @@ module Formtastic #:nodoc:
|
|
904
1047
|
#
|
905
1048
|
def field_set_and_list_wrapping_for_method(method, options, contents)
|
906
1049
|
template.content_tag(:fieldset,
|
907
|
-
%{<legend>#{
|
1050
|
+
%{<legend>#{self.label(method, options.slice(:label, :required).merge!(:as_span => true))}</legend>} +
|
908
1051
|
template.content_tag(:ol, contents)
|
909
1052
|
)
|
910
1053
|
end
|
@@ -924,11 +1067,12 @@ module Formtastic #:nodoc:
|
|
924
1067
|
|
925
1068
|
if column
|
926
1069
|
# handle the special cases where the column type doesn't map to an input method
|
927
|
-
return :time_zone if column.type == :string && method.to_s =~ /
|
1070
|
+
return :time_zone if column.type == :string && method.to_s =~ /time_zone/
|
928
1071
|
return :select if column.type == :integer && method.to_s =~ /_id$/
|
929
1072
|
return :datetime if column.type == :timestamp
|
930
1073
|
return :numeric if [:integer, :float, :decimal].include?(column.type)
|
931
1074
|
return :password if column.type == :string && method.to_s =~ /password/
|
1075
|
+
return :country if column.type == :string && method.to_s =~ /country/
|
932
1076
|
|
933
1077
|
# otherwise assume the input name will be the same as the column type (eg string_input)
|
934
1078
|
return column.type
|