formtastic 2.0.2 → 2.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. data/.gitignore +4 -1
  2. data/Appraisals +11 -0
  3. data/CHANGELOG +2 -2
  4. data/Gemfile +0 -2
  5. data/README.textile +183 -151
  6. data/Rakefile +9 -5
  7. data/app/assets/stylesheets/formtastic.css +16 -3
  8. data/formtastic.gemspec +6 -2
  9. data/gemfiles/rails-3.0.gemfile +7 -0
  10. data/gemfiles/rails-3.1.gemfile +7 -0
  11. data/gemfiles/rails-3.2.gemfile +7 -0
  12. data/lib/formtastic.rb +10 -2
  13. data/lib/formtastic/actions.rb +11 -0
  14. data/lib/formtastic/actions/base.rb +156 -0
  15. data/lib/formtastic/actions/button_action.rb +72 -0
  16. data/lib/formtastic/actions/buttonish.rb +17 -0
  17. data/lib/formtastic/actions/input_action.rb +68 -0
  18. data/lib/formtastic/actions/link_action.rb +87 -0
  19. data/lib/formtastic/engine.rb +5 -1
  20. data/lib/formtastic/form_builder.rb +3 -0
  21. data/lib/formtastic/helpers.rb +2 -1
  22. data/lib/formtastic/helpers/action_helper.rb +109 -0
  23. data/lib/formtastic/helpers/actions_helper.rb +168 -0
  24. data/lib/formtastic/helpers/buttons_helper.rb +22 -9
  25. data/lib/formtastic/helpers/errors_helper.rb +0 -54
  26. data/lib/formtastic/helpers/fieldset_wrapper.rb +10 -5
  27. data/lib/formtastic/helpers/form_helper.rb +2 -2
  28. data/lib/formtastic/helpers/input_helper.rb +1 -10
  29. data/lib/formtastic/helpers/inputs_helper.rb +6 -3
  30. data/lib/formtastic/i18n.rb +3 -2
  31. data/lib/formtastic/inputs/base.rb +11 -3
  32. data/lib/formtastic/inputs/base/choices.rb +6 -1
  33. data/lib/formtastic/inputs/base/collections.rb +36 -13
  34. data/lib/formtastic/inputs/base/grouped_collections.rb +1 -1
  35. data/lib/formtastic/inputs/base/numeric.rb +50 -0
  36. data/lib/formtastic/inputs/base/options.rb +1 -1
  37. data/lib/formtastic/inputs/base/placeholder.rb +17 -0
  38. data/lib/formtastic/inputs/base/stringish.rb +2 -7
  39. data/lib/formtastic/inputs/base/timeish.rb +21 -5
  40. data/lib/formtastic/inputs/base/validations.rb +1 -1
  41. data/lib/formtastic/inputs/base/wrapping.rb +10 -3
  42. data/lib/formtastic/inputs/boolean_input.rb +10 -2
  43. data/lib/formtastic/inputs/check_boxes_input.rb +18 -9
  44. data/lib/formtastic/inputs/country_input.rb +2 -2
  45. data/lib/formtastic/inputs/date_input.rb +19 -1
  46. data/lib/formtastic/inputs/datetime_input.rb +1 -1
  47. data/lib/formtastic/inputs/email_input.rb +2 -1
  48. data/lib/formtastic/inputs/file_input.rb +1 -1
  49. data/lib/formtastic/inputs/hidden_input.rb +1 -1
  50. data/lib/formtastic/inputs/number_input.rb +6 -36
  51. data/lib/formtastic/inputs/password_input.rb +2 -1
  52. data/lib/formtastic/inputs/phone_input.rb +3 -2
  53. data/lib/formtastic/inputs/radio_input.rb +1 -1
  54. data/lib/formtastic/inputs/range_input.rb +8 -32
  55. data/lib/formtastic/inputs/search_input.rb +2 -1
  56. data/lib/formtastic/inputs/select_input.rb +56 -28
  57. data/lib/formtastic/inputs/string_input.rb +3 -1
  58. data/lib/formtastic/inputs/text_input.rb +5 -4
  59. data/lib/formtastic/inputs/time_input.rb +4 -8
  60. data/lib/formtastic/inputs/time_zone_input.rb +1 -1
  61. data/lib/formtastic/inputs/url_input.rb +2 -1
  62. data/lib/formtastic/localized_string.rb +6 -94
  63. data/lib/formtastic/localizer.rb +110 -0
  64. data/lib/formtastic/version.rb +1 -1
  65. data/lib/generators/formtastic/form/form_generator.rb +105 -0
  66. data/lib/generators/templates/_form.html.erb +2 -2
  67. data/lib/generators/templates/_form.html.haml +4 -4
  68. data/lib/generators/templates/_form.html.slim +2 -2
  69. data/lib/generators/templates/formtastic.rb +4 -0
  70. data/lib/locale/en.yml +2 -0
  71. data/sample/basic_inputs.html +22 -0
  72. data/spec/actions/button_action_spec.rb +63 -0
  73. data/spec/actions/generic_action_spec.rb +484 -0
  74. data/spec/actions/input_action_spec.rb +59 -0
  75. data/spec/actions/link_action_spec.rb +92 -0
  76. data/spec/builder/semantic_fields_for_spec.rb +14 -0
  77. data/spec/generators/formtastic/form/form_generator_spec.rb +118 -0
  78. data/spec/generators/formtastic/install/install_generator_spec.rb +47 -0
  79. data/spec/helpers/action_helper_spec.rb +365 -0
  80. data/spec/helpers/actions_helper_spec.rb +143 -0
  81. data/spec/helpers/buttons_helper_spec.rb +39 -23
  82. data/spec/helpers/commit_button_helper_spec.rb +153 -93
  83. data/spec/helpers/inputs_helper_spec.rb +14 -0
  84. data/spec/i18n_spec.rb +11 -0
  85. data/spec/inputs/boolean_input_spec.rb +31 -2
  86. data/spec/inputs/check_boxes_input_spec.rb +29 -1
  87. data/spec/inputs/date_input_spec.rb +95 -0
  88. data/spec/inputs/datetime_input_spec.rb +49 -0
  89. data/spec/inputs/email_input_spec.rb +28 -0
  90. data/spec/inputs/file_input_spec.rb +28 -0
  91. data/spec/inputs/hidden_input_spec.rb +28 -0
  92. data/spec/inputs/include_blank_spec.rb +53 -45
  93. data/spec/inputs/number_input_spec.rb +34 -4
  94. data/spec/inputs/password_input_spec.rb +28 -0
  95. data/spec/inputs/phone_input_spec.rb +28 -0
  96. data/spec/inputs/placeholder_spec.rb +10 -10
  97. data/spec/inputs/radio_input_spec.rb +51 -6
  98. data/spec/inputs/range_input_spec.rb +30 -2
  99. data/spec/inputs/search_input_spec.rb +27 -0
  100. data/spec/inputs/select_input_spec.rb +52 -6
  101. data/spec/inputs/string_input_spec.rb +28 -0
  102. data/spec/inputs/text_input_spec.rb +27 -0
  103. data/spec/inputs/time_input_spec.rb +67 -1
  104. data/spec/inputs/time_zone_input_spec.rb +28 -0
  105. data/spec/inputs/url_input_spec.rb +28 -0
  106. data/spec/inputs/with_options_spec.rb +43 -0
  107. data/spec/spec_helper.rb +22 -6
  108. data/spec/support/custom_macros.rb +6 -134
  109. data/spec/support/test_environment.rb +0 -1
  110. metadata +104 -17
  111. data/lib/formtastic/helpers/semantic_form_helper.rb +0 -11
  112. data/lib/formtastic/inputs/numeric_input.rb +0 -21
  113. data/lib/formtastic/railtie.rb +0 -12
  114. data/lib/formtastic/semantic_form_builder.rb +0 -11
  115. data/spec/builder/errors_spec.rb +0 -203
  116. data/spec/inputs/numeric_input_spec.rb +0 -41
