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 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, add it as a gem source:
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:q
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 (a commented out Formtastic config initializer)
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. 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:
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 association), followed by a submit button:
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. 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):
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 => 'Author' %>
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. 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:
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. 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")
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
- * :select (a select menu) - default for ActiveRecord associations (belongs_to, has_many, has_and_belongs_to_many)
240
- * :check_boxes (a set of check_box inputs) - alternative to :select has_many and has_and_belongs_to_many associations
241
- * :radio (a set of radio inputs) - alternative to :select for ActiveRecord belongs_to associations
242
- * :time_zone (a select input) - default for :string column types with 'time_zone' in the method name
243
- * :password (a password input) - default for :string column types with 'password' in the method name
244
- * :text (a textarea) - default for :text column types
245
- * :date (a date select) - default for :date column types
246
- * :datetime (a date and time select) - default for :datetime and :timestamp column types
247
- * :time (a time select) - default for :time column types
248
- * :boolean (a checkbox) - default for :boolean column types
249
- * :string (a text field) - default for :string column types
250
- * :numeric (a text field, like string) - default for :integer, :float and :decimal column types
251
- * :file (a file field) - default for paperclip or attachment_fu attributes
252
- * :country (a select menu of country names) - default for :string columns named "country", requires a country_select plugin to be installed
253
- * :hidden (a hidden field) - creates a hidden field (added for compatibility)
254
-
255
-
256
- 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.
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
- 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.
261
-
259
+ h3. Basic Localization
262
260
 
263
- h3. Label/Hint-localization
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
- Enhanced localization (labels + hints + titles/legends, with Formtastic):
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 @post do |form| %>
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@ # By model and action
377
- 2. @formtastic.{titles,labels,hints}.MODEL.ATTRIBUTE@ # By model
378
- 3. @formtastic.{titles,labels,hints}.ATTRIBUTE@ # Global default
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
- *Note:* For @title@: ATTRIBUTE is a KEY chosen by you, e.g. in step 4 example above: @:post_details@.
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 :required option (it checks the validations on the model instead).
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@. You can "view the configuration file on GitHub":http://github.com/justinfrench/formtastic/blob/master/generators/formtastic/templates/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. We've just recently jumped to an 0.9 version number, signalling that we consider this a 1.0 release candidate, and that the API won't change significantly for the 1.x series.
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 :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)
422
- * rspec, rspec_hpricot_matchers and rcov gems (plus any of their own dependencies) are required for the test suite
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). Patches are welcome to allow backwards compatibility, but I don't have the energy!
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/, where your contributions, forkings, comments and feedback are greatly welcomed.
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
- value = args.first.is_a?(String) ? args.shift : save_or_create_button_text
299
- options = args.shift || {}
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
- template.content_tag(:li, self.submit(value, button_html), :class => "commit")
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.to_s)
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
- self.label(method, input << self.label(method, options_for_label(options)), options_for_label(options))
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] = localized_attribute_string(method, options[:hint], :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] = localized_attribute_string(html_options[:name], html_options[:name], :title) if html_options[:name].is_a?(Symbol)
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 || column.to_s =~ /_id$/
1097
- parent_class = if reflection
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 localized_attribute_string(attr_name, attr_value, i18n_key)
1247
- if attr_value.is_a?(String)
1248
- attr_value
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 = attr_value.nil? ? @@i18n_lookups_by_default : (attr_value != false)
1251
-
1256
+ use_i18n = value.nil? ? @@i18n_lookups_by_default : (value != false)
1257
+
1252
1258
  if use_i18n
1253
- model_name = @object.class.name.underscore
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 = attr_name.to_s
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.#{i18n_key.to_s.pluralize}")
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.