formtastic 1.2.4 → 3.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +46 -0
  5. data/.yardopts +1 -0
  6. data/Appraisals +43 -0
  7. data/CHANGELOG +54 -0
  8. data/DEPRECATIONS +52 -0
  9. data/Gemfile +3 -0
  10. data/README.md +629 -0
  11. data/RELEASE_PROCESS +6 -0
  12. data/Rakefile +35 -0
  13. data/app/assets/stylesheets/formtastic.css +289 -0
  14. data/app/assets/stylesheets/formtastic_ie6.css +33 -0
  15. data/app/assets/stylesheets/formtastic_ie7.css +23 -0
  16. data/formtastic.gemspec +42 -0
  17. data/gemfiles/rails_3.2.gemfile +9 -0
  18. data/gemfiles/rails_4.0.4.gemfile +8 -0
  19. data/gemfiles/rails_4.1.gemfile +8 -0
  20. data/gemfiles/rails_4.2.gemfile +8 -0
  21. data/gemfiles/rails_4.gemfile +8 -0
  22. data/gemfiles/rails_5.0.gemfile +8 -0
  23. data/gemfiles/rails_edge.gemfile +15 -0
  24. data/lib/formtastic.rb +40 -1945
  25. data/lib/formtastic/action_class_finder.rb +18 -0
  26. data/lib/formtastic/actions.rb +11 -0
  27. data/lib/formtastic/actions/base.rb +156 -0
  28. data/lib/formtastic/actions/button_action.rb +67 -0
  29. data/lib/formtastic/actions/buttonish.rb +17 -0
  30. data/lib/formtastic/actions/input_action.rb +70 -0
  31. data/lib/formtastic/actions/link_action.rb +88 -0
  32. data/lib/formtastic/deprecation.rb +42 -0
  33. data/lib/formtastic/engine.rb +11 -0
  34. data/lib/formtastic/form_builder.rb +124 -0
  35. data/lib/formtastic/helpers.rb +16 -0
  36. data/lib/formtastic/helpers/action_helper.rb +162 -0
  37. data/lib/formtastic/helpers/actions_helper.rb +168 -0
  38. data/lib/formtastic/helpers/enum.rb +13 -0
  39. data/lib/formtastic/helpers/errors_helper.rb +81 -0
  40. data/lib/formtastic/helpers/fieldset_wrapper.rb +80 -0
  41. data/lib/formtastic/helpers/file_column_detection.rb +16 -0
  42. data/lib/formtastic/helpers/form_helper.rb +203 -0
  43. data/lib/formtastic/helpers/input_helper.rb +407 -0
  44. data/lib/formtastic/helpers/inputs_helper.rb +411 -0
  45. data/lib/formtastic/helpers/reflection.rb +37 -0
  46. data/lib/formtastic/html_attributes.rb +32 -0
  47. data/lib/formtastic/i18n.rb +4 -2
  48. data/lib/formtastic/input_class_finder.rb +18 -0
  49. data/lib/formtastic/inputs.rb +39 -0
  50. data/lib/formtastic/inputs/base.rb +76 -0
  51. data/lib/formtastic/inputs/base/associations.rb +31 -0
  52. data/lib/formtastic/inputs/base/choices.rb +108 -0
  53. data/lib/formtastic/inputs/base/collections.rb +159 -0
  54. data/lib/formtastic/inputs/base/database.rb +22 -0
  55. data/lib/formtastic/inputs/base/datetime_pickerish.rb +85 -0
  56. data/lib/formtastic/inputs/base/errors.rb +58 -0
  57. data/lib/formtastic/inputs/base/fileish.rb +23 -0
  58. data/lib/formtastic/inputs/base/hints.rb +31 -0
  59. data/lib/formtastic/inputs/base/html.rb +53 -0
  60. data/lib/formtastic/inputs/base/labelling.rb +52 -0
  61. data/lib/formtastic/inputs/base/naming.rb +42 -0
  62. data/lib/formtastic/inputs/base/numeric.rb +50 -0
  63. data/lib/formtastic/inputs/base/options.rb +17 -0
  64. data/lib/formtastic/inputs/base/placeholder.rb +17 -0
  65. data/lib/formtastic/inputs/base/stringish.rb +38 -0
  66. data/lib/formtastic/inputs/base/timeish.rb +241 -0
  67. data/lib/formtastic/inputs/base/validations.rb +215 -0
  68. data/lib/formtastic/inputs/base/wrapping.rb +50 -0
  69. data/lib/formtastic/inputs/boolean_input.rb +118 -0
  70. data/lib/formtastic/inputs/check_boxes_input.rb +197 -0
  71. data/lib/formtastic/inputs/color_input.rb +42 -0
  72. data/lib/formtastic/inputs/country_input.rb +86 -0
  73. data/lib/formtastic/inputs/datalist_input.rb +41 -0
  74. data/lib/formtastic/inputs/date_picker_input.rb +93 -0
  75. data/lib/formtastic/inputs/date_select_input.rb +34 -0
  76. data/lib/formtastic/inputs/datetime_picker_input.rb +103 -0
  77. data/lib/formtastic/inputs/datetime_select_input.rb +12 -0
  78. data/lib/formtastic/inputs/email_input.rb +41 -0
  79. data/lib/formtastic/inputs/file_input.rb +42 -0
  80. data/lib/formtastic/inputs/hidden_input.rb +62 -0
  81. data/lib/formtastic/inputs/number_input.rb +88 -0
  82. data/lib/formtastic/inputs/password_input.rb +41 -0
  83. data/lib/formtastic/inputs/phone_input.rb +42 -0
  84. data/lib/formtastic/inputs/radio_input.rb +163 -0
  85. data/lib/formtastic/inputs/range_input.rb +95 -0
  86. data/lib/formtastic/inputs/search_input.rb +41 -0
  87. data/lib/formtastic/inputs/select_input.rb +235 -0
  88. data/lib/formtastic/inputs/string_input.rb +36 -0
  89. data/lib/formtastic/inputs/text_input.rb +48 -0
  90. data/lib/formtastic/inputs/time_picker_input.rb +99 -0
  91. data/lib/formtastic/inputs/time_select_input.rb +38 -0
  92. data/lib/formtastic/inputs/time_zone_input.rb +58 -0
  93. data/lib/formtastic/inputs/url_input.rb +41 -0
  94. data/lib/formtastic/localized_string.rb +17 -0
  95. data/lib/formtastic/localizer.rb +152 -0
  96. data/lib/formtastic/namespaced_class_finder.rb +99 -0
  97. data/lib/formtastic/util.rb +35 -16
  98. data/lib/formtastic/version.rb +3 -0
  99. data/lib/generators/formtastic/form/form_generator.rb +64 -37
  100. data/lib/generators/formtastic/input/input_generator.rb +46 -0
  101. data/lib/generators/formtastic/install/install_generator.rb +13 -5
  102. data/lib/generators/templates/_form.html.erb +10 -4
  103. data/lib/generators/templates/_form.html.haml +8 -4
  104. data/lib/generators/templates/_form.html.slim +8 -0
  105. data/lib/generators/templates/formtastic.rb +77 -44
  106. data/lib/generators/templates/input.rb +19 -0
  107. data/lib/locale/en.yml +3 -0
  108. data/sample/basic_inputs.html +224 -0
  109. data/sample/config.ru +69 -0
  110. data/sample/index.html +14 -0
  111. data/spec/action_class_finder_spec.rb +12 -0
  112. data/spec/actions/button_action_spec.rb +63 -0
  113. data/spec/actions/generic_action_spec.rb +521 -0
  114. data/spec/actions/input_action_spec.rb +59 -0
  115. data/spec/actions/link_action_spec.rb +92 -0
  116. data/spec/builder/custom_builder_spec.rb +116 -0
  117. data/spec/builder/error_proc_spec.rb +27 -0
  118. data/spec/builder/semantic_fields_for_spec.rb +142 -0
  119. data/spec/fast_spec_helper.rb +12 -0
  120. data/spec/generators/formtastic/form/form_generator_spec.rb +131 -0
  121. data/spec/generators/formtastic/input/input_generator_spec.rb +124 -0
  122. data/spec/generators/formtastic/install/install_generator_spec.rb +47 -0
  123. data/spec/helpers/action_helper_spec.rb +19 -0
  124. data/spec/helpers/actions_helper_spec.rb +143 -0
  125. data/spec/helpers/form_helper_spec.rb +218 -0
  126. data/spec/helpers/input_helper_spec.rb +6 -0
  127. data/spec/helpers/inputs_helper_spec.rb +655 -0
  128. data/spec/helpers/namespaced_action_helper_spec.rb +43 -0
  129. data/spec/helpers/namespaced_input_helper_spec.rb +36 -0
  130. data/spec/helpers/reflection_helper_spec.rb +32 -0
  131. data/spec/helpers/semantic_errors_helper_spec.rb +112 -0
  132. data/spec/i18n_spec.rb +210 -0
  133. data/spec/input_class_finder_spec.rb +10 -0
  134. data/spec/inputs/base/collections_spec.rb +76 -0
  135. data/spec/inputs/base/validations_spec.rb +342 -0
  136. data/spec/inputs/boolean_input_spec.rb +254 -0
  137. data/spec/inputs/check_boxes_input_spec.rb +546 -0
  138. data/spec/inputs/color_input_spec.rb +97 -0
  139. data/spec/inputs/country_input_spec.rb +133 -0
  140. data/spec/inputs/custom_input_spec.rb +55 -0
  141. data/spec/inputs/datalist_input_spec.rb +61 -0
  142. data/spec/inputs/date_picker_input_spec.rb +449 -0
  143. data/spec/inputs/date_select_input_spec.rb +235 -0
  144. data/spec/inputs/datetime_picker_input_spec.rb +490 -0
  145. data/spec/inputs/datetime_select_input_spec.rb +193 -0
  146. data/spec/inputs/email_input_spec.rb +85 -0
  147. data/spec/inputs/file_input_spec.rb +89 -0
  148. data/spec/inputs/hidden_input_spec.rb +135 -0
  149. data/spec/inputs/include_blank_spec.rb +78 -0
  150. data/spec/inputs/label_spec.rb +149 -0
  151. data/spec/inputs/number_input_spec.rb +815 -0
  152. data/spec/inputs/password_input_spec.rb +99 -0
  153. data/spec/inputs/phone_input_spec.rb +85 -0
  154. data/spec/inputs/placeholder_spec.rb +71 -0
  155. data/spec/inputs/radio_input_spec.rb +328 -0
  156. data/spec/inputs/range_input_spec.rb +505 -0
  157. data/spec/inputs/readonly_spec.rb +50 -0
  158. data/spec/inputs/search_input_spec.rb +84 -0
  159. data/spec/inputs/select_input_spec.rb +615 -0
  160. data/spec/inputs/string_input_spec.rb +260 -0
  161. data/spec/inputs/text_input_spec.rb +187 -0
  162. data/spec/inputs/time_picker_input_spec.rb +455 -0
  163. data/spec/inputs/time_select_input_spec.rb +248 -0
  164. data/spec/inputs/time_zone_input_spec.rb +143 -0
  165. data/spec/inputs/url_input_spec.rb +85 -0
  166. data/spec/inputs/with_options_spec.rb +43 -0
  167. data/spec/localizer_spec.rb +130 -0
  168. data/spec/namespaced_class_finder_spec.rb +79 -0
  169. data/spec/spec.opts +2 -0
  170. data/spec/spec_helper.rb +525 -0
  171. data/spec/support/custom_macros.rb +564 -0
  172. data/spec/support/deprecation.rb +6 -0
  173. data/spec/support/shared_examples.rb +1313 -0
  174. data/spec/support/specialized_class_finder_shared_example.rb +27 -0
  175. data/spec/support/test_environment.rb +31 -0
  176. data/spec/util_spec.rb +66 -0
  177. metadata +434 -161
  178. data/README.textile +0 -682
  179. data/generators/form/USAGE +0 -16
  180. data/generators/form/form_generator.rb +0 -111
  181. data/generators/formtastic/formtastic_generator.rb +0 -26
  182. data/init.rb +0 -5
  183. data/lib/formtastic/layout_helper.rb +0 -12
  184. data/lib/formtastic/railtie.rb +0 -14
  185. data/lib/generators/templates/formtastic.css +0 -145
  186. data/lib/generators/templates/formtastic_changes.css +0 -14
  187. data/lib/generators/templates/rails2/_form.html.erb +0 -5
  188. data/lib/generators/templates/rails2/_form.html.haml +0 -4
  189. data/rails/init.rb +0 -2
