justinfrench-formtastic 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -1,4 +1,4 @@
1
- h1. Formtastic 0.1.4
1
+ h1. Formtastic 0.1.5
2
2
 
3
3
  Formtastic is a Rails FormBuilder DSL (with some other goodies) to make it far easier to create beautiful, semantically rich, syntactically awesome, readily stylable and wonderfully accessible HTML forms in your Rails applications.
4
4
 
@@ -86,7 +86,7 @@ And then add it as a dependency in your environment.rb file:
86
86
  config.gem "justinfrench-formtastic",
87
87
  :lib => 'formtastic',
88
88
  :source => 'http://gems.github.com',
89
- :version => '0.1.4'
89
+ :version => '0.1.5'
90
90
  </pre>
91
91
 
92
92
  If you're a little more old school, install it as a plugin:
@@ -156,18 +156,20 @@ If you want to customize the label text, or render some hint text below the fiel
156
156
  <% end %>
157
157
  </pre>
158
158
 
159
- If you want to customize html elements for any non button inputs you just need
160
- to specify the :input_html options hash.
159
+ If you want to customize html elements for any non button inputs and the li
160
+ wrapper you just need to specify the :input_html and :wrapper_html options hash.
161
161
 
162
162
  <pre>
163
163
  <% semantic_form_for @post do |form| %>
164
164
  <%= form.input :title, :input_html => {:size => 60} %>
165
- <%= form.input :body %>
165
+ <%= form.input :body, :wrapper_html => { :class => 'body_wrapper' } %>
166
166
  <%= form.input :created_at, :input_html => {:disabled => true} %>
167
167
  <%= form.buttons %>
168
168
  <% end %>
169
169
  </pre>
170
170
 
171
+ To customize buttons, :button_html is available.
172
+
171
173
  Nested forms (Rails 2.3) are also supported. You can do it in the Rails way:
172
174
 
173
175
  <pre>
@@ -212,13 +214,13 @@ h2. The Available Inputs
212
214
 
213
215
  * :select (a select menu) - default for ActiveRecord associations (belongs_to, has_many, has_and_belongs_to_many)
214
216
  * :radio (a set of radio inputs) - alternative to :select for ActiveRecord belongs_to associations
217
+ * :time_zone (a select input) - default for :string column types with 'time_zone' in the method name
215
218
  * :password (a password input) - default for :string column types with 'password' in the method name
216
219
  * :text (a textarea) - default for :text column types
217
220
  * :date (a date select) - default for :date column types
218
221
  * :datetime (a date and time select) - default for :datetime and :timestamp column types
219
222
  * :time (a time select) - default for :time column types
220
223
  * :boolean (a checkbox) - default for :boolean column types
221
- * :boolean_select (a yes/no select box)
222
224
  * :string (a text field) - default for :string column types
223
225
  * :numeric (a text field, like string) - default for :integer, :float and :decimal column types
224
226
  * :file (a file field) - default for paperclip or attachment_fu attributes
@@ -329,21 +331,23 @@ h2. Contributors
329
331
  Formtastic wouldn't be as awesome as it is today if it weren't for the wonderful contributions of these fine, fine coders. An extra huge thanks goes out to "José Valim":http://github.com/josevalim for nearly 50 patches.
330
332
 
331
333
  * "Justin French":http://justinfrench.com
334
+ * "José Valim":http://github.com/josevalim
335
+ * "Jeff Smick":http://github.com/sprsquish
336
+ * "Tien Dung":http://github.com/tiendung
337
+ * "Mark Mansour":http://stateofflux.com
338
+ * "Andy Pearson":http://github.com/andypearson
339
+ * "negonicrac":http://github.com/negonicrac
332
340
  * "Xavier Shay":http://rhnh.net
333
- * "Bin Dong":http://github.com/dongbin
334
- * "Ben Hamill":http://blog.benhamill.com/
335
341
  * "Pat Allan":http://github.com/freelancing-god
