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/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 :name, :label => "Full Name"%>
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 (otherwise a legend is not generated).
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 = @object.class.reflections.map { |n,_| n if _.macro == :belongs_to }
239
- args += @object.class.content_columns.map(&:name)
240
- args -= %w[created_at updated_at created_on updated_on lock_version version]
263
+ args = self.association_columns(:belongs_to)
264
+ args += self.content_columns
265
+ args -= RESERVED_COLUMNS
241
266
  args.compact!
242
267
  end
243
- contents = args.map { |method| input(method.to_sym) }
244
-
245
- field_set_and_list_wrapping(html_options, contents)
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, :default => fallback_text, :scope => [:formtastic])) unless text.is_a?(::String)
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
- if options.delete(:as_span)
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) && [:sentence, :list, :first].include?(@@inline_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
- end
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 => generate_html_id(input_name, value.to_s.gsub(/\s/, '_').gsub(/\W/, '').downcase)
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.translate(:'date.order').is_a?(Array) ? I18n.translate(:'date.order') : nil
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
- html_id = generate_html_id(method, "#{position[input]}i")
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}".intern]
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 => html_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 => html_id) +
849
- template.send("select_#{input}".intern, datetime, opts, html_options.merge(:id => html_id))
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 + field_set_and_list_wrapping_for_method(method, options, list_items_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><span>Authors</span></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
- html_options.merge!(:id => generate_html_id(input_name, value.to_s.gsub(/\s/, '_').gsub(/\W/, '').downcase))
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 => html_options[:id]
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
- def field_set_and_list_wrapping(html_options, contents='', &block) #:nodoc:
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
- html_options[:name] = localized_string(html_options[:name], html_options[:name], :title) if html_options[:name].is_a?(Symbol)
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 + template.content_tag(:ol, contents),
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
- %{<legend>#{self.label(method, options_for_label(options).merge!(:as_span => true))}</legend>} +
1091
- template.content_tag(:ol, contents)
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('yes', :default => 'Yes', :scope => [:formtastic])
1193
- options[:false] ||= I18n.t('no', :default => 'No', :scope => [:formtastic])
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, options.merge(:default => defaults,
1330
- :scope => :"formtastic.#{type.to_s.pluralize}"))
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
- private
1345
-
1346
- def set_include_blank(options)
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
@@ -1,7 +1,7 @@
1
1
  en:
2
2
  formtastic:
3
- "yes": 'Yes'
4
- "no": 'No'
3
+ :yes: 'Yes'
4
+ :no: 'No'
5
5
  create: 'Create'
6
6
  save: 'Save'
7
7
  submit: 'Submit'
@@ -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
@@ -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