@@ -78,9 +78,9 @@ module Formtastic
78
78
  # @example Include seconds with times (excluded by default)
79
79
  # <%= f.input :publish_at, :as => :time, :include_seconds => true %>
80
80
  #
81
- # @example Specify if there should be a blank option at the start of each select or not
82
- # <%= f.input :publish_at, :as => :time, :include_blank=> true %>
83
- # <%= f.input :publish_at, :as => :time, :include_blank=> false %>
81
+ # @example Specify if there should be a blank option at the start of each select or not. Note that, unlike select inputs, :include_blank does not accept a string value.
82
+ # <%= f.input :publish_at, :as => :time, :include_blank => true %>
83
+ # <%= f.input :publish_at, :as => :time, :include_blank => false %>
84
84
  #
85
85
  # @todo Document i18n
86
86
  # @todo Check what other Rails options are supported (`start_year`, `end_year`, `use_month_numbers`, `use_short_month`, `add_month_numbers`, `prompt`), write tests for them, and otherwise support them
@@ -156,10 +156,18 @@ module Formtastic
156
156
  end
157
157
 
158
158
  def fragment_input_html(fragment)
159
- opts = input_options.merge(:prefix => object_name, :field_name => fragment_name(fragment), :default => value, :include_blank => include_blank?)
159
+ opts = input_options.merge(:prefix => fragment_prefix, :field_name => fragment_name(fragment), :default => value, :include_blank => include_blank?)
160
160
  template.send(:"select_#{fragment}", value, opts, input_html_options.merge(:id => fragment_id(fragment)))