336
- * "negonicrac":http://github.com/negonicrac
337
- * "Andy Pearson":http://github.com/andypearson
338
- * "Mark Mansour":http://stateofflux.com
339
- * "Tien Dung":http://github.com/tiendung
340
- * "Sascha Hoellger":http://github.com/mitnal
341
- * "Jeff Smick":http://github.com/sprsquish
342
- * "José Valim":http://github.com/josevalim
343
- * "Greg Fitzgerald":http://github.com/gregf/
344
342
  * "Gareth Townsend":http://github.com/quamen
343
+ * "Sascha Hoellger":http://github.com/mitnal
344
+ * "Andrew Carpenter":http://github.com/andrewcarpenter
345
345
  * "Jack Dempsey":http://github.com/jackdempsey/
346
+ * "Greg Fitzgerald":http://github.com/gregf/
347
+ * "Hector E. Gomez Morales":http://github.com/hectoregm
348
+ * "Ben Hamill":http://blog.benhamill.com/
346
349
  * "Simon Chiu":http://github.com/tolatomeow
350
+ * "Bin Dong":http://github.com/dongbin
347
351
 
348
352
 
349
353
  h2. Hey, join the Google group!
data/lib/formtastic.rb CHANGED
@@ -35,7 +35,6 @@ module Formtastic #:nodoc:
35
35
  STRING_MAPPINGS = [ :string, :password, :numeric ]
36
36
 
37
37
  attr_accessor :template
38
- attr_writer :nested_child_index
39
38
 
40
39
  # Returns a suitable form input for the given +method+, using the database column information
41
40
  # and other factors (like the method name) to figure out what you probably want.
@@ -47,6 +46,7 @@ module Formtastic #:nodoc:
47
46
  # * :required - specify if the column is required (true) or not (false)
48
47
  # * :hint - provide some text to hint or help the user provide the correct information for a field
49
48
  # * :input_html - provide options that will be passed down to the generated input
49
+ # * :wrapper_html - provide options that will be passed down to the li wrapper
50
50
  #
51
51
  # Input Types:
52
52
  #
@@ -55,6 +55,7 @@ module Formtastic #:nodoc:
55
55
  # columns all map to a single numeric_input, for example).
56
56
  #
57
57
  # * :select (a select menu for associations) - default to association names
58
+ # * :time_zone (a select menu with time zones)
58
59
  # * :radio (a set of radio inputs for associations) - default to association names
59
60
  # * :password (a password input) - default for :string column types with 'password' in the method name
60
61
  # * :text (a textarea) - default for :text column types
@@ -77,29 +78,25 @@ module Formtastic #:nodoc:
77
78
  # <% end %>
78
79
  #
79
80
  def input(method, options = {})
80
- options[:required] = method_required?(method, options[:required])
81
+ options[:required] = method_required?(method) unless options.key?(:required)
81
82
  options[:as] ||= default_input_type(method)
82
83
 
83
- options[:label] ||= if @object
84
- @object.class.human_attribute_name(method.to_s)
85
- else
86
- method.to_s.send(@@label_str_method)
87
- end
84
+ html_class = [ options[:as], (options[:required] ? :required : :optional) ]
85
+ html_class << 'error' if @object && @object.respond_to?(:errors) && @object.errors.on(method.to_s)
86
+
87
+ wrapper_html = options.delete(:wrapper_html) || {}
88
+ wrapper_html[:id] ||= generate_html_id(method)
89
+ wrapper_html[:class] = (html_class << wrapper_html[:class]).flatten.compact.join(' ')
88
90
 
89
91
  if [:boolean_select, :boolean_radio].include?(options[:as])
90
92
  ::ActiveSupport::Deprecation.warn(":as => :#{options[:as]} is deprecated, use :as => :#{options[:as].to_s[8..-1]} instead", caller[3..-1])
91
93
  end
92
94
 
93
- html_class = [ options[:as], (options[:required] ? :required : :optional) ].join(' ')
94
- html_class << ' error' if @object && @object.errors.on(method.to_s)
95
-
96
- html_id = generate_html_id(method)
97
-
98
95
  list_item_content = @@inline_order.map do |type|
99
96
  send(:"inline_#{type}_for", method, options)
100
97
  end.compact.join("\n")
101
98
 
102
- return template.content_tag(:li, list_item_content, { :id => html_id, :class => html_class })
99
+ return template.content_tag(:li, list_item_content, wrapper_html)
103
100
  end
