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 CHANGED
@@ -1,4 +1,4 @@
1
- h1. Formtastic 0.1.4
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, has_many or has_and_belongs_to_many association) and a submit button:
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/option, or break the form into two fieldsets, the DSL is pretty comprehensive:
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 in the formtastic way:
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
- Each category will be wrapped in a fieldset with legend "Category #1",
214
- "Category #2" and so on. But please notice that this works only with Rails 2.3.
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. An extra huge thanks goes out to "José Valim":http://github.com/josevalim for nearly 50 patches.
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 - obviously inspired by Yahoo's reset.css, but scoped to just form.formtastic
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-left:25%; }
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
- form.formtastic fieldset ol li { margin-bottom:1.5em; }
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:#cc0000; margin:0.5em 0 0 25%; }
77
- form.formtastic fieldset ol li ul.errors { color:#cc0000; margin:0.5em 0 0 25%; list-style:square; }
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 ol { margin-bottom:-0.6em; }
102
- form.formtastic fieldset ol li.radio fieldset ol li { margin:0.1em 0 0.5em 0; }
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.2em; }
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 (or fieldset legend) text
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, options[:required])
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, required_option) #:nodoc:
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
- input_label(method, options.delete(:label), options.slice(:required)) + send(INPUT_MAPPINGS[type], method, html_options)
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
- input_label(input_name, options.delete(:label), options.slice(:required)) +
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
- input_label(method, options.delete(:label), options.slice(:required)) +
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 (VehicleOwner.find(:all) in the example above).
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 ? @@required_string : @@optional_string
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? Proc
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>#{input_label(method, options.delete(:label), options.slice(:required), true)}</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 =~ /time_?zone/
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