161
161
  end
162
162
 
163
+ def fragment_prefix
164
+ if builder.options.key?(:index)
165
+ object_name + "[#{builder.options[:index]}]"
166
+ else
167
+ object_name
168
+ end
169
+ end
170
+
163
171
  # TODO extract to BlankOptions or similar -- Select uses similar code
164
172
  def include_blank?
165
173
  options.key?(:include_blank) ? options[:include_blank] : builder.include_blank_for_select_by_default
@@ -193,7 +201,7 @@ module Formtastic
193
201
  def fragments_label
194
202
  if render_label?
195
203
  template.content_tag(:legend,
196
- builder.label(method, label_text, :for => "#{input_html_options[:id]}_1i"),
204
+ builder.label(method, label_text, :for => fragment_id(fragments.first)),
197
205
  :class => "label"
198
206
  )
199
207
  else
@@ -211,6 +219,14 @@ module Formtastic
211
219
  "".html_safe
212
220
  end
213
221
 
222
+ def hidden_field_name(fragment)
223
+ if builder.options.key?(:index)
224
+ "#{object_name}[#{builder.options[:index]}][#{fragment_name(fragment)}]"
225
+ else
226
+ "#{object_name}[#{fragment_name(fragment)}]"
227
+ end
228
+ end
229
+
214
230
  end
215
231
  end
216
232
  end
@@ -108,7 +108,7 @@ module Formtastic
108
108
  validation.kind == :numericality
109
109
  end
110
110
  if validation
111
- validation.options[:step]
111
+ validation.options[:step] || (1 if validation_integer_only?)
112
112
  else
113
113
  nil
114
114
  end
@@ -14,9 +14,16 @@ module Formtastic
14
14
  end
15
15
 
16
16
  def wrapper_html_options