@@ -0,0 +1,411 @@
1
+ module Formtastic
2
+ module Helpers
3
+
4
+ # {#inputs} is used to wrap a series of form items in a `<fieldset>` and `<ol>`, with each item
5
+ # in the list containing the markup representing a single {#input}.
6
+ #
7
+ # {#inputs} is usually called with a block containing a series of {#input} methods:
8
+ #
9
+ # <%= semantic_form_for @post do |f| %>
10
+ # <%= f.inputs do %>
11
+ # <%= f.input :title %>
12
+ # <%= f.input :body %>
13
+ # <% end %>
14
+ # <% end %>
15
+ #
16
+ # The HTML output will be something like:
17
+ #
18
+ # <form class="formtastic" method="post" action="...">
19
+ # <fieldset>
20
+ # <ol>
21
+ # <li class="string required" id="post_title_input">
22
+ # ...
23
+ # </li>
24
+ # <li class="text required" id="post_body_input">
25
+ # ...
26
+ # </li>
27
+ # </ol>
28
+ # </fieldset>
29
+ # </form>
30
+ #
31
+ # It's important to note that the `semantic_form_for` and {#inputs} blocks wrap the
32
+ # standard Rails `form_for` helper and FormBuilder, so you have full access to every standard
33
+ # Rails form helper, with any HTML markup and ERB syntax, allowing you to "break free" from
34
+ # Formtastic when it doesn't suit:
35
+ #
36
+ # <%= semantic_form_for @post do |f| %>
37
+ # <%= f.inputs do %>
38
+ # <%= f.input :title %>
39
+ # <li>
40
+ # <%= f.text_area :body %>
41
+ # <li>
42
+ # <% end %>
43
+ # <% end %>
44
+ #
45
+ # @see Formtastic::Helpers::InputHelper#input
46
+ module InputsHelper
47
+ include Formtastic::Helpers::FieldsetWrapper
48
+ include Formtastic::LocalizedString
49
+
50
+ # {#inputs} creates an input fieldset and ol tag wrapping for use around a set of inputs. It can be
51
+ # called either with a block (in which you can do the usual Rails form stuff, HTML, ERB, etc),
52
+ # or with a list of fields (accepting all default arguments and options). These two examples
53
+ # are functionally equivalent:
54
+ #
55
+ # # With a block:
56
+ # <% semantic_form_for @post do |form| %>
57
+ # <% f.inputs do %>
58
+ # <%= f.input :title %>
59
+ # <%= f.input :body %>
60
+ # <% end %>
61
+ # <% end %>
62
+ #
63
+ # # With a list of fields (short hand syntax):
64
+ # <% semantic_form_for @post do |form| %>
65
+ # <%= f.inputs :title, :body %>
66
+ # <% end %>
67
+ #
68
+ # # Output:
69
+ # <form ...>
70
+ # <fieldset class="inputs">
71
+ # <ol>
72
+ # <li class="string">...</li>
73
+ # <li class="text">...</li>
74
+ # </ol>
75
+ # </fieldset>
76
+ # </form>
77
+ #
78
+ # **Quick Forms**
79
+ #
80
+ # Quick, scaffolding-style forms can be easily rendered for rapid early development if called
81
+ # without a block or a field list. In the case an input is rendered for **most** columns in
82
+ # the model's database table (like Rails' scaffolding) plus inputs for some model associations.
83
+ #
84
+ # In this case, all inputs are rendered with default options and arguments. You'll want more
85
+ # control than this in a production application, but it's a great way to get started, then
86
+ # come back later to customise the form with a field list or a block of inputs. Example:
87
+ #
88
+ # <% semantic_form_for @post do |form| %>
89
+ # <%= f.inputs %>
90
+ # <% end %>
91
+ #
92
+ # **Nested Attributes**
93
+ #
94
+ # One of the most complicated parts of Rails forms comes when nesting the inputs for
95
+ # attrinbutes on associated models. Formtastic can take the pain away for many (but not all)
96
+ # situations.
97
+ #
98
+ # Given the following models:
99
+ #
100
+ # # Models
101
+ # class User < ActiveRecord::Base
102
+ # has_one :profile
103
+ # accepts_nested_attributes_for :profile
104
+ # end
105
+ # class Profile < ActiveRecord::Base
106
+ # belongs_to :user
107
+ # end
108
+ #
109
+ # Formtastic provides a helper called `semantic_fields_for`, which wraps around Rails' built-in
110
+ # `fields_for` helper for backwards compatibility with previous versions of Formtastic, and for
111
+ # a consistent method naming API. The following examples are functionally equivalent:
112
+ #
113
+ # <% semantic_form_for @user do |form| %>
114
+ # <%= f.inputs :name, :email %>
115
+ #
116
+ # <% f.semantic_fields_for :profile do |profile| %>
117
+ # <% profile.inputs do %>
118
+ # <%= profile.input :biography %>
119
+ # <%= profile.input :twitter_name %>
120
+ # <% end %>
121
+ # <% end %>
122
+ # <% end %>
123
+ #
124
+ # <% semantic_form_for @user do |form| %>
125
+ # <%= f.inputs :name, :email %>
126
+ #
127
+ # <% f.fields_for :profile do |profile| %>
128
+ # <% profile.inputs do %>
129
+ # <%= profile.input :biography %>
130
+ # <%= profile.input :twitter_name %>
131
+ # <% end %>
132
+ # <% end %>
133
+ # <% end %>
134
+ #
135
+ # {#inputs} also provides a DSL similar to `fields_for` / `semantic_fields_for` to reduce the
136
+ # lines of code a little:
137
+ #
138
+ # <% semantic_form_for @user do |f| %>
139
+ # <%= f.inputs :name, :email %>
140
+ #
141
+ # <% f.inputs :for => :profile do %>
142
+ # <%= profile.input :biography %>
143
+ # <%= profile.input :twitter_name %>
144
+ # <%= profile.input :shoe_size %>
145
+ # <% end %>
146
+ # <% end %>
147
+ #
148
+ # The `:for` option also works with short hand syntax:
149
+ #
150
+ # <% semantic_form_for @post do |form| %>
151
+ # <%= f.inputs :name, :email %>
152
+ # <%= f.inputs :biography, :twitter_name, :shoe_size, :for => :profile %>
153
+ # <% end %>
154
+ #
155
+ # {#inputs} will always create a new `<fieldset>` wrapping, so only use it when it makes sense
156
+ # in the document structure and semantics (using `semantic_fields_for` otherwise).
157
+ #
158
+ # All options except `:name`, `:title` and `:for` will be passed down to the fieldset as HTML
159
+ # attributes (id, class, style, etc).
160
+ #
161
+ # When nesting `inputs()` inside another `inputs()` block, the nested content will
162
+ # automatically be wrapped in an `<li>` tag to preserve the HTML validity (a `<fieldset>`
163
+ # cannot be a direct descendant of an `<ol>`.
164
+ #
165
+ #
166
+ # @option *args :for [Symbol, ActiveModel, Array]
167
+ # The contents of this option is passed down to Rails' fields_for() helper, so it accepts the same values.
168
+ #
169
+ # @option *args :name [String]
170
+ # The optional name passed into the `<legend>` tag within the fieldset (alias of `:title`)
171
+ #
172
+ # @option *args :title [String]
173
+ # The optional name passed into the `<legend>` tag within the fieldset (alias of `:name`)
174
+ #
175
+ #
176
+ # @example Quick form: Render a scaffold-like set of inputs for automatically guessed attributes and simple associations on the model, with all default arguments and options
177
+ # <% semantic_form_for @post do |form| %>
178
+ # <%= f.inputs %>
179
+ # <% end %>
180
+ #
181
+ # @example Quick form: Skip one or more fields
182
+ # <%= f.inputs :except => [:featured, :something_for_admin_only] %>
183
+ # <%= f.inputs :except => :featured %>
184
+ #
185
+ # @example Short hand: Render inputs for a named set of attributes and simple associations on the model, with all default arguments and options
186
+ # <% semantic_form_for @post do |form| %>
187
+ # <%= f.inputs :title, :body, :user, :categories %>
188
+ # <% end %>
189
+ #
190
+ # @example Block: Render inputs for attributes and simple associations with full control over arguments and options
191
+ # <% semantic_form_for @post do |form| %>
192
+ # <%= f.inputs do %>
193
+ # <%= f.input :title ... %>
194
+ # <%= f.input :body ... %>
195
+ # <%= f.input :user ... %>
196
+ # <%= f.input :categories ... %>
197
+ # <% end %>
198
+ # <% end %>
199
+ #
200
+ # @example Multiple blocks: Render inputs in multiple fieldsets
201
+ # <% semantic_form_for @post do |form| %>
202
+ # <%= f.inputs do %>
203
+ # <%= f.input :title ... %>
204
+ # <%= f.input :body ... %>
205
+ # <% end %>
206
+ # <%= f.inputs do %>
207
+ # <%= f.input :user ... %>
208
+ # <%= f.input :categories ... %>
209
+ # <% end %>
210
+ # <% end %>
211
+ #
212
+ # @example Provide text for the `<legend>` to name a fieldset (with a block)
213
+ # <% semantic_form_for @post do |form| %>
214
+ # <%= f.inputs :name => 'Write something:' do %>
215
+ # <%= f.input :title ... %>
216
+ # <%= f.input :body ... %>
217
+ # <% end %>
218
+ # <%= f.inputs do :name => 'Advanced options:' do %>
219
+ # <%= f.input :user ... %>
220
+ # <%= f.input :categories ... %>
221
+ # <% end %>
222
+ # <% end %>
223
+ #
224
+ # @example Provide text for the `<legend>` to name a fieldset (with short hand)
225
+ # <% semantic_form_for @post do |form| %>
226
+ # <%= f.inputs :title, :body, :name => 'Write something:'%>
227
+ # <%= f.inputs :user, :cateogies, :name => 'Advanced options:' %>
228
+ # <% end %>
229
+ #
230
+ # @example Inputs for nested attributes (don't forget `accepts_nested_attributes_for` in your model, see Rails' `fields_for` documentation)
231
+ # <% semantic_form_for @user do |form| %>
232
+ # <%= f.inputs do %>
233
+ # <%= f.input :name ... %>
234
+ # <%= f.input :email ... %>
235
+ # <% end %>
236
+ # <%= f.inputs :for => :profile do |profile| %>
237
+ # <%= profile.input :user ... %>
238
+ # <%= profile.input :categories ... %>
239
+ # <% end %>
240
+ # <% end %>
241
+ #
242
+ # @example Inputs for nested record (don't forget `accepts_nested_attributes_for` in your model, see Rails' `fields_for` documentation)
243
+ # <% semantic_form_for @user do |form| %>
244
+ # <%= f.inputs do %>
245
+ # <%= f.input :name ... %>
246
+ # <%= f.input :email ... %>
247
+ # <% end %>
248
+ # <%= f.inputs :for => @user.profile do |profile| %>
249
+ # <%= profile.input :user ... %>
250
+ # <%= profile.input :categories ... %>
251
+ # <% end %>
252
+ # <% end %>
253
+ #
254
+ # @example Inputs for nested record with a different name (don't forget `accepts_nested_attributes_for` in your model, see Rails' `fields_for` documentation)
255
+ # <% semantic_form_for @user do |form| %>
256
+ # <%= f.inputs do %>
257
+ # <%= f.input :name ... %>
258
+ # <%= f.input :email ... %>
259
+ # <% end %>
260
+ # <%= f.inputs :for => [:user_profile, @user.profile] do |profile| %>
261
+ # <%= profile.input :user ... %>
262
+ # <%= profile.input :categories ... %>
263
+ # <% end %>
264
+ # <% end %>
265
+ #
266
+ # @example Nesting {#inputs} blocks requires an extra `<li>` tag for valid markup
267
+ # <% semantic_form_for @user do |form| %>
268
+ # <%= f.inputs do %>
269
+ # <%= f.input :name ... %>
270
+ # <%= f.input :email ... %>
271
+ # <li>
272
+ # <%= f.inputs :for => [:user_profile, @user.profile] do |profile| %>
273
+ # <%= profile.input :user ... %>
274
+ # <%= profile.input :categories ... %>
275
+ # <% end %>
276
+ # </li>
277
+ # <% end %>
278
+ # <% end %>
279
+ def inputs(*args, &block)
280
+ wrap_it = @already_in_an_inputs_block ? true : false
281
+ @already_in_an_inputs_block = true
282
+
283
+ title = field_set_title_from_args(*args)
284
+ html_options = args.extract_options!
285
+ html_options[:class] ||= "inputs"
286
+ html_options[:name] = title
287
+ skipped_args = Array.wrap html_options.delete(:except)
288
+
289
+ out = begin
290
+ if html_options[:for] # Nested form
291
+ inputs_for_nested_attributes(*(args << html_options), &block)
292
+ elsif block_given?
293
+ field_set_and_list_wrapping(*(args << html_options), &block)
294
+ else
295
+ legend = args.shift if args.first.is_a?(::String)
296
+ args = default_columns_for_object - skipped_args if @object && args.empty?
297
+ contents = fieldset_contents_from_column_list(args)
298
+ args.unshift(legend) if legend.present?
299
+ field_set_and_list_wrapping(*((args << html_options) << contents))
300
+ end
301
+ end
302
+
303
+ out = template.content_tag(:li, out, :class => "input") if wrap_it
304
+ @already_in_an_inputs_block = wrap_it
305
+ out
306
+ end
307
+
308
+ protected
309
+
310
+ def default_columns_for_object
311
+ cols = association_columns(:belongs_to)
312
+ cols += content_columns
313
+ cols -= Formtastic::FormBuilder.skipped_columns
314
+ cols.compact
315
+ end
316
+
317
+ def fieldset_contents_from_column_list(columns)
318
+ columns.collect do |method|
319
+ if @object
320
+ if @object.class.respond_to?(:reflect_on_association)
321
+ if (@object.class.reflect_on_association(method.to_sym) && @object.class.reflect_on_association(method.to_sym).options[:polymorphic] == true)
322
+ raise PolymorphicInputWithoutCollectionError.new("Please provide a collection for :#{method} input (you'll need to use block form syntax). Inputs for polymorphic associations can only be used when an explicit :collection is provided.")
323
+ end
324
+ elsif @object.class.respond_to?(:associations)
325
+ if (@object.class.associations[method.to_sym] && @object.class.associations[method.to_sym].options[:polymorphic] == true)
326
+ raise PolymorphicInputWithoutCollectionError.new("Please provide a collection for :#{method} input (you'll need to use block form syntax). Inputs for polymorphic associations can only be used when an explicit :collection is provided.")
327
+ end
328
+ end
329
+ end
330
+ input(method.to_sym)
331
+ end
332
+ end
333
+
334
+ # Collects association columns (relation columns) for the current form object class. Skips
335
+ # polymorphic associations because we can't guess which class to use for an automatically
336
+ # generated input.
337
+ def association_columns(*by_associations) # @private
338
+ if @object.present? && @object.class.respond_to?(:reflections)
339
+ @object.class.reflections.collect do |name, association_reflection|
340
+ if by_associations.present?
341
+ if by_associations.include?(association_reflection.macro) && association_reflection.options[:polymorphic] != true
342
+ name
343
+ end
344
+ else
345
+ name
346
+ end
347
+ end.compact
348
+ else
349
+ []
350
+ end
351
+ end
352
+
353
+ # Collects content columns (non-relation columns) for the current form object class.
354
+ def content_columns # @private
355
+ # TODO: NameError is raised by Inflector.constantize. Consider checking if it exists instead.
356
+ begin klass = model_name.constantize; rescue NameError; return [] end
357
+ return [] unless klass.respond_to?(:content_columns)
358
+ klass.content_columns.collect { |c| c.name.to_sym }.compact
359
+ end
360
+
361
+ # Deals with :for option when it's supplied to inputs methods. Additional
362
+ # options to be passed down to :for should be supplied using :for_options
363
+ # key.
364
+ #
365
+ # It should raise an error if a block with arity zero is given.
366
+ def inputs_for_nested_attributes(*args, &block) # @private
367
+ options = args.extract_options!
368
+ args << options.merge!(:parent => { :builder => self, :for => options[:for] })
369
+
370
+ fields_for_block = if block_given?
371
+ raise ArgumentError, 'You gave :for option with a block to inputs method, ' <<
372
+ 'but the block does not accept any argument.' if block.arity <= 0
373
+ lambda do |f|
374
+ contents = f.inputs(*args) do
375
+ if block.arity == 1 # for backwards compatibility with REE & Ruby 1.8.x
376
+ yield(f)
377
+ else
378
+ index = parent_child_index(options[:parent]) if options[:parent]
379
+ yield(f, index)
380
+ end
381
+ end
382
+ template.concat(contents)
383
+ end
384
+ else
385
+ lambda do |f|
386
+ contents = f.inputs(*args)
387
+ template.concat(contents)
388
+ end
389
+ end
390
+
391
+ fields_for_args = [options.delete(:for), options.delete(:for_options) || {}].flatten(1)
392
+ fields_for(*fields_for_args, &fields_for_block)
393
+ end
394
+
395
+ def field_set_title_from_args(*args) # @private
396
+ options = args.extract_options!
397
+ options[:name] ||= options.delete(:title)
398
+ title = options[:name]
399
+
400
+ if title.blank?
401
+ valid_name_classes = [::String, ::Symbol]
402
+ valid_name_classes.delete(::Symbol) if !block_given? && (args.first.is_a?(::Symbol) && content_columns.include?(args.first))
403
+ title = args.shift if valid_name_classes.any? { |valid_name_class| args.first.is_a?(valid_name_class) }
404
+ end
405
+ title = localized_string(title, title, :title) if title.is_a?(::Symbol)
406
+ title
407
+ end
408
+
409
+ end
410
+ end
411
+ end
@@ -0,0 +1,37 @@
1
+ module Formtastic
2
+ module Helpers
3
+ # @private
4
+ module Reflection
5
+ # If an association method is passed in (f.input :author) try to find the
6
+ # reflection object.
7
+ def reflection_for(method) # @private
8
+ if @object.class.respond_to?(:reflect_on_association)
9
+ @object.class.reflect_on_association(method)
10
+ elsif @object.class.respond_to?(:associations) # MongoMapper uses the 'associations(method)' instead
11
+ @object.class.associations[method]
12
+ end
13
+ end
14
+
15
+ def association_macro_for_method(method) # @private
16
+ reflection = reflection_for(method)
17
+ reflection.macro if reflection
18
+ end
19
+
20
+ def association_primary_key_for_method(method) # @private
21
+ reflection = reflection_for(method)
22
+ if reflection
23
+ case association_macro_for_method(method)
24
+ when :has_and_belongs_to_many, :has_many, :references_and_referenced_in_many, :references_many
25
+ :"#{method.to_s.singularize}_ids"
26
+ else
27
+ return reflection.foreign_key.to_sym if reflection.respond_to?(:foreign_key)
28
+ return reflection.options[:foreign_key].to_sym unless reflection.options[:foreign_key].blank?
29
+ :"#{method}_id"
30
+ end
31
+ else
32
+ method.to_sym
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,32 @@
1
+ module Formtastic
2
+ # @private
3
+ module HtmlAttributes
4
+ # Returns a namespace passed by option or inherited from parent builders / class configuration
5
+ def dom_id_namespace
6
+ namespace = options[:custom_namespace]
7
+ parent = options[:parent_builder]
8
+
9
+ case
10
+ when namespace then namespace
11
+ when parent && parent != self then parent.dom_id_namespace
12
+ else custom_namespace
13
+ end
14
+ end
15
+
16
+ protected
17
+
18
+ def humanized_attribute_name(method)
19
+ if @object && @object.class.respond_to?(:human_attribute_name)
20
+ humanized_name = @object.class.human_attribute_name(method.to_s)
21
+ if humanized_name == method.to_s.send(:humanize)
22
+ method.to_s.send(label_str_method)
23
+ else
24
+ humanized_name
25
+ end
26
+ else
27
+ method.to_s.send(label_str_method)
28
+ end
29
+ end
30
+
31
+ end
32
+ end