104
101
 
105
102
  # Creates an input fieldset and ol tag wrapping for use around a set of inputs. It can be
@@ -233,21 +230,15 @@ module Formtastic #:nodoc:
233
230
  html_options = args.extract_options!
234
231
  html_options[:class] ||= "inputs"
235
232
 
236
- if fields_for_object = html_options.delete(:for)
237
- html_options.merge!(:parent_builder => self)
238
- inputs_for_nested_attributes(fields_for_object, args << html_options,
239
- html_options.delete(:for_options) || {}, &block)
233
+ if html_options[:for]
234
+ inputs_for_nested_attributes(args, html_options, &block)
240
235
  elsif block_given?
241
236
  field_set_and_list_wrapping(html_options, &block)
242
237
  else
243
238
  if @object && args.empty?
244
- # Get all belongs_to association
245
239
  args = @object.class.reflections.map { |n,_| n if _.macro == :belongs_to }
246
-
247
- # Get content columns and remove timestamps columns from it
248
240
  args += @object.class.content_columns.map(&:name)
249
241
  args -= %w[created_at updated_at created_on updated_on]
250
-
251
242
  args.compact!
252
243
  end
253
244
  contents = args.map { |method| input(method.to_sym) }
@@ -285,9 +276,10 @@ module Formtastic #:nodoc:
285
276
  #
286
277
  # <%= form.commit_button "Go" %> => <input name="commit" type="submit" value="Go" />
287
278
  #
288
- def commit_button(value=nil, options = {})
289
- value ||= save_or_create_button_text
290
- template.content_tag(:li, template.submit_tag(value), :class => "commit")
279
+ def commit_button(value=nil, options={})
280
+ value ||= save_or_create_button_text
281
+ button_html = options.delete(:button_html) || {}
282
+ template.content_tag(:li, self.submit(value, button_html), :class => "commit")
291
283
  end
292
284
 
293
285
  # A thin wrapper around #fields_for to set :builder => Formtastic::SemanticFormBuilder
@@ -316,6 +308,25 @@ module Formtastic #:nodoc:
316
308
  fields_for(record_or_name_or_array, *args, &block)
317
309
  end
318
310
 
311
+ # Generates the label for the input. Accepts the same arguments as Rails
312
+ # label method and a fourth one that allows the label to be generated
313
+ # as span tag with class label.
314
+ #
315
+ # :required can be also sent as option. When true, marks a filed as required,
316
+ # when false marks it as optional. When nil, does nothing.
317
+ #
318
+ def label(method, text, options={}, as_span=false)
319
+ text ||= humanized_attribute_name(method)
320
+ text << required_or_optional_string(options.delete(:required))
321
+
322
+ if as_span
323
+ options[:class] ||= 'label'
324
+ template.content_tag(:span, text, options)
325
+ else
326
+ super(method, text, options)
327
+ end
328
+ end
329
+
319
330
  protected
320
331
 
321
332
  # Deals with :for option when it's supplied to inputs methods. Additional
@@ -324,17 +335,20 @@ module Formtastic #:nodoc:
324
335
  #
325
336
  # It should raise an error if a block with arity zero is given.
326
337
  #
327
- def inputs_for_nested_attributes(fields_for_object, inputs, options, &block)
338
+ def inputs_for_nested_attributes(args, options, &block)
339
+ args << options.merge!(:parent => { :builder => self, :for => options[:for] })
340
+
328
341
  fields_for_block = if block_given?
329
342
  raise ArgumentError, 'You gave :for option with a block to inputs method, ' <<
330
343
  'but the block does not accept any argument.' if block.arity <= 0
331
344
 
332
- proc { |f| f.inputs(*inputs){ block.call(f) } }
345
+ proc { |f| f.inputs(*args){ block.call(f) } }
333
346
  else
334
- proc { |f| f.inputs(*inputs) }
347
+ proc { |f| f.inputs(*args) }
335
348
  end
336
349
 
337
- semantic_fields_for(*(Array(fields_for_object) << options), &fields_for_block)
350
+ fields_for_args = [options.delete(:for), options.delete(:for_options) || {}].flatten
351
+ semantic_fields_for(*fields_for_args, &fields_for_block)
338
352
  end
