formtastic 1.2.4 → 3.1.5

Sign up to get free protection for your applications and to get access to all the features.
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