17
- opts = options[:wrapper_html] || {}
18
- opts[:class] ||= []
19
- opts[:class] = [opts[:class].to_s] unless opts[:class].is_a?(Array)
17
+ opts = (options[:wrapper_html] || {}).dup
18
+ opts[:class] =
19
+ case opts[:class]
20
+ when Array
21
+ opts[:class].dup
22
+ when nil
23
+ []
24
+ else
25
+ [opts[:class].to_s]
26
+ end
20
27
  opts[:class] << as
21
28
  opts[:class] << "input"
22
29
  opts[:class] << "error" if errors?
@@ -28,7 +28,7 @@ module Formtastic
28
28
  # @example Set the values for the checked and unchecked states
29
29
  # <%= f.input :published, :checked_value => "yes", :unchecked_value => "no" %>
30
30
  #
31
- # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
31
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
32
32
  class BooleanInput
33
33
  include Base
34
34
 
@@ -84,7 +84,15 @@ module Formtastic
84
84
  end
85
85
 
86
86
  def input_html_options
87
- {:name => "#{object_name}[#{method}]"}.merge(super)
87
+ {:name => input_html_options_name}.merge(super)
88
+ end
89
+
90
+ def input_html_options_name
91
+ if builder.options.key?(:index)
92
+ "#{object_name}[#{builder.options[:index]}][#{method}]"
93
+ else
94
+ "#{object_name}[#{method}]"
95
+ end
88
96
  end
89
97
 
90
98
  def checked?
@@ -73,7 +73,7 @@ module Formtastic
73
73
  # @example `:value_as_class` can be used to add a class to the `<li>` wrapped around each choice using the checkbox value for custom styling of each choice
74
74
  # <%= f.input :categories, :as => :check_boxes, :value_as_class => true %>
75
75
  #
76
- # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
76
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
77
77
  # @see Formtastic::Inputs::BooleanInput BooleanInput for a single checkbox for boolean (checked = true) inputs
78
78
  #
79
79
  # @todo Do/can we support the per-item HTML options like RadioInput?
@@ -152,13 +152,7 @@ module Formtastic
152
152
  end
153
153
 
154
154
  def selected_values
155
- if object.respond_to?(method)
156
- selected_items = [object.send(method)].compact.flatten
157
-
158
- [*selected_items.map { |o| send_or_call_or_object(value_method, o) }].compact
159
- else
160
- []
161
- end
155
+ @selected_values ||= make_selected_values
162
156
  end
163
157
 
164
158
  def disabled_values
@@ -172,9 +166,24 @@ module Formtastic
172
166
  end
173
167
 
174
168
  def input_name
175
- "#{object_name}[#{association_primary_key || method}][]"
169
+ if builder.options.key?(:index)
170
+ "#{object_name}[#{builder.options[:index]}][#{association_primary_key || method}][]"
171
+ else
172
+ "#{object_name}[#{association_primary_key || method}][]"
173
+ end
176
174
  end
177
175
 
176
+ protected
177
+
178
+ def make_selected_values
179
+ if object.respond_to?(method)
180
+ selected_items = [object.send(method)].compact.flatten
181
+
182
+ [*selected_items.map { |o| send_or_call_or_object(value_method, o) }].compact
183
+ else
184
+ []
185
+ end
186
+ end
178
187
  end
179
188
  end
180
189
  end
@@ -8,7 +8,7 @@ module Formtastic
8
8
  # * install any other country_select plugin that behaves in a similar way
9
9
  # * roll your own `country_select` helper with the same args and options as the Rails one
10
10
  #
11
- # By default, Formtastic includes a handfull of English-speaking countries as "priority
11
+ # By default, Formtastic includes a handful of English-speaking countries as "priority
12
12
  # counties", which can be set in the `priority_countries` configuration array in the
13
13
  # formtastic.rb initializer to suit your market and user base (see README for more info on
14
14
  # configuration). Additionally, it is possible to set the :priority_countries on a per-input
@@ -45,7 +45,7 @@ module Formtastic
45
45
  # # ...
46
46
  # </li>
47
47
  #
