justinfrench-formtastic 0.1.7 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -227,6 +227,7 @@ Customize the HTML attributes for the @<li>@ wrapper around every input with the
227
227
  h2. The Available Inputs
228
228
 
229
229
  * :select (a select menu) - default for ActiveRecord associations (belongs_to, has_many, has_and_belongs_to_many)
230
+ * :check_boxes (a set of check_box inputs) - alternative to :select has_many and has_and_belongs_to_many associations
230
231
  * :radio (a set of radio inputs) - alternative to :select for ActiveRecord belongs_to associations
231
232
  * :time_zone (a select input) - default for :string column types with 'time_zone' in the method name
232
233
  * :password (a password input) - default for :string column types with 'password' in the method name
@@ -238,6 +239,9 @@ h2. The Available Inputs
238
239
  * :string (a text field) - default for :string column types
239
240
  * :numeric (a text field, like string) - default for :integer, :float and :decimal column types
240
241
  * :file (a file field) - default for paperclip or attachment_fu attributes
242
+ * :country (a select menu of country names) - default for :string columns named "country", requires a country_select plugin to be installed
243
+ * :hidden (a hidden field) - creates a hidden field (added for compatibility)
244
+
241
245
 
242
246
  The documentation is pretty good for each of these (what it does, what the output is, what the options are, etc) so go check it out.
243
247
 
@@ -284,6 +288,9 @@ If you wish, put something like this in config/initializers/formtastic_config.rb
284
288
  # the input, in the following order: hints, input and errors. You can
285
289
  # customize it doing just as below:
286
290
  Formtastic::SemanticFormBuilder.inline_order = [:hints, :input, :errors]
291
+
292
+ # Set the default "priority countries" to suit your user base when using :as => :country
293
+ Formtastic::SemanticFormBuilder.priority_countries = ["Australia", "New Zealand"]
287
294
  </pre>
288
295
 
289
296
 
@@ -313,6 +320,7 @@ h2. Dependencies
313
320
  There are none, but...
314
321
 
315
322
  * if you have the "ValidationReflection":http://github.com/redinger/validation_reflection plugin is installed, you won't have to specify the :required option (it checks the validations on the model instead)
323
+ * if you want to use the :country input, you'll need to install the "iso-3166-country-select plugin":http://github.com/rails/iso-3166-country-select (or any other country_select plugin with the same API)
316
324
  * rspec, rspec_hpricot_matchers and rcov gems (plus any of their own dependencies) are required for the test suite
317
325
 
318
326
 
@@ -342,10 +350,8 @@ A proof-of-concept (very much a work-in-progress) stylesheet is provided which y
342
350
 
343
351
  h2. Contributors
344
352
 
345
- 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.
353
+ Formtastic is maintained by "Justin French":http://justinfrench.com and "José Valim":http://github.com/josevalim, but it wouldn't be as awesome as it is today if it weren't for the wonderful contributions of these fine, fine coders.
346
354
 
347
- * "Justin French":http://justinfrench.com
348
- * "José Valim":http://github.com/josevalim
349
355
  * "Jeff Smick":http://github.com/sprsquish
350
356
  * "Tien Dung":http://github.com/tiendung
351
357
  * "Mark Mansour":http://stateofflux.com
@@ -59,7 +59,7 @@ form.formtastic fieldset ol li li label { line-height:100%; padding-top:0; }
59
59
  form.formtastic fieldset ol li li label input { line-height:100%; vertical-align:middle; margin-top:-0.1em;}
60
60
 
61
61
 
62
- /* NESTED FIELDSETS AND LEGENDS (radio and date/time inputs use nested fieldsets)
62
+ /* NESTED FIELDSETS AND LEGENDS (radio, check boxes and date/time inputs use nested fieldsets)
63
63
  --------------------------------------------------------------------------------------------------*/
64
64
  form.formtastic fieldset ol li fieldset { position:relative; }
65
65
  form.formtastic fieldset ol li fieldset legend { position:absolute; width:25%; padding-top:0.1em; }
@@ -91,7 +91,12 @@ form.formtastic fieldset ol li.numeric input { width:74%; }
91
91
  form.formtastic fieldset ol li.text textarea { width:74%; }
92
92
 
93
93
 
94
- /* CHECKBOX OVERRIDES
94
+ /* HIDDEN OVERRIDES
95
+ --------------------------------------------------------------------------------------------------*/
96
+ form.formtastic fieldset ol li.hidden { display:none; }
97
+
98
+
99
+ /* BOOLEAN OVERRIDES
95
100
  --------------------------------------------------------------------------------------------------*/
96
101
  form.formtastic fieldset ol li.boolean label { padding-left:25%; width:auto; }
97
102
  form.formtastic fieldset ol li.boolean label input { margin:0 0.5em 0 0.2em; }