339
353
 
340
354
  # Remove any Formtastic-specific options before passing the down options.
@@ -374,9 +388,7 @@ module Formtastic #:nodoc:
374
388
  # * if the :required option isn't provided, and the plugin isn't available, the value of the
375
389
  # configuration option @@all_fields_required_by_default is used.
376
390
  #
377
- def method_required?(attribute, required_option) #:nodoc:
378
- return required_option unless required_option.nil?
379
-
391
+ def method_required?(attribute) #:nodoc:
380
392
  if @object && @object.class.respond_to?(:reflect_on_all_validations)
381
393
  attribute_sym = attribute.to_s.sub(/_id$/, '').to_sym
382
394
 
@@ -398,7 +410,8 @@ module Formtastic #:nodoc:
398
410
  html_options = options.delete(:input_html) || {}
399
411
  html_options = default_string_options(method).merge(html_options) if STRING_MAPPINGS.include?(type)
400
412
 
401
- input_label(method, options.delete(:label), options.slice(:required)) + send(INPUT_MAPPINGS[type], method, html_options)
413
+ self.label(method, options.delete(:label), options.slice(:required)) +
414
+ self.send(INPUT_MAPPINGS[type], method, html_options)
402
415
  end
403
416
 
404
417
  # Outputs a label and a select box containing options from the parent
@@ -490,11 +503,25 @@ module Formtastic #:nodoc:
490
503
  end
491
504
 
492
505
  input_name = generate_association_input_name(method)
493
- input_label(input_name, options.delete(:label), options.slice(:required)) +
506
+ self.label(input_name, options.delete(:label), options.slice(:required)) +
494
507
  self.select(input_name, collection, set_options(options), html_options)
495
508
  end
496
509
  alias :boolean_select_input :select_input
497
510
 
511
+ # Outputs a timezone select input as Rails' time_zone_select helper. You
512
+ # can give priority zones as option.
513
+ #
514
+ # Examples:
515
+ #
516
+ # f.input :time_zone, :as => :time_zone, :priority_zones => /Australia/
517
+ #
518
+ def time_zone_input(method, options)
519
+ html_options = options.delete(:input_html) || {}
520
+
521
+ self.label(method, options.delete(:label), options.slice(:required)) +
522
+ self.time_zone_select(method, options.delete(:priority_zones), set_options(options), html_options)
523
+ end
524
+
498
525
  # Outputs a fieldset containing a legend for the label text, and an ordered list (ol) of list
499
526
  # items, one for each possible choice in the belongs_to association. Each li contains a
500
527
  # label and a radio input.
@@ -668,18 +695,16 @@ module Formtastic #:nodoc:
668
695
 
669
696
  # Outputs a label containing a checkbox and the label text. The label defaults
670
697
  # to the column name (method name) and can be altered with the :label option.
671
- #
672
- # Different from other inputs, :required options has no effect here and
673
698
  # :checked_value and :unchecked_value options are also available.
674
699
  #
675
700
  def boolean_input(method, options)
676
701
  html_options = options.delete(:input_html) || {}
677
702
 
678
- content = self.check_box(method, set_options(options).merge(html_options),
679
- options.delete(:checked_value) || '1', options.delete(:unchecked_value) || '0')
703
+ input = self.check_box(method, set_options(options).merge(html_options),
704
+ options.delete(:checked_value) || '1', options.delete(:unchecked_value) || '0')
680
705
 
681
- # required does not make sense in check box
682
- input_label(method, content + options.delete(:label), :skip_required => true)
706
+ label = options.delete(:label) || humanized_attribute_name(method)
707
+ self.label(method, input + label, options.slice(:required))
683
708
  end
684
709
 
685
710
  # Generates an input for the given method using the type supplied with :as.
@@ -703,16 +728,24 @@ module Formtastic #:nodoc:
703
728
  # or as sentence. If :none is set, no error is shown.
704
729
  #
705
730
  def inline_errors_for(method, options) #:nodoc:
