formtastic 0.9.2 → 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|