48
- # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
48
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
49
49
  class CountryInput
50
50
  include Base
51
51
 
@@ -2,7 +2,7 @@ module Formtastic
2
2
  module Inputs
3
3
  # Outputs a series of select boxes for the fragments that make up a date (year, month, day).
4
4
  #
5
- # @see Formtastic::Inputs::Timeish Timeish module for documetation of date, time and datetime input options.
5
+ # @see Formtastic::Inputs::Base::Timeish Timeish module for documentation of date, time and datetime input options.
6
6
  class DateInput
7
7
  include Base
8
8
  include Base::Timeish
@@ -11,6 +11,24 @@ module Formtastic
11
11
  def time_fragments
12
12
  []
13
13
  end
14
+
15
+ def hidden_date_fragments
16
+ default_date_fragments - date_fragments
17
+ end
18
+
19
+ def hidden_fragments
20
+ hidden_date_fragments.map do |fragment|
21
+ template.hidden_field_tag(hidden_field_name(fragment), fragment_value(fragment), :id => fragment_id(fragment), :disabled => input_html_options[:disabled] )
22
+ end.join.html_safe
23
+ end
24
+
25
+ def fragment_value(fragment)
26
+ if fragment == :year
27
+ Time.now.year
28
+ else
29
+ '1'
30
+ end
31
+ end
14
32
  end
15
33
  end
16
34
  end
@@ -3,7 +3,7 @@ module Formtastic
3
3
 
4
4
  # Outputs a series of select boxes for the fragments that make up a date and time (year, month, day, hour, minute, second).
5
5
  #
6
- # @see Formtastic::Inputs::Timeish Timeish module for documetation of date, time and datetime input options.
6
+ # @see Formtastic::Inputs::Base::Timeish Timeish module for documentation of date, time and datetime input options.
7
7
  class DatetimeInput
8
8
  include Base
9
9
  include Base::Timeish
@@ -24,10 +24,11 @@ module Formtastic
24
24
  # </fieldset>
25
25
  # </form>
26
26
  #
27
- # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
27
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
28
28
  class EmailInput
29
29
  include Base
30
30
  include Base::Stringish
31
+ include Base::Placeholder
31
32
 
32
33
  def to_html
33
34
  input_wrapping do
@@ -28,7 +28,7 @@ module Formtastic
28
28
  # </fieldset>
29
29
  # </form>
30
30
  #
31
- # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
31
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
32
32
  class FileInput
33
33
  include Base
34
34
  def to_html
@@ -27,7 +27,7 @@ module Formtastic
27
27
  #
28
28
  # form.formtastic li.hidden { display:none; }
29
29
  #
30
- # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
30
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
31
31
  class HiddenInput
32
32
  include Base
33
33
 
@@ -65,11 +65,12 @@ module Formtastic
65
65
  # <%= f.input :shoe_size, :as => :number, :in => 3..15, :step => 1 %>
66
66
  # <%= f.input :shoe_size, :as => :number, :input_html => { :in => 3..15, :step => 1 } %>
67
67
  #
68
- # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
68
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
69
69
  # @see http://api.rubyonrails.org/classes/ActiveModel/Validations/HelperMethods.html#method-i-validates_numericality_of Rails' Numericality validation documentation
70
70
  class NumberInput
71
71
  include Base
72
- include Base::Stringish
72
+ include Base::Numeric
73
+ include Base::Placeholder
73
74
 
74
75
  def to_html
75
76
  input_wrapping do
@@ -77,42 +78,11 @@ module Formtastic
77
78
  builder.number_field(method, input_html_options)
78
79
  end
79
80
  end
80
-
81
- def input_html_options
82
- defaults = super
83
-
84
- if in_option
85
- defaults[:min] = in_option.to_a.min
86
- defaults[:max] = in_option.to_a.max
87
- else
88
- defaults[:min] ||= min_option
89
- defaults[:max] ||= max_option
90
- end
91
- defaults[:step] ||= step_option
92
- defaults
93
- end
94
-
81
+
95
82
  def step_option