706
- return nil unless @object && [:sentence, :list].include?(@@inline_errors)
731
+ return nil unless @object && @object.respond_to?(:errors) && [:sentence, :list].include?(@@inline_errors)
732
+
733
+ # Ruby 1.9: Strings are not Enumerable, ie no String#to_a
734
+ errors = @object.errors.on(method.to_s)
735
+ unless errors.respond_to?(:to_a)
736
+ errors = [errors]
737
+ else
738
+ errors = errors.to_a
739
+ end
707
740
 
708
- errors = @object.errors.on(method.to_s).to_a
709
741
  send("error_#{@@inline_errors}", errors) unless errors.empty?
710
742
  end
711
743
 
712
744
  # Generates hints for the given method using the text supplied in :hint.
713
745
  #
714
746
  def inline_hints_for(method, options) #:nodoc:
715
- options[:hint].blank? ? '' : template.content_tag(:p, options[:hint], :class => 'inline-hints')
747
+ return if options[:hint].blank?
748
+ template.content_tag(:p, options[:hint], :class => 'inline-hints')
716
749
  end
717
750
 
718
751
  # Creates an error sentence by calling to_sentence on the errors array.
@@ -731,31 +764,23 @@ module Formtastic #:nodoc:
731
764
  template.content_tag(:ul, list_elements.join("\n"), :class => 'errors')
732
765
  end
733
766
 
734
- # Generates the label for the input. Accepts the same options as Rails label
735
- # method and a fourth option that allows the label to be generated as span
736
- # with class label.
737
- #
738
- def input_label(method, text, options={}, as_span=false) #:nodoc:
739
- text << required_or_optional_string(options.delete(:required)) unless options.delete(:skip_required)
740
-
741
- if as_span
742
- options[:class] ||= 'label'
743
- template.content_tag(:span, text, options)
744
- else
745
- self.label(method, text, options)
746
- end
747
- end
748
-
749
767
  # Generates the required or optional string. If the value set is a proc,
750
768
  # it evaluates the proc first.
751
769
  #
752
770
  def required_or_optional_string(required) #:nodoc:
753
- string_or_proc = required ? @@required_string : @@optional_string
771
+ string_or_proc = case required
772
+ when true
773
+ @@required_string
774
+ when false
775
+ @@optional_string
776
+ else
777
+ required
778
+ end
754
779
 
755
- if string_or_proc.is_a? Proc
780
+ if string_or_proc.is_a?(Proc)
756
781
  string_or_proc.call
757
782
  else
758
- string_or_proc
783
+ string_or_proc.to_s
759
784
  end
760
785
  end
761
786
 
@@ -768,23 +793,18 @@ module Formtastic #:nodoc:
768
793
  # And it will generate a fieldset for each task with legend 'Task #1', 'Task #2',
769
794
  # 'Task #3' and so on.
770
795
  #
771
- # If you are using inputs :for, for more than one association in the same
772
- # form builder, you might want to set the nested_child_index as well. You
773
- # can do that doing:
774
- #
775
- # f.nested_child_index = -1
776
- #
777
- def field_set_and_list_wrapping(html_options, contents = '', &block) #:nodoc:
778
- # Generates the legend text allowing nested_child_index support for interpolation
779
- legend_text = html_options.delete(:name).to_s
780
- legend_text %= html_options[:parent_builder].instance_variable_get('@nested_child_index').to_i + 1
796
+ def field_set_and_list_wrapping(html_options, contents='', &block) #:nodoc:
797
+ legend = html_options.delete(:name).to_s
798
+ legend %= parent_child_index(html_options[:parent]) if html_options[:parent]
799
+ legend = template.content_tag(:legend, template.content_tag(:span, legend)) unless legend.blank?
781
800
 
782
- legend = legend_text.blank? ? "" : template.content_tag(:legend, template.content_tag(:span, legend_text))
783
801
  contents = template.capture(&block) if block_given?
784
802
 
803
+ # Ruby 1.9: String#to_s behavior changed, need to make an explicit join.
804
+ contents = contents.join if contents.respond_to?(:join)
785
805
  fieldset = template.content_tag(:fieldset,
786
806
  legend + template.content_tag(:ol, contents),
787
- html_options.except(:builder, :parent_builder)
807
+ html_options.except(:builder, :parent)
788
808
  )
