nofxx-formtastic 0.1.8 → 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|