96
- return options[:step] if options.key?(:step)
97
- return validation_step if validation_step
98
- return 1 if validation_integer_only?
99
- "any"
100
- end
101
-
102
- def min_option
103
- return options[:min] if options.key?(:min)
104
- validation_min
83
+ super || "any"
105
84
  end
106
-
107
- def max_option
108
- return options[:max] if options.key?(:max)
109
- validation_max
110
- end
111
-
112
- def in_option
113
- options[:in]
114
- end
115
-
85
+
116
86
  end
117
87
  end
118
88
  end
@@ -24,10 +24,11 @@ module Formtastic
24
24
  # </fieldset>
25
25
  # </form>
26
26
  #
27
- # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
27
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
28
28
  class PasswordInput
29
29
  include Base
30
30
  include Base::Stringish
31
+ include Base::Placeholder
31
32
 
32
33
  def to_html
33
34
  input_wrapping do
@@ -18,16 +18,17 @@ module Formtastic
18
18
  # <ol>
19
19
  # <li class="phone">
20
20
  # <label for="user_mobile">Mobile</label>
21
- # <input type="phone" id="user_mobile" name="user[mobile]">
21
+ # <input type="tel" id="user_mobile" name="user[mobile]">
22
22
  # </li>
23
23
  # </ol>
24
24
  # </fieldset>
25
25
  # </form>
26
26
  #
27
- # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
27
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
28
28
  class PhoneInput
29
29
  include Base
30
30
  include Base::Stringish
31
+ include Base::Placeholder
31
32
 
32
33
  def to_html
33
34
  input_wrapping do
@@ -114,7 +114,7 @@ module Formtastic
114
114
  # @example Set HTML options on a specific radio input option with a 3rd element in the array for a collection member
115
115
  # <%= f.input :author, :as => :radio, :collection => [["Test", 'test'], ["Try", "try", {:disabled => true}]]
116
116
  #
117
- # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
117
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
118
118
  # @see Formtastic::Inputs::RadioInput as an alternative for `belongs_to` associations
119
119
  #
120
120
  # @todo :disabled like CheckBoxes?
@@ -64,13 +64,12 @@ module Formtastic
64
64
  # <%= f.input :shoe_size, :as => :range, :in => 3..15, :step => 1 %>
65
65
  # <%= f.input :shoe_size, :as => :range, :input_html => { :in => 3..15, :step => 1 } %>
66
66
  #
67
- # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
67
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
68
68
  # @see http://api.rubyonrails.org/classes/ActiveModel/Validations/HelperMethods.html#method-i-validates_numericality_of Rails' Numericality validation documentation
69
69
  #
70
- # @todo Is it still correct for this to be Stringish?
71
70
  class RangeInput
72
71
  include Base
73
- include Base::Stringish
72
+ include Base::Numeric
74
73
 
75
74
  def to_html
76
75
  input_wrapping do
@@ -78,40 +77,17 @@ module Formtastic
78
77
  builder.range_field(method, input_html_options)
79
78
  end
80
79
  end
81
-
82
- def input_html_options
83
- defaults = super
84
-
85
- if in_option
86
- defaults[:min] = in_option.to_a.min
87
- defaults[:max] = in_option.to_a.max
88
- else
89
- defaults[:min] ||= min_option
90
- defaults[:max] ||= max_option
91
- end
92
- defaults[:step] ||= step_option
93
- defaults
94
- end
95
-
96
- def step_option
97
- return options[:step] if options.key?(:step)
98
- return validation_step if validation_step
99
- return 1 if validation_integer_only?
100
- 1
101
- end
102
-
80
+
103
81
  def min_option
104
- return options[:min] if options.key?(:min)
105
- validation_min || 1
82
+ super || 1
106
83
  end