789
809
 
790
810
  template.concat(fieldset) if block_given?
@@ -796,12 +816,12 @@ module Formtastic #:nodoc:
796
816
  #
797
817
  def field_set_and_list_wrapping_for_method(method, options, contents)
798
818
  template.content_tag(:fieldset,
799
- %{<legend>#{input_label(method, options.delete(:label), options.slice(:required), true)}</legend>} +
819
+ %{<legend>#{self.label(method, options.delete(:label), options.slice(:required), true)}</legend>} +
800
820
  template.content_tag(:ol, contents)
801
821
  )
802
822
  end
803
823
 
804
- # For methods that have a database column, take a best guess as to what the inout method
824
+ # For methods that have a database column, take a best guess as to what the input method
805
825
  # should be. In most cases, it will just return the column type (eg :string), but for special
806
826
  # cases it will simplify (like the case of :integer, :float & :decimal to :numeric), or do
807
827
  # something different (like :password and :select).
@@ -812,23 +832,22 @@ module Formtastic #:nodoc:
812
832
  def default_input_type(method) #:nodoc:
813
833
  return :string if @object.nil?
814
834
 
815
- # Find the column object by attribute
816
835
  column = @object.column_for_attribute(method) if @object.respond_to?(:column_for_attribute)
817
836
 
818
- # Associations map by default to a select
819
- return :select if column.nil? && find_reflection(method)
820
-
821
837
  if column
822
838
  # handle the special cases where the column type doesn't map to an input method
823
- return :select if column.type == :integer && method.to_s =~ /_id$/
824
- return :datetime if column.type == :timestamp
825
- return :numeric if [:integer, :float, :decimal].include?(column.type)
826
- return :password if column.type == :string && method.to_s =~ /password/
839
+ return :time_zone if column.type == :string && method.to_s =~ /time_zone/
840
+ return :select if column.type == :integer && method.to_s =~ /_id$/
841
+ return :datetime if column.type == :timestamp
842
+ return :numeric if [:integer, :float, :decimal].include?(column.type)
843
+ return :password if column.type == :string && method.to_s =~ /password/
844
+
827
845
  # otherwise assume the input name will be the same as the column type (eg string_input)
828
846
  return column.type
829
847
  else
830
848
  obj = @object.send(method) if @object.respond_to?(method)
831
849
 
850
+ return :select if find_reflection(method)
832
851
  return :file if obj && @@file_methods.any? { |m| obj.respond_to?(m) }
833
852
  return :password if method.to_s =~ /password/
834
853
  return :string
@@ -913,7 +932,7 @@ module Formtastic #:nodoc:
913
932
  # reflection object.
914
933
  #
915
934
  def find_reflection(method)
916
- object.class.reflect_on_association(method) if object.class.respond_to?(:reflect_on_association)
935
+ @object.class.reflect_on_association(method) if @object.class.respond_to?(:reflect_on_association)
917
936
  end
918
937
 
919
938
  # Generates default_string_options by retrieving column information from
@@ -947,10 +966,34 @@ module Formtastic #:nodoc:
947
966
  "#{sanitized_object_name}#{index}_#{sanitized_method_name}_#{value}"
948
967
  end
949
968
 
969
+ # Gets the nested_child_index value from the parent builder. In Rails 2.3
970
+ # it always returns a fixnum. In next versions it returns a hash with each
971
+ # association that the parent builds.
972
+ #
973
+ def parent_child_index(parent)
974
+ duck = parent[:builder].instance_variable_get('@nested_child_index')
975
+
976
+ if duck.is_a?(Hash)
977
+ child = parent[:for]
978
+ child = child.first if child.respond_to?(:first)
979
+ duck[child].to_i + 1
980
+ else
981
+ duck.to_i + 1
982
+ end
983
+ end
984
+
950
985
  def sanitized_object_name
951
986
  @sanitized_object_name ||= @object_name.to_s.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
952
987
  end
953
988
 
989
+ def humanized_attribute_name(method)
990
+ if @object && @object.class.respond_to?(:human_attribute_name)
991
+ @object.class.human_attribute_name(method.to_s)
992
+ else
993
+ method.to_s.send(@@label_str_method)
994
+ end
995
+ end
996
+
954
997
  end
955
998
 
956
999
  # Wrappers around form_for (etc) with :builder => SemanticFormBuilder.
@@ -561,7 +561,7 @@ describe 'Formtastic' do
561
561
  end
562
562
 
563
563
  it 'should call the corresponding input method' do
564
- [:select, :radio, :date, :datetime, :time, :boolean].each do |input_style|
564
+ [:select, :time_zone, :radio, :date, :datetime, :time, :boolean].each do |input_style|
565
565
  @new_post.stub!(:generic_column_name)
566
566
  @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
567
567
  semantic_form_for(@new_post) do |builder|
@@ -647,6 +647,47 @@ describe 'Formtastic' do
647
647
  end
648
648
 
649
649
  end
650
+
651
+ describe ':wrapper_html option' do
652
+
653
+ describe 'when provided' do
654
+ it 'should be passed down to the li tag' do
655
+ semantic_form_for(@new_post) do |builder|
656
+ concat(builder.input(:title, :wrapper_html => {:id => :another_id}))
657
+ end
658
+ output_buffer.should have_tag("form li#another_id")
659
+ end
660
+
661
+ it 'should append given classes to li default classes' do
662
+ semantic_form_for(@new_post) do |builder|
663
+ concat(builder.input(:title, :wrapper_html => {:class => :another_class}, :required => true))
664
+ end
665
+ output_buffer.should have_tag("form li.string")
666
+ output_buffer.should have_tag("form li.required")
667
+ output_buffer.should have_tag("form li.another_class")
668
+ end
669
+
670
+ it 'should allow classes to be an array' do
671
+ semantic_form_for(@new_post) do |builder|
672
+ concat(builder.input(:title, :wrapper_html => {:class => [ :my_class, :another_class ]}))
673
+ end
674
+ output_buffer.should have_tag("form li.string")
675
+ output_buffer.should have_tag("form li.my_class")
676
+ output_buffer.should have_tag("form li.another_class")
677
+ end
678
+ end
679
+
680
+ describe 'when not provided' do
681
+ it 'should use default id and class' do
682
+ semantic_form_for(@new_post) do |builder|
683
+ concat(builder.input(:title))
684
+ end
685
+ output_buffer.should have_tag("form li#post_title_input")
686
+ output_buffer.should have_tag("form li.string")
687
+ end
688
+ end
689
+
690
+ end
650
691
  end
651
692
 
652
693
  describe ':as any type of input' do
@@ -971,6 +1012,57 @@ describe 'Formtastic' do
971
1012
  end
972
1013
  end
973
1014
 
1015
+ describe ":as => :time_zone" do
1016
+ before do
1017
+ @new_post.stub!(:time_zone)
1018
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :string))
1019
+
1020
+ semantic_form_for(@new_post) do |builder|
1021
+ concat(builder.input(:time_zone))
1022
+ end
1023
+ end
1024
+
1025
+ it "should have a time_zone class on the wrapper" do
1026
+ output_buffer.should have_tag('form li.time_zone')
1027
+ end
1028
+
1029
+ it 'should have a post_title_input id on the wrapper' do
1030
+ output_buffer.should have_tag('form li#post_time_zone_input')
1031
+ end
1032
+
1033
+ it 'should generate a label for the input' do
1034
+ output_buffer.should have_tag('form li label')
1035
+ output_buffer.should have_tag('form li label[@for="post_time_zone"')
1036
+ output_buffer.should have_tag('form li label', /Time zone/)
1037
+ end
1038
+
1039
+ it "should generate a select" do
1040
+ output_buffer.should have_tag("form li select")
1041
+ output_buffer.should have_tag("form li select#post_time_zone")
1042
+ output_buffer.should have_tag("form li select[@name=\"post[time_zone]\"]")
1043
+ end
1044
+
1045
+ it 'should use input_html to style inputs' do
1046
+ semantic_form_for(@new_post) do |builder|
1047
+ concat(builder.input(:time_zone, :input_html => { :class => 'myclass' }))
1048
+ end
1049
+ output_buffer.should have_tag("form li select.myclass")
1050
+ end
1051
+
1052
+ it 'should generate input and labels even if no object is given' do
1053
+ semantic_form_for(:project, :url => 'http://test.host/') do |builder|
1054
+ concat(builder.input(:time_zone, :as => :time_zone))
1055
+ end
1056
+
1057
+ output_buffer.should have_tag('form li label')
1058
+ output_buffer.should have_tag('form li label[@for="project_time_zone"')
1059
+ output_buffer.should have_tag('form li label', /Time zone/)
1060
+
1061
+ output_buffer.should have_tag("form li select")
1062
+ output_buffer.should have_tag("form li select#project_time_zone")
1063
+ output_buffer.should have_tag("form li select[@name=\"project[time_zone]\"]")
1064
+ end
1065
+ end
974
1066
 
