formtastic 0.9.2 → 0.9.3
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 +35 -4
- data/Rakefile +47 -22
- data/generators/formtastic/templates/formtastic.css +1 -0
- data/lib/formtastic/i18n.rb +27 -0
- data/lib/formtastic.rb +151 -81
- data/lib/locale/en.yml +2 -2
- data/spec/commit_button_spec.rb +5 -5
- data/spec/custom_builder_spec.rb +8 -8
- data/spec/custom_macros.rb +3 -3
- data/spec/errors_spec.rb +6 -6
- data/spec/form_helper_spec.rb +4 -4
- data/spec/i18n_spec.rb +83 -0
- data/spec/input_spec.rb +22 -21
- data/spec/inputs/check_boxes_input_spec.rb +3 -3
- data/spec/inputs/country_input_spec.rb +1 -1
- data/spec/inputs/date_input_spec.rb +2 -2
- data/spec/inputs/datetime_input_spec.rb +5 -5
- data/spec/inputs/hidden_input_spec.rb +13 -2
- data/spec/inputs/radio_input_spec.rb +3 -3
- data/spec/inputs/select_input_spec.rb +83 -4
- data/spec/inputs/time_input_spec.rb +2 -2
- data/spec/inputs_spec.rb +28 -16
- data/spec/label_spec.rb +0 -6
- data/spec/semantic_fields_for_spec.rb +1 -1
- data/spec/spec_helper.rb +4 -2
- metadata +58 -6
data/lib/formtastic.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), *%w[formtastic i18n])
|
2
3
|
|
3
4
|
module Formtastic #:nodoc:
|
4
5
|
|
@@ -7,7 +8,7 @@ module Formtastic #:nodoc:
|
|
7
8
|
@@default_text_field_size = 50
|
8
9
|
@@all_fields_required_by_default = true
|
9
10
|
@@include_blank_for_select_by_default = true
|
10
|
-
@@required_string = proc { %{<abbr title="#{I18n.t 'formtastic.required', :default => 'required'}">*</abbr>} }
|
11
|
+
@@required_string = proc { %{<abbr title="#{::Formtastic::I18n.t 'formtastic.required', :default => 'required'}">*</abbr>} }
|
11
12
|
@@optional_string = ''
|
12
13
|
@@inline_errors = :sentence
|
13
14
|
@@label_str_method = :humanize
|
@@ -22,10 +23,14 @@ module Formtastic #:nodoc:
|
|
22
23
|
:required_string, :optional_string, :inline_errors, :label_str_method, :collection_label_methods,
|
23
24
|
:inline_order, :file_methods, :priority_countries, :i18n_lookups_by_default, :default_commit_button_accesskey
|
24
25
|
|
26
|
+
RESERVED_COLUMNS = [:created_at, :updated_at, :created_on, :updated_on, :lock_version, :version]
|
27
|
+
|
28
|
+
INLINE_ERROR_TYPES = [:sentence, :list, :first]
|
29
|
+
|
25
30
|
I18N_SCOPES = [ '{{model}}.{{action}}.{{attribute}}',
|
26
31
|
'{{model}}.{{attribute}}',
|
27
32
|
'{{attribute}}']
|
28
|
-
|
33
|
+
|
29
34
|
attr_accessor :template
|
30
35
|
|
31
36
|
# Returns a suitable form input for the given +method+, using the database column information
|
@@ -65,7 +70,8 @@ module Formtastic #:nodoc:
|
|
65
70
|
#
|
66
71
|
# <% semantic_form_for @employee do |form| %>
|
67
72
|
# <% form.inputs do -%>
|
68
|
-
# <%= form.input :
|
73
|
+
# <%= form.input :secret, :value => "Hello" %>
|
74
|
+
# <%= form.input :name, :label => "Full Name" %>
|
69
75
|
# <%= form.input :manager_id, :as => :radio %>
|
70
76
|
# <%= form.input :hired_at, :as => :date, :label => "Date Hired" %>
|
71
77
|
# <%= form.input :phone, :required => false, :hint => "Eg: +1 555 1234" %>
|
@@ -136,11 +142,16 @@ module Formtastic #:nodoc:
|
|
136
142
|
# <%= form.inputs %>
|
137
143
|
# <% end %>
|
138
144
|
#
|
145
|
+
# With a few arguments:
|
146
|
+
# <% semantic_form_for @post do |form| %>
|
147
|
+
# <%= form.inputs "Post details", :title, :body %>
|
148
|
+
# <% end %>
|
149
|
+
#
|
139
150
|
# === Options
|
140
151
|
#
|
141
|
-
# All options (with the exception of :name) are passed down to the fieldset as HTML
|
142
|
-
# attributes (id, class, style, etc). If provided, the :name option is passed into a
|
143
|
-
# legend tag inside the fieldset
|
152
|
+
# All options (with the exception of :name/:title) are passed down to the fieldset as HTML
|
153
|
+
# attributes (id, class, style, etc). If provided, the :name/:title option is passed into a
|
154
|
+
# legend tag inside the fieldset.
|
144
155
|
#
|
145
156
|
# # With a block:
|
146
157
|
# <% semantic_form_for @post do |form| %>
|
@@ -154,6 +165,11 @@ module Formtastic #:nodoc:
|
|
154
165
|
# <%= form.inputs :title, :body, :name => "Create a new post", :style => "border:1px;" %>
|
155
166
|
# <% end %>
|
156
167
|
#
|
168
|
+
# # ...or the equivalent:
|
169
|
+
# <% semantic_form_for @post do |form| %>
|
170
|
+
# <%= form.inputs "Create a new post", :title, :body, :style => "border:1px;" %>
|
171
|
+
# <% end %>
|
172
|
+
#
|
157
173
|
# === It's basically a fieldset!
|
158
174
|
#
|
159
175
|
# Instead of hard-coding fieldsets & legends into your form to logically group related fields,
|
@@ -168,6 +184,9 @@ module Formtastic #:nodoc:
|
|
168
184
|
# <%= f.input :created_at %>
|
169
185
|
# <%= f.input :user_id, :label => "Author" %>
|
170
186
|
# <% end %>
|
187
|
+
# <% f.inputs "Extra" do %>
|
188
|
+
# <%= f.input :update_at %>
|
189
|
+
# <% end %>
|
171
190
|
# <% end %>
|
172
191
|
#
|
173
192
|
# # Output:
|
@@ -185,6 +204,12 @@ module Formtastic #:nodoc:
|
|
185
204
|
# <li class="select">...</li>
|
186
205
|
# </ol>
|
187
206
|
# </fieldset>
|
207
|
+
# <fieldset class="inputs">
|
208
|
+
# <legend><span>Extra</span></legend>
|
209
|
+
# <ol>
|
210
|
+
# <li class="datetime">...</li>
|
211
|
+
# </ol>
|
212
|
+
# </fieldset>
|
188
213
|
# </form>
|
189
214
|
#
|
190
215
|
# === Nested attributes
|
@@ -232,17 +257,19 @@ module Formtastic #:nodoc:
|
|
232
257
|
if html_options[:for]
|
233
258
|
inputs_for_nested_attributes(args, html_options, &block)
|
234
259
|
elsif block_given?
|
235
|
-
field_set_and_list_wrapping(html_options, &block)
|
260
|
+
field_set_and_list_wrapping(*(args << html_options), &block)
|
236
261
|
else
|
237
262
|
if @object && args.empty?
|
238
|
-
args =
|
239
|
-
args +=
|
240
|
-
args -=
|
263
|
+
args = self.association_columns(:belongs_to)
|
264
|
+
args += self.content_columns
|
265
|
+
args -= RESERVED_COLUMNS
|
241
266
|
args.compact!
|
242
267
|
end
|
243
|
-
|
244
|
-
|
245
|
-
|
268
|
+
legend = args.shift if args.first.is_a?(::String)
|
269
|
+
contents = args.collect { |method| input(method.to_sym) }
|
270
|
+
args.unshift(legend) if legend.present?
|
271
|
+
|
272
|
+
field_set_and_list_wrapping(*((args << html_options) << contents))
|
246
273
|
end
|
247
274
|
end
|
248
275
|
alias :input_field_set :inputs
|
@@ -295,7 +322,7 @@ module Formtastic #:nodoc:
|
|
295
322
|
fallback_text ||= "#{key.to_s.humanize} {{model}}"
|
296
323
|
|
297
324
|
text = (self.localized_string(key, text, :action, :model => object_name) ||
|
298
|
-
::I18n.t(key, :model => object_name
|
325
|
+
::Formtastic::I18n.t(key, :model => object_name)) unless text.is_a?(::String)
|
299
326
|
|
300
327
|
button_html = options.delete(:button_html) || {}
|
301
328
|
button_html.merge!(:class => [button_html[:class], key].compact.join(' '))
|
@@ -326,7 +353,7 @@ module Formtastic #:nodoc:
|
|
326
353
|
#
|
327
354
|
def semantic_fields_for(record_or_name_or_array, *args, &block)
|
328
355
|
opts = args.extract_options!
|
329
|
-
opts.merge!(:builder => Formtastic::SemanticFormHelper.builder)
|
356
|
+
opts.merge!(:builder => ::Formtastic::SemanticFormHelper.builder)
|
330
357
|
args.push(opts)
|
331
358
|
fields_for(record_or_name_or_array, *args, &block)
|
332
359
|
end
|
@@ -338,7 +365,6 @@ module Formtastic #:nodoc:
|
|
338
365
|
# * :required - Appends an abbr tag if :required is true
|
339
366
|
# * :label - An alternative form to give the label content. Whenever label
|
340
367
|
# is false, a blank string is returned.
|
341
|
-
# * :as_span - When true returns a span tag with class label instead of a label element
|
342
368
|
# * :input_name - Gives the input to match for. This is needed when you want to
|
343
369
|
# to call f.label :authors but it should match :author_ids.
|
344
370
|
#
|
@@ -362,17 +388,12 @@ module Formtastic #:nodoc:
|
|
362
388
|
end
|
363
389
|
text = localized_string(method, text, :label) || humanized_attribute_name(method)
|
364
390
|
text += required_or_optional_string(options.delete(:required))
|
365
|
-
|
391
|
+
|
366
392
|
# special case for boolean (checkbox) labels, which have a nested input
|
367
393
|
text = (options.delete(:label_prefix_for_nested_input) || "") + text
|
368
|
-
|
394
|
+
|
369
395
|
input_name = options.delete(:input_name) || method
|
370
|
-
|
371
|
-
options[:class] ||= 'label'
|
372
|
-
template.content_tag(:span, text, options)
|
373
|
-
else
|
374
|
-
super(input_name, text, options)
|
375
|
-
end
|
396
|
+
super(input_name, text, options)
|
376
397
|
end
|
377
398
|
|
378
399
|
# Generates error messages for the given method. Errors can be shown as list,
|
@@ -387,15 +408,41 @@ module Formtastic #:nodoc:
|
|
387
408
|
# end
|
388
409
|
#
|
389
410
|
def inline_errors_for(method, options=nil) #:nodoc:
|
390
|
-
return nil unless @object && @object.respond_to?(:errors) &&
|
411
|
+
return nil unless @object && @object.respond_to?(:errors) && INLINE_ERROR_TYPES.include?(@@inline_errors)
|
391
412
|
|
392
413
|
errors = @object.errors[method.to_sym]
|
393
|
-
send("error_#{@@inline_errors}", Array(errors)) unless errors.blank?
|
414
|
+
send(:"error_#{@@inline_errors}", Array(errors)) unless errors.blank?
|
394
415
|
end
|
395
416
|
alias :errors_on :inline_errors_for
|
396
417
|
|
397
418
|
protected
|
398
419
|
|
420
|
+
# Collects content columns (non-relation columns) for the current form object class.
|
421
|
+
#
|
422
|
+
def content_columns
|
423
|
+
if @object.present?
|
424
|
+
@object.class.name.constantize.content_columns.collect { |c| c.name.to_sym }.compact
|
425
|
+
else
|
426
|
+
@object_name.to_s.classify.constantize.content_columns.collect { |c| c.name.to_sym }.compact rescue []
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
# Collects association columns (relation columns) for the current form object class.
|
431
|
+
#
|
432
|
+
def association_columns(*by_associations)
|
433
|
+
if @object.present?
|
434
|
+
@object.class.reflections.collect do |name, _|
|
435
|
+
if by_associations.present?
|
436
|
+
name if by_associations.include?(_.macro)
|
437
|
+
else
|
438
|
+
name
|
439
|
+
end
|
440
|
+
end.compact
|
441
|
+
else
|
442
|
+
[]
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
399
446
|
# Prepare options to be sent to label
|
400
447
|
#
|
401
448
|
def options_for_label(options)
|
@@ -478,7 +525,7 @@ module Formtastic #:nodoc:
|
|
478
525
|
html_options = options.delete(:input_html) || {}
|
479
526
|
html_options = default_string_options(method, type).merge(html_options) if [:numeric, :string, :password].include?(type)
|
480
527
|
|
481
|
-
self.label(method, options_for_label(options))
|
528
|
+
self.label(method, options_for_label(options)) <<
|
482
529
|
self.send(form_helper_method, method, html_options)
|
483
530
|
end
|
484
531
|
|
@@ -491,17 +538,17 @@ module Formtastic #:nodoc:
|
|
491
538
|
def password_input(method, options)
|
492
539
|
basic_input_helper(:password_field, :password, method, options)
|
493
540
|
end
|
494
|
-
|
541
|
+
|
495
542
|
# Outputs a label and standard Rails text field inside the wrapper.
|
496
543
|
def numeric_input(method, options)
|
497
544
|
basic_input_helper(:text_field, :numeric, method, options)
|
498
545
|
end
|
499
|
-
|
546
|
+
|
500
547
|
# Ouputs a label and standard Rails text area inside the wrapper.
|
501
548
|
def text_input(method, options)
|
502
549
|
basic_input_helper(:text_area, :text, method, options)
|
503
550
|
end
|
504
|
-
|
551
|
+
|
505
552
|
# Outputs a label and a standard Rails file field inside the wrapper.
|
506
553
|
def file_input(method, options)
|
507
554
|
basic_input_helper(:file_field, :file, method, options)
|
@@ -510,7 +557,12 @@ module Formtastic #:nodoc:
|
|
510
557
|
# Outputs a hidden field inside the wrapper, which should be hidden with CSS.
|
511
558
|
# Additionals options can be given and will be sent straight to hidden input
|
512
559
|
# element.
|
560
|
+
#
|
513
561
|
def hidden_input(method, options)
|
562
|
+
options ||= {}
|
563
|
+
if options[:input_html].present?
|
564
|
+
options[:value] = options[:input_html][:value] if options[:input_html][:value].present?
|
565
|
+
end
|
514
566
|
self.hidden_field(method, set_options(options))
|
515
567
|
end
|
516
568
|
|
@@ -625,12 +677,10 @@ module Formtastic #:nodoc:
|
|
625
677
|
options[:include_blank] = false
|
626
678
|
html_options[:multiple] ||= true
|
627
679
|
html_options[:size] ||= 5
|
628
|
-
|
629
|
-
|
680
|
+
end
|
630
681
|
input_name = generate_association_input_name(method)
|
631
|
-
self.label(method, options_for_label(options).merge(:input_name => input_name)) +
|
632
682
|
|
633
|
-
if options[:group_by]
|
683
|
+
select_html = if options[:group_by]
|
634
684
|
# The grouped_options_select is a bit counter intuitive and not optimised (mostly due to ActiveRecord).
|
635
685
|
# The formtastic user however shouldn't notice this too much.
|
636
686
|
raw_collection = find_raw_collection_for_column(method, options.reverse_merge(:find_options => { :include => options[:group_by] }))
|
@@ -648,6 +698,8 @@ module Formtastic #:nodoc:
|
|
648
698
|
collection = find_collection_for_column(method, options)
|
649
699
|
self.select(input_name, collection, set_options(options), html_options)
|
650
700
|
end
|
701
|
+
|
702
|
+
self.label(method, options_for_label(options).merge(:input_name => input_name)) << select_html
|
651
703
|
end
|
652
704
|
alias :boolean_select_input :select_input
|
653
705
|
|
@@ -661,7 +713,7 @@ module Formtastic #:nodoc:
|
|
661
713
|
def time_zone_input(method, options)
|
662
714
|
html_options = options.delete(:input_html) || {}
|
663
715
|
|
664
|
-
self.label(method, options_for_label(options))
|
716
|
+
self.label(method, options_for_label(options)) <<
|
665
717
|
self.time_zone_select(method, options.delete(:priority_zones), set_options(options), html_options)
|
666
718
|
end
|
667
719
|
|
@@ -732,22 +784,24 @@ module Formtastic #:nodoc:
|
|
732
784
|
|
733
785
|
input_name = generate_association_input_name(method)
|
734
786
|
value_as_class = options.delete(:value_as_class)
|
787
|
+
input_ids = []
|
735
788
|
|
736
789
|
list_item_content = collection.map do |c|
|
737
790
|
label = c.is_a?(Array) ? c.first : c
|
738
791
|
value = c.is_a?(Array) ? c.last : c
|
739
792
|
html_options[:checked] = options.delete(:selected) unless options[:selected].blank?
|
740
793
|
|
794
|
+
input_ids << input_id = generate_html_id(input_name, value.to_s.gsub(/\s/, '_').gsub(/\W/, '').downcase)
|
741
795
|
li_content = template.content_tag(:label,
|
742
796
|
"#{self.radio_button(input_name, value, html_options)} #{label}",
|
743
|
-
:for =>
|
797
|
+
:for => input_id
|
744
798
|
)
|
745
799
|
|
746
800
|
li_options = value_as_class ? { :class => value.to_s.downcase } : {}
|
747
801
|
template.content_tag(:li, li_content, li_options)
|
748
802
|
end
|
749
803
|
|
750
|
-
field_set_and_list_wrapping_for_method(method, options, list_item_content)
|
804
|
+
field_set_and_list_wrapping_for_method(method, options.merge(:label_for => input_ids.first), list_item_content)
|
751
805
|
end
|
752
806
|
alias :boolean_radio_input :radio_input
|
753
807
|
|
@@ -761,7 +815,6 @@ module Formtastic #:nodoc:
|
|
761
815
|
date_or_datetime_input(method, options.merge(:discard_hour => true))
|
762
816
|
end
|
763
817
|
|
764
|
-
|
765
818
|
# Outputs a fieldset with a legend for the method label, and a ordered list (ol) of list
|
766
819
|
# items (li), one for each fragment for the date (year, month, day, hour, min, sec). Each li
|
767
820
|
# contains a label (eg "Year") and a select box. See date_or_datetime_input for a more
|
@@ -773,7 +826,6 @@ module Formtastic #:nodoc:
|
|
773
826
|
date_or_datetime_input(method, options)
|
774
827
|
end
|
775
828
|
|
776
|
-
|
777
829
|
# Outputs a fieldset with a legend for the method label, and a ordered list (ol) of list
|
778
830
|
# items (li), one for each fragment for the time (hour, minute, second). Each li contains a label
|
779
831
|
# (eg "Hour") and a select box. See date_or_datetime_input for a more detailed output example.
|
@@ -784,7 +836,6 @@ module Formtastic #:nodoc:
|
|
784
836
|
date_or_datetime_input(method, options.merge(:discard_year => true, :discard_month => true, :discard_day => true))
|
785
837
|
end
|
786
838
|
|
787
|
-
|
788
839
|
# <fieldset>
|
789
840
|
# <legend>Created At</legend>
|
790
841
|
# <ol>
|
@@ -819,7 +870,8 @@ module Formtastic #:nodoc:
|
|
819
870
|
#
|
820
871
|
def date_or_datetime_input(method, options)
|
821
872
|
position = { :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6 }
|
822
|
-
i18n_date_order = I18n.
|
873
|
+
i18n_date_order = ::I18n.t(:order, :scope => [:date])
|
874
|
+
i18n_date_order = nil unless i18n_date_order.is_a?(Array)
|
823
875
|
inputs = options.delete(:order) || i18n_date_order || [:year, :month, :day]
|
824
876
|
|
825
877
|
time_inputs = [:hour, :minute]
|
@@ -831,30 +883,31 @@ module Formtastic #:nodoc:
|
|
831
883
|
# Gets the datetime object. It can be a Fixnum, Date or Time, or nil.
|
832
884
|
datetime = @object ? @object.send(method) : nil
|
833
885
|
html_options = options.delete(:input_html) || {}
|
886
|
+
input_ids = []
|
834
887
|
|
835
888
|
(inputs + time_inputs).each do |input|
|
836
|
-
|
889
|
+
input_ids << input_id = generate_html_id(method, "#{position[input]}i")
|
890
|
+
|
837
891
|
field_name = "#{method}(#{position[input]}i)"
|
838
|
-
if options["discard_#{input}"
|
892
|
+
if options[:"discard_#{input}"]
|
839
893
|
break if time_inputs.include?(input)
|
840
894
|
|
841
|
-
hidden_value = datetime.respond_to?(input) ? datetime.send(input) : datetime
|
842
|
-
hidden_fields_capture << template.hidden_field_tag("#{@object_name}[#{field_name}]", (hidden_value || 1), :id =>
|
895
|
+
hidden_value = datetime.respond_to?(input) ? datetime.send(input.to_sym) : datetime
|
896
|
+
hidden_fields_capture << template.hidden_field_tag("#{@object_name}[#{field_name}]", (hidden_value || 1), :id => input_id)
|
843
897
|
else
|
844
898
|
opts = set_options(options).merge(:prefix => @object_name, :field_name => field_name)
|
845
|
-
item_label_text = I18n.t(input.to_s, :default => input.to_s.humanize, :scope => [:datetime, :prompts])
|
899
|
+
item_label_text = ::I18n.t(input.to_s, :default => input.to_s.humanize, :scope => [:datetime, :prompts])
|
846
900
|
|
847
901
|
list_items_capture << template.content_tag(:li,
|
848
|
-
template.content_tag(:label, item_label_text, :for =>
|
849
|
-
template.send("select_#{input}"
|
902
|
+
template.content_tag(:label, item_label_text, :for => input_id) <<
|
903
|
+
template.send(:"select_#{input}", datetime, opts, html_options.merge(:id => input_id))
|
850
904
|
)
|
851
905
|
end
|
852
906
|
end
|
853
907
|
|
854
|
-
hidden_fields_capture
|
908
|
+
hidden_fields_capture << field_set_and_list_wrapping_for_method(method, options.merge(:label_for => input_ids.first), list_items_capture)
|
855
909
|
end
|
856
910
|
|
857
|
-
|
858
911
|
# Outputs a fieldset containing a legend for the label text, and an ordered list (ol) of list
|
859
912
|
# items, one for each possible choice in the belongs_to association. Each li contains a
|
860
913
|
# label and a check_box input.
|
@@ -868,7 +921,7 @@ module Formtastic #:nodoc:
|
|
868
921
|
# Output:
|
869
922
|
#
|
870
923
|
# <fieldset>
|
871
|
-
# <legend><
|
924
|
+
# <legend class="label"><label>Authors</label></legend>
|
872
925
|
# <ol>
|
873
926
|
# <li>
|
874
927
|
# <input type="hidden" name="book[author_id][1]" value="">
|
@@ -927,26 +980,27 @@ module Formtastic #:nodoc:
|
|
927
980
|
value_as_class = options.delete(:value_as_class)
|
928
981
|
unchecked_value = options.delete(:unchecked_value) || ''
|
929
982
|
html_options = { :name => "#{@object_name}[#{input_name}][]" }.merge(html_options)
|
983
|
+
input_ids = []
|
930
984
|
|
931
985
|
list_item_content = collection.map do |c|
|
932
986
|
label = c.is_a?(Array) ? c.first : c
|
933
987
|
value = c.is_a?(Array) ? c.last : c
|
934
988
|
|
935
|
-
|
936
|
-
|
989
|
+
input_ids << input_id = generate_html_id(input_name, value.to_s.gsub(/\s/, '_').gsub(/\W/, '').downcase)
|
990
|
+
html_options.merge!(:id => input_id)
|
991
|
+
|
937
992
|
li_content = template.content_tag(:label,
|
938
993
|
"#{self.check_box(input_name, html_options, value, unchecked_value)} #{label}",
|
939
|
-
:for =>
|
994
|
+
:for => input_id
|
940
995
|
)
|
941
996
|
|
942
997
|
li_options = value_as_class ? { :class => value.to_s.downcase } : {}
|
943
998
|
template.content_tag(:li, li_content, li_options)
|
944
999
|
end
|
945
1000
|
|
946
|
-
field_set_and_list_wrapping_for_method(method, options, list_item_content)
|
1001
|
+
field_set_and_list_wrapping_for_method(method, options.merge(:label_for => input_ids.first), list_item_content)
|
947
1002
|
end
|
948
|
-
|
949
|
-
|
1003
|
+
|
950
1004
|
# Outputs a country select input, wrapping around a regular country_select helper.
|
951
1005
|
# Rails doesn't come with a country_select helper by default any more, so you'll need to install
|
952
1006
|
# the "official" plugin, or, if you wish, any other country_select plugin that behaves in the
|
@@ -967,10 +1021,9 @@ module Formtastic #:nodoc:
|
|
967
1021
|
html_options = options.delete(:input_html) || {}
|
968
1022
|
priority_countries = options.delete(:priority_countries) || @@priority_countries
|
969
1023
|
|
970
|
-
self.label(method, options_for_label(options))
|
1024
|
+
self.label(method, options_for_label(options)) <<
|
971
1025
|
self.country_select(method, priority_countries, set_options(options), html_options)
|
972
1026
|
end
|
973
|
-
|
974
1027
|
|
975
1028
|
# Outputs a label containing a checkbox and the label text. The label defaults
|
976
1029
|
# to the column name (method name) and can be altered with the :label option.
|
@@ -991,7 +1044,7 @@ module Formtastic #:nodoc:
|
|
991
1044
|
|
992
1045
|
# Generates an input for the given method using the type supplied with :as.
|
993
1046
|
def inline_input_for(method, options)
|
994
|
-
send("#{options.delete(:as)}_input", method, options)
|
1047
|
+
send(:"#{options.delete(:as)}_input", method, options)
|
995
1048
|
end
|
996
1049
|
|
997
1050
|
# Generates hints for the given method using the text supplied in :hint.
|
@@ -1050,12 +1103,29 @@ module Formtastic #:nodoc:
|
|
1050
1103
|
#
|
1051
1104
|
# f.inputs :name => 'Task #%i', :for => :tasks
|
1052
1105
|
#
|
1106
|
+
# or the shorter equivalent:
|
1107
|
+
#
|
1108
|
+
# f.inputs 'Task #%i', :for => :tasks
|
1109
|
+
#
|
1053
1110
|
# And it will generate a fieldset for each task with legend 'Task #1', 'Task #2',
|
1054
1111
|
# 'Task #3' and so on.
|
1055
1112
|
#
|
1056
|
-
|
1113
|
+
# Note: Special case for the inline inputs (non-block):
|
1114
|
+
# f.inputs "My little legend", :title, :body, :author # Explicit legend string => "My little legend"
|
1115
|
+
# f.inputs :my_little_legend, :title, :body, :author # Localized (118n) legend with I18n key => I18n.t(:my_little_legend, ...)
|
1116
|
+
# f.inputs :title, :body, :author # First argument is a column => (no legend)
|
1117
|
+
#
|
1118
|
+
def field_set_and_list_wrapping(*args, &block) #:nodoc:
|
1119
|
+
contents = args.last.is_a?(::Hash) ? '' : args.pop.flatten
|
1120
|
+
html_options = args.extract_options!
|
1121
|
+
|
1057
1122
|
html_options[:name] ||= html_options.delete(:title)
|
1058
|
-
|
1123
|
+
if html_options[:name].blank?
|
1124
|
+
valid_name_classes = [::String, ::Symbol]
|
1125
|
+
valid_name_classes.delete(::Symbol) if !block_given? && (args.first.is_a?(::Symbol) && self.content_columns.include?(args.first))
|
1126
|
+
html_options[:name] = args.shift if valid_name_classes.any? { |valid_name_class| args.first.is_a?(valid_name_class) }
|
1127
|
+
end
|
1128
|
+
html_options[:name] = localized_string(html_options[:name], html_options[:name], :title) if html_options[:name].is_a?(::Symbol)
|
1059
1129
|
|
1060
1130
|
legend = html_options.delete(:name).to_s
|
1061
1131
|
legend %= parent_child_index(html_options[:parent]) if html_options[:parent]
|
@@ -1072,7 +1142,7 @@ module Formtastic #:nodoc:
|
|
1072
1142
|
# Ruby 1.9: String#to_s behavior changed, need to make an explicit join.
|
1073
1143
|
contents = contents.join if contents.respond_to?(:join)
|
1074
1144
|
fieldset = template.content_tag(:fieldset,
|
1075
|
-
legend
|
1145
|
+
legend << template.content_tag(:ol, contents),
|
1076
1146
|
html_options.except(:builder, :parent)
|
1077
1147
|
)
|
1078
1148
|
|
@@ -1087,9 +1157,11 @@ module Formtastic #:nodoc:
|
|
1087
1157
|
contents = contents.join if contents.respond_to?(:join)
|
1088
1158
|
|
1089
1159
|
template.content_tag(:fieldset,
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1160
|
+
template.content_tag(:legend,
|
1161
|
+
self.label(method, options_for_label(options).merge(:for => options.delete(:label_for))), :class => 'label'
|
1162
|
+
) <<
|
1163
|
+
template.content_tag(:ol, contents)
|
1164
|
+
)
|
1093
1165
|
end
|
1094
1166
|
|
1095
1167
|
# For methods that have a database column, take a best guess as to what the input method
|
@@ -1142,7 +1214,7 @@ module Formtastic #:nodoc:
|
|
1142
1214
|
collection = find_raw_collection_for_column(column, options)
|
1143
1215
|
|
1144
1216
|
# Return if we have an Array of strings, fixnums or arrays
|
1145
|
-
return collection if collection.instance_of?(Array) &&
|
1217
|
+
return collection if (collection.instance_of?(Array) || collection.instance_of?(Range)) &&
|
1146
1218
|
[Array, Fixnum, String, Symbol].include?(collection.first.class)
|
1147
1219
|
|
1148
1220
|
label, value = detect_label_and_value_method!(collection, options)
|
@@ -1189,8 +1261,8 @@ module Formtastic #:nodoc:
|
|
1189
1261
|
# is provided.
|
1190
1262
|
#
|
1191
1263
|
def create_boolean_collection(options)
|
1192
|
-
options[:true] ||= I18n.t(
|
1193
|
-
options[:false] ||= I18n.t(
|
1264
|
+
options[:true] ||= ::Formtastic::I18n.t(:yes)
|
1265
|
+
options[:false] ||= ::Formtastic::I18n.t(:no)
|
1194
1266
|
options[:value_as_class] = true unless options.key?(:value_as_class)
|
1195
1267
|
|
1196
1268
|
[ [ options.delete(:true), true], [ options.delete(:false), false ] ]
|
@@ -1213,7 +1285,7 @@ module Formtastic #:nodoc:
|
|
1213
1285
|
end
|
1214
1286
|
else
|
1215
1287
|
method
|
1216
|
-
end
|
1288
|
+
end.to_sym
|
1217
1289
|
end
|
1218
1290
|
|
1219
1291
|
# If an association method is passed in (f.input :author) try to find the
|
@@ -1301,7 +1373,7 @@ module Formtastic #:nodoc:
|
|
1301
1373
|
# 'formtastic.labels.post.title'
|
1302
1374
|
# 'formtastic.labels.title'
|
1303
1375
|
#
|
1304
|
-
# NOTE: Generic, but only used for form input labels/hints.
|
1376
|
+
# NOTE: Generic, but only used for form input titles/labels/hints/actions (titles = legends, actions = buttons).
|
1305
1377
|
#
|
1306
1378
|
def localized_string(key, value, type, options = {})
|
1307
1379
|
key = value if value.is_a?(::Symbol)
|
@@ -1326,8 +1398,8 @@ module Formtastic #:nodoc:
|
|
1326
1398
|
end
|
1327
1399
|
defaults << ''
|
1328
1400
|
|
1329
|
-
i18n_value = ::I18n.t(defaults.shift,
|
1330
|
-
|
1401
|
+
i18n_value = ::Formtastic::I18n.t(defaults.shift,
|
1402
|
+
options.merge(:default => defaults,:scope => type.to_s.pluralize.to_sym))
|
1331
1403
|
i18n_value.blank? ? nil : i18n_value
|
1332
1404
|
end
|
1333
1405
|
end
|
@@ -1341,14 +1413,12 @@ module Formtastic #:nodoc:
|
|
1341
1413
|
end
|
1342
1414
|
end
|
1343
1415
|
|
1344
|
-
|
1345
|
-
|
1346
|
-
|
1347
|
-
unless options.key?(:include_blank) || options.key?(:prompt)
|
1348
|
-
options[:include_blank] = @@include_blank_for_select_by_default
|
1349
|
-
end
|
1350
|
-
options
|
1416
|
+
def set_include_blank(options)
|
1417
|
+
unless options.key?(:include_blank) || options.key?(:prompt)
|
1418
|
+
options[:include_blank] = @@include_blank_for_select_by_default
|
1351
1419
|
end
|
1420
|
+
options
|
1421
|
+
end
|
1352
1422
|
|
1353
1423
|
end
|
1354
1424
|
|
@@ -1385,7 +1455,7 @@ module Formtastic #:nodoc:
|
|
1385
1455
|
# ...
|
1386
1456
|
# <% end %>
|
1387
1457
|
module SemanticFormHelper
|
1388
|
-
@@builder = Formtastic::SemanticFormBuilder
|
1458
|
+
@@builder = ::Formtastic::SemanticFormBuilder
|
1389
1459
|
mattr_accessor :builder
|
1390
1460
|
|
1391
1461
|
@@default_field_error_proc = nil
|
data/lib/locale/en.yml
CHANGED
data/spec/commit_button_spec.rb
CHANGED
@@ -46,12 +46,12 @@ describe 'SemanticFormBuilder#commit_button' do
|
|
46
46
|
describe "its accesskey" do
|
47
47
|
|
48
48
|
it 'should allow nil default' do
|
49
|
-
Formtastic::SemanticFormBuilder.default_commit_button_accesskey.should == nil
|
49
|
+
::Formtastic::SemanticFormBuilder.default_commit_button_accesskey.should == nil
|
50
50
|
output_buffer.should_not have_tag('li.commit input[@accesskey]')
|
51
51
|
end
|
52
52
|
|
53
53
|
it 'should use the default if set' do
|
54
|
-
Formtastic::SemanticFormBuilder.default_commit_button_accesskey = 's'
|
54
|
+
::Formtastic::SemanticFormBuilder.default_commit_button_accesskey = 's'
|
55
55
|
@new_post.stub!(:new_record?).and_return(false)
|
56
56
|
semantic_form_for(@new_post) do |builder|
|
57
57
|
concat(builder.commit_button('text', :button_html => {}))
|
@@ -60,7 +60,7 @@ describe 'SemanticFormBuilder#commit_button' do
|
|
60
60
|
end
|
61
61
|
|
62
62
|
it 'should use the value set in options over the default' do
|
63
|
-
Formtastic::SemanticFormBuilder.default_commit_button_accesskey = 's'
|
63
|
+
::Formtastic::SemanticFormBuilder.default_commit_button_accesskey = 's'
|
64
64
|
@new_post.stub!(:new_record?).and_return(false)
|
65
65
|
semantic_form_for(@new_post) do |builder|
|
66
66
|
concat(builder.commit_button('text', :accesskey => 'o'))
|
@@ -70,7 +70,7 @@ describe 'SemanticFormBuilder#commit_button' do
|
|
70
70
|
end
|
71
71
|
|
72
72
|
it 'should use the value set in button_html over options' do
|
73
|
-
Formtastic::SemanticFormBuilder.default_commit_button_accesskey = 's'
|
73
|
+
::Formtastic::SemanticFormBuilder.default_commit_button_accesskey = 's'
|
74
74
|
@new_post.stub!(:new_record?).and_return(false)
|
75
75
|
semantic_form_for(@new_post) do |builder|
|
76
76
|
concat(builder.commit_button('text', :accesskey => 'o', :button_html => {:accesskey => 't'}))
|
@@ -81,7 +81,7 @@ describe 'SemanticFormBuilder#commit_button' do
|
|
81
81
|
end
|
82
82
|
|
83
83
|
after do
|
84
|
-
Formtastic::SemanticFormBuilder.default_commit_button_accesskey = nil
|
84
|
+
::Formtastic::SemanticFormBuilder.default_commit_button_accesskey = nil
|
85
85
|
end
|
86
86
|
|
87
87
|
end
|
data/spec/custom_builder_spec.rb
CHANGED
@@ -5,7 +5,7 @@ describe 'Formtastic::SemanticFormHelper.builder' do
|
|
5
5
|
|
6
6
|
include FormtasticSpecHelper
|
7
7
|
|
8
|
-
class MyCustomFormBuilder < Formtastic::SemanticFormBuilder
|
8
|
+
class MyCustomFormBuilder < ::Formtastic::SemanticFormBuilder
|
9
9
|
def awesome_input(method, options)
|
10
10
|
self.text_field(method)
|
11
11
|
end
|
@@ -17,28 +17,28 @@ describe 'Formtastic::SemanticFormHelper.builder' do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
it 'is the Formtastic::SemanticFormBuilder by default' do
|
20
|
-
Formtastic::SemanticFormHelper.builder.should == Formtastic::SemanticFormBuilder
|
20
|
+
::Formtastic::SemanticFormHelper.builder.should == ::Formtastic::SemanticFormBuilder
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'can be configured to use your own custom form builder' do
|
24
24
|
# Set it to a custom builder class
|
25
|
-
Formtastic::SemanticFormHelper.builder = MyCustomFormBuilder
|
26
|
-
Formtastic::SemanticFormHelper.builder.should == MyCustomFormBuilder
|
25
|
+
::Formtastic::SemanticFormHelper.builder = MyCustomFormBuilder
|
26
|
+
::Formtastic::SemanticFormHelper.builder.should == MyCustomFormBuilder
|
27
27
|
|
28
28
|
# Reset it to the default
|
29
|
-
Formtastic::SemanticFormHelper.builder = Formtastic::SemanticFormBuilder
|
30
|
-
Formtastic::SemanticFormHelper.builder.should == Formtastic::SemanticFormBuilder
|
29
|
+
::Formtastic::SemanticFormHelper.builder = ::Formtastic::SemanticFormBuilder
|
30
|
+
::Formtastic::SemanticFormHelper.builder.should == ::Formtastic::SemanticFormBuilder
|
31
31
|
end
|
32
32
|
|
33
33
|
describe "when using a custom builder" do
|
34
34
|
|
35
35
|
before do
|
36
36
|
@new_post.stub!(:title)
|
37
|
-
Formtastic::SemanticFormHelper.builder = MyCustomFormBuilder
|
37
|
+
::Formtastic::SemanticFormHelper.builder = MyCustomFormBuilder
|
38
38
|
end
|
39
39
|
|
40
40
|
after do
|
41
|
-
Formtastic::SemanticFormHelper.builder = Formtastic::SemanticFormBuilder
|
41
|
+
::Formtastic::SemanticFormHelper.builder = ::Formtastic::SemanticFormBuilder
|
42
42
|
end
|
43
43
|
|
44
44
|
describe "semantic_form_for" do
|