107
84
 
108
85
  def max_option
109
- return options[:max] if options.key?(:max)
110
- validation_max || 100
86
+ super || 100
111
87
  end
112
-
113
- def in_option
114
- options[:in]
88
+
89
+ def step_option
90
+ super || 1
115
91
  end
116
92
 
117
93
  end
@@ -23,10 +23,11 @@ module Formtastic
23
23
  # </fieldset>
24
24
  # </form>
25
25
  #
26
- # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
26
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
27
27
  class SearchInput
28
28
  include Base
29
29
  include Base::Stringish
30
+ include Base::Placeholder
30
31
 
31
32
  def to_html
32
33
  input_wrapping do
@@ -108,12 +108,12 @@ module Formtastic
108
108
  #
109
109
  # @example The `:member_label` can be used to call a different method (or a Proc) on each object in the collection for rendering the label text (it'll try the methods like `to_s` in `collection_label_methods` config by default)
110
110
  # <%= f.input :author, :as => :select, :member_label => :name %>
111
- # <%= f.input :author, :as => :select, :member_label => :name_with_post_count
112
- # <%= f.input :author, :as => :select, :member_label => Proc.new { |a| "#{c.name} (#{pluralize("post", a.posts.count)})" }
111
+ # <%= f.input :author, :as => :select, :member_label => :name_with_post_count %>
112
+ # <%= f.input :author, :as => :select, :member_label => Proc.new { |a| "#{c.name} (#{pluralize("post", a.posts.count)})" } %>
113
113
  #
114
114
  # @example The `:member_value` can be used to call a different method (or a Proc) on each object in the collection for rendering the value for each checkbox (it'll try the methods like `id` in `collection_value_methods` config by default)
115
115
  # <%= f.input :author, :as => :select, :member_value => :login %>
116
- # <%= f.input :author, :as => :select, :member_value => Proc.new { |c| c.full_name.downcase.underscore }
116
+ # <%= f.input :author, :as => :select, :member_value => Proc.new { |c| c.full_name.downcase.underscore } %>
117
117
  #
118
118
  # @example Set HTML attributes on the `<select>` tag with `:input_html`
119
119
  # <%= f.input :authors, :as => :select, :input_html => { :size => 20, :multiple => true, :class => "special" } %>
@@ -121,15 +121,21 @@ module Formtastic
121
121
  # @example Set HTML attributes on the `<li>` wrapper with `:wrapper_html`
122
122
  # <%= f.input :authors, :as => :select, :wrapper_html => { :class => "special" } %>
123
123
  #
124
- # @example Exclude or include the blank option at the top of the select, or change the prompt
125
- # <%= f.input :author, :as => :select, :input_html => { :include_blank => false } %>
126
- # <%= f.input :author, :as => :select, :input_html => { :include_blank => true } %>
127
- # <%= f.input :author, :as => :select, :input_html => { :prompt => "Please select an Author..." } %>
124
+ # @example Exclude, include, or customize the blank option at the top of the select. Always shown, even if the field already has a value. Suitable for optional inputs.
125
+ # <%= f.input :author, :as => :select, :include_blank => false %>
126
+ # <%= f.input :author, :as => :select, :include_blank => true %> => <option value=""></option>
127
+ # <%= f.input :author, :as => :select, :include_blank => "No author" %>
128
+ #
129
+ # @example Exclude, include, or customize the prompt at the top of the select. Only shown if the field does not have a value. Suitable for required inputs.
130
+ # <%= f.input :author, :as => :select, :prompt => false %>
131
+ # <%= f.input :author, :as => :select, :prompt => true %> => <option value="">Please select</option>
132
+ # <%= f.input :author, :as => :select, :prompt => "Please select an author" %>
133
+ #
128
134
  #
129
135
  # @example Group options an `<optgroup>` with the `:group_by` and `:group_label` options (`belongs_to` associations only)