975
1067
  describe ':as => :radio' do
976
1068
 
@@ -1145,7 +1237,7 @@ describe 'Formtastic' do
1145
1237
 
1146
1238
  it 'should have a label inside the wrapper' do
1147
1239
  output_buffer.should have_tag('form li label')
1148
- output_buffer.should have_tag('form li label', /Posts/)
1240
+ output_buffer.should have_tag('form li label', /Post ids/)
1149
1241
  output_buffer.should have_tag("form li label[@for='author_post_ids']")
1150
1242
  end
1151
1243
 
@@ -1187,7 +1279,7 @@ describe 'Formtastic' do
1187
1279
 
1188
1280
  it 'should have a label inside the wrapper' do
1189
1281
  output_buffer.should have_tag('form li label')
1190
- output_buffer.should have_tag('form li label', /Authors/)
1282
+ output_buffer.should have_tag('form li label', /Author ids/)
1191
1283
  output_buffer.should have_tag("form li label[@for='post_author_ids']")
1192
1284
  end
1193
1285
 
@@ -1967,7 +2059,7 @@ describe 'Formtastic' do
1967
2059
 
1968
2060
  it 'should send parent_builder as an option to allow child index interpolation' do
1969
2061
  semantic_form_for(@new_post) do |builder|
1970
- builder.should_receive(:instance_variable_get).with('@nested_child_index').and_return(0)
2062
+ builder.instance_variable_set('@nested_child_index', 0)
1971
2063
  builder.inputs :for => [:author, @bob], :name => 'Author #%i' do |bob_builder|
