formtastic 0.9.0 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +91 -83
- data/lib/formtastic.rb +77 -62
- data/rails/init.rb +0 -1
- data/spec/buttons_spec.rb +149 -0
- data/spec/commit_button_spec.rb +344 -0
- data/spec/custom_builder_spec.rb +62 -0
- data/spec/error_proc_spec.rb +27 -0
- data/spec/errors_spec.rb +78 -0
- data/spec/form_helper_spec.rb +110 -0
- data/spec/include_blank_spec.rb +70 -0
- data/spec/input_spec.rb +2157 -0
- data/spec/inputs_spec.rb +374 -0
- data/spec/label_spec.rb +54 -0
- data/spec/semantic_fields_for_spec.rb +42 -0
- data/spec/test_helper.rb +132 -0
- metadata +24 -5
- data/lib/justin_french/formtastic.rb +0 -11
- data/spec/formtastic_spec.rb +0 -3341
data/README.textile
CHANGED
@@ -42,34 +42,34 @@ I also wrote the accompanying HTML output I expected, favoring something very si
|
|
42
42
|
|
43
43
|
h2. It's better than _SomeOtherFormBuilder_ because...
|
44
44
|
|
45
|
-
* it can handle @belongs_to@ associations (like Post belongs_to :author), rendering a select or set of radio inputs with choices from the parent model
|
46
|
-
* it can handle @has_many@ and @has_and_belongs_to_many@ associations (like Post has_many :tags), rendering a multi-select with choices from the child models
|
47
|
-
* it's Rails 2.3-ready (including nested forms)
|
45
|
+
* it can handle @belongs_to@ associations (like Post belongs_to :author), rendering a select or set of radio inputs with choices from the parent model.
|
46
|
+
* it can handle @has_many@ and @has_and_belongs_to_many@ associations (like: Post has_many :tags), rendering a multi-select with choices from the child models.
|
47
|
+
* it's Rails 2.3-ready (including nested forms).
|
48
48
|
* it has internationalization (I18n)!
|
49
|
-
* it's _really_ quick to get started with a basic form in place (4 lines), then go back to add in more detail if you need it
|
50
|
-
* there's heaps of elements, id and class attributes for you to hook in your CSS and JS
|
51
|
-
* it handles real world stuff like inline hints, inline error messages & help text
|
52
|
-
* it doesn't hijack or change any of the standard Rails form inputs, so you can still use them as expected (even mix and match)
|
53
|
-
* it's got absolutely awesome spec coverage
|
54
|
-
* there's a bunch of people using and working on it (it's not just one developer building half a solution)
|
49
|
+
* it's _really_ quick to get started with a basic form in place (4 lines), then go back to add in more detail if you need it.
|
50
|
+
* there's heaps of elements, id and class attributes for you to hook in your CSS and JS.
|
51
|
+
* it handles real world stuff like inline hints, inline error messages & help text.
|
52
|
+
* it doesn't hijack or change any of the standard Rails form inputs, so you can still use them as expected (even mix and match).
|
53
|
+
* it's got absolutely awesome spec coverage.
|
54
|
+
* there's a bunch of people using and working on it (it's not just one developer building half a solution).
|
55
55
|
|
56
56
|
|
57
57
|
h2. Why?
|
58
58
|
|
59
|
-
* web apps = lots of forms
|
60
|
-
* forms are so friggin' boring to code
|
61
|
-
* semantically rich & accessible forms really are possible
|
62
|
-
* the "V" is way behind the "M" and "C" in Rails' MVC – it's the ugly sibling
|
63
|
-
* best practices and common patterns have to start somewhere
|
64
|
-
* i need a challenge
|
59
|
+
* web apps = lots of forms.
|
60
|
+
* forms are so friggin' boring to code.
|
61
|
+
* semantically rich & accessible forms really are possible.
|
62
|
+
* the "V" is way behind the "M" and "C" in Rails' MVC – it's the ugly sibling.
|
63
|
+
* best practices and common patterns have to start somewhere.
|
64
|
+
* i need a challenge.
|
65
65
|
|
66
66
|
|
67
67
|
h2. Opinions
|
68
68
|
|
69
|
-
* it should be easier to do things the right way than the wrong way
|
70
|
-
* sometimes _more mark-up_ is better
|
71
|
-
* elements and attribute hooks are _gold_ for stylesheet authors
|
72
|
-
* make the common things we do easy, yet still ensure uncommon things are still possible
|
69
|
+
* it should be easier to do things the right way than the wrong way.
|
70
|
+
* sometimes _more mark-up_ is better.
|
71
|
+
* elements and attribute hooks are _gold_ for stylesheet authors.
|
72
|
+
* make the common things we do easy, yet still ensure uncommon things are still possible.
|
73
73
|
|
74
74
|
|
75
75
|
h2. Documentation
|
@@ -79,13 +79,13 @@ RDoc documentation _should_ be automatically generated after each commit and mad
|
|
79
79
|
|
80
80
|
h2. Installation
|
81
81
|
|
82
|
-
The gem is hosted on gemcutter, so if you haven't already
|
82
|
+
The gem is hosted on gemcutter, so *if you haven't already*, add it as a gem source:
|
83
83
|
|
84
84
|
<pre>
|
85
85
|
sudo gem sources -a http://gemcutter.org/
|
86
86
|
</pre>
|
87
87
|
|
88
|
-
Then install the Formtastic gem:
|
88
|
+
Then install the Formtastic gem:
|
89
89
|
|
90
90
|
<pre>
|
91
91
|
sudo gem install formtastic
|
@@ -93,11 +93,11 @@ Then install the Formtastic gem:q
|
|
93
93
|
|
94
94
|
Optionally, run @./script/generate formtastic@ to copy the following files into your app:
|
95
95
|
|
96
|
-
* config/initializers/formtastic.rb
|
97
|
-
* public/stylesheets/formtastic.css
|
98
|
-
* public/stylesheets/formtastic_changes.css
|
96
|
+
* @config/initializers/formtastic.rb@ - a commented out Formtastic config initializer
|
97
|
+
* @public/stylesheets/formtastic.css@
|
98
|
+
* @public/stylesheets/formtastic_changes.css@
|
99
99
|
|
100
|
-
A proof-of-concept stylesheet is provided which you can include in your layout.
|
100
|
+
A proof-of-concept stylesheet is provided which you can include in your layout. Customization is best achieved by overriding these styles in an additional stylesheet so that the Formtastic styles can be updated without clobbering your changes. If you want to use these stylesheets, add both to your layout:
|
101
101
|
|
102
102
|
<pre>
|
103
103
|
<%= stylesheet_link_tag "formtastic" %>
|
@@ -109,7 +109,7 @@ h2. Usage
|
|
109
109
|
|
110
110
|
Forms are really boring to code... you want to get onto the good stuff as fast as possible.
|
111
111
|
|
112
|
-
This renders a set of inputs (one for _most_ columns in the database table, and one for each ActiveRecord belongs_to
|
112
|
+
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:
|
113
113
|
|
114
114
|
<pre>
|
115
115
|
<% semantic_form_for @user do |form| %>
|
@@ -127,7 +127,7 @@ If you want to specify the order of the fields, skip some of the fields or even
|
|
127
127
|
<% end %>
|
128
128
|
</pre>
|
129
129
|
|
130
|
-
If you want control over the input type Formtastic uses for each field, you can expand the @inputs@ and @buttons@ blocks.
|
130
|
+
If you want control over the input type Formtastic uses for each field, you can expand the @inputs@ and @buttons@ blocks. This specifies the @:section@ input should be a set of radio buttons (rather than the default select box), and that the @:created_at@ field should be a string (rather than the default datetime selects):
|
131
131
|
|
132
132
|
<pre>
|
133
133
|
<% semantic_form_for @post do |form| %>
|
@@ -165,14 +165,13 @@ If you want to customize the label text, or render some hint text below the fiel
|
|
165
165
|
<% end %>
|
166
166
|
</pre>
|
167
167
|
|
168
|
-
|
169
168
|
Nested forms (Rails 2.3) are also supported. You can do it in the Rails way:
|
170
169
|
|
171
170
|
<pre>
|
172
171
|
<% semantic_form_for @post do |form| %>
|
173
172
|
<%= form.inputs :title, :body, :created_at %>
|
174
173
|
<% form.semantic_fields_for :author do |author| %>
|
175
|
-
<%= author.inputs :first_name, :last_name, :name =>
|
174
|
+
<%= author.inputs :first_name, :last_name, :name => "Author" %>
|
176
175
|
<% end %>
|
177
176
|
<%= form.buttons %>
|
178
177
|
<% end %>
|
@@ -188,7 +187,7 @@ Or the Formtastic way with the @:for@ option:
|
|
188
187
|
<% end %>
|
189
188
|
</pre>
|
190
189
|
|
191
|
-
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:
|
190
|
+
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:
|
192
191
|
|
193
192
|
<pre>
|
194
193
|
<% semantic_form_for @post do |form| %>
|
@@ -199,7 +198,7 @@ When working in has many association, you can even supply "%i" in your fieldset
|
|
199
198
|
</pre>
|
200
199
|
|
201
200
|
|
202
|
-
Customize HTML attributes for any input using the @:input_html@ option.
|
201
|
+
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:
|
203
202
|
|
204
203
|
<pre>
|
205
204
|
<% semantic_form_for @post do |form| %>
|
@@ -221,8 +220,8 @@ The same can be done for buttons with the @:button_html@ option:
|
|
221
220
|
<% end %>
|
222
221
|
</pre>
|
223
222
|
|
224
|
-
Customize the HTML attributes for the @<li>@ wrapper around every input with the @:wrapper_html@ option hash.
|
225
|
-
|
223
|
+
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"@)
|
224
|
+
|
226
225
|
<pre>
|
227
226
|
<% semantic_form_for @post do |form| %>
|
228
227
|
<%= form.input :title, :wrapper_html => { :class => "important" } %>
|
@@ -233,36 +232,33 @@ Customize the HTML attributes for the @<li>@ wrapper around every input with the
|
|
233
232
|
</pre>
|
234
233
|
|
235
234
|
|
236
|
-
|
237
235
|
h2. The Available Inputs
|
238
236
|
|
239
|
-
|
240
|
-
|
241
|
-
*
|
242
|
-
*
|
243
|
-
*
|
244
|
-
*
|
245
|
-
*
|
246
|
-
*
|
247
|
-
*
|
248
|
-
*
|
249
|
-
*
|
250
|
-
*
|
251
|
-
*
|
252
|
-
*
|
253
|
-
*
|
254
|
-
|
255
|
-
|
256
|
-
|
237
|
+
The Formtastic input types:
|
238
|
+
|
239
|
+
* @:select@ - a select menu. Default for ActiveRecord associations: @belongs_to@, @has_many@, and @has_and_belongs_to_many@.
|
240
|
+
* @:check_boxes@ - a set of check_box inputs. Alternative to @:select@ for ActiveRecord-associations: @has_many@, and @has_and_belongs_to_many@.
|
241
|
+
* @:radio@ - a set of radio inputs. Alternative to @:select@ for ActiveRecord-associations: @belongs_to@.
|
242
|
+
* @:time_zone@ - a select input. Default for column types: @:string@ with name matching @"time_zone"@.
|
243
|
+
* @:password@ - a password input. Default for column types: @:string@ with name matching @"password"@.
|
244
|
+
* @:text@ - a textarea. Default for column types: @:text@.
|
245
|
+
* @:date@ - a date select. Default for column types: @:date@.
|
246
|
+
* @:datetime@ - a date and time select. Default for column types: @:datetime@ and @:timestamp@.
|
247
|
+
* @:time@ - a time select. Default for column types: @:time@.
|
248
|
+
* @:boolean@ - a checkbox. Default for column types: @:boolean@.
|
249
|
+
* @:string@ - a text field. Default for column types: @:string@.
|
250
|
+
* @:numeric@ - a text field (just like string). Default for column types: @:integer@, @:float@, and @:decimal@.
|
251
|
+
* @:file@ - a file field. Default for file-attachment attributes matching: "paperclip":http://github.com/thoughtbot/paperclip or "attachment_fu":http://github.com/technoweenie/attachment_fu.
|
252
|
+
* @:country@ - a select menu of country names. Default for column types: :string with name @"country"@ - requires a *country_select* plugin to be installed.
|
253
|
+
* @:hidden@ - a hidden field. Creates a hidden field (added for compatibility).
|
254
|
+
|
255
|
+
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.
|
257
256
|
|
258
257
|
h2. Internationalization (I18n)
|
259
258
|
|
260
|
-
|
261
|
-
|
259
|
+
h3. Basic Localization
|
262
260
|
|
263
|
-
|
264
|
-
|
265
|
-
Formtastic supports localized *labels* and *hints* using the I18n API for more advanced usage. Your forms can now be DRYer and more flexible than ever, and still fully localized. This is how:
|
261
|
+
Formtastic got some neat I18n-features. ActiveRecord object names and attributes are, by default, taken from calling @@object.human_name@ and @@object.human_attribute_name(attr)@ respectively. There are a few words specific to Formtastic that can be translated. See @lib/locale/en.yml@ for more information.
|
266
262
|
|
267
263
|
Basic localization (labels only, with ActiveRecord):
|
268
264
|
|
@@ -274,17 +270,19 @@ Basic localization (labels only, with ActiveRecord):
|
|
274
270
|
<% end %>
|
275
271
|
</pre>
|
276
272
|
|
277
|
-
*Note:* This is perfectly fine if you just want your labels to be translated using *ActiveRecord I18n attribute translations*, and you don't use input hints. But what if you do? And what if you don't want same labels in all forms?
|
273
|
+
*Note:* This is perfectly fine if you just want your labels/attributes and/or models to be translated using *ActiveRecord I18n attribute translations*, and you don't use input hints and legends. But what if you do? And what if you don't want same labels in all forms?
|
274
|
+
|
275
|
+
h3. Enhanced Localization (Formtastic I18n API)
|
278
276
|
|
279
|
-
|
277
|
+
Formtastic supports localized *labels*, *hints*, *legends*, *actions* using the I18n API for more advanced usage. Your forms can now be DRYer and more flexible than ever, and still fully localized. This is how:
|
280
278
|
|
281
|
-
1. Enable I18n lookups by default (@config/initializers/formtastic.rb@)
|
279
|
+
*1. Enable I18n lookups by default (@config/initializers/formtastic.rb@):*
|
282
280
|
|
283
281
|
<pre>
|
284
282
|
Formtastic::SemanticFormBuilder.i18n_lookups_by_default = true
|
285
283
|
</pre>
|
286
284
|
|
287
|
-
2. Add some cool label-translations/variants (@config/locale/en.yml@)
|
285
|
+
*2. Add some cool label-translations/variants (@config/locale/en.yml@):*
|
288
286
|
|
289
287
|
<pre>
|
290
288
|
en:
|
@@ -301,24 +299,30 @@ Enhanced localization (labels + hints + titles/legends, with Formtastic):
|
|
301
299
|
post:
|
302
300
|
title: "Choose a good title for you post."
|
303
301
|
body: "Write something inspiring here."
|
302
|
+
actions:
|
303
|
+
create: "Create my {{model}}"
|
304
|
+
update: "Save changes"
|
305
|
+
dummie: "Launch!"
|
304
306
|
</pre>
|
305
307
|
|
306
308
|
*Note:* We are using English here still, but you get the point.
|
307
309
|
|
308
|
-
3. ...and now you'll get
|
310
|
+
*3. ...and now you'll get:*
|
309
311
|
|
310
312
|
<pre>
|
311
|
-
<% semantic_form_for
|
313
|
+
<% semantic_form_for Post.new do |form| %>
|
312
314
|
<% inputs do %>
|
313
315
|
<%= form.input :title %> # => :label => "Choose a title...", :hint => "Choose a good title for you post."
|
314
316
|
<%= form.input :body %> # => :label => "Write something...", :hint => "Write something inspiring here."
|
315
317
|
<%= form.input :section %> # => :label => I18n.t('activerecord.attributes.user.section') or 'Section'
|
316
318
|
<% end %>
|
317
|
-
|
319
|
+
<% buttons do %>
|
320
|
+
<%= form.commit_button %> # => "Create my {{model}}"
|
321
|
+
<% end %>
|
318
322
|
<% end %>
|
319
323
|
</pre>
|
320
324
|
|
321
|
-
4. Localized titles (a.k.a. legends)
|
325
|
+
*4. Localized titles (a.k.a. legends):*
|
322
326
|
|
323
327
|
_Note: Slightly different because Formtastic can't guess how you group fields in a form._
|
324
328
|
|
@@ -331,7 +335,7 @@ _Note: Slightly different because Formtastic can't guess how you group fields in
|
|
331
335
|
<% end %>
|
332
336
|
</pre>
|
333
337
|
|
334
|
-
5. Override I18n settings
|
338
|
+
*5. Override I18n settings:*
|
335
339
|
|
336
340
|
<pre>
|
337
341
|
<% semantic_form_for @post do |form| %>
|
@@ -340,7 +344,9 @@ _Note: Slightly different because Formtastic can't guess how you group fields in
|
|
340
344
|
<%= form.input :body, :hint => false %> # => :label => "Write something..."
|
341
345
|
<%= form.input :section, :label => 'Some section' %> # => :label => 'Some section'
|
342
346
|
<% end %>
|
343
|
-
|
347
|
+
<% buttons do %>
|
348
|
+
<%= form.commit_button :dummie %> # => "Launch!"
|
349
|
+
<% end %>
|
344
350
|
<% end %>
|
345
351
|
</pre>
|
346
352
|
|
@@ -359,23 +365,25 @@ If I18n-lookups is disabled, i.e.:
|
|
359
365
|
<%= form.input :body, :label => true %> # => :label => "Write something..."
|
360
366
|
<%= form.input :section, :label => true %> # => :label => I18n.t('activerecord.attributes.user.section') or 'Section'
|
361
367
|
<% end %>
|
362
|
-
|
368
|
+
% buttons do %>
|
369
|
+
<%= form.commit_button true %> # => "Save changes" (if we are in edit that is...)
|
370
|
+
<% end %>
|
363
371
|
<% end %>
|
364
372
|
</pre>
|
365
373
|
|
366
|
-
6. Advanced I18n lookups
|
374
|
+
*6. Advanced I18n lookups*
|
367
375
|
|
368
376
|
For more flexible forms; Formtastic find translations using a bottom-up approach taking the following variables in account:
|
369
377
|
|
370
378
|
* @MODEL@, e.g. "post"
|
371
379
|
* @ACTION@, e.g. "edit"
|
372
|
-
* @ATTRIBUTE@, e.g. "title"
|
380
|
+
* @KEY/ATTRIBUTE@, e.g. "title", :my_custom_key, ...
|
373
381
|
|
374
382
|
...in the following order:
|
375
383
|
|
376
|
-
1. @formtastic.{titles,labels,hints}.MODEL.ACTION.ATTRIBUTE@
|
377
|
-
2. @formtastic.{titles,labels,hints}.MODEL.ATTRIBUTE@
|
378
|
-
3. @formtastic.{titles,labels,hints}.ATTRIBUTE@
|
384
|
+
1. @formtastic.{titles,labels,hints,actions}.MODEL.ACTION.ATTRIBUTE@ - by model and action
|
385
|
+
2. @formtastic.{titles,labels,hints,actions}.MODEL.ATTRIBUTE@ - by model
|
386
|
+
3. @formtastic.{titles,labels,hints,actions}.ATTRIBUTE@ - global default
|
379
387
|
|
380
388
|
...which means that you can define translations like this:
|
381
389
|
|
@@ -395,41 +403,41 @@ For more flexible forms; Formtastic find translations using a bottom-up approach
|
|
395
403
|
body: "Edit body"
|
396
404
|
</pre>
|
397
405
|
|
398
|
-
|
406
|
+
Values for @labels@/@hints@/@actions@ are can take values: @String@ (explicit value), @Symbol@ (i18n-lookup-key relative to the current "type", e.g. actions:), @true@ (force I18n lookup), @false@ (force no I18n lookup). Titles (legends) can only take: @String@ and @Symbol@ - true/false have no meaning.
|
399
407
|
|
400
408
|
|
401
409
|
h2. ValidationReflection plugin
|
402
410
|
|
403
|
-
If you have the "ValidationReflection":http://github.com/redinger/validation_reflection plugin installed, you won't have to specify the
|
411
|
+
If you have the "ValidationReflection":http://github.com/redinger/validation_reflection plugin installed, you won't have to specify the @:required@ option (it checks the validations on the model instead).
|
404
412
|
|
405
413
|
|
406
414
|
h2. Configuration
|
407
415
|
|
408
|
-
Run @./script/generate formtastic@ to copy a commented out config file into @config/initializers/formtastic.rb@.
|
416
|
+
Run @./script/generate formtastic@ to copy a commented out config file into @config/initializers/formtastic.rb@. You can "view the configuration file on GitHub":http://github.com/justinfrench/formtastic/blob/master/generators/formtastic/templates/formtastic.rb
|
409
417
|
|
410
418
|
|
411
419
|
h2. Status
|
412
420
|
|
413
|
-
Formtastic has been in active development for about a year.
|
421
|
+
Formtastic has been in active development for about a year. We've just recently jumped to an 0.9 version number, signaling that we consider this a 1.0 release candidate, and that the API won't change significantly for the 1.x series.
|
414
422
|
|
415
423
|
|
416
424
|
h2. Dependencies
|
417
425
|
|
418
426
|
There are none, but...
|
419
427
|
|
420
|
-
* 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)
|
421
|
-
* if you want to use the
|
422
|
-
* rspec
|
428
|
+
* 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).
|
429
|
+
* 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).
|
430
|
+
* "rspec":http://github.com/dchelimsky/rspec/, "rspec_hpricot_matchers":http://rubyforge.org/projects/rspec-hpricot/ and "rcov":http://github.com/relevance/rcov gems (plus any of their own dependencies) are required for the test suite.
|
423
431
|
|
424
432
|
|
425
433
|
h2. Compatibility
|
426
434
|
|
427
|
-
I'm only testing Formtastic with the latest Rails 2.2.x stable release, and it should be fine under Rails 2.3 as well (including nested forms).
|
435
|
+
I'm only testing Formtastic with the latest Rails 2.2.x stable release, and it should be fine under Rails 2.3 as well (including nested forms). Patches are welcome to allow backwards compatibility, but I don't have the energy!
|
428
436
|
|
429
437
|
|
430
438
|
h2. Got TextMate?
|
431
439
|
|
432
|
-
Well...there's a TextMate-bundle in town, dedicated to make usage of Formtastic in the TextMate editor even more of a breeze:
|
440
|
+
Well...there's a TextMate-bundle in town, dedicated to make usage of Formtastic in the "TextMate":http://macromates.com/ editor even more of a breeze:
|
433
441
|
|
434
442
|
"Formtastic.tmbundle":http://github.com/grimen/formtastic_tmbundle
|
435
443
|
|
@@ -463,7 +471,7 @@ Please join the "Formtastic Google Group":http://groups.google.com.au/group/form
|
|
463
471
|
|
464
472
|
h2. Project Info
|
465
473
|
|
466
|
-
Formtastic is hosted on Github: http://github.com/justinfrench/formtastic
|
474
|
+
Formtastic is hosted on Github: "http://github.com/justinfrench/formtastic":http://github.com/justinfrench/formtastic, where your contributions, forkings, comments and feedback are greatly welcomed.
|
467
475
|
|
468
476
|
|
469
477
|
Copyright (c) 2007-2008 Justin French, released under the MIT license.
|
data/lib/formtastic.rb
CHANGED
@@ -16,10 +16,11 @@ module Formtastic #:nodoc:
|
|
16
16
|
@@file_methods = [ :file?, :public_filename ]
|
17
17
|
@@priority_countries = ["Australia", "Canada", "United Kingdom", "United States"]
|
18
18
|
@@i18n_lookups_by_default = false
|
19
|
+
@@default_commit_button_accesskey = nil
|
19
20
|
|
20
21
|
cattr_accessor :default_text_field_size, :all_fields_required_by_default, :include_blank_for_select_by_default,
|
21
22
|
:required_string, :optional_string, :inline_errors, :label_str_method, :collection_label_methods,
|
22
|
-
:inline_order, :file_methods, :priority_countries, :i18n_lookups_by_default
|
23
|
+
:inline_order, :file_methods, :priority_countries, :i18n_lookups_by_default, :default_commit_button_accesskey
|
23
24
|
|
24
25
|
I18N_SCOPES = [ '{{model}}.{{action}}.{{attribute}}',
|
25
26
|
'{{model}}.{{attribute}}',
|
@@ -93,10 +94,6 @@ module Formtastic #:nodoc:
|
|
93
94
|
wrapper_html[:id] ||= generate_html_id(method)
|
94
95
|
wrapper_html[:class] = (html_class << wrapper_html[:class]).flatten.compact.join(' ')
|
95
96
|
|
96
|
-
if [:boolean_select, :boolean_radio].include?(options[:as])
|
97
|
-
::ActiveSupport::Deprecation.warn(":as => :#{options[:as]} is deprecated, use :as => :#{options[:as].to_s[8..-1]} instead", caller[3..-1])
|
98
|
-
end
|
99
|
-
|
100
97
|
if options[:input_html] && options[:input_html][:id]
|
101
98
|
options[:label_html] ||= {}
|
102
99
|
options[:label_html][:for] ||= options[:input_html][:id]
|
@@ -251,7 +248,7 @@ module Formtastic #:nodoc:
|
|
251
248
|
if @object && args.empty?
|
252
249
|
args = @object.class.reflections.map { |n,_| n if _.macro == :belongs_to }
|
253
250
|
args += @object.class.content_columns.map(&:name)
|
254
|
-
args -= %w[created_at updated_at created_on updated_on lock_version]
|
251
|
+
args -= %w[created_at updated_at created_on updated_on lock_version version]
|
255
252
|
args.compact!
|
256
253
|
end
|
257
254
|
contents = args.map { |method| input(method.to_sym) }
|
@@ -287,18 +284,42 @@ module Formtastic #:nodoc:
|
|
287
284
|
#
|
288
285
|
# The value of the button text can be overridden:
|
289
286
|
#
|
290
|
-
# <%= form.commit_button "Go" %> => <input name="commit" type="submit" value="Go" />
|
287
|
+
# <%= form.commit_button "Go" %> => <input name="commit" type="submit" value="Go" class="{create|update|submit}" />
|
288
|
+
# <%= form.commit_button :label => "Go" %> => <input name="commit" type="submit" value="Go" class="{create|update|submit}" />
|
291
289
|
#
|
292
290
|
# And you can pass html atributes down to the input, with or without the button text:
|
293
291
|
#
|
294
|
-
# <%= form.commit_button "Go" %> => <input name="commit" type="submit" value="Go" />
|
295
|
-
# <%= form.commit_button :class => "pretty" %> => <input name="commit" type="submit" value="Save Post" class="pretty" />
|
296
|
-
|
292
|
+
# <%= form.commit_button "Go" %> => <input name="commit" type="submit" value="Go" class="{create|update|submit}" />
|
293
|
+
# <%= form.commit_button :class => "pretty" %> => <input name="commit" type="submit" value="Save Post" class="pretty {create|update|submit}" />
|
294
|
+
#
|
297
295
|
def commit_button(*args)
|
298
|
-
|
299
|
-
|
296
|
+
options = args.extract_options!
|
297
|
+
text = options.delete(:label) || args.shift
|
298
|
+
|
299
|
+
if @object
|
300
|
+
key = @object.new_record? ? :create : :update
|
301
|
+
object_name = @object.class.human_name
|
302
|
+
|
303
|
+
if key == :update
|
304
|
+
# Note: Fallback on :save-key (deprecated), :update makes more sense in the REST-world.
|
305
|
+
fallback_text = ::I18n.t(:save, :model => object_name, :default => "Save {{model}}", :scope => [:formtastic])
|
306
|
+
::ActiveSupport::Deprecation.warn "Formtastic I18n: Key 'formtastic.save' is now deprecated in favor 'formtastic.update'."
|
307
|
+
end
|
308
|
+
else
|
309
|
+
key = :submit
|
310
|
+
object_name = @object_name.to_s.send(@@label_str_method)
|
311
|
+
end
|
312
|
+
fallback_text ||= "#{key.to_s.humanize} {{model}}"
|
313
|
+
|
314
|
+
text = (self.localized_string(key, text, :action, :model => object_name) ||
|
315
|
+
::I18n.t(key, :model => object_name, :default => fallback_text, :scope => [:formtastic])) unless text.is_a?(::String)
|
316
|
+
|
300
317
|
button_html = options.delete(:button_html) || {}
|
301
|
-
|
318
|
+
button_html.merge!(:class => [button_html[:class], key].compact.join(' '))
|
319
|
+
element_class = ['commit', options.delete(:class)].compact.join(' ') # TODO: Add class reflecting on form action.
|
320
|
+
accesskey = (options.delete(:accesskey) || @@default_commit_button_accesskey) unless button_html.has_key?(:accesskey)
|
321
|
+
button_html = button_html.merge(:accesskey => accesskey) if accesskey
|
322
|
+
template.content_tag(:li, self.submit(text, button_html), :class => element_class)
|
302
323
|
end
|
303
324
|
|
304
325
|
# A thin wrapper around #fields_for to set :builder => Formtastic::SemanticFormBuilder
|
@@ -356,10 +377,12 @@ module Formtastic #:nodoc:
|
|
356
377
|
text = options_or_text
|
357
378
|
options ||= {}
|
358
379
|
end
|
359
|
-
|
360
|
-
text = localized_attribute_string(method, text, :label) || humanized_attribute_name(method)
|
380
|
+
text = localized_string(method, text, :label) || humanized_attribute_name(method)
|
361
381
|
text += required_or_optional_string(options.delete(:required))
|
362
|
-
|
382
|
+
|
383
|
+
# special case for boolean (checkbox) labels, which have a nested input
|
384
|
+
text = (options.delete(:label_prefix_for_nested_input) || "") + text
|
385
|
+
|
363
386
|
input_name = options.delete(:input_name) || method
|
364
387
|
if options.delete(:as_span)
|
365
388
|
options[:class] ||= 'label'
|
@@ -425,22 +448,6 @@ module Formtastic #:nodoc:
|
|
425
448
|
:as, :hint, :input_html, :label_html, :value_as_class)
|
426
449
|
end
|
427
450
|
|
428
|
-
# Create a default button text. If the form is working with a object, it
|
429
|
-
# defaults to "Create {{model}}" or "Save {{model}}" depending if we are working
|
430
|
-
# with a new_record or not.
|
431
|
-
#
|
432
|
-
# When not working with models, it defaults to "Submit object".
|
433
|
-
#
|
434
|
-
def save_or_create_button_text(prefix='Submit') #:nodoc:
|
435
|
-
if @object
|
436
|
-
prefix = @object.new_record? ? 'Create' : 'Save'
|
437
|
-
object_name = @object.class.human_name
|
438
|
-
else
|
439
|
-
object_name = @object_name.to_s.send(@@label_str_method)
|
440
|
-
end
|
441
|
-
I18n.t(prefix.downcase, :model => object_name, :default => "#{prefix} {{model}}", :scope => [:formtastic])
|
442
|
-
end
|
443
|
-
|
444
451
|
# Determins if the attribute (eg :title) should be considered required or not.
|
445
452
|
#
|
446
453
|
# * if the :required option was provided in the options hash, the true/false value will be
|
@@ -467,7 +474,7 @@ module Formtastic #:nodoc:
|
|
467
474
|
@@all_fields_required_by_default
|
468
475
|
end
|
469
476
|
end
|
470
|
-
|
477
|
+
|
471
478
|
# Determines whether the given options evaluate to true
|
472
479
|
def options_require_validation?(options) #nodoc
|
473
480
|
if_condition = !options[:if].nil?
|
@@ -475,12 +482,12 @@ module Formtastic #:nodoc:
|
|
475
482
|
|
476
483
|
condition = if condition.respond_to?(:call)
|
477
484
|
condition.call(@object)
|
478
|
-
elsif @object.respond_to?(condition
|
485
|
+
elsif condition.is_a?(::Symbol) && @object.respond_to?(condition)
|
479
486
|
@object.send(condition)
|
480
487
|
else
|
481
488
|
condition
|
482
489
|
end
|
483
|
-
|
490
|
+
|
484
491
|
if_condition ? !!condition : !condition
|
485
492
|
end
|
486
493
|
|
@@ -590,10 +597,7 @@ module Formtastic #:nodoc:
|
|
590
597
|
def select_input(method, options)
|
591
598
|
collection = find_collection_for_column(method, options)
|
592
599
|
html_options = options.delete(:input_html) || {}
|
593
|
-
|
594
|
-
unless options.key?(:include_blank) || options.key?(:prompt)
|
595
|
-
options[:include_blank] = @@include_blank_for_select_by_default
|
596
|
-
end
|
600
|
+
options = set_include_blank(options)
|
597
601
|
|
598
602
|
reflection = find_reflection(method)
|
599
603
|
if reflection && [ :has_many, :has_and_belongs_to_many ].include?(reflection.macro)
|
@@ -708,6 +712,7 @@ module Formtastic #:nodoc:
|
|
708
712
|
#
|
709
713
|
# Some of Rails' options for select_date are supported, but not everything yet.
|
710
714
|
def date_input(method, options)
|
715
|
+
options = set_include_blank(options)
|
711
716
|
date_or_datetime_input(method, options.merge(:discard_hour => true))
|
712
717
|
end
|
713
718
|
|
@@ -719,6 +724,7 @@ module Formtastic #:nodoc:
|
|
719
724
|
#
|
720
725
|
# Some of Rails' options for select_date are supported, but not everything yet.
|
721
726
|
def datetime_input(method, options)
|
727
|
+
options = set_include_blank(options)
|
722
728
|
date_or_datetime_input(method, options)
|
723
729
|
end
|
724
730
|
|
@@ -729,6 +735,7 @@ module Formtastic #:nodoc:
|
|
729
735
|
#
|
730
736
|
# Some of Rails' options for select_time are supported, but not everything yet.
|
731
737
|
def time_input(method, options)
|
738
|
+
options = set_include_blank(options)
|
732
739
|
date_or_datetime_input(method, options.merge(:discard_year => true, :discard_month => true, :discard_day => true))
|
733
740
|
end
|
734
741
|
|
@@ -929,8 +936,12 @@ module Formtastic #:nodoc:
|
|
929
936
|
|
930
937
|
input = self.check_box(method, set_options(options).merge(html_options),
|
931
938
|
options.delete(:checked_value) || '1', options.delete(:unchecked_value) || '0')
|
932
|
-
|
933
|
-
|
939
|
+
options = options_for_label(options)
|
940
|
+
|
941
|
+
# the label() method will insert this nested input into the label at the last minute
|
942
|
+
options[:label_prefix_for_nested_input] = input
|
943
|
+
|
944
|
+
self.label(method, options)
|
934
945
|
end
|
935
946
|
|
936
947
|
# Generates an input for the given method using the type supplied with :as.
|
@@ -953,7 +964,7 @@ module Formtastic #:nodoc:
|
|
953
964
|
# Generates hints for the given method using the text supplied in :hint.
|
954
965
|
#
|
955
966
|
def inline_hints_for(method, options) #:nodoc:
|
956
|
-
options[:hint] =
|
967
|
+
options[:hint] = localized_string(method, options[:hint], :hint)
|
957
968
|
return if options[:hint].blank?
|
958
969
|
template.content_tag(:p, options[:hint], :class => 'inline-hints')
|
959
970
|
end
|
@@ -1005,7 +1016,7 @@ module Formtastic #:nodoc:
|
|
1005
1016
|
#
|
1006
1017
|
def field_set_and_list_wrapping(html_options, contents='', &block) #:nodoc:
|
1007
1018
|
html_options[:name] ||= html_options.delete(:title)
|
1008
|
-
html_options[:name] =
|
1019
|
+
html_options[:name] = localized_string(html_options[:name], html_options[:name], :title) if html_options[:name].is_a?(Symbol)
|
1009
1020
|
|
1010
1021
|
legend = html_options.delete(:name).to_s
|
1011
1022
|
legend %= parent_child_index(html_options[:parent]) if html_options[:parent]
|
@@ -1093,15 +1104,8 @@ module Formtastic #:nodoc:
|
|
1093
1104
|
|
1094
1105
|
collection = if options[:collection]
|
1095
1106
|
options.delete(:collection)
|
1096
|
-
elsif reflection
|
1097
|
-
|
1098
|
-
reflection.klass
|
1099
|
-
else
|
1100
|
-
::ActiveSupport::Deprecation.warn("The _id way of doing things is deprecated. Please use the association method (#{column.to_s.sub(/_id$/,'')})", caller[3..-1])
|
1101
|
-
column.to_s.sub(/_id$/,'').camelize.constantize
|
1102
|
-
end
|
1103
|
-
|
1104
|
-
parent_class.find(:all)
|
1107
|
+
elsif reflection
|
1108
|
+
reflection.klass.find(:all)
|
1105
1109
|
else
|
1106
1110
|
create_boolean_collection(options)
|
1107
1111
|
end
|
@@ -1243,16 +1247,18 @@ module Formtastic #:nodoc:
|
|
1243
1247
|
#
|
1244
1248
|
# NOTE: Generic, but only used for form input labels/hints.
|
1245
1249
|
#
|
1246
|
-
def
|
1247
|
-
if
|
1248
|
-
|
1250
|
+
def localized_string(key, value, type, options = {})
|
1251
|
+
key = value if value.is_a?(::Symbol)
|
1252
|
+
|
1253
|
+
if value.is_a?(::String)
|
1254
|
+
value
|
1249
1255
|
else
|
1250
|
-
use_i18n =
|
1251
|
-
|
1256
|
+
use_i18n = value.nil? ? @@i18n_lookups_by_default : (value != false)
|
1257
|
+
|
1252
1258
|
if use_i18n
|
1253
|
-
model_name
|
1259
|
+
model_name = (@object ? @object.class.name : @object_name.to_s.send(@@label_str_method)).underscore
|
1254
1260
|
action_name = template.params[:action].to_s rescue ''
|
1255
|
-
attribute_name =
|
1261
|
+
attribute_name = key.to_s
|
1256
1262
|
|
1257
1263
|
defaults = I18N_SCOPES.collect do |i18n_scope|
|
1258
1264
|
i18n_path = i18n_scope.dup
|
@@ -1264,13 +1270,13 @@ module Formtastic #:nodoc:
|
|
1264
1270
|
end
|
1265
1271
|
defaults << ''
|
1266
1272
|
|
1267
|
-
i18n_value = ::I18n.t(defaults.shift, :default => defaults,
|
1268
|
-
:scope => "formtastic.#{
|
1273
|
+
i18n_value = ::I18n.t(defaults.shift, options.merge(:default => defaults,
|
1274
|
+
:scope => :"formtastic.#{type.to_s.pluralize}"))
|
1269
1275
|
i18n_value.blank? ? nil : i18n_value
|
1270
1276
|
end
|
1271
1277
|
end
|
1272
1278
|
end
|
1273
|
-
|
1279
|
+
|
1274
1280
|
def send_or_call(duck, object)
|
1275
1281
|
if duck.is_a?(Proc)
|
1276
1282
|
duck.call(object)
|
@@ -1279,6 +1285,15 @@ module Formtastic #:nodoc:
|
|
1279
1285
|
end
|
1280
1286
|
end
|
1281
1287
|
|
1288
|
+
private
|
1289
|
+
|
1290
|
+
def set_include_blank(options)
|
1291
|
+
unless options.key?(:include_blank) || options.key?(:prompt)
|
1292
|
+
options[:include_blank] = @@include_blank_for_select_by_default
|
1293
|
+
end
|
1294
|
+
options
|
1295
|
+
end
|
1296
|
+
|
1282
1297
|
end
|
1283
1298
|
|
1284
1299
|
# Wrappers around form_for (etc) with :builder => SemanticFormBuilder.
|