justinfrench-formtastic 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.textile +127 -11
- data/generators/formtastic_stylesheets/templates/formtastic.css +1 -0
- data/lib/formtastic.rb +112 -37
- data/lib/justin_french/formtastic.rb +3 -3
- data/spec/formtastic_spec.rb +216 -68
- metadata +4 -3
data/README.textile
CHANGED
|
@@ -72,6 +72,11 @@ h2. Opinions
|
|
|
72
72
|
* make the common things we do easy, yet still ensure uncommon things are still possible
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
h2. Documentation
|
|
76
|
+
|
|
77
|
+
RDoc documentation _should_ be automatically generated after each commit and made available on the "rdoc.info website":http://rdoc.info/projects/justinfrench/formtastic.
|
|
78
|
+
|
|
79
|
+
|
|
75
80
|
h2. Installation
|
|
76
81
|
|
|
77
82
|
You can (and should) get it as a gem:
|
|
@@ -245,6 +250,123 @@ h2. The Available Inputs
|
|
|
245
250
|
|
|
246
251
|
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.
|
|
247
252
|
|
|
253
|
+
h2. Internationalization (I18n)
|
|
254
|
+
|
|
255
|
+
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.
|
|
256
|
+
|
|
257
|
+
h3. Label/Hint-localization
|
|
258
|
+
|
|
259
|
+
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:
|
|
260
|
+
|
|
261
|
+
Basic localization (labels only):
|
|
262
|
+
|
|
263
|
+
<pre>
|
|
264
|
+
<% semantic_form_for @post do |form| %>
|
|
265
|
+
<%= form.input :title %> # => :label => I18n.t('activerecord.attributes.user.title') or 'Title'
|
|
266
|
+
<%= form.input :body %> # => :label => I18n.t('activerecord.attributes.user.body') or 'Body'
|
|
267
|
+
<%= form.input :section %> # => :label => I18n.t('activerecord.attributes.user.section') or 'Section'
|
|
268
|
+
<% end %>
|
|
269
|
+
</pre>
|
|
270
|
+
|
|
271
|
+
*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?
|
|
272
|
+
|
|
273
|
+
Enhanced localization (labels and hints):
|
|
274
|
+
|
|
275
|
+
1. Enable I18n lookups by default (@config/initializers/formtastic.rb@):
|
|
276
|
+
|
|
277
|
+
<pre>
|
|
278
|
+
Formtastic::SemanticFormBuilder.i18n_lookups_by_default = true
|
|
279
|
+
</pre>
|
|
280
|
+
|
|
281
|
+
2. Add some cool label-translations/variants (@config/locale/en.yml@):
|
|
282
|
+
|
|
283
|
+
<pre>
|
|
284
|
+
en:
|
|
285
|
+
formtastic:
|
|
286
|
+
labels:
|
|
287
|
+
post:
|
|
288
|
+
title: "Choose a title..."
|
|
289
|
+
body: "Write something..."
|
|
290
|
+
hints:
|
|
291
|
+
post:
|
|
292
|
+
title: "Choose a good title for you post."
|
|
293
|
+
body: "Write something inspiring here."
|
|
294
|
+
</pre>
|
|
295
|
+
|
|
296
|
+
*Note:* We are using English here still, but you get the point.
|
|
297
|
+
|
|
298
|
+
3. ...and now you'll get:
|
|
299
|
+
|
|
300
|
+
<pre>
|
|
301
|
+
<% semantic_form_for @post do |form| %>
|
|
302
|
+
<%= form.input :title %> # => :label => "Choose a title...", :hint => "Choose a good title for you post."
|
|
303
|
+
<%= form.input :body %> # => :label => "Write something...", :hint => "Write something inspiring here."
|
|
304
|
+
<%= form.input :section %> # => :label => I18n.t('activerecord.attributes.user.section') or 'Section'
|
|
305
|
+
<% end %>
|
|
306
|
+
</pre>
|
|
307
|
+
|
|
308
|
+
4. Override I18n settings:
|
|
309
|
+
|
|
310
|
+
<pre>
|
|
311
|
+
<% semantic_form_for @post do |form| %>
|
|
312
|
+
<%= form.input :title %> # => :label => "Choose a title...", :hint => "Choose a good title for you post."
|
|
313
|
+
<%= form.input :body, :hint => false %> # => :label => "Write something..."
|
|
314
|
+
<%= form.input :section %> # => :label => I18n.t('activerecord.attributes.user.section') or 'Section'
|
|
315
|
+
<% end %>
|
|
316
|
+
</pre>
|
|
317
|
+
|
|
318
|
+
If I18n-lookups is disabled, i.e.:
|
|
319
|
+
|
|
320
|
+
<pre>
|
|
321
|
+
Formtastic::SemanticFormBuilder.i18n_lookups_by_default = false
|
|
322
|
+
</pre>
|
|
323
|
+
|
|
324
|
+
...then you can enable I18n within the forms instead:
|
|
325
|
+
|
|
326
|
+
<pre>
|
|
327
|
+
<% semantic_form_for @post do |form| %>
|
|
328
|
+
<%= form.input :title, :label => true %> # => :label => "Choose a title..."
|
|
329
|
+
<%= form.input :body, :label => true %> # => :label => "Write something..."
|
|
330
|
+
<%= form.input :section, :label => true %> # => :label => I18n.t('activerecord.attributes.user.section') or 'Section'
|
|
331
|
+
<% end %>
|
|
332
|
+
</pre>
|
|
333
|
+
|
|
334
|
+
5. Advanced I18n lookups
|
|
335
|
+
|
|
336
|
+
For more flexible forms; Formtastic find translations using a bottom-up approach taking the following variables in account:
|
|
337
|
+
|
|
338
|
+
* @model@, e.g. "post"
|
|
339
|
+
* @action@, e.g. "edit"
|
|
340
|
+
* @attribute@, e.g. "title"
|
|
341
|
+
|
|
342
|
+
...in the following order:
|
|
343
|
+
|
|
344
|
+
1. @formtastic.{labels,hints}.MODEL.ACTION.ATTRIBUTE@ # By model and action
|
|
345
|
+
2. @formtastic.{labels,hints}.MODEL.ATTRIBUTE@ # By model
|
|
346
|
+
3. @formtastic.{labels,hints}.ATTRIBUTE@ # Global default
|
|
347
|
+
|
|
348
|
+
...which means that you can define translations like this:
|
|
349
|
+
|
|
350
|
+
<pre>
|
|
351
|
+
en:
|
|
352
|
+
formtastic:
|
|
353
|
+
labels:
|
|
354
|
+
title: "Title" # Default global value
|
|
355
|
+
article:
|
|
356
|
+
body: "Article content"
|
|
357
|
+
post:
|
|
358
|
+
new:
|
|
359
|
+
title: "Choose a title..."
|
|
360
|
+
body: "Write something..."
|
|
361
|
+
edit:
|
|
362
|
+
title: "Edit title"
|
|
363
|
+
body: "Edit body"
|
|
364
|
+
...
|
|
365
|
+
</pre>
|
|
366
|
+
|
|
367
|
+
h2. ValidationReflection plugin
|
|
368
|
+
|
|
369
|
+
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).
|
|
248
370
|
|
|
249
371
|
h2. Configuration
|
|
250
372
|
|
|
@@ -291,19 +413,13 @@ If you wish, put something like this in config/initializers/formtastic_config.rb
|
|
|
291
413
|
|
|
292
414
|
# Set the default "priority countries" to suit your user base when using :as => :country
|
|
293
415
|
Formtastic::SemanticFormBuilder.priority_countries = ["Australia", "New Zealand"]
|
|
416
|
+
|
|
417
|
+
# Specifies if labels/hints for input fields automatically be looked up using I18n.
|
|
418
|
+
# Default value: false. Overridden for specific fields by setting value to true,
|
|
419
|
+
# i.e. :label => true, or :hint => true (or opposite depending on initialized value)
|
|
420
|
+
# Formtastic::SemanticFormBuilder.i18n_lookups_by_default = false
|
|
294
421
|
</pre>
|
|
295
422
|
|
|
296
|
-
|
|
297
|
-
h2. Internationalization (I18n)
|
|
298
|
-
|
|
299
|
-
Supports I18n! 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.
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
h2. ValidationReflection plugin
|
|
303
|
-
|
|
304
|
-
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).
|
|
305
|
-
|
|
306
|
-
|
|
307
423
|
h2. Status
|
|
308
424
|
|
|
309
425
|
*THINGS ARE GOING TO CHANGE A BIT BEFORE WE HIT 1.0.*
|
|
@@ -83,6 +83,7 @@ form.formtastic fieldset ol li ul.errors li { padding:0; border:none; display:li
|
|
|
83
83
|
/* STRING & NUMERIC OVERRIDES
|
|
84
84
|
--------------------------------------------------------------------------------------------------*/
|
|
85
85
|
form.formtastic fieldset ol li.string input { width:74%; }
|
|
86
|
+
form.formtastic fieldset ol li.password input { width:74%; }
|
|
86
87
|
form.formtastic fieldset ol li.numeric input { width:74%; }
|
|
87
88
|
|
|
88
89
|
|
data/lib/formtastic.rb
CHANGED
|
@@ -19,10 +19,15 @@ module Formtastic #:nodoc:
|
|
|
19
19
|
@@inline_order = [ :input, :hints, :errors ]
|
|
20
20
|
@@file_methods = [ :file?, :public_filename ]
|
|
21
21
|
@@priority_countries = ["Australia", "Canada", "United Kingdom", "United States"]
|
|
22
|
+
@@i18n_lookups_by_default = false
|
|
22
23
|
|
|
23
24
|
cattr_accessor :default_text_field_size, :all_fields_required_by_default, :required_string,
|
|
24
25
|
:optional_string, :inline_errors, :label_str_method, :collection_label_methods,
|
|
25
|
-
:inline_order, :file_methods, :priority_countries
|
|
26
|
+
:inline_order, :file_methods, :priority_countries, :i18n_lookups_by_default
|
|
27
|
+
|
|
28
|
+
I18N_SCOPES = [ '{{model}}.{{action}}.{{attribute}}',
|
|
29
|
+
'{{model}}.{{attribute}}',
|
|
30
|
+
'{{attribute}}']
|
|
26
31
|
|
|
27
32
|
# Keeps simple mappings in a hash
|
|
28
33
|
INPUT_MAPPINGS = {
|
|
@@ -85,7 +90,7 @@ module Formtastic #:nodoc:
|
|
|
85
90
|
options[:as] ||= default_input_type(method)
|
|
86
91
|
|
|
87
92
|
html_class = [ options[:as], (options[:required] ? :required : :optional) ]
|
|
88
|
-
html_class << 'error' if @object && @object.respond_to?(:errors) && @object.errors
|
|
93
|
+
html_class << 'error' if @object && @object.respond_to?(:errors) && @object.errors[method.to_sym]
|
|
89
94
|
|
|
90
95
|
wrapper_html = options.delete(:wrapper_html) || {}
|
|
91
96
|
wrapper_html[:id] ||= generate_html_id(method)
|
|
@@ -95,7 +100,15 @@ module Formtastic #:nodoc:
|
|
|
95
100
|
::ActiveSupport::Deprecation.warn(":as => :#{options[:as]} is deprecated, use :as => :#{options[:as].to_s[8..-1]} instead", caller[3..-1])
|
|
96
101
|
end
|
|
97
102
|
|
|
98
|
-
|
|
103
|
+
if options[:input_html] && options[:input_html][:id]
|
|
104
|
+
options[:label_html] ||= {}
|
|
105
|
+
options[:label_html][:for] ||= options[:input_html][:id]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
input_parts = @@inline_order.dup
|
|
109
|
+
input_parts.delete(:errors) if options[:as] == :hidden
|
|
110
|
+
|
|
111
|
+
list_item_content = input_parts.map do |type|
|
|
99
112
|
send(:"inline_#{type}_for", method, options)
|
|
100
113
|
end.compact.join("\n")
|
|
101
114
|
|
|
@@ -325,6 +338,8 @@ module Formtastic #:nodoc:
|
|
|
325
338
|
# * :label - An alternative form to give the label content. Whenever label
|
|
326
339
|
# is false, a blank string is returned.
|
|
327
340
|
# * :as_span - When true returns a span tag with class label instead of a label element
|
|
341
|
+
# * :input_name - Gives the input to match for. This is needed when you want to
|
|
342
|
+
# to call f.label :authors but it should match :author_ids.
|
|
328
343
|
#
|
|
329
344
|
# == Examples
|
|
330
345
|
#
|
|
@@ -337,23 +352,23 @@ module Formtastic #:nodoc:
|
|
|
337
352
|
#
|
|
338
353
|
def label(method, options_or_text=nil, options=nil)
|
|
339
354
|
if options_or_text.is_a?(Hash)
|
|
340
|
-
return if options_or_text[:label] == false
|
|
341
|
-
|
|
355
|
+
return "" if options_or_text[:label] == false
|
|
342
356
|
options = options_or_text
|
|
343
|
-
text
|
|
357
|
+
text = options.delete(:label)
|
|
344
358
|
else
|
|
345
|
-
text
|
|
359
|
+
text = options_or_text
|
|
346
360
|
options ||= {}
|
|
347
361
|
end
|
|
348
362
|
|
|
349
|
-
text
|
|
350
|
-
text
|
|
363
|
+
text = localized_attribute_string(method, text, :label) || humanized_attribute_name(method)
|
|
364
|
+
text += required_or_optional_string(options.delete(:required))
|
|
351
365
|
|
|
366
|
+
input_name = options.delete(:input_name) || method
|
|
352
367
|
if options.delete(:as_span)
|
|
353
368
|
options[:class] ||= 'label'
|
|
354
369
|
template.content_tag(:span, text, options)
|
|
355
370
|
else
|
|
356
|
-
super(
|
|
371
|
+
super(input_name, text, options)
|
|
357
372
|
end
|
|
358
373
|
end
|
|
359
374
|
|
|
@@ -371,13 +386,19 @@ module Formtastic #:nodoc:
|
|
|
371
386
|
def inline_errors_for(method, options=nil) #:nodoc:
|
|
372
387
|
return nil unless @object && @object.respond_to?(:errors) && [:sentence, :list].include?(@@inline_errors)
|
|
373
388
|
|
|
374
|
-
errors = @object.errors
|
|
389
|
+
errors = @object.errors[method.to_sym]
|
|
375
390
|
send("error_#{@@inline_errors}", Array(errors)) unless errors.blank?
|
|
376
391
|
end
|
|
377
392
|
alias :errors_on :inline_errors_for
|
|
378
393
|
|
|
379
394
|
protected
|
|
380
395
|
|
|
396
|
+
# Prepare options to be sent to label
|
|
397
|
+
#
|
|
398
|
+
def options_for_label(options)
|
|
399
|
+
options.slice(:label, :required).merge!(options.fetch(:label_html, {}))
|
|
400
|
+
end
|
|
401
|
+
|
|
381
402
|
# Deals with :for option when it's supplied to inputs methods. Additional
|
|
382
403
|
# options to be passed down to :for should be supplied using :for_options
|
|
383
404
|
# key.
|
|
@@ -457,9 +478,9 @@ module Formtastic #:nodoc:
|
|
|
457
478
|
#
|
|
458
479
|
def input_simple(type, method, options)
|
|
459
480
|
html_options = options.delete(:input_html) || {}
|
|
460
|
-
html_options = default_string_options(method).merge(html_options) if STRING_MAPPINGS.include?(type)
|
|
481
|
+
html_options = default_string_options(method, type).merge(html_options) if STRING_MAPPINGS.include?(type)
|
|
461
482
|
|
|
462
|
-
self.label(method, options
|
|
483
|
+
self.label(method, options_for_label(options)) +
|
|
463
484
|
self.send(INPUT_MAPPINGS[type], method, html_options)
|
|
464
485
|
end
|
|
465
486
|
|
|
@@ -510,10 +531,10 @@ module Formtastic #:nodoc:
|
|
|
510
531
|
# </select>
|
|
511
532
|
#
|
|
512
533
|
#
|
|
513
|
-
# You can customize the options available in the select by passing in a collection (Array
|
|
514
|
-
#
|
|
515
|
-
#
|
|
516
|
-
#
|
|
534
|
+
# You can customize the options available in the select by passing in a collection (an Array or
|
|
535
|
+
# Hash) through the :collection option. If not provided, the choices are found by inferring the
|
|
536
|
+
# parent's class name from the method name and simply calling find(:all) on it
|
|
537
|
+
# (VehicleOwner.find(:all) in the example above).
|
|
517
538
|
#
|
|
518
539
|
# Examples:
|
|
519
540
|
#
|
|
@@ -521,6 +542,7 @@ module Formtastic #:nodoc:
|
|
|
521
542
|
# f.input :author, :collection => Author.find(:all)
|
|
522
543
|
# f.input :author, :collection => [@justin, @kate]
|
|
523
544
|
# f.input :author, :collection => {@justin.name => @justin.id, @kate.name => @kate.id}
|
|
545
|
+
# f.input :author, :collection => ["Justin", "Kate", "Amelia", "Gus", "Meg"]
|
|
524
546
|
#
|
|
525
547
|
# Note: This input looks for a label method in the parent association.
|
|
526
548
|
#
|
|
@@ -565,12 +587,13 @@ module Formtastic #:nodoc:
|
|
|
565
587
|
|
|
566
588
|
reflection = find_reflection(method)
|
|
567
589
|
if reflection && [ :has_many, :has_and_belongs_to_many ].include?(reflection.macro)
|
|
590
|
+
options[:include_blank] = false
|
|
568
591
|
html_options[:multiple] ||= true
|
|
569
592
|
html_options[:size] ||= 5
|
|
570
593
|
end
|
|
571
594
|
|
|
572
595
|
input_name = generate_association_input_name(method)
|
|
573
|
-
self.label(
|
|
596
|
+
self.label(method, options_for_label(options).merge(:input_name => input_name)) +
|
|
574
597
|
self.select(input_name, collection, set_options(options), html_options)
|
|
575
598
|
end
|
|
576
599
|
alias :boolean_select_input :select_input
|
|
@@ -585,7 +608,7 @@ module Formtastic #:nodoc:
|
|
|
585
608
|
def time_zone_input(method, options)
|
|
586
609
|
html_options = options.delete(:input_html) || {}
|
|
587
610
|
|
|
588
|
-
self.label(method, options
|
|
611
|
+
self.label(method, options_for_label(options)) +
|
|
589
612
|
self.time_zone_select(method, options.delete(:priority_zones), set_options(options), html_options)
|
|
590
613
|
end
|
|
591
614
|
|
|
@@ -611,16 +634,17 @@ module Formtastic #:nodoc:
|
|
|
611
634
|
# </ol>
|
|
612
635
|
# </fieldset>
|
|
613
636
|
#
|
|
614
|
-
# You can customize the options available in the
|
|
615
|
-
#
|
|
616
|
-
#
|
|
617
|
-
#
|
|
637
|
+
# You can customize the options available in the select by passing in a collection (an Array or
|
|
638
|
+
# Hash) through the :collection option. If not provided, the choices are found by inferring the
|
|
639
|
+
# parent's class name from the method name and simply calling find(:all) on it
|
|
640
|
+
# (Author.find(:all) in the example above).
|
|
618
641
|
#
|
|
619
642
|
# Examples:
|
|
620
643
|
#
|
|
621
644
|
# f.input :author, :as => :radio, :collection => @authors
|
|
622
645
|
# f.input :author, :as => :radio, :collection => Author.find(:all)
|
|
623
646
|
# f.input :author, :as => :radio, :collection => [@justin, @kate]
|
|
647
|
+
# f.input :author, :collection => ["Justin", "Kate", "Amelia", "Gus", "Meg"]
|
|
624
648
|
#
|
|
625
649
|
# You can also customize the text label inside each option tag, by naming the correct method
|
|
626
650
|
# (:full_name, :display_name, :account_number, etc) to call on each object in the collection
|
|
@@ -865,7 +889,7 @@ module Formtastic #:nodoc:
|
|
|
865
889
|
html_options = options.delete(:input_html) || {}
|
|
866
890
|
priority_countries = options.delete(:priority_countries) || @@priority_countries
|
|
867
891
|
|
|
868
|
-
self.label(method, options
|
|
892
|
+
self.label(method, options_for_label(options)) +
|
|
869
893
|
self.country_select(method, priority_countries, set_options(options), html_options)
|
|
870
894
|
end
|
|
871
895
|
|
|
@@ -881,7 +905,7 @@ module Formtastic #:nodoc:
|
|
|
881
905
|
options.delete(:checked_value) || '1', options.delete(:unchecked_value) || '0')
|
|
882
906
|
|
|
883
907
|
label = options.delete(:label) || humanized_attribute_name(method)
|
|
884
|
-
self.label(method, input + label, options
|
|
908
|
+
self.label(method, input + label, options_for_label(options))
|
|
885
909
|
end
|
|
886
910
|
|
|
887
911
|
# Generates an input for the given method using the type supplied with :as.
|
|
@@ -904,6 +928,7 @@ module Formtastic #:nodoc:
|
|
|
904
928
|
# Generates hints for the given method using the text supplied in :hint.
|
|
905
929
|
#
|
|
906
930
|
def inline_hints_for(method, options) #:nodoc:
|
|
931
|
+
options[:hint] = localized_attribute_string(method, options[:hint], :hint)
|
|
907
932
|
return if options[:hint].blank?
|
|
908
933
|
template.content_tag(:p, options[:hint], :class => 'inline-hints')
|
|
909
934
|
end
|
|
@@ -978,7 +1003,7 @@ module Formtastic #:nodoc:
|
|
|
978
1003
|
contents = contents.join if contents.respond_to?(:join)
|
|
979
1004
|
|
|
980
1005
|
template.content_tag(:fieldset,
|
|
981
|
-
%{<legend>#{self.label(method, options
|
|
1006
|
+
%{<legend>#{self.label(method, options_for_label(options).merge!(:as_span => true))}</legend>} +
|
|
982
1007
|
template.content_tag(:ol, contents)
|
|
983
1008
|
)
|
|
984
1009
|
end
|
|
@@ -992,8 +1017,6 @@ module Formtastic #:nodoc:
|
|
|
992
1017
|
# default is a :string, a similar behaviour to Rails' scaffolding.
|
|
993
1018
|
#
|
|
994
1019
|
def default_input_type(method) #:nodoc:
|
|
995
|
-
return :string if @object.nil?
|
|
996
|
-
|
|
997
1020
|
column = @object.column_for_attribute(method) if @object.respond_to?(:column_for_attribute)
|
|
998
1021
|
|
|
999
1022
|
if column
|
|
@@ -1008,10 +1031,13 @@ module Formtastic #:nodoc:
|
|
|
1008
1031
|
# otherwise assume the input name will be the same as the column type (eg string_input)
|
|
1009
1032
|
return column.type
|
|
1010
1033
|
else
|
|
1011
|
-
|
|
1034
|
+
if @object
|
|
1035
|
+
return :select if find_reflection(method)
|
|
1036
|
+
|
|
1037
|
+
file = @object.send(method) if @object.respond_to?(method)
|
|
1038
|
+
return :file if file && @@file_methods.any? { |m| file.respond_to?(m) }
|
|
1039
|
+
end
|
|
1012
1040
|
|
|
1013
|
-
return :select if find_reflection(method)
|
|
1014
|
-
return :file if obj && @@file_methods.any? { |m| obj.respond_to?(m) }
|
|
1015
1041
|
return :password if method.to_s =~ /password/
|
|
1016
1042
|
return :string
|
|
1017
1043
|
end
|
|
@@ -1073,13 +1099,14 @@ module Formtastic #:nodoc:
|
|
|
1073
1099
|
options[:false] ||= I18n.t('no', :default => 'No', :scope => [:formtastic])
|
|
1074
1100
|
options[:value_as_class] = true unless options.key?(:value_as_class)
|
|
1075
1101
|
|
|
1076
|
-
|
|
1102
|
+
[ [ options.delete(:true), true], [ options.delete(:false), false ] ]
|
|
1077
1103
|
end
|
|
1078
1104
|
|
|
1079
1105
|
# Used by association inputs (select, radio) to generate the name that should
|
|
1080
1106
|
# be used for the input
|
|
1081
1107
|
#
|
|
1082
1108
|
# belongs_to :author; f.input :author; will generate 'author_id'
|
|
1109
|
+
# belongs_to :entity, :foreign_key = :owner_id; f.input :author; will generate 'owner_id'
|
|
1083
1110
|
# has_many :authors; f.input :authors; will generate 'author_ids'
|
|
1084
1111
|
# has_and_belongs_to_many will act like has_many
|
|
1085
1112
|
#
|
|
@@ -1088,7 +1115,7 @@ module Formtastic #:nodoc:
|
|
|
1088
1115
|
if [:has_and_belongs_to_many, :has_many].include?(reflection.macro)
|
|
1089
1116
|
"#{method.to_s.singularize}_ids"
|
|
1090
1117
|
else
|
|
1091
|
-
"#{method}_id"
|
|
1118
|
+
reflection.options[:foreign_key] || "#{method}_id"
|
|
1092
1119
|
end
|
|
1093
1120
|
else
|
|
1094
1121
|
method
|
|
@@ -1105,10 +1132,10 @@ module Formtastic #:nodoc:
|
|
|
1105
1132
|
# Generates default_string_options by retrieving column information from
|
|
1106
1133
|
# the database.
|
|
1107
1134
|
#
|
|
1108
|
-
def default_string_options(method) #:nodoc:
|
|
1135
|
+
def default_string_options(method, type) #:nodoc:
|
|
1109
1136
|
column = @object.column_for_attribute(method) if @object.respond_to?(:column_for_attribute)
|
|
1110
1137
|
|
|
1111
|
-
if column.nil? || column.limit.nil?
|
|
1138
|
+
if type == :numeric || column.nil? || column.limit.nil?
|
|
1112
1139
|
{ :size => @@default_text_field_size }
|
|
1113
1140
|
else
|
|
1114
1141
|
{ :maxlength => column.limit, :size => [column.limit, @@default_text_field_size].min }
|
|
@@ -1128,8 +1155,8 @@ module Formtastic #:nodoc:
|
|
|
1128
1155
|
else
|
|
1129
1156
|
index = ""
|
|
1130
1157
|
end
|
|
1131
|
-
sanitized_method_name = method_name.to_s.
|
|
1132
|
-
|
|
1158
|
+
sanitized_method_name = method_name.to_s.gsub(/[\?\/\-]$/, '')
|
|
1159
|
+
|
|
1133
1160
|
"#{sanitized_object_name}#{index}_#{sanitized_method_name}_#{value}"
|
|
1134
1161
|
end
|
|
1135
1162
|
|
|
@@ -1161,6 +1188,54 @@ module Formtastic #:nodoc:
|
|
|
1161
1188
|
end
|
|
1162
1189
|
end
|
|
1163
1190
|
|
|
1191
|
+
# Internal generic method for looking up localized values within Formtastic
|
|
1192
|
+
# using I18n, if no explicit value is set and I18n-lookups are enabled.
|
|
1193
|
+
#
|
|
1194
|
+
# Enabled/Disable this by setting:
|
|
1195
|
+
#
|
|
1196
|
+
# Formtastic::SemanticFormBuilder.i18n_lookups_by_default = true/false
|
|
1197
|
+
#
|
|
1198
|
+
# Lookup priority:
|
|
1199
|
+
#
|
|
1200
|
+
# 'formtastic.{{type}}.{{model}}.{{action}}.{{attribute}}'
|
|
1201
|
+
# 'formtastic.{{type}}.{{model}}.{{attribute}}'
|
|
1202
|
+
# 'formtastic.{{type}}.{{attribute}}'
|
|
1203
|
+
#
|
|
1204
|
+
# Example:
|
|
1205
|
+
#
|
|
1206
|
+
# 'formtastic.labels.post.edit.title'
|
|
1207
|
+
# 'formtastic.labels.post.title'
|
|
1208
|
+
# 'formtastic.labels.title'
|
|
1209
|
+
#
|
|
1210
|
+
# NOTE: Generic, but only used for form input labels/hints.
|
|
1211
|
+
#
|
|
1212
|
+
def localized_attribute_string(attr_name, attr_value, i18n_key)
|
|
1213
|
+
if attr_value.is_a?(String)
|
|
1214
|
+
attr_value
|
|
1215
|
+
else
|
|
1216
|
+
use_i18n = attr_value.nil? ? @@i18n_lookups_by_default : attr_value
|
|
1217
|
+
if use_i18n
|
|
1218
|
+
model_name = @object.class.name.underscore
|
|
1219
|
+
action_name = template.params[:action].to_s rescue ''
|
|
1220
|
+
attribute_name = attr_name.to_s
|
|
1221
|
+
|
|
1222
|
+
defaults = I18N_SCOPES.collect do |i18n_scope|
|
|
1223
|
+
i18n_path = i18n_scope.dup
|
|
1224
|
+
i18n_path.gsub!('{{action}}', action_name)
|
|
1225
|
+
i18n_path.gsub!('{{model}}', model_name)
|
|
1226
|
+
i18n_path.gsub!('{{attribute}}', attribute_name)
|
|
1227
|
+
i18n_path.gsub!('..', '.')
|
|
1228
|
+
i18n_path.to_sym
|
|
1229
|
+
end
|
|
1230
|
+
defaults << ''
|
|
1231
|
+
|
|
1232
|
+
i18n_value = ::I18n.t(defaults.shift, :default => defaults,
|
|
1233
|
+
:scope => "formtastic.#{i18n_key.to_s.pluralize}")
|
|
1234
|
+
i18n_value.blank? ? nil : i18n_value
|
|
1235
|
+
end
|
|
1236
|
+
end
|
|
1237
|
+
end
|
|
1238
|
+
|
|
1164
1239
|
end
|
|
1165
1240
|
|
|
1166
1241
|
# Wrappers around form_for (etc) with :builder => SemanticFormBuilder.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
module JustinFrench
|
|
2
|
-
module Formtastic
|
|
3
|
-
class SemanticFormBuilder < ::Formtastic::SemanticFormBuilder
|
|
1
|
+
module JustinFrench #:nodoc:
|
|
2
|
+
module Formtastic #:nodoc:
|
|
3
|
+
class SemanticFormBuilder < ::Formtastic::SemanticFormBuilder #:nodoc:
|
|
4
4
|
def initialize(*args)
|
|
5
5
|
::ActiveSupport::Deprecation.warn("JustinFrench::Formtastic::SemanticFormBuilder is deprecated. User Formtastic::SemanticFormBuilder instead", caller)
|
|
6
6
|
super
|
data/spec/formtastic_spec.rb
CHANGED
|
@@ -62,7 +62,7 @@ describe 'Formtastic' do
|
|
|
62
62
|
@fred.stub!(:login).and_return('fred_smith')
|
|
63
63
|
@fred.stub!(:id).and_return(37)
|
|
64
64
|
@fred.stub!(:new_record?).and_return(false)
|
|
65
|
-
@fred.stub!(:errors).and_return(mock('errors', :
|
|
65
|
+
@fred.stub!(:errors).and_return(mock('errors', :[] => nil))
|
|
66
66
|
|
|
67
67
|
@bob = mock('user')
|
|
68
68
|
@bob.stub!(:class).and_return(Author)
|
|
@@ -72,20 +72,20 @@ describe 'Formtastic' do
|
|
|
72
72
|
@bob.stub!(:posts).and_return([])
|
|
73
73
|
@bob.stub!(:post_ids).and_return([])
|
|
74
74
|
@bob.stub!(:new_record?).and_return(false)
|
|
75
|
-
@bob.stub!(:errors).and_return(mock('errors', :
|
|
75
|
+
@bob.stub!(:errors).and_return(mock('errors', :[] => nil))
|
|
76
76
|
|
|
77
77
|
Author.stub!(:find).and_return([@fred, @bob])
|
|
78
78
|
Author.stub!(:human_attribute_name).and_return { |column_name| column_name.humanize }
|
|
79
79
|
Author.stub!(:human_name).and_return('Author')
|
|
80
80
|
Author.stub!(:reflect_on_all_validations).and_return([])
|
|
81
|
-
Author.stub!(:reflect_on_association).and_return { |column_name| mock('reflection', :klass => Post, :macro => :has_many) if column_name == :posts }
|
|
81
|
+
Author.stub!(:reflect_on_association).and_return { |column_name| mock('reflection', :options => {}, :klass => Post, :macro => :has_many) if column_name == :posts }
|
|
82
82
|
|
|
83
83
|
# Sometimes we need a mock @post object and some Authors for belongs_to
|
|
84
84
|
@new_post = mock('post')
|
|
85
85
|
@new_post.stub!(:class).and_return(Post)
|
|
86
86
|
@new_post.stub!(:id).and_return(nil)
|
|
87
87
|
@new_post.stub!(:new_record?).and_return(true)
|
|
88
|
-
@new_post.stub!(:errors).and_return(mock('errors', :
|
|
88
|
+
@new_post.stub!(:errors).and_return(mock('errors', :[] => nil))
|
|
89
89
|
@new_post.stub!(:author).and_return(nil)
|
|
90
90
|
|
|
91
91
|
@freds_post = mock('post')
|
|
@@ -97,7 +97,7 @@ describe 'Formtastic' do
|
|
|
97
97
|
@freds_post.stub!(:authors).and_return([@fred])
|
|
98
98
|
@freds_post.stub!(:author_ids).and_return([@fred.id])
|
|
99
99
|
@freds_post.stub!(:new_record?).and_return(false)
|
|
100
|
-
@freds_post.stub!(:errors).and_return(mock('errors', :
|
|
100
|
+
@freds_post.stub!(:errors).and_return(mock('errors', :[] => nil))
|
|
101
101
|
@fred.stub!(:posts).and_return([@freds_post])
|
|
102
102
|
@fred.stub!(:post_ids).and_return([@freds_post.id])
|
|
103
103
|
|
|
@@ -107,9 +107,9 @@ describe 'Formtastic' do
|
|
|
107
107
|
Post.stub!(:reflect_on_association).and_return do |column_name|
|
|
108
108
|
case column_name
|
|
109
109
|
when :author, :author_status
|
|
110
|
-
mock('reflection', :klass => Author, :macro => :belongs_to)
|
|
110
|
+
mock('reflection', :options => {}, :klass => Author, :macro => :belongs_to)
|
|
111
111
|
when :authors
|
|
112
|
-
mock('reflection', :klass => Author, :macro => :has_and_belongs_to_many)
|
|
112
|
+
mock('reflection', :options => {}, :klass => Author, :macro => :has_and_belongs_to_many)
|
|
113
113
|
end
|
|
114
114
|
end
|
|
115
115
|
Post.stub!(:find).and_return([@freds_post])
|
|
@@ -326,7 +326,7 @@ describe 'Formtastic' do
|
|
|
326
326
|
|
|
327
327
|
it 'should return nil if label is false' do
|
|
328
328
|
semantic_form_for(@new_post) do |builder|
|
|
329
|
-
builder.label(:login, :label => false).should
|
|
329
|
+
builder.label(:login, :label => false).should be_blank
|
|
330
330
|
end
|
|
331
331
|
end
|
|
332
332
|
end
|
|
@@ -336,8 +336,8 @@ describe 'Formtastic' do
|
|
|
336
336
|
before(:each) do
|
|
337
337
|
@title_errors = ['must not be blank', 'must be longer than 10 characters', 'must be awesome']
|
|
338
338
|
@errors = mock('errors')
|
|
339
|
-
@errors.stub!(:
|
|
340
|
-
@errors.stub!(:
|
|
339
|
+
@errors.stub!(:[]).with(:title).and_return(@title_errors)
|
|
340
|
+
@errors.stub!(:[]).with(:body).and_return(nil)
|
|
341
341
|
@new_post.stub!(:errors).and_return(@errors)
|
|
342
342
|
end
|
|
343
343
|
|
|
@@ -573,13 +573,22 @@ describe 'Formtastic' do
|
|
|
573
573
|
|
|
574
574
|
describe 'when not provided' do
|
|
575
575
|
|
|
576
|
-
it 'should default to a string for forms without objects' do
|
|
576
|
+
it 'should default to a string for forms without objects unless column is password' do
|
|
577
577
|
semantic_form_for(:project, :url => 'http://test.host') do |builder|
|
|
578
578
|
concat(builder.input(:anything))
|
|
579
579
|
end
|
|
580
580
|
output_buffer.should have_tag('form li.string')
|
|
581
581
|
end
|
|
582
582
|
|
|
583
|
+
it 'should default to password for forms without objects if column is password' do
|
|
584
|
+
semantic_form_for(:project, :url => 'http://test.host') do |builder|
|
|
585
|
+
concat(builder.input(:password))
|
|
586
|
+
concat(builder.input(:password_confirmation))
|
|
587
|
+
concat(builder.input(:confirm_password))
|
|
588
|
+
end
|
|
589
|
+
output_buffer.should have_tag('form li.password', :count => 3)
|
|
590
|
+
end
|
|
591
|
+
|
|
583
592
|
it 'should default to a string for methods on objects that don\'t respond to "column_for_attribute"' do
|
|
584
593
|
@new_post.stub!(:method_without_a_database_column)
|
|
585
594
|
@new_post.stub!(:column_for_attribute).and_return(nil)
|
|
@@ -688,9 +697,8 @@ describe 'Formtastic' do
|
|
|
688
697
|
end
|
|
689
698
|
|
|
690
699
|
describe ':label option' do
|
|
691
|
-
|
|
700
|
+
|
|
692
701
|
describe 'when provided' do
|
|
693
|
-
|
|
694
702
|
it 'should be passed down to the label tag' do
|
|
695
703
|
semantic_form_for(@new_post) do |builder|
|
|
696
704
|
concat(builder.input(:title, :label => "Kustom"))
|
|
@@ -698,35 +706,89 @@ describe 'Formtastic' do
|
|
|
698
706
|
output_buffer.should have_tag("form li label", /Kustom/)
|
|
699
707
|
end
|
|
700
708
|
|
|
709
|
+
it 'should not generate a label if false' do
|
|
710
|
+
semantic_form_for(@new_post) do |builder|
|
|
711
|
+
concat(builder.input(:title, :label => false))
|
|
712
|
+
end
|
|
713
|
+
output_buffer.should_not have_tag("form li label")
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
it 'should be dupped if frozen' do
|
|
717
|
+
semantic_form_for(@new_post) do |builder|
|
|
718
|
+
concat(builder.input(:title, :label => "Kustom".freeze))
|
|
719
|
+
end
|
|
720
|
+
output_buffer.should have_tag("form li label", /Kustom/)
|
|
721
|
+
end
|
|
701
722
|
end
|
|
702
723
|
|
|
703
724
|
describe 'when not provided' do
|
|
704
|
-
describe '
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
725
|
+
describe 'when localized label is NOT provided' do
|
|
726
|
+
describe 'and object is not given' do
|
|
727
|
+
it 'should default the humanized method name, passing it down to the label tag' do
|
|
728
|
+
Formtastic::SemanticFormBuilder.label_str_method = :humanize
|
|
729
|
+
|
|
730
|
+
semantic_form_for(:project, :url => 'http://test.host') do |builder|
|
|
731
|
+
concat(builder.input(:meta_description))
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
output_buffer.should have_tag("form li label", /#{'meta_description'.humanize}/)
|
|
735
|
+
end
|
|
736
|
+
end
|
|
737
|
+
|
|
738
|
+
describe 'and object is given' do
|
|
739
|
+
it 'should delegate the label logic to class human attribute name and pass it down to the label tag' do
|
|
740
|
+
@new_post.stub!(:meta_description) # a two word method name
|
|
741
|
+
@new_post.class.should_receive(:human_attribute_name).with('meta_description').and_return('meta_description'.humanize)
|
|
742
|
+
|
|
743
|
+
semantic_form_for(@new_post) do |builder|
|
|
744
|
+
concat(builder.input(:meta_description))
|
|
745
|
+
end
|
|
746
|
+
|
|
747
|
+
output_buffer.should have_tag("form li label", /#{'meta_description'.humanize}/)
|
|
710
748
|
end
|
|
711
|
-
|
|
712
|
-
output_buffer.should have_tag("form li label", /#{'meta_description'.humanize}/)
|
|
713
749
|
end
|
|
714
750
|
end
|
|
715
|
-
|
|
716
|
-
describe '
|
|
717
|
-
|
|
718
|
-
@
|
|
719
|
-
@
|
|
720
|
-
|
|
751
|
+
|
|
752
|
+
describe 'when localized label is provided' do
|
|
753
|
+
before do
|
|
754
|
+
@localized_label_text = 'Localized title'
|
|
755
|
+
@default_localized_label_text = 'Default localized title'
|
|
756
|
+
::I18n.backend.store_translations :en,
|
|
757
|
+
:formtastic => {
|
|
758
|
+
:labels => {
|
|
759
|
+
:title => @default_localized_label_text,
|
|
760
|
+
:post => {
|
|
761
|
+
:title => @localized_label_text
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
::Formtastic::SemanticFormBuilder.i18n_lookups_by_default = false
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
it 'should render a label with localized label (I18n)' do
|
|
721
769
|
semantic_form_for(@new_post) do |builder|
|
|
722
|
-
concat(builder.input(:
|
|
770
|
+
concat(builder.input(:title, :label => true))
|
|
723
771
|
end
|
|
724
|
-
|
|
725
|
-
|
|
772
|
+
output_buffer.should have_tag('form li label', @localized_label_text)
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
it 'should render a hint paragraph containing an optional localized label (I18n) if first is not set' do
|
|
776
|
+
::I18n.backend.store_translations :en,
|
|
777
|
+
:formtastic => {
|
|
778
|
+
:labels => {
|
|
779
|
+
:post => {
|
|
780
|
+
:title => nil
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
semantic_form_for(@new_post) do |builder|
|
|
785
|
+
concat(builder.input(:title, :label => true))
|
|
786
|
+
end
|
|
787
|
+
output_buffer.should have_tag('form li label', @default_localized_label_text)
|
|
726
788
|
end
|
|
727
789
|
end
|
|
728
790
|
end
|
|
729
|
-
|
|
791
|
+
|
|
730
792
|
end
|
|
731
793
|
|
|
732
794
|
describe ':hint option' do
|
|
@@ -742,12 +804,63 @@ describe 'Formtastic' do
|
|
|
742
804
|
end
|
|
743
805
|
|
|
744
806
|
describe 'when not provided' do
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
807
|
+
describe 'when localized hint (I18n) is provided' do
|
|
808
|
+
before do
|
|
809
|
+
@localized_hint_text = "This is the localized hint."
|
|
810
|
+
@default_localized_hint_text = "This is the default localized hint."
|
|
811
|
+
::I18n.backend.store_translations :en,
|
|
812
|
+
:formtastic => {
|
|
813
|
+
:hints => {
|
|
814
|
+
:title => @default_localized_hint_text,
|
|
815
|
+
:post => {
|
|
816
|
+
:title => @localized_hint_text
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
::Formtastic::SemanticFormBuilder.i18n_lookups_by_default = false
|
|
821
|
+
end
|
|
822
|
+
|
|
823
|
+
describe 'when provided value (hint value) is set to TRUE' do
|
|
824
|
+
it 'should render a hint paragraph containing a localized hint (I18n)' do
|
|
825
|
+
semantic_form_for(@new_post) do |builder|
|
|
826
|
+
concat(builder.input(:title, :hint => true))
|
|
827
|
+
end
|
|
828
|
+
output_buffer.should have_tag('form li p.inline-hints', @localized_hint_text)
|
|
829
|
+
end
|
|
830
|
+
|
|
831
|
+
it 'should render a hint paragraph containing an optional localized hint (I18n) if first is not set' do
|
|
832
|
+
::I18n.backend.store_translations :en,
|
|
833
|
+
:formtastic => {
|
|
834
|
+
:hints => {
|
|
835
|
+
:post => {
|
|
836
|
+
:title => nil
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
semantic_form_for(@new_post) do |builder|
|
|
841
|
+
concat(builder.input(:title, :hint => true))
|
|
842
|
+
end
|
|
843
|
+
output_buffer.should have_tag('form li p.inline-hints', @default_localized_hint_text)
|
|
844
|
+
end
|
|
845
|
+
end
|
|
846
|
+
|
|
847
|
+
describe 'when provided value (label value) is set to FALSE' do
|
|
848
|
+
it 'should not render a hint paragraph' do
|
|
849
|
+
semantic_form_for(@new_post) do |builder|
|
|
850
|
+
concat(builder.input(:title, :hint => false))
|
|
851
|
+
end
|
|
852
|
+
output_buffer.should_not have_tag('form li p.inline-hints', @localized_hint_text)
|
|
853
|
+
end
|
|
854
|
+
end
|
|
855
|
+
end
|
|
856
|
+
|
|
857
|
+
describe 'when localized hint (I18n) is not provided' do
|
|
858
|
+
it 'should not render a hint paragraph' do
|
|
859
|
+
semantic_form_for(@new_post) do |builder|
|
|
860
|
+
concat(builder.input(:title))
|
|
861
|
+
end
|
|
862
|
+
output_buffer.should_not have_tag('form li p.inline-hints')
|
|
749
863
|
end
|
|
750
|
-
output_buffer.should_not have_tag("form li p.inline-hints")
|
|
751
864
|
end
|
|
752
865
|
end
|
|
753
866
|
|
|
@@ -809,7 +922,7 @@ describe 'Formtastic' do
|
|
|
809
922
|
before do
|
|
810
923
|
@title_errors = ['must not be blank', 'must be longer than 10 characters', 'must be awesome']
|
|
811
924
|
@errors = mock('errors')
|
|
812
|
-
@errors.stub!(:
|
|
925
|
+
@errors.stub!(:[]).with(:title).and_return(@title_errors)
|
|
813
926
|
@new_post.stub!(:errors).and_return(@errors)
|
|
814
927
|
end
|
|
815
928
|
|
|
@@ -922,31 +1035,33 @@ describe 'Formtastic' do
|
|
|
922
1035
|
output_buffer.should have_tag("form li input[@name=\"post[title]\"]")
|
|
923
1036
|
end
|
|
924
1037
|
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1038
|
+
unless type == :numeric
|
|
1039
|
+
it 'should have a maxlength matching the column limit' do
|
|
1040
|
+
@new_post.column_for_attribute(:title).limit.should == 50
|
|
1041
|
+
output_buffer.should have_tag("form li input[@maxlength='50']")
|
|
1042
|
+
end
|
|
929
1043
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
1044
|
+
it 'should use default_text_field_size for columns longer than default_text_field_size' do
|
|
1045
|
+
default_size = Formtastic::SemanticFormBuilder.default_text_field_size
|
|
1046
|
+
@new_post.stub!(:column_for_attribute).and_return(mock('column', :type => type, :limit => default_size * 2))
|
|
933
1047
|
|
|
934
|
-
|
|
935
|
-
|
|
1048
|
+
semantic_form_for(@new_post) do |builder|
|
|
1049
|
+
concat(builder.input(:title, :as => type))
|
|
1050
|
+
end
|
|
1051
|
+
|
|
1052
|
+
output_buffer.should have_tag("form li input[@size='#{default_size}']")
|
|
936
1053
|
end
|
|
937
1054
|
|
|
938
|
-
|
|
939
|
-
|
|
1055
|
+
it 'should use the column size for columns shorter than default_text_field_size' do
|
|
1056
|
+
column_limit_shorted_than_default = 1
|
|
1057
|
+
@new_post.stub!(:column_for_attribute).and_return(mock('column', :type => type, :limit => column_limit_shorted_than_default))
|
|
940
1058
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1059
|
+
semantic_form_for(@new_post) do |builder|
|
|
1060
|
+
concat(builder.input(:title, :as => type))
|
|
1061
|
+
end
|
|
944
1062
|
|
|
945
|
-
|
|
946
|
-
concat(builder.input(:title, :as => type))
|
|
1063
|
+
output_buffer.should have_tag("form li input[@size='#{column_limit_shorted_than_default}']")
|
|
947
1064
|
end
|
|
948
|
-
|
|
949
|
-
output_buffer.should have_tag("form li input[@size='#{column_limit_shorted_than_default}']")
|
|
950
1065
|
end
|
|
951
1066
|
|
|
952
1067
|
it 'should use default_text_field_size for methods without database columns' do
|
|
@@ -967,6 +1082,13 @@ describe 'Formtastic' do
|
|
|
967
1082
|
output_buffer.should have_tag("form li input.myclass")
|
|
968
1083
|
end
|
|
969
1084
|
|
|
1085
|
+
it 'should consider input_html :id in labels' do
|
|
1086
|
+
semantic_form_for(@new_post) do |builder|
|
|
1087
|
+
concat(builder.input(:title, :as => type, :input_html => { :id => 'myid' }))
|
|
1088
|
+
end
|
|
1089
|
+
output_buffer.should have_tag('form li label[@for="myid"]')
|
|
1090
|
+
end
|
|
1091
|
+
|
|
970
1092
|
it 'should generate input and labels even if no object is given' do
|
|
971
1093
|
semantic_form_for(:project, :url => 'http://test.host/') do |builder|
|
|
972
1094
|
concat(builder.input(:title, :as => type))
|
|
@@ -1080,11 +1202,11 @@ describe 'Formtastic' do
|
|
|
1080
1202
|
|
|
1081
1203
|
describe ":as => :hidden" do
|
|
1082
1204
|
before do
|
|
1083
|
-
@new_post.stub!(:
|
|
1205
|
+
@new_post.stub!(:secret)
|
|
1084
1206
|
@new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :string))
|
|
1085
1207
|
|
|
1086
1208
|
semantic_form_for(@new_post) do |builder|
|
|
1087
|
-
concat(builder.input(:
|
|
1209
|
+
concat(builder.input(:secret, :as => :hidden))
|
|
1088
1210
|
end
|
|
1089
1211
|
end
|
|
1090
1212
|
|
|
@@ -1093,7 +1215,7 @@ describe 'Formtastic' do
|
|
|
1093
1215
|
end
|
|
1094
1216
|
|
|
1095
1217
|
it 'should have a post_hidden_input id on the wrapper' do
|
|
1096
|
-
output_buffer.should have_tag('form li#
|
|
1218
|
+
output_buffer.should have_tag('form li#post_secret_input')
|
|
1097
1219
|
end
|
|
1098
1220
|
|
|
1099
1221
|
it 'should not generate a label for the input' do
|
|
@@ -1101,10 +1223,24 @@ describe 'Formtastic' do
|
|
|
1101
1223
|
end
|
|
1102
1224
|
|
|
1103
1225
|
it "should generate a input field" do
|
|
1104
|
-
output_buffer.should have_tag("form li input#
|
|
1226
|
+
output_buffer.should have_tag("form li input#post_secret")
|
|
1105
1227
|
output_buffer.should have_tag("form li input[@type=\"hidden\"]")
|
|
1106
|
-
output_buffer.should have_tag("form li input[@name=\"post[
|
|
1228
|
+
output_buffer.should have_tag("form li input[@name=\"post[secret]\"]")
|
|
1229
|
+
end
|
|
1230
|
+
|
|
1231
|
+
it "should not render inline errors" do
|
|
1232
|
+
@errors = mock('errors')
|
|
1233
|
+
@errors.stub!(:[]).with(:secret).and_return(["foo", "bah"])
|
|
1234
|
+
@new_post.stub!(:errors).and_return(@errors)
|
|
1235
|
+
|
|
1236
|
+
semantic_form_for(@new_post) do |builder|
|
|
1237
|
+
concat(builder.input(:secret, :as => :hidden))
|
|
1238
|
+
end
|
|
1239
|
+
|
|
1240
|
+
output_buffer.should_not have_tag("form li p.inline-errors")
|
|
1241
|
+
output_buffer.should_not have_tag("form li ul.errors")
|
|
1107
1242
|
end
|
|
1243
|
+
|
|
1108
1244
|
end
|
|
1109
1245
|
|
|
1110
1246
|
describe ":as => :time_zone" do
|
|
@@ -1247,7 +1383,7 @@ describe 'Formtastic' do
|
|
|
1247
1383
|
before do
|
|
1248
1384
|
@new_post.stub!(:author).and_return(@bob)
|
|
1249
1385
|
@new_post.stub!(:author_id).and_return(@bob.id)
|
|
1250
|
-
Post.stub!(:reflect_on_association).and_return { |column_name| mock('reflection', :klass => Author, :macro => :belongs_to) }
|
|
1386
|
+
Post.stub!(:reflect_on_association).and_return { |column_name| mock('reflection', :options => {}, :klass => Author, :macro => :belongs_to) }
|
|
1251
1387
|
end
|
|
1252
1388
|
|
|
1253
1389
|
describe 'for belongs_to association' do
|
|
@@ -1393,7 +1529,11 @@ describe 'Formtastic' do
|
|
|
1393
1529
|
it 'should create a select without size' do
|
|
1394
1530
|
output_buffer.should_not have_tag('form li select[@size]')
|
|
1395
1531
|
end
|
|
1396
|
-
|
|
1532
|
+
|
|
1533
|
+
it 'should have a blank option' do
|
|
1534
|
+
output_buffer.should have_tag("form li select option[@value='']")
|
|
1535
|
+
end
|
|
1536
|
+
|
|
1397
1537
|
it 'should have a select option for each Author' do
|
|
1398
1538
|
output_buffer.should have_tag('form li select option', :count => Author.find(:all).size + 1)
|
|
1399
1539
|
Author.find(:all).each do |author|
|
|
@@ -1435,7 +1575,7 @@ describe 'Formtastic' do
|
|
|
1435
1575
|
|
|
1436
1576
|
it 'should have a label inside the wrapper' do
|
|
1437
1577
|
output_buffer.should have_tag('form li label')
|
|
1438
|
-
output_buffer.should have_tag('form li label', /Post
|
|
1578
|
+
output_buffer.should have_tag('form li label', /Post/)
|
|
1439
1579
|
output_buffer.should have_tag("form li label[@for='author_post_ids']")
|
|
1440
1580
|
end
|
|
1441
1581
|
|
|
@@ -1449,11 +1589,15 @@ describe 'Formtastic' do
|
|
|
1449
1589
|
end
|
|
1450
1590
|
|
|
1451
1591
|
it 'should have a select option for each Post' do
|
|
1452
|
-
output_buffer.should have_tag('form li select option', :count => Post.find(:all).size
|
|
1592
|
+
output_buffer.should have_tag('form li select option', :count => Post.find(:all).size)
|
|
1453
1593
|
Post.find(:all).each do |post|
|
|
1454
1594
|
output_buffer.should have_tag("form li select option[@value='#{post.id}']", /#{post.to_label}/)
|
|
1455
1595
|
end
|
|
1456
1596
|
end
|
|
1597
|
+
|
|
1598
|
+
it 'should not have a blank option' do
|
|
1599
|
+
output_buffer.should_not have_tag("form li select option[@value='']")
|
|
1600
|
+
end
|
|
1457
1601
|
|
|
1458
1602
|
it 'should have one option with a "selected" attribute' do
|
|
1459
1603
|
output_buffer.should have_tag('form li select option[@selected]', :count => 1)
|
|
@@ -1477,7 +1621,7 @@ describe 'Formtastic' do
|
|
|
1477
1621
|
|
|
1478
1622
|
it 'should have a label inside the wrapper' do
|
|
1479
1623
|
output_buffer.should have_tag('form li label')
|
|
1480
|
-
output_buffer.should have_tag('form li label', /Author
|
|
1624
|
+
output_buffer.should have_tag('form li label', /Author/)
|
|
1481
1625
|
output_buffer.should have_tag("form li label[@for='post_author_ids']")
|
|
1482
1626
|
end
|
|
1483
1627
|
|
|
@@ -1491,11 +1635,15 @@ describe 'Formtastic' do
|
|
|
1491
1635
|
end
|
|
1492
1636
|
|
|
1493
1637
|
it 'should have a select option for each Author' do
|
|
1494
|
-
output_buffer.should have_tag('form li select option', :count => Author.find(:all).size
|
|
1638
|
+
output_buffer.should have_tag('form li select option', :count => Author.find(:all).size)
|
|
1495
1639
|
Author.find(:all).each do |author|
|
|
1496
1640
|
output_buffer.should have_tag("form li select option[@value='#{author.id}']", /#{author.to_label}/)
|
|
1497
1641
|
end
|
|
1498
1642
|
end
|
|
1643
|
+
|
|
1644
|
+
it 'should not have a blank option' do
|
|
1645
|
+
output_buffer.should_not have_tag("form li select option[@value='']")
|
|
1646
|
+
end
|
|
1499
1647
|
|
|
1500
1648
|
it 'should have one option with a "selected" attribute' do
|
|
1501
1649
|
output_buffer.should have_tag('form li select option[@selected]', :count => 1)
|
|
@@ -2463,8 +2611,8 @@ describe 'Formtastic' do
|
|
|
2463
2611
|
describe 'without a block' do
|
|
2464
2612
|
|
|
2465
2613
|
before do
|
|
2466
|
-
Post.stub!(:reflections).and_return({:author => mock('reflection', :macro => :belongs_to),
|
|
2467
|
-
:comments => mock('reflection', :macro => :has_many) })
|
|
2614
|
+
Post.stub!(:reflections).and_return({:author => mock('reflection', :options => {}, :macro => :belongs_to),
|
|
2615
|
+
:comments => mock('reflection', :options => {}, :macro => :has_many) })
|
|
2468
2616
|
Post.stub!(:content_columns).and_return([mock('column', :name => 'title'), mock('column', :name => 'body'), mock('column', :name => 'created_at')])
|
|
2469
2617
|
Author.stub!(:find).and_return([@fred, @bob])
|
|
2470
2618
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: justinfrench-formtastic
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Justin French
|
|
@@ -9,7 +9,7 @@ autorequire: formtastic
|
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
11
|
|
|
12
|
-
date: 2009-
|
|
12
|
+
date: 2009-08-30 00:00:00 -07:00
|
|
13
13
|
default_executable:
|
|
14
14
|
dependencies: []
|
|
15
15
|
|
|
@@ -36,6 +36,7 @@ files:
|
|
|
36
36
|
- spec/test_helper.rb
|
|
37
37
|
has_rdoc: false
|
|
38
38
|
homepage: http://github.com/justinfrench/formtastic/tree/master
|
|
39
|
+
licenses:
|
|
39
40
|
post_install_message:
|
|
40
41
|
rdoc_options:
|
|
41
42
|
- --charset=UTF-8
|
|
@@ -56,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
56
57
|
requirements: []
|
|
57
58
|
|
|
58
59
|
rubyforge_project:
|
|
59
|
-
rubygems_version: 1.
|
|
60
|
+
rubygems_version: 1.3.5
|
|
60
61
|
signing_key:
|
|
61
62
|
specification_version: 3
|
|
62
63
|
summary: A Rails form builder plugin/gem with semantically rich and accessible markup
|