@@ -106,6 +111,16 @@ form.formtastic fieldset ol li.radio fieldset ol li label { float:none; width:10
106
111
  form.formtastic fieldset ol li.radio fieldset ol li label input { margin-right:0.2em; }
107
112
 
108
113
 
114
+ /* CHECK BOXES (COLLECTION) OVERRIDES
115
+ --------------------------------------------------------------------------------------------------*/
116
+ form.formtastic fieldset ol li.check_boxes { }
117
+ form.formtastic fieldset ol li.check_boxes fieldset ol { margin-bottom:-0.6em; }
118
+ form.formtastic fieldset ol li.check_boxes fieldset ol li { margin:0.1em 0 0.5em 0; }
119
+ form.formtastic fieldset ol li.check_boxes fieldset ol li label { float:none; width:100%; }
120
+ form.formtastic fieldset ol li.check_boxes fieldset ol li label input { margin-right:0.2em; }
121
+
122
+
123
+
109
124
  /* DATE & TIME OVERRIDES
110
125
  --------------------------------------------------------------------------------------------------*/
111
126
  form.formtastic fieldset ol li.date fieldset ol li,
data/lib/formtastic.rb CHANGED
@@ -18,13 +18,13 @@ module Formtastic #:nodoc:
18
18
  @@collection_label_methods = %w[to_label display_name full_name name title username login value to_s]
19
19
  @@inline_order = [ :input, :hints, :errors ]
20
20
  @@file_methods = [ :file?, :public_filename ]
21
+ @@priority_countries = ["Australia", "Canada", "United Kingdom", "United States"]
21
22
 
22
23
  cattr_accessor :default_text_field_size, :all_fields_required_by_default, :required_string,
23
24
  :optional_string, :inline_errors, :label_str_method, :collection_label_methods,
24
- :inline_order, :file_methods
25
+ :inline_order, :file_methods, :priority_countries
25
26
 
26
27
  # Keeps simple mappings in a hash
27
- #
28
28
  INPUT_MAPPINGS = {
29
29
  :string => :text_field,
30
30
  :password => :password_field,
@@ -42,7 +42,7 @@ module Formtastic #:nodoc:
42
42
  # Options:
43
43
  #
44
44
  # * :as - override the input type (eg force a :string to render as a :password field)
45
- # * :label - use something other than the method name as the label (or fieldset legend) text
45
+ # * :label - use something other than the method name as the label text, when false no label is printed
46
46
  # * :required - specify if the column is required (true) or not (false)
47
47
  # * :hint - provide some text to hint or help the user provide the correct information for a field
48
48
  # * :input_html - provide options that will be passed down to the generated input
@@ -55,8 +55,9 @@ 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
+ # * :check_boxes (a set of check_box inputs for associations) - alternative to :select has_many and has_and_belongs_to_many associations
59
+ # * :radio (a set of radio inputs for associations) - alternative to :select belongs_to associations
58
60
  # * :time_zone (a select menu with time zones)
59
- # * :radio (a set of radio inputs for associations) - default to association names
60
61
  # * :password (a password input) - default for :string column types with 'password' in the method name
61
62
  # * :text (a textarea) - default for :text column types
62
63
  # * :date (a date select) - default for :date column types
@@ -65,6 +66,8 @@ module Formtastic #:nodoc:
65
66
  # * :boolean (a checkbox) - default for :boolean column types (you can also have booleans as :select and :radio)
66
67
  # * :string (a text field) - default for :string column types
67
68
  # * :numeric (a text field, like string) - default for :integer, :float and :decimal column types
69
+ # * :country (a select menu of country names) - requires a country_select plugin to be installed
70
+ # * :hidden (a hidden field) - creates a hidden field (added for compatibility)
68
71
  #
69
72
  # Example:
70
73
  #
@@ -308,18 +311,39 @@ module Formtastic #:nodoc:
308
311
  fields_for(record_or_name_or_array, *args, &block)
309
312
  end
310
313
 
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
+ # Generates the label for the input. It also accepts the same arguments as
315
+ # Rails label method. It has three options that are not supported by Rails
316
+ # label method:
317
+ #
318
+ # * :required - Appends an abbr tag if :required is true
319
+ # * :label - An alternative form to give the label content. Whenever label
320
+ # is false, a blank string is returned.
321
+ # * :as_span - When true returns a span tag with class label instead of a label element
322
+ #
323
+ # == Examples
324
+ #
325
+ # f.label :title # like in rails, except that it searches the label on I18n API too
314
326
  #
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.
327
+ # f.label :title, "Your post title"
328
+ # f.label :title, :label => "Your post title" # Added for formtastic API
317
329
  #
318
- def label(method, text=nil, options={}, as_span=false)
330
+ # f.label :title, :required => true # Returns <label>Title<abbr title="required">*</abbr></label>
331
+ #
332
+ def label(method, options_or_text=nil, options=nil)
333
+ if options_or_text.is_a?(Hash)
334
+ return if options_or_text[:label] == false
335
+
336
+ options = options_or_text
337
+ text = options.delete(:label)
338
+ else
339
+ text = options_or_text
340
+ options ||= {}
341
+ end
342
+
319
343
  text ||= humanized_attribute_name(method)
320
344
  text << required_or_optional_string(options.delete(:required))
321
345
 
322
- if as_span
346
+ if options.delete(:as_span)
323
347
  options[:class] ||= 'label'
324
348
  template.content_tag(:span, text, options)
325
349
  else
@@ -327,6 +351,25 @@ module Formtastic #:nodoc:
327
351
  end
328
352
  end
329
353
 
354
+ # Generates error messages for the given method. Errors can be shown as list
355
+ # or as sentence. If :none is set, no error is shown.
356
+ #
357
+ # This method is also aliased as errors_on, so you can call on your custom
358
+ # inputs as well:
359
+ #
360
+ # semantic_form_for :post do |f|
361
+ # f.text_field(:body)
362
+ # f.errors_on(:body)
363
+ # end
364
+ #
365
+ def inline_errors_for(method, options=nil) #:nodoc:
366
+ return nil unless @object && @object.respond_to?(:errors) && [:sentence, :list].include?(@@inline_errors)
367
+
368
+ errors = @object.errors.on(method.to_s)
369
+ send("error_#{@@inline_errors}", Array(errors)) unless errors.blank?
370
+ end
371
+ alias :errors_on :inline_errors_for
372
+
330
373
  protected
331
374
 
332
375
  # Deals with :for option when it's supplied to inputs methods. Additional
@@ -410,10 +453,18 @@ module Formtastic #:nodoc:
410
453
  html_options = options.delete(:input_html) || {}
411
454
  html_options = default_string_options(method).merge(html_options) if STRING_MAPPINGS.include?(type)
412
455
 
413
- self.label(method, options.delete(:label), options.slice(:required)) +
456
+ self.label(method, options.slice(:label, :required)) +
414
457
  self.send(INPUT_MAPPINGS[type], method, html_options)
415
458
  end
416
459
 
460
+ # Outputs a hidden field inside the wrapper, which should be hidden with CSS.
461
+ # Additionals options can be given and will be sent straight to hidden input
462
+ # element.
463
+ #
464
+ def hidden_input(method, options)
465
+ self.hidden_field(method, set_options(options))
466
+ end
467
+
417
468
  # Outputs a label and a select box containing options from the parent
418
469
  # (belongs_to, has_many, has_and_belongs_to_many) association. If an association
419
470
  # is has_many or has_and_belongs_to_many the select box will be set as multi-select
@@ -503,7 +554,7 @@ module Formtastic #:nodoc:
503
554
  end
504
555
 
505
556
  input_name = generate_association_input_name(method)
506
- self.label(input_name, options.delete(:label), options.slice(:required)) +
557
+ self.label(input_name, options.slice(:label, :required)) +
507
558
  self.select(input_name, collection, set_options(options), html_options)
508
559
  end
509
560
  alias :boolean_select_input :select_input
@@ -518,7 +569,7 @@ module Formtastic #:nodoc:
518
569
  def time_zone_input(method, options)
519
570
  html_options = options.delete(:input_html) || {}
520
571
 
521
- self.label(method, options.delete(:label), options.slice(:required)) +
572
+ self.label(method, options.slice(:label, :required)) +
522
573
  self.time_zone_select(method, options.delete(:priority_zones), set_options(options), html_options)
523
574
  end
524
575
 
@@ -547,7 +598,7 @@ module Formtastic #:nodoc:
547
598
  # You can customize the options available in the set by passing in a collection (Array) of
548
599
  # ActiveRecord objects through the :collection option. If not provided, the choices are found
549
600
  # by inferring the parent's class name from the method name and simply calling find(:all) on
550
- # it (VehicleOwner.find(:all) in the example above).
601
+ # it (Author.find(:all) in the example above).
551
602
  #
552
603
  # Examples:
553
604
  #
@@ -665,6 +716,7 @@ module Formtastic #:nodoc:
665
716
  time_inputs << [:second] if options[:include_seconds]
666
717
 
667
718
  list_items_capture = ""
719
+ hidden_fields_capture = ""
668
720
 
669
721
  # Gets the datetime object. It can be a Fixnum, Date or Time, or nil.
670
722
  datetime = @object ? @object.send(method) : nil
@@ -673,26 +725,135 @@ module Formtastic #:nodoc:
673
725
  (inputs + time_inputs).each do |input|
674
726
  html_id = generate_html_id(method, "#{position[input]}i")
675
727
  field_name = "#{method}(#{position[input]}i)"
676
-
677
- list_items_capture << if options["discard_#{input}".intern]
728
+ if options["discard_#{input}".intern]
678
729
  break if time_inputs.include?(input)
679
-
730
+
680
731
  hidden_value = datetime.respond_to?(input) ? datetime.send(input) : datetime
681
- template.hidden_field_tag("#{@object_name}[#{field_name}]", (hidden_value || 1), :id => html_id)
732
+ hidden_fields_capture << template.hidden_field_tag("#{@object_name}[#{field_name}]", (hidden_value || 1), :id => html_id)
682
733
  else
683
734
  opts = set_options(options).merge(:prefix => @object_name, :field_name => field_name)
684
735
  item_label_text = I18n.t(input.to_s, :default => input.to_s.humanize, :scope => [:datetime, :prompts])
685
736
 
686
- template.content_tag(:li,
737
+ list_items_capture << template.content_tag(:li,
687
738
  template.content_tag(:label, item_label_text, :for => html_id) +
688
739
  template.send("select_#{input}".intern, datetime, opts, html_options.merge(:id => html_id))
689
740
  )
690
741
  end
691
742
  end
692
743
 
693
- field_set_and_list_wrapping_for_method(method, options, list_items_capture)
744
+ hidden_fields_capture + field_set_and_list_wrapping_for_method(method, options, list_items_capture)
694
745
  end
695
746
 
747
+
748
+ # Outputs a fieldset containing a legend for the label text, and an ordered list (ol) of list
749
+ # items, one for each possible choice in the belongs_to association. Each li contains a
750
+ # label and a check_box input.
751
+ #
752
+ # This is an alternative for has many and has and belongs to many associations.
753
+ #
754
+ # Example:
755
+ #
756
+ # f.input :author, :as => :check_boxes
757
+ #
758
+ # Output:
759
+ #
760
+ # <fieldset>
761
+ # <legend><span>Authors</span></legend>
762
+ # <ol>
763
+ # <li>
764
+ # <input type="hidden" name="book[author_id][1]" value="">
765
+ # <label for="book_author_id_1"><input id="book_author_id_1" name="book[author_id][1]" type="checkbox" value="1" /> Justin French</label>
766
+ # </li>
767
+ # <li>
768
+ # <input type="hidden" name="book[author_id][2]" value="">
769
+ # <label for="book_author_id_2"><input id="book_author_id_2" name="book[owner_id][2]" type="checkbox" value="2" /> Kate French</label>
770
+ # </li>
771
+ # </ol>
772
+ # </fieldset>
773
+ #
774
+ # Notice that the value of the checkbox is the same as the id and the hidden
775
+ # field has empty value. You can override the hidden field value using the
776
+ # unchecked_value option.
777
+ #
778
+ # You can customize the options available in the set by passing in a collection (Array) of
779
+ # ActiveRecord objects through the :collection option. If not provided, the choices are found
780
+ # by inferring the parent's class name from the method name and simply calling find(:all) on
781
+ # it (Author.find(:all) in the example above).
782
+ #
783
+ # Examples:
784
+ #
785
+ # f.input :author, :as => :check_boxes, :collection => @authors
786
+ # f.input :author, :as => :check_boxes, :collection => Author.find(:all)
787
+ # f.input :author, :as => :check_boxes, :collection => [@justin, @kate]
788
+ #
789
+ # You can also customize the text label inside each option tag, by naming the correct method
790
+ # (:full_name, :display_name, :account_number, etc) to call on each object in the collection
791
+ # by passing in the :label_method option. By default the :label_method is whichever element of
792
+ # Formtastic::SemanticFormBuilder.collection_label_methods is found first.
793
+ #
794
+ # Examples:
795
+ #
796
+ # f.input :author, :as => :check_boxes, :label_method => :full_name
797
+ # f.input :author, :as => :check_boxes, :label_method => :display_name
798
+ # f.input :author, :as => :check_boxes, :label_method => :to_s
799
+ # f.input :author, :as => :check_boxes, :label_method => :label
800
+ #
801
+ # You can set :value_as_class => true if you want that LI wrappers contains
802
+ # a class with the wrapped checkbox input value.
803
+ #
804
+ def check_boxes_input(method, options)
805
+ collection = find_collection_for_column(method, options)
806
+ html_options = options.delete(:input_html) || {}
807
+
808
+ input_name = generate_association_input_name(method)
809
+ value_as_class = options.delete(:value_as_class)
810
+ unchecked_value = options.delete(:unchecked_value) || ''
811
+ html_options = { :name => "#{@object_name}[#{input_name}][]" }.merge(html_options)
812
+
813
+ list_item_content = collection.map do |c|
814
+ label = c.is_a?(Array) ? c.first : c
815
+ value = c.is_a?(Array) ? c.last : c
816
+
817
+ html_options.merge!(:id => generate_html_id(input_name, value.to_s.downcase))
818
+
819
+ li_content = template.content_tag(:label,
820
+ "#{self.check_box(input_name, html_options, value, unchecked_value)} #{label}",
821
+ :for => html_options[:id]
822
+ )
823
+
824
+ li_options = value_as_class ? { :class => value.to_s.downcase } : {}
825
+ template.content_tag(:li, li_content, li_options)
826
+ end
827
+
828
+ field_set_and_list_wrapping_for_method(method, options, list_item_content)
829
+ end
830
+
831
+
832
+ # Outputs a country select input, wrapping around a regular country_select helper.
833
+ # Rails doesn't come with a country_select helper by default any more, so you'll need to install
834
+ # the "official" plugin, or, if you wish, any other country_select plugin that behaves in the
835
+ # same way.
836
+ #
837
+ # The Rails plugin iso-3166-country-select plugin can be found "here":http://github.com/rails/iso-3166-country-select.
838
+ #
839
+ # By default, Formtastic includes a handfull of english-speaking countries as "priority counties",
840
+ # which you can change to suit your market and user base (see README for more info on config).
841
+ #
842
+ # Examples:
843
+ # f.input :location, :as => :country # use Formtastic::SemanticFormBuilder.priority_countries array for the priority countries
844
+ # f.input :location, :as => :country, :priority_countries => /Australia/ # set your own
845
+ #
846
+ def country_input(method, options)
847
+ raise "To use the :country input, please install a country_select plugin, like this one: http://github.com/rails/iso-3166-country-select" unless self.respond_to?(:country_select)
848
+
849
+ html_options = options.delete(:input_html) || {}
850
+ priority_countries = options.delete(:priority_countries) || @@priority_countries
851
+
852
+ self.label(method, options.slice(:label, :required)) +
853
+ self.country_select(method, priority_countries, set_options(options), html_options)
854
+ end
855
+
856
+
696
857
  # Outputs a label containing a checkbox and the label text. The label defaults
697
858
  # to the column name (method name) and can be altered with the :label option.
698
859
  # :checked_value and :unchecked_value options are also available.
@@ -724,23 +885,6 @@ module Formtastic #:nodoc:
724
885
  end
725
886
  end
726
887
 
727
- # Generates error messages for the given method. Errors can be shown as list
728
- # or as sentence. If :none is set, no error is shown.
729
- #
730
- def inline_errors_for(method, options) #:nodoc:
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
740
-
741
- send("error_#{@@inline_errors}", errors) unless errors.empty?
742
- end
743
-
744
888
  # Generates hints for the given method using the text supplied in :hint.
745
889
  #
746
890
  def inline_hints_for(method, options) #:nodoc:
@@ -816,7 +960,7 @@ module Formtastic #:nodoc:
816
960
  #
817
961
  def field_set_and_list_wrapping_for_method(method, options, contents)
818
962
  template.content_tag(:fieldset,
819
- %{<legend>#{self.label(method, options.delete(:label), options.slice(:required), true)}</legend>} +
963
+ %{<legend>#{self.label(method, options.slice(:label, :required).merge!(:as_span => true))}</legend>} +
820
964
  template.content_tag(:ol, contents)
821
965
  )
822
966
  end
@@ -841,6 +985,7 @@ module Formtastic #:nodoc:
841
985
  return :datetime if column.type == :timestamp
842
986
  return :numeric if [:integer, :float, :decimal].include?(column.type)
843
987
  return :password if column.type == :string && method.to_s =~ /password/
988
+ return :country if column.type == :string && method.to_s =~ /country/
844
989
 
845
990
  # otherwise assume the input name will be the same as the column type (eg string_input)
846
991
  return column.type
@@ -234,6 +234,10 @@ describe 'Formtastic' do
234
234
  @new_post.stub!(:body)
235
235
  @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
236
236
  end
237
+
238
+ after do
239
+ Formtastic::SemanticFormHelper.builder = Formtastic::SemanticFormBuilder
240
+ end
237
241
 
238
242
  it "can be overridden" do
239
243
 
@@ -293,15 +297,92 @@ describe 'Formtastic' do
293
297
  end
294
298
  end
295
299
 
296
- it 'should append required note' do
300
+ it 'should be printed as span' do
297
301
  semantic_form_for(@new_post) do |builder|
298
- builder.label(:login, nil, :required => true).should have_tag('label abbr')
302
+ builder.label(:login, nil, { :required => true, :as_span => true }).should have_tag('span.label abbr')
299
303
  end
300
304
  end
301
305
 
302
- it 'should be printed as span' do
303
- semantic_form_for(@new_post) do |builder|
304
- builder.label(:login, nil, { :required => true }, true).should have_tag('span.label abbr')
306
+ describe 'when required is given' do
307
+ it 'should append a required note' do
308
+ semantic_form_for(@new_post) do |builder|
309
+ builder.label(:login, nil, :required => true).should have_tag('label abbr')
310
+ end
311
+ end
312
+
313
+ it 'should allow require option to be given as second argument' do
314
+ semantic_form_for(@new_post) do |builder|
315
+ builder.label(:login, :required => true).should have_tag('label abbr')
316
+ end
317
+ end
318
+ end
319
+
320
+ describe 'when label is given' do
321
+ it 'should allow the text to be given as label option' do
322
+ semantic_form_for(@new_post) do |builder|
323
+ builder.label(:login, :required => true, :label => 'My label').should have_tag('label', :with => /My label/)
324
+ end
325
+ end
326
+
327
+ it 'should return nil if label is false' do
328
+ semantic_form_for(@new_post) do |builder|
329
+ builder.label(:login, :label => false).should be_nil
330
+ end
331
+ end
332
+ end
333
+ end
334
+
335
+ describe '#errors_on' do
336
+ before(:each) do
337
+ @title_errors = ['must not be blank', 'must be longer than 10 characters', 'must be awesome']
338
+ @errors = mock('errors')
339
+ @errors.stub!(:on).with('title').and_return(@title_errors)
340
+ @errors.stub!(:on).with('body').and_return(nil)
341
+ @new_post.stub!(:errors).and_return(@errors)
342
+ end
343
+
344
+ describe 'and the errors will be displayed as a sentence' do
345
+ it 'should render a paragraph with the errors joined into a sentence' do
346
+ Formtastic::SemanticFormBuilder.inline_errors = :sentence
347
+ semantic_form_for(@new_post) do |builder|
348
+ builder.errors_on(:title).should have_tag('p.inline-errors', @title_errors.to_sentence)
349
+ end
350
+ end
351
+ end
352
+
353
+ describe 'and the errors will be displayed as a list' do
354
+ it 'should render an unordered list with the class errors' do
355
+ Formtastic::SemanticFormBuilder.inline_errors = :list
356
+ semantic_form_for(@new_post) do |builder|
357
+ builder.errors_on(:title).should have_tag('ul.errors')
358
+ end
359
+ end
360
+
361
+ it 'should include a list element for each of the errors within the unordered list' do
362
+ Formtastic::SemanticFormBuilder.inline_errors = :list
363
+ semantic_form_for(@new_post) do |builder|
364
+ @title_errors.each do |error|
365
+ builder.errors_on(:title).should have_tag('ul.errors li', error)
366
+ end
367
+ end
368
+ end
369
+ end
370
+
371
+ describe 'but the errors will not be shown' do
372
+ it 'should return nil' do
373
+ Formtastic::SemanticFormBuilder.inline_errors = :none
374
+ semantic_form_for(@new_post) do |builder|
375
+ builder.errors_on(:title).should be_nil
376
+ end
377
+ end
378
+ end
379
+
380
+ describe 'and no error is found on the method' do
381
+ it 'should return nil' do
382
+ Formtastic::SemanticFormBuilder.inline_errors = :sentence
383
+ semantic_form_for(@new_post) do |builder|
384
+ builder.errors_on(:body).should be_nil
385
+ end
305
386
  end
306
387
  end
307
388
  end
@@ -558,6 +639,10 @@ describe 'Formtastic' do
558
639
  default_input_type(:float).should == :numeric
559
640
  default_input_type(:decimal).should == :numeric
560
641
  end
642
+
643
+ it 'should default to :country for :string columns named country' do
644
+ default_input_type(:string, :country).should == :country
645
+ end
561
646
 
562
647
  describe 'defaulting to file column' do
563
648
  Formtastic::SemanticFormBuilder.file_methods.each do |method|
@@ -581,7 +666,7 @@ describe 'Formtastic' do
581
666
  end
582
667
 
583
668
  it 'should call the corresponding input method' do
584
- [:select, :time_zone, :radio, :date, :datetime, :time, :boolean].each do |input_style|
669
+ [:select, :time_zone, :radio, :date, :datetime, :time, :boolean, :check_boxes, :hidden].each do |input_style|
585
670
  @new_post.stub!(:generic_column_name)
586
671
  @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
587
672
  semantic_form_for(@new_post) do |builder|
@@ -721,7 +806,6 @@ describe 'Formtastic' do
721
806
  end
722
807
 
723
808
  describe 'when there are errors on the object for this method' do
724
-
725
809
  before do
726
810
  @title_errors = ['must not be blank', 'must be longer than 10 characters', 'must be awesome']
727
811
  @errors = mock('errors')
@@ -743,65 +827,24 @@ describe 'Formtastic' do
743
827
  output_buffer.should_not have_tag('div.fieldWithErrors')
744
828
  end
745
829
 
746
- describe 'and the errors will be displayed as a sentence' do
747
-
748
- before do
749
- Formtastic::SemanticFormBuilder.inline_errors = :sentence
750
- semantic_form_for(@new_post) do |builder|
751
- concat(builder.input(:title))
752
- end
753
- end
754
-
755
- it 'should render a paragraph with the errors joined into a sentence' do
756
- output_buffer.should have_tag('form li.error p.inline-errors', @title_errors.to_sentence)
757
- end
758
-
759
- end
760
-
761
- describe 'and the errors will be displayed as a list' do
762
-
763
- before do
764
- Formtastic::SemanticFormBuilder.inline_errors = :list
765
- semantic_form_for(@new_post) do |builder|
766
- concat(builder.input(:title))
767
- end
768
- end
769
-
770
- it 'should render an unordered list with the class errors' do
771
- output_buffer.should have_tag('form li.error ul.errors')
772
- end
773
-
774
- it 'should include a list element for each of the errors within the unordered list' do
775
- @title_errors.each do |error|
776
- output_buffer.should have_tag('form li.error ul.errors li', error)
777
- end
830
+ it 'should render a paragraph for the errors' do
831
+ Formtastic::SemanticFormBuilder.inline_errors = :sentence
832
+ semantic_form_for(@new_post) do |builder|
833
+ concat(builder.input(:title))
778
834
  end
779
-
835
+ output_buffer.should have_tag('form li.error p.inline-errors')
780
836
  end
781
837
 
782
- describe 'but the errors will not be shown' do
783
-
784
- before do
785
- Formtastic::SemanticFormBuilder.inline_errors = :none
786
- semantic_form_for(@new_post) do |builder|
787
- concat(builder.input(:title))
788
- end
789
- end
790
-
791
- it 'should not display an error sentence' do
792
- output_buffer.should_not have_tag('form li.error p.inline-errors')
793
- end
794
-
795
- it 'should not display an error list' do
796
- output_buffer.should_not have_tag('form li.error ul.errors')
838
+ it 'should not display an error list' do
839
+ Formtastic::SemanticFormBuilder.inline_errors = :list
840
+ semantic_form_for(@new_post) do |builder|
841
+ concat(builder.input(:title))
797
842
  end
798
-
843
+ output_buffer.should have_tag('form li.error ul.errors')
799
844
  end
800
-
801
845
  end
802
846
 
803
847
  describe 'when there are no errors on the object for this method' do
804
-
805
848
  before do
806
849
  semantic_form_for(@new_post) do |builder|
807
850
  concat(builder.input(:title))
@@ -819,11 +862,9 @@ describe 'Formtastic' do
819
862
  it 'should not display an error list' do
820
863
  output_buffer.should_not have_tag('form li.error ul.errors')
821
864
  end
822
-
823
865
  end
824
866
 
825
867
  describe 'when no object is provided' do
826
-
827
868
  before do
828
869
  semantic_form_for(:project, :url => 'http://test.host') do |builder|
829
870
  concat(builder.input(:title))
@@ -841,7 +882,6 @@ describe 'Formtastic' do
841
882
  it 'should not display an error list' do
842
883
  output_buffer.should_not have_tag('form li.error ul.errors')
843
884
  end
844
-
845
885
  end
846
886
  end
847
887
 
@@ -1008,30 +1048,65 @@ describe 'Formtastic' do
1008
1048
 
1009
1049
  end
1010
1050
 
1011
- it 'should generate input and labels even if no object is given' do
1012
- semantic_form_for(:project, :url => 'http://test.host/') do |builder|
1013
- concat(builder.input(:title, :as => type))
1051
+ describe 'when no object is given' do
1052
+ before(:each) do
1053
+ semantic_form_for(:project, :url => 'http://test.host/') do |builder|
1054
+ concat(builder.input(:title, :as => type))
1055
+ end
1014
1056
  end
1015
1057
 
1016
- output_buffer.should have_tag('form li label')
1017
- output_buffer.should have_tag('form li label[@for="project_title"')
1018
- output_buffer.should have_tag('form li label', /Title/)
1058
+ it 'should generate input' do
1059
+ if template_method.to_s =~ /_field$/ # password_field
1060
+ output_buffer.should have_tag("form li input")
1061
+ output_buffer.should have_tag("form li input#project_title")
1062
+ output_buffer.should have_tag("form li input[@type=\"#{input_type}\"]")
1063
+ output_buffer.should have_tag("form li input[@name=\"project[title]\"]")
1064
+ else
1065
+ output_buffer.should have_tag("form li #{input_type}")
1066
+ output_buffer.should have_tag("form li #{input_type}#project_title")
1067
+ output_buffer.should have_tag("form li #{input_type}[@name=\"project[title]\"]")
1068
+ end
1069
+ end
1019
1070
 
1020
- if template_method.to_s =~ /_field$/ # password_field
1021
- output_buffer.should have_tag("form li input")
1022
- output_buffer.should have_tag("form li input#project_title")
1023
- output_buffer.should have_tag("form li input[@type=\"#{input_type}\"]")
1024
- output_buffer.should have_tag("form li input[@name=\"project[title]\"]")
1025
- else
1026
- output_buffer.should have_tag("form li #{input_type}")
1027
- output_buffer.should have_tag("form li #{input_type}#project_title")
1028
- output_buffer.should have_tag("form li #{input_type}[@name=\"project[title]\"]")
1071
+ it 'should generate labels' do
1072
+ output_buffer.should have_tag('form li label')
1073
+ output_buffer.should have_tag('form li label[@for="project_title"')
1074
+ output_buffer.should have_tag('form li label', /Title/)
1029
1075
  end
1030
1076
  end
1031
1077
 
1032
1078
  end
1033
1079
  end
1034
1080
 
1081
+ describe ":as => :hidden" do
1082
+ before do
1083
+ @new_post.stub!(:hidden)
1084
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :string))
1085
+
1086
+ semantic_form_for(@new_post) do |builder|
1087
+ concat(builder.input(:hidden, :as => :hidden))
1088
+ end
1089
+ end
1090
+
1091
+ it "should have a hidden class on the wrapper" do
1092
+ output_buffer.should have_tag('form li.hidden')
1093
+ end
1094
+
1095
+ it 'should have a post_hidden_input id on the wrapper' do
1096
+ output_buffer.should have_tag('form li#post_hidden_input')
1097
+ end
1098
+
1099
+ it 'should not generate a label for the input' do
1100
+ output_buffer.should_not have_tag('form li label')
1101
+ end
1102
+
1103
+ it "should generate a input field" do
1104
+ output_buffer.should have_tag("form li input#post_hidden")
1105
+ output_buffer.should have_tag("form li input[@type=\"hidden\"]")
1106
+ output_buffer.should have_tag("form li input[@name=\"post[hidden]\"]")
1107
+ end
1108
+ end
1109
+
1035
1110
  describe ":as => :time_zone" do
1036
1111
  before do
1037
1112
  @new_post.stub!(:time_zone)
@@ -1069,27 +1144,110 @@ describe 'Formtastic' do
1069
1144
  output_buffer.should have_tag("form li select.myclass")
1070
1145
  end
1071
1146
 
1072
- it 'should generate input and labels even if no object is given' do
1073
- semantic_form_for(:project, :url => 'http://test.host/') do |builder|
1074
- concat(builder.input(:time_zone, :as => :time_zone))
1147
+ describe 'when no object is given' do
1148
+ before(:each) do
1149
+ semantic_form_for(:project, :url => 'http://test.host/') do |builder|
1150
+ concat(builder.input(:time_zone, :as => :time_zone))
1151
+ end
1075
1152
  end
1076
1153
 
1077
- output_buffer.should have_tag('form li label')
1078
- output_buffer.should have_tag('form li label[@for="project_time_zone"')
1079
- output_buffer.should have_tag('form li label', /Time zone/)
1154
+ it 'should generate labels' do
1155
+ output_buffer.should have_tag('form li label')
1156
+ output_buffer.should have_tag('form li label[@for="project_time_zone"')
1157
+ output_buffer.should have_tag('form li label', /Time zone/)
1158
+ end
1080
1159
 
1081
- output_buffer.should have_tag("form li select")
1082
- output_buffer.should have_tag("form li select#project_time_zone")
1083
- output_buffer.should have_tag("form li select[@name=\"project[time_zone]\"]")
1160
+ it 'should generate select inputs' do
1161
+ output_buffer.should have_tag("form li select")
1162
+ output_buffer.should have_tag("form li select#project_time_zone")
1163
+ output_buffer.should have_tag("form li select[@name=\"project[time_zone]\"]")
1164
+ end
1084
1165
  end
1085
1166
  end
1167
+
1168
+ describe ":as => :country" do
1169
+
1170
+ before do
1171
+ @new_post.stub!(:country)
1172
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :string))
1173
+ end
1174
+
1175
+ describe "when country_select is not available as a helper from a plugin" do
1176
+
1177
+ it "should raise an error, sugesting the author installs a plugin" do
1178
+ lambda {
1179
+ semantic_form_for(@new_post) do |builder|
1180
+ concat(builder.input(:country, :as => :country))
1181
+ end
1182
+ }.should raise_error
1183
+ end
1184
+
1185
+ end
1186
+
1187
+ describe "when country_select is available as a helper (from a plugin)" do
1188
+
1189
+ before do
1190
+ semantic_form_for(@new_post) do |builder|
1191
+ builder.stub!(:country_select).and_return("<select><option>...</option></select>")
1192
+ concat(builder.input(:country, :as => :country))
1193
+ end
1194
+ end
1195
+
1196
+ it "should have a time_zone class on the wrapper" do
1197
+ output_buffer.should have_tag('form li.country')
1198
+ end
1199
+
1200
+ it 'should have a post_title_input id on the wrapper' do
1201
+ output_buffer.should have_tag('form li#post_country_input')
1202
+ end
1203
+
1204
+ it 'should generate a label for the input' do
1205
+ output_buffer.should have_tag('form li label')
1206
+ output_buffer.should have_tag('form li label[@for="post_country"')
1207
+ output_buffer.should have_tag('form li label', /Country/)
1208
+ end
1086
1209
 
1210
+ it "should generate a select" do
1211
+ output_buffer.should have_tag("form li select")
1212
+ end
1213
+
1214
+ end
1215
+
1216
+ describe ":priority_countries option" do
1217
+
1218
+ it "should be passed down to the country_select helper when provided" do
1219
+ priority_countries = ["Foo", "Bah"]
1220
+ semantic_form_for(@new_post) do |builder|
1221
+ builder.stub!(:country_select).and_return("<select><option>...</option></select>")
1222
+ builder.should_receive(:country_select).with(:country, priority_countries, {}, {}).and_return("<select><option>...</option></select>")
1223
+
1224
+ concat(builder.input(:country, :as => :country, :priority_countries => priority_countries))
1225
+ end
1226
+ end
1227
+
1228
+ it "should default to the @@priority_countries config when absent" do
1229
+ priority_countries = Formtastic::SemanticFormBuilder.priority_countries
1230
+ priority_countries.should_not be_empty
1231
+ priority_countries.should_not be_nil
1232
+
1233
+ semantic_form_for(@new_post) do |builder|
1234
+ builder.stub!(:country_select).and_return("<select><option>...</option></select>")
1235
+ builder.should_receive(:country_select).with(:country, priority_countries, {}, {}).and_return("<select><option>...</option></select>")
1236
+
1237
+ concat(builder.input(:country, :as => :country))
1238
+ end
1239
+ end
1240
+
1241
+ end
1242
+
1243
+ end
1244
+
1087
1245
  describe ':as => :radio' do
1088
1246
 
1089
1247
  before do
1090
1248
  @new_post.stub!(:author).and_return(@bob)
1091
1249
  @new_post.stub!(:author_id).and_return(@bob.id)
1092
- @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :integer, :limit => 255))
1250
+ Post.stub!(:reflect_on_association).and_return { |column_name| mock('reflection', :klass => Author, :macro => :belongs_to) }
1093
1251
  end