130
136
  # <%= f.input :author, :as => :select, :group_by => :continent %>
131
137
  #
132
- # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
138
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
133
139
  # @see Formtastic::Inputs::CheckBoxesInput CheckBoxesInput as an alternative for `has_many` and `has_and_belongs_to_many` associations
134
140
  # @see Formtastic::Inputs::RadioInput RadioInput as an alternative for `belongs_to` associations
135
141
  #
@@ -138,46 +144,56 @@ module Formtastic
138
144
  include Base
139
145
  include Base::Collections
140
146
  include Base::GroupedCollections
141
-
147
+
142
148
  def to_html
143
149
  input_wrapping do
150
+ hidden_input <<
144
151
  label_html <<
145
152
  (options[:group_by] ? grouped_select_html : select_html)
146
153
  end
147
154
  end
148
-
155
+
149
156
  def select_html
150
157
  builder.select(input_name, collection, input_options, input_html_options)
151
158
  end
152
-
159
+
153
160
  def grouped_select_html
154
161
  builder.grouped_collection_select(
155
- input_name,
162
+ input_name,
156
163
  grouped_collection,
157
- group_association,
164
+ group_association,
158
165
  group_label_method,
159
- value_method,
166
+ value_method,
160
167
  label_method,
161
- input_options,
168
+ input_options,
162
169
  input_html_options
163
170
  )
164
171
  end
172
+
173
+ def include_blank
174
+ options.key?(:include_blank) ? options[:include_blank] : (single? && builder.include_blank_for_select_by_default)
175
+ end
165
176
 
166
- def include_blank?
167
- return options[:prompt] if options.key?(:prompt)
168
- return options[:include_blank] if options.key?(:include_blank)
169
- return true if (single? && builder.include_blank_for_select_by_default)
170
- false
177
+ def hidden_input
178
+ if multiple?
179
+ template.hidden_field_tag(input_html_options_name_multiple, '', :id => nil)
180
+ else
181
+ "".html_safe
182
+ end
183
+ end
184
+
185
+ def prompt?
186
+ !!options[:prompt]
171
187
  end
172
188
 
173
189
  def label_html_options
174
190
  super.merge(:for => input_html_options[:id])
175
191
  end
176
-
192
+
177
193
  def input_options
178
- super.merge({:include_blank => include_blank?, :prompt => nil})
194
+ super.merge :include_blank => (include_blank unless prompt?)
179
195
  end
180
-
196
+
181
197
  def input_html_options
182
198
  extra_input_html_options.merge(super)
183
199
  end
@@ -185,22 +201,34 @@ module Formtastic
185
201
  def extra_input_html_options
186
202
  {
187
203
  :multiple => multiple?,
188
- :name => "#{object_name}[#{association_primary_key}]#{'[]' if multiple?}"
204
+ :name => multiple? ? input_html_options_name_multiple : input_html_options_name
189
205
  }
190
206
  end
191
207
 
208
+ def input_html_options_name
209
+ if builder.options.key?(:index)
210
+ "#{object_name}[#{builder.options[:index]}][#{association_primary_key}]"
211
+ else
212
+ "#{object_name}[#{association_primary_key}]"
213
+ end
214
+ end
215
+
216
+ def input_html_options_name_multiple
217
+ input_html_options_name + "[]"
218
+ end
219
+
192
220
  def multiple_by_association?
193
221
  reflection && [ :has_many, :has_and_belongs_to_many ].include?(reflection.macro)
194
222
  end
195
-
223
+
196
224
  def multiple_by_options?
197
225
  options[:multiple] || (options[:input_html] && options[:input_html][:multiple])
198
226
  end
199
-
227
+
200
228
  def multiple?
201
- multiple_by_options? || multiple_by_association?
229
+ multiple_by_options? || multiple_by_association?
202
230
  end
203
-
231
+
204
232
  def single?
205
233
  !multiple?
206
234
  end