1972
2064
  concat('input')
1973
2065
  end
@@ -1975,6 +2067,17 @@ describe 'Formtastic' do
1975
2067
 
1976
2068
  output_buffer.should have_tag('fieldset legend', 'Author #1')
1977
2069
  end
2070
+
2071
+ it 'should also provide child index interpolation when nested child index is a hash' do
2072
+ semantic_form_for(@new_post) do |builder|
2073
+ builder.instance_variable_set('@nested_child_index', :author => 10)
2074
+ builder.inputs :for => [:author, @bob], :name => 'Author #%i' do |bob_builder|
2075
+ concat('input')
2076
+ end
2077
+ end
2078
+
2079
+ output_buffer.should have_tag('fieldset legend', 'Author #11')
2080
+ end
1978
2081
  end
1979
2082
 
1980
2083
  describe 'when a :name option is provided' do
@@ -2311,6 +2414,16 @@ describe 'Formtastic' do
2311
2414
  output_buffer.should have_tag('li.commit input[@name="commit"]')
2312
2415
  end
2313
2416
 
2417
+ it 'should pass options given in :button_html to the button' do
2418
+ @new_post.stub!(:new_record?).and_return(false)
2419
+ semantic_form_for(@new_post) do |builder|
2420
+ concat(builder.commit_button('text', :button_html => {:class => 'my_class', :id => 'my_id'}))
2421
+ end
2422
+
2423
+ output_buffer.should have_tag('li.commit input#my_id')
2424
+ output_buffer.should have_tag('li.commit input.my_class')
2425
+ end
2426
+
2314
2427
  end
2315
2428
 
2316
2429
  describe 'when used on an existing record' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: justinfrench-formtastic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin French
@@ -9,7 +9,7 @@ autorequire: formtastic
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-10 00:00:00 -07:00
12
+ date: 2009-04-19 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15