1094
1252
 
1095
1253
  describe 'for belongs_to association' do
@@ -1103,7 +1261,7 @@ describe 'Formtastic' do
1103
1261
  output_buffer.should have_tag('form li.radio')
1104
1262
  end
1105
1263
 
1106
- it 'should have a post_author_id_input id on the wrapper' do
1264
+ it 'should have a post_author_input id on the wrapper' do
1107
1265
  output_buffer.should have_tag('form li#post_author_input')
1108
1266
  end
1109
1267
 
@@ -1118,7 +1276,7 @@ describe 'Formtastic' do
1118
1276
  output_buffer.should have_tag('form li fieldset ol li', :count => Author.find(:all).size)
1119
1277
  end
1120
1278
 
1121
- it 'should have one option with a "selected" attribute' do
1279
+ it 'should have one option with a "checked" attribute' do
1122
1280
  output_buffer.should have_tag('form li input[@checked]', :count => 1)
1123
1281
  end
1124
1282
 
@@ -1126,10 +1284,8 @@ describe 'Formtastic' do
1126
1284
 
1127
1285
  it 'should contain a label for the radio input with a nested input and label text' do
1128
1286
  Author.find(:all).each do |author|
1129
- output_buffer.should have_tag('form li fieldset ol li label')
1130
1287
  output_buffer.should have_tag('form li fieldset ol li label', /#{author.to_label}/)
1131
1288
  output_buffer.should have_tag("form li fieldset ol li label[@for='post_author_id_#{author.id}']")
1132
- output_buffer.should have_tag("form li fieldset ol li label input")
1133
1289
  end
1134
1290
  end
1135
1291
 
@@ -1161,29 +1317,39 @@ describe 'Formtastic' do
1161
1317
  end
1162
1318
  end
1163
1319
 
1164
- it 'should generate a fieldset, legend, labels and inputs even if no object is given' do
1165
- output_buffer.replace ''
1320
+ describe 'and no object is given' do
1321
+ before(:each) do
1322
+ output_buffer.replace ''
1323
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
1324
+ concat(builder.input(:author_id, :as => :radio, :collection => Author.find(:all)))
1325
+ end
1326
+ end
1166
1327
 
1167
- semantic_form_for(:project, :url => 'http://test.host') do |builder|
1168
- concat(builder.input(:author_id, :as => :radio, :collection => Author.find(:all)))
1328
+ it 'should generate a fieldset with legend' do
1329
+ output_buffer.should have_tag('form li fieldset legend', /Author/)
1169
1330
  end
1170
1331
 
1171
- output_buffer.should have_tag('form li fieldset legend', /Author/)
1172
- output_buffer.should have_tag('form li fieldset ol li', :count => Author.find(:all).size)
1332
+ it 'shold generate an li tag for each item in the collection' do
1333
+ output_buffer.should have_tag('form li fieldset ol li', :count => Author.find(:all).size)
1334
+ end
1173
1335
 
1174
- Author.find(:all).each do |author|
1175
- output_buffer.should have_tag('form li fieldset ol li label', /#{author.to_label}/)
1176
- output_buffer.should have_tag("form li fieldset ol li label[@for='project_author_id_#{author.id}']")
1336
+ it 'should generate labels for each item' do
1337
+ Author.find(:all).each do |author|
1338
+ output_buffer.should have_tag('form li fieldset ol li label', /#{author.to_label}/)
1339
+ output_buffer.should have_tag("form li fieldset ol li label[@for='project_author_id_#{author.id}']")
1340
+ end
1341
+ end
1177
1342
 
1178
- output_buffer.should have_tag("form li fieldset ol li label input#project_author_id_#{author.id}")
1179
- output_buffer.should have_tag("form li fieldset ol li label input[@type='radio']")
1180
- output_buffer.should have_tag("form li fieldset ol li label input[@value='#{author.id}']")
1181
- output_buffer.should have_tag("form li fieldset ol li label input[@name='project[author_id]']")
1343
+ it 'should generate inputs for each item' do
1344
+ Author.find(:all).each do |author|
1345
+ output_buffer.should have_tag("form li fieldset ol li label input#project_author_id_#{author.id}")
1346
+ output_buffer.should have_tag("form li fieldset ol li label input[@type='radio']")
1347
+ output_buffer.should have_tag("form li fieldset ol li label input[@value='#{author.id}']")
1348
+ output_buffer.should have_tag("form li fieldset ol li label input[@name='project[author_id]']")
1349
+ end
1182
1350
  end
1183
1351
  end
1184
-
1185
1352
  end
1186
-
1187
1353
  end
1188
1354
 
1189
1355
  describe ':as => :select' do
@@ -1341,19 +1507,126 @@ describe 'Formtastic' do
1341
1507
  end
1342
1508
  end
1343
1509
 
1344
- it 'should generate label, select and options even if object is not given' do
1345
- semantic_form_for(:project, :url => 'http://test.host') do |builder|
1346
- concat(builder.input(:author, :as => :select, :collection => Author.find(:all)))
1510
+ describe 'when no object is given' do
1511
+ before(:each) do
1512
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
1513
+ concat(builder.input(:author, :as => :select, :collection => Author.find(:all)))
1514
+ end
1347
1515
  end
1348
1516
 
1349
- output_buffer.should have_tag('form li label', /Author/)
1350
- output_buffer.should have_tag("form li label[@for='project_author']")
1517
+ it 'should generate label' do
1518
+ output_buffer.should have_tag('form li label', /Author/)
1519
+ output_buffer.should have_tag("form li label[@for='project_author']")
1520
+ end
1351
1521
 
1352
- output_buffer.should have_tag('form li select#project_author')
1353
- output_buffer.should have_tag('form li select option', :count => Author.find(:all).size)
1522
+ it 'should generate select inputs' do
1523
+ output_buffer.should have_tag('form li select#project_author')
1524
+ output_buffer.should have_tag('form li select option', :count => Author.find(:all).size)
1525
+ end
1354
1526
 
1355
- Author.find(:all).each do |author|
1356
- output_buffer.should have_tag("form li select option[@value='#{author.id}']", /#{author.to_label}/)
1527
+ it 'should generate an option to each item' do
1528
+ Author.find(:all).each do |author|
1529
+ output_buffer.should have_tag("form li select option[@value='#{author.id}']", /#{author.to_label}/)
1530
+ end
1531
+ end
1532
+ end
1533
+ end
1534
+
1535
+ describe ':as => :check_boxes' do
1536
+
1537
+ describe 'for a has_many association' do
1538
+ before do
1539
+ semantic_form_for(@fred) do |builder|
1540
+ concat(builder.input(:posts, :as => :check_boxes, :value_as_class => true))
1541
+ end
1542
+ end
1543
+
1544
+ it 'should have a check_boxes class on the wrapper' do
1545
+ output_buffer.should have_tag('form li.check_boxes')
1546
+ end
1547
+
1548
+ it 'should have a author_posts_input id on the wrapper' do
1549
+ output_buffer.should have_tag('form li#author_posts_input')
1550
+ end
1551
+
1552
+ it 'should generate a fieldset and legend containing label text for the input' do
1553
+ output_buffer.should have_tag('form li fieldset')
1554
+ output_buffer.should have_tag('form li fieldset legend')
1555
+ output_buffer.should have_tag('form li fieldset legend', /Posts/)
1556
+ end
1557
+
1558
+ it 'should generate an ordered list with a list item for each choice' do
1559
+ output_buffer.should have_tag('form li fieldset ol')
1560
+ output_buffer.should have_tag('form li fieldset ol li', :count => Post.find(:all).size)
1561
+ end
1562
+
1563
+ it 'should have one option with a "checked" attribute' do
1564
+ output_buffer.should have_tag('form li input[@checked]', :count => 1)
1565
+ end
1566
+
1567
+ it 'should generate hidden inputs with default value blank' do
1568
+ output_buffer.should have_tag("form li fieldset ol li label input[@type='hidden'][@value='']", :count => Post.find(:all).size)
1569
+ end
1570
+
1571
+ describe "each choice" do
1572
+
1573
+ it 'should contain a label for the radio input with a nested input and label text' do
1574
+ Post.find(:all).each do |post|
1575
+ output_buffer.should have_tag('form li fieldset ol li label', /#{post.to_label}/)
1576
+ output_buffer.should have_tag("form li fieldset ol li label[@for='author_post_ids_#{post.id}']")
1577
+ end
1578
+ end
1579
+
1580
+ it 'should use values as li.class when value_as_class is true' do
1581
+ Post.find(:all).each do |post|
1582
+ output_buffer.should have_tag("form li fieldset ol li.#{post.id} label")
1583
+ end
1584
+ end
1585
+
1586
+ it 'should have a checkbox input for each post' do
1587
+ Post.find(:all).each do |post|
1588
+ output_buffer.should have_tag("form li fieldset ol li label input#author_post_ids_#{post.id}")
1589
+ output_buffer.should have_tag("form li fieldset ol li label input[@name='author[post_ids][]']", :count => 2)
1590
+ end
1591
+ end
1592
+
1593
+ it "should mark input as checked if it's the the existing choice" do
1594
+ Post.find(:all).include?(@fred.posts.first).should be_true
1595
+ output_buffer.should have_tag("form li fieldset ol li label input[@checked='checked']")
1596
+ end
1597
+ end
1598
+
1599
+ describe 'and no object is given' do
1600
+ before(:each) do
1601
+ output_buffer.replace ''
1602
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
1603
+ concat(builder.input(:author_id, :as => :check_boxes, :collection => Author.find(:all)))
1604
+ end
1605
+ end
1606
+
1607
+ it 'should generate a fieldset with legend' do
1608
+ output_buffer.should have_tag('form li fieldset legend', /Author/)
1609
+ end
1610
+
1611
+ it 'shold generate an li tag for each item in the collection' do
1612
+ output_buffer.should have_tag('form li fieldset ol li', :count => Author.find(:all).size)
1613
+ end
1614
+
1615
+ it 'should generate labels for each item' do
1616
+ Author.find(:all).each do |author|
1617
+ output_buffer.should have_tag('form li fieldset ol li label', /#{author.to_label}/)
1618
+ output_buffer.should have_tag("form li fieldset ol li label[@for='project_author_id_#{author.id}']")
1619
+ end
1620
+ end
1621
+
1622
+ it 'should generate inputs for each item' do
1623
+ Author.find(:all).each do |author|
1624
+ output_buffer.should have_tag("form li fieldset ol li label input#project_author_id_#{author.id}")
1625
+ output_buffer.should have_tag("form li fieldset ol li label input[@type='checkbox']")
1626
+ output_buffer.should have_tag("form li fieldset ol li label input[@value='#{author.id}']")
1627
+ output_buffer.should have_tag("form li fieldset ol li label input[@name='project[author_id][]']")
1628
+ end
1629
+ end
1357
1630
  end
1358
1631
  end
1359
1632
  end
@@ -1366,7 +1639,7 @@ describe 'Formtastic' do
1366
1639
  @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :integer, :limit => 255))
1367
1640
  end
1368
1641
 
1369
- { :select => :option, :radio => :input }.each do |type, countable|
1642
+ { :select => :option, :radio => :input, :check_boxes => :'input[@type="checkbox"]' }.each do |type, countable|
1370
1643
 
1371
1644
  describe ":as => #{type.inspect}" do
1372
1645
  describe 'when the :collection option is not provided' do
@@ -1421,9 +1694,9 @@ describe 'Formtastic' do
1421
1694
  concat(builder.input(:category_name, :as => type, :collection => @categories))
1422
1695
  end
1423
1696
 
1424
- @categories.each do |item|
1425
- output_buffer.should have_tag("form li.#{type}", /#{item}/)
1426
- output_buffer.should have_tag("form li.#{type} #{countable}[@value=#{item}]")
1697
+ @categories.each do |value|
1698
+ output_buffer.should have_tag("form li.#{type}", /#{value}/)
1699
+ output_buffer.should have_tag("form li.#{type} #{countable}[@value='#{value}']")
1427
1700
  end
1428
1701
  end
1429
1702
 
@@ -1456,7 +1729,7 @@ describe 'Formtastic' do
1456
1729
 
1457
1730
  @categories.each do |label, value|
1458
1731
  output_buffer.should have_tag("form li.#{type}", /#{label}/)
1459
- output_buffer.should have_tag("form li.#{type} #{countable}[@value=#{value}]")
1732
+ output_buffer.should have_tag("form li.#{type} #{countable}[@value='#{value}']")
1460
1733
  end
1461
1734
  end
1462
1735
  end
@@ -1469,12 +1742,13 @@ describe 'Formtastic' do
1469
1742
 
1470
1743
  it "should use the first value as the label text and the last value as the value attribute for #{countable}" do
1471
1744
  semantic_form_for(@new_post) do |builder|
1472
- concat(builder.input(:category_name, :as => :radio, :collection => @categories))
1745
+ concat(builder.input(:category_name, :as => type, :collection => @categories))
1473
1746
  end
1474
1747
 
1475
- @categories.each do |label, value|
1476
- output_buffer.should have_tag('form li fieldset ol li label', /#{label}/i)
1477
- output_buffer.should have_tag('form li fieldset ol li label input[@value='+value+']')
1748
+ @categories.each do |text, value|
1749
+ label = type == :select ? :option : :label
1750
+ output_buffer.should have_tag("form li.#{type} #{label}", /#{text}/i)
1751
+ output_buffer.should have_tag("form li.#{type} #{countable}[@value='#{value.to_s}']")
1478
1752
  end
1479
1753
  end
1480
1754
  end
@@ -1487,12 +1761,13 @@ describe 'Formtastic' do
1487
1761
 
1488
1762
  it "should use the symbol as the label text and value for each #{countable}" do
1489
1763
  semantic_form_for(@new_post) do |builder|
1490
- concat(builder.input(:category_name, :as => :radio, :collection => @categories))
1764
+ concat(builder.input(:category_name, :as => type, :collection => @categories))
1491
1765
  end
1492
1766
 
1493
1767
  @categories.each do |value|
1494
- output_buffer.should have_tag('form li fieldset ol li label', /#{value}/i)
1495
- output_buffer.should have_tag('form li fieldset ol li label input[@value='+value.to_s+']')
1768
+ label = type == :select ? :option : :label
1769
+ output_buffer.should have_tag("form li.#{type} #{label}", /#{value}/i)
1770
+ output_buffer.should have_tag("form li.#{type} #{countable}[@value='#{value.to_s}']")
1496
1771
  end
1497
1772
  end
1498
1773
  end
@@ -1549,8 +1824,15 @@ describe 'Formtastic' do
1549
1824
  end
1550
1825
 
1551
1826
  end
1827
+ end
1828
+ end
1829
+
1830
+ describe 'for boolean attributes' do
1552
1831
 
1553
- describe 'when attribute is a boolean' do
1832
+ { :select => :option, :radio => :input }.each do |type, countable|
1833
+ checked_or_selected = { :select => :selected, :radio => :checked }[type]
1834
+
1835
+ describe ":as => #{type.inspect}" do
1554
1836
 
1555
1837
  before do
1556
1838
  @new_post.stub!(:allow_comments)
@@ -1598,8 +1880,6 @@ describe 'Formtastic' do
1598
1880
  end
1599
1881
  end
1600
1882
 
1601
- checked_or_selected = { :select => :selected, :radio => :checked }[type]
1602
-
1603
1883
  describe 'when the value is nil' do
1604
1884
  before do
1605
1885
  @new_post.stub!(:allow_comments).and_return(nil)
@@ -1667,7 +1947,6 @@ describe 'Formtastic' do
1667
1947
  end
1668
1948
  end
1669
1949
 
1670
-
1671
1950
  end
1672
1951
  end
1673
1952
  end
@@ -1775,7 +2054,7 @@ describe 'Formtastic' do
1775
2054
  concat(builder.input(:publish_at, :as => :datetime, :discard_day => true))
1776
2055
  end
1777
2056
 
1778
- output_buffer.should have_tag("form li fieldset ol input[@type='hidden'][@value='1']")
2057
+ output_buffer.should have_tag("form li input[@type='hidden'][@value='1']")
1779
2058
  end
1780
2059
 
1781
2060
  it 'should use default attribute value when it is not nil' do
@@ -1784,7 +2063,7 @@ describe 'Formtastic' do
1784
2063
  concat(builder.input(:publish_at, :as => :datetime, :discard_day => true))
1785
2064
  end
1786
2065
 
1787
- output_buffer.should have_tag("form li fieldset ol input[@type='hidden'][@value='27']")
2066
+ output_buffer.should have_tag("form li input[@type='hidden'][@value='27']")
1788
2067
  end
1789
2068
  end
1790
2069
 
@@ -1857,16 +2136,25 @@ describe 'Formtastic' do
1857
2136
  end
1858
2137
  end
1859
2138
 
1860
- it 'should have fieldset, legend, label and selects even if object is not given' do
1861
- output_buffer.replace ''
2139
+ describe 'when no object is given' do
2140
+ before(:each) do
2141
+ output_buffer.replace ''
2142
+ semantic_form_for(:project, :url => 'http://test.host') do |@builder|
2143
+ concat(@builder.input(:publish_at, :as => :datetime))
2144
+ end
2145
+ end
1862
2146
 
1863
- semantic_form_for(:project, :url => 'http://test.host') do |@builder|
1864
- concat(@builder.input(:publish_at, :as => :datetime))
2147
+ it 'should have fieldset with legend' do
2148
+ output_buffer.should have_tag('form li.datetime fieldset legend', /Publish at/)
1865
2149
  end
1866
2150
 
1867
- output_buffer.should have_tag('form li.datetime fieldset legend', /Publish at/)
1868
- output_buffer.should have_tag('form li.datetime fieldset ol li label', :count => 5)
1869
- output_buffer.should have_tag('form li.datetime fieldset ol li select', :count => 5)
2151
+ it 'should have labels for each input' do
2152
+ output_buffer.should have_tag('form li.datetime fieldset ol li label', :count => 5)
2153
+ end
2154
+
2155
+ it 'should have selects for each inputs' do
2156
+ output_buffer.should have_tag('form li.datetime fieldset ol li select', :count => 5)
2157
+ end
1870
2158
  end
1871
2159
  end
1872
2160
 
@@ -1903,8 +2191,9 @@ describe 'Formtastic' do
1903
2191
  output_buffer.should have_tag('form li.time fieldset ol li label', /minute/i)
1904
2192
  end
1905
2193
 
1906
- it 'should have five selects for hour and minute' do
1907
- output_buffer.should have_tag('form li.time fieldset ol li select', :count => 2)
2194
+ it 'should have two selects for hour and minute' do
2195
+ #output_buffer.should have_tag('form li.time fieldset ol li select', :count => 2)
2196
+ output_buffer.should have_tag('form li.time fieldset ol li', :count => 2)
1908
2197
  end
1909
2198
  end
1910
2199
 
@@ -1983,7 +2272,6 @@ describe 'Formtastic' do
1983
2272
  end
1984
2273
  end
1985
2274
 
1986
-
1987
2275
  describe '#inputs' do
1988
2276
 
1989
2277
  describe 'with a block' 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.7
4
+ version: 0.1.9
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-22 00:00:00 -07:00
12
+ date: 2009-05-21 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -25,24 +25,19 @@ files:
25
25
  - MIT-LICENSE
26
26
  - README.textile
27
27
  - Rakefile
28
- - rails/init.rb
29
- - lib/formtastic.rb
30
- - lib/justin_french
31
- - lib/justin_french/formtastic.rb
32
- - lib/locale
33
- - lib/locale/en.yml
34
- - generators/formtastic_stylesheets
35
28
  - generators/formtastic_stylesheets/formtastic_stylesheets_generator.rb
36
- - generators/formtastic_stylesheets/templates
37
29
  - generators/formtastic_stylesheets/templates/formtastic.css
38
30
  - generators/formtastic_stylesheets/templates/formtastic_changes.css
31
+ - lib/formtastic.rb
32
+ - lib/justin_french/formtastic.rb
33
+ - lib/locale/en.yml
34
+ - rails/init.rb
39
35
  - spec/formtastic_spec.rb
40
36
  - spec/test_helper.rb
41
- has_rdoc: true
37
+ has_rdoc: false
42
38
  homepage: http://github.com/justinfrench/formtastic/tree/master
43
39
  post_install_message:
44
40
  rdoc_options:
45
- - --inline-source
46
41
  - --charset=UTF-8
47
42
  require_paths:
48
43
  - lib
@@ -63,7 +58,8 @@ requirements: []
63
58
  rubyforge_project:
64
59
  rubygems_version: 1.2.0
65
60
  signing_key:
66
- specification_version: 2
61
+ specification_version: 3
67
62
  summary: A Rails form builder plugin/gem with semantically rich and accessible markup
68
- test_files: []
69
-
63
+ test_files:
64
+ - spec/formtastic_spec.rb
65
+ - spec/test_helper.rb