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 +9 -3
- data/generators/formtastic_stylesheets/templates/formtastic.css +17 -2
- data/lib/formtastic.rb +184 -39
- data/spec/formtastic_spec.rb +430 -142
- metadata +11 -15
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.
|
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
|
-
/*
|
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
|
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.
|
312
|
-
# label method
|
313
|
-
#
|
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
|
-
# :
|
316
|
-
#
|
327
|
+
# f.label :title, "Your post title"
|
328
|
+
# f.label :title, :label => "Your post title" # Added for formtastic API
|
317
329
|
#
|
318
|
-
|
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.
|
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.
|
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.
|
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 (
|
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.
|
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
|
data/spec/formtastic_spec.rb
CHANGED
@@ -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
|
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
|
-
|
303
|
-
|
304
|
-
|
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
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
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
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
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
|
-
|
1012
|
-
|
1013
|
-
|
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
|
-
|
1017
|
-
|
1018
|
-
|
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
|
-
|
1021
|
-
output_buffer.should have_tag(
|
1022
|
-
output_buffer.should have_tag(
|
1023
|
-
output_buffer.should have_tag(
|
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
|
-
|
1073
|
-
|
1074
|
-
|
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
|
-
|
1078
|
-
|
1079
|
-
|
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
|
-
|
1082
|
-
|
1083
|
-
|
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
|
-
|
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
|
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 "
|
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
|
-
|
1165
|
-
|
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
|
-
|
1168
|
-
|
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
|
-
|
1172
|
-
|
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
|
-
|
1175
|
-
|
1176
|
-
|
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
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
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
|
-
|
1345
|
-
|
1346
|
-
|
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
|
-
|
1350
|
-
|
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
|
-
|
1353
|
-
|
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
|
-
|
1356
|
-
|
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 |
|
1425
|
-
output_buffer.should have_tag("form li.#{type}", /#{
|
1426
|
-
output_buffer.should have_tag("form li.#{type} #{countable}[@value
|
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
|
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 =>
|
1745
|
+
concat(builder.input(:category_name, :as => type, :collection => @categories))
|
1473
1746
|
end
|
1474
1747
|
|
1475
|
-
@categories.each do |
|
1476
|
-
|
1477
|
-
output_buffer.should have_tag(
|
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 =>
|
1764
|
+
concat(builder.input(:category_name, :as => type, :collection => @categories))
|
1491
1765
|
end
|
1492
1766
|
|
1493
1767
|
@categories.each do |value|
|
1494
|
-
|
1495
|
-
output_buffer.should have_tag(
|
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
|
-
|
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
|
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
|
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
|
-
|
1861
|
-
|
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
|
-
|
1864
|
-
|
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
|
-
|
1868
|
-
|
1869
|
-
|
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
|
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.
|
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-
|
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:
|
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:
|
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
|