actionview 4.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionview might be problematic. Click here for more details.

Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +274 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +34 -0
  5. data/lib/action_view.rb +97 -0
  6. data/lib/action_view/base.rb +205 -0
  7. data/lib/action_view/buffers.rb +49 -0
  8. data/lib/action_view/context.rb +36 -0
  9. data/lib/action_view/dependency_tracker.rb +93 -0
  10. data/lib/action_view/digestor.rb +116 -0
  11. data/lib/action_view/flows.rb +76 -0
  12. data/lib/action_view/helpers.rb +64 -0
  13. data/lib/action_view/helpers/active_model_helper.rb +49 -0
  14. data/lib/action_view/helpers/asset_tag_helper.rb +322 -0
  15. data/lib/action_view/helpers/asset_url_helper.rb +355 -0
  16. data/lib/action_view/helpers/atom_feed_helper.rb +203 -0
  17. data/lib/action_view/helpers/cache_helper.rb +200 -0
  18. data/lib/action_view/helpers/capture_helper.rb +216 -0
  19. data/lib/action_view/helpers/controller_helper.rb +25 -0
  20. data/lib/action_view/helpers/csrf_helper.rb +30 -0
  21. data/lib/action_view/helpers/date_helper.rb +1075 -0
  22. data/lib/action_view/helpers/debug_helper.rb +39 -0
  23. data/lib/action_view/helpers/form_helper.rb +1876 -0
  24. data/lib/action_view/helpers/form_options_helper.rb +843 -0
  25. data/lib/action_view/helpers/form_tag_helper.rb +746 -0
  26. data/lib/action_view/helpers/javascript_helper.rb +75 -0
  27. data/lib/action_view/helpers/number_helper.rb +425 -0
  28. data/lib/action_view/helpers/output_safety_helper.rb +38 -0
  29. data/lib/action_view/helpers/record_tag_helper.rb +108 -0
  30. data/lib/action_view/helpers/rendering_helper.rb +90 -0
  31. data/lib/action_view/helpers/sanitize_helper.rb +256 -0
  32. data/lib/action_view/helpers/tag_helper.rb +176 -0
  33. data/lib/action_view/helpers/tags.rb +41 -0
  34. data/lib/action_view/helpers/tags/base.rb +148 -0
  35. data/lib/action_view/helpers/tags/check_box.rb +64 -0
  36. data/lib/action_view/helpers/tags/checkable.rb +16 -0
  37. data/lib/action_view/helpers/tags/collection_check_boxes.rb +44 -0
  38. data/lib/action_view/helpers/tags/collection_helpers.rb +85 -0
  39. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +36 -0
  40. data/lib/action_view/helpers/tags/collection_select.rb +28 -0
  41. data/lib/action_view/helpers/tags/color_field.rb +25 -0
  42. data/lib/action_view/helpers/tags/date_field.rb +13 -0
  43. data/lib/action_view/helpers/tags/date_select.rb +72 -0
  44. data/lib/action_view/helpers/tags/datetime_field.rb +22 -0
  45. data/lib/action_view/helpers/tags/datetime_local_field.rb +19 -0
  46. data/lib/action_view/helpers/tags/datetime_select.rb +8 -0
  47. data/lib/action_view/helpers/tags/email_field.rb +8 -0
  48. data/lib/action_view/helpers/tags/file_field.rb +8 -0
  49. data/lib/action_view/helpers/tags/grouped_collection_select.rb +29 -0
  50. data/lib/action_view/helpers/tags/hidden_field.rb +8 -0
  51. data/lib/action_view/helpers/tags/label.rb +65 -0
  52. data/lib/action_view/helpers/tags/month_field.rb +13 -0
  53. data/lib/action_view/helpers/tags/number_field.rb +18 -0
  54. data/lib/action_view/helpers/tags/password_field.rb +12 -0
  55. data/lib/action_view/helpers/tags/radio_button.rb +31 -0
  56. data/lib/action_view/helpers/tags/range_field.rb +8 -0
  57. data/lib/action_view/helpers/tags/search_field.rb +24 -0
  58. data/lib/action_view/helpers/tags/select.rb +41 -0
  59. data/lib/action_view/helpers/tags/tel_field.rb +8 -0
  60. data/lib/action_view/helpers/tags/text_area.rb +18 -0
  61. data/lib/action_view/helpers/tags/text_field.rb +29 -0
  62. data/lib/action_view/helpers/tags/time_field.rb +13 -0
  63. data/lib/action_view/helpers/tags/time_select.rb +8 -0
  64. data/lib/action_view/helpers/tags/time_zone_select.rb +20 -0
  65. data/lib/action_view/helpers/tags/url_field.rb +8 -0
  66. data/lib/action_view/helpers/tags/week_field.rb +13 -0
  67. data/lib/action_view/helpers/text_helper.rb +447 -0
  68. data/lib/action_view/helpers/translation_helper.rb +111 -0
  69. data/lib/action_view/helpers/url_helper.rb +625 -0
  70. data/lib/action_view/layouts.rb +426 -0
  71. data/lib/action_view/locale/en.yml +56 -0
  72. data/lib/action_view/log_subscriber.rb +44 -0
  73. data/lib/action_view/lookup_context.rb +249 -0
  74. data/lib/action_view/model_naming.rb +12 -0
  75. data/lib/action_view/path_set.rb +77 -0
  76. data/lib/action_view/railtie.rb +49 -0
  77. data/lib/action_view/record_identifier.rb +84 -0
  78. data/lib/action_view/renderer/abstract_renderer.rb +47 -0
  79. data/lib/action_view/renderer/partial_renderer.rb +492 -0
  80. data/lib/action_view/renderer/renderer.rb +50 -0
  81. data/lib/action_view/renderer/streaming_template_renderer.rb +103 -0
  82. data/lib/action_view/renderer/template_renderer.rb +96 -0
  83. data/lib/action_view/rendering.rb +145 -0
  84. data/lib/action_view/routing_url_for.rb +109 -0
  85. data/lib/action_view/tasks/dependencies.rake +17 -0
  86. data/lib/action_view/template.rb +340 -0
  87. data/lib/action_view/template/error.rb +141 -0
  88. data/lib/action_view/template/handlers.rb +53 -0
  89. data/lib/action_view/template/handlers/builder.rb +26 -0
  90. data/lib/action_view/template/handlers/erb.rb +145 -0
  91. data/lib/action_view/template/handlers/raw.rb +11 -0
  92. data/lib/action_view/template/resolver.rb +329 -0
  93. data/lib/action_view/template/text.rb +34 -0
  94. data/lib/action_view/template/types.rb +57 -0
  95. data/lib/action_view/test_case.rb +272 -0
  96. data/lib/action_view/testing/resolvers.rb +50 -0
  97. data/lib/action_view/vendor/html-scanner.rb +20 -0
  98. data/lib/action_view/vendor/html-scanner/html/document.rb +68 -0
  99. data/lib/action_view/vendor/html-scanner/html/node.rb +532 -0
  100. data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +188 -0
  101. data/lib/action_view/vendor/html-scanner/html/selector.rb +830 -0
  102. data/lib/action_view/vendor/html-scanner/html/tokenizer.rb +107 -0
  103. data/lib/action_view/vendor/html-scanner/html/version.rb +11 -0
  104. data/lib/action_view/version.rb +11 -0
  105. data/lib/action_view/view_paths.rb +96 -0
  106. metadata +218 -0
@@ -0,0 +1,39 @@
1
+ module ActionView
2
+ # = Action View Debug Helper
3
+ #
4
+ # Provides a set of methods for making it easier to debug Rails objects.
5
+ module Helpers
6
+ module DebugHelper
7
+
8
+ include TagHelper
9
+
10
+ # Returns a YAML representation of +object+ wrapped with <pre> and </pre>.
11
+ # If the object cannot be converted to YAML using +to_yaml+, +inspect+ will be called instead.
12
+ # Useful for inspecting an object at the time of rendering.
13
+ #
14
+ # @user = User.new({ username: 'testing', password: 'xyz', age: 42}) %>
15
+ # debug(@user)
16
+ # # =>
17
+ # <pre class='debug_dump'>--- !ruby/object:User
18
+ # attributes:
19
+ # &nbsp; updated_at:
20
+ # &nbsp; username: testing
21
+ #
22
+ # &nbsp; age: 42
23
+ # &nbsp; password: xyz
24
+ # &nbsp; created_at:
25
+ # attributes_cache: {}
26
+ #
27
+ # new_record: true
28
+ # </pre>
29
+ def debug(object)
30
+ Marshal::dump(object)
31
+ object = ERB::Util.html_escape(object.to_yaml).gsub(" ", "&nbsp; ").html_safe
32
+ content_tag(:pre, object, :class => "debug_dump")
33
+ rescue Exception # errors from Marshal or YAML
34
+ # Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
35
+ content_tag(:code, object.inspect, :class => "debug_dump")
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,1876 @@
1
+ require 'cgi'
2
+ require 'action_view/helpers/date_helper'
3
+ require 'action_view/helpers/tag_helper'
4
+ require 'action_view/helpers/form_tag_helper'
5
+ require 'action_view/helpers/active_model_helper'
6
+ require 'action_view/model_naming'
7
+ require 'active_support/core_ext/module/attribute_accessors'
8
+ require 'active_support/core_ext/hash/slice'
9
+ require 'active_support/core_ext/string/output_safety'
10
+ require 'active_support/core_ext/string/inflections'
11
+
12
+ module ActionView
13
+ # = Action View Form Helpers
14
+ module Helpers
15
+ # Form helpers are designed to make working with resources much easier
16
+ # compared to using vanilla HTML.
17
+ #
18
+ # Typically, a form designed to create or update a resource reflects the
19
+ # identity of the resource in several ways: (i) the url that the form is
20
+ # sent to (the form element's +action+ attribute) should result in a request
21
+ # being routed to the appropriate controller action (with the appropriate <tt>:id</tt>
22
+ # parameter in the case of an existing resource), (ii) input fields should
23
+ # be named in such a way that in the controller their values appear in the
24
+ # appropriate places within the +params+ hash, and (iii) for an existing record,
25
+ # when the form is initially displayed, input fields corresponding to attributes
26
+ # of the resource should show the current values of those attributes.
27
+ #
28
+ # In Rails, this is usually achieved by creating the form using +form_for+ and
29
+ # a number of related helper methods. +form_for+ generates an appropriate <tt>form</tt>
30
+ # tag and yields a form builder object that knows the model the form is about.
31
+ # Input fields are created by calling methods defined on the form builder, which
32
+ # means they are able to generate the appropriate names and default values
33
+ # corresponding to the model attributes, as well as convenient IDs, etc.
34
+ # Conventions in the generated field names allow controllers to receive form data
35
+ # nicely structured in +params+ with no effort on your side.
36
+ #
37
+ # For example, to create a new person you typically set up a new instance of
38
+ # +Person+ in the <tt>PeopleController#new</tt> action, <tt>@person</tt>, and
39
+ # in the view template pass that object to +form_for+:
40
+ #
41
+ # <%= form_for @person do |f| %>
42
+ # <%= f.label :first_name %>:
43
+ # <%= f.text_field :first_name %><br />
44
+ #
45
+ # <%= f.label :last_name %>:
46
+ # <%= f.text_field :last_name %><br />
47
+ #
48
+ # <%= f.submit %>
49
+ # <% end %>
50
+ #
51
+ # The HTML generated for this would be (modulus formatting):
52
+ #
53
+ # <form action="/people" class="new_person" id="new_person" method="post">
54
+ # <div style="margin:0;padding:0;display:inline">
55
+ # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
56
+ # </div>
57
+ # <label for="person_first_name">First name</label>:
58
+ # <input id="person_first_name" name="person[first_name]" type="text" /><br />
59
+ #
60
+ # <label for="person_last_name">Last name</label>:
61
+ # <input id="person_last_name" name="person[last_name]" type="text" /><br />
62
+ #
63
+ # <input name="commit" type="submit" value="Create Person" />
64
+ # </form>
65
+ #
66
+ # As you see, the HTML reflects knowledge about the resource in several spots,
67
+ # like the path the form should be submitted to, or the names of the input fields.
68
+ #
69
+ # In particular, thanks to the conventions followed in the generated field names, the
70
+ # controller gets a nested hash <tt>params[:person]</tt> with the person attributes
71
+ # set in the form. That hash is ready to be passed to <tt>Person.create</tt>:
72
+ #
73
+ # if @person = Person.create(params[:person])
74
+ # # success
75
+ # else
76
+ # # error handling
77
+ # end
78
+ #
79
+ # Interestingly, the exact same view code in the previous example can be used to edit
80
+ # a person. If <tt>@person</tt> is an existing record with name "John Smith" and ID 256,
81
+ # the code above as is would yield instead:
82
+ #
83
+ # <form action="/people/256" class="edit_person" id="edit_person_256" method="post">
84
+ # <div style="margin:0;padding:0;display:inline">
85
+ # <input name="_method" type="hidden" value="patch" />
86
+ # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
87
+ # </div>
88
+ # <label for="person_first_name">First name</label>:
89
+ # <input id="person_first_name" name="person[first_name]" type="text" value="John" /><br />
90
+ #
91
+ # <label for="person_last_name">Last name</label>:
92
+ # <input id="person_last_name" name="person[last_name]" type="text" value="Smith" /><br />
93
+ #
94
+ # <input name="commit" type="submit" value="Update Person" />
95
+ # </form>
96
+ #
97
+ # Note that the endpoint, default values, and submit button label are tailored for <tt>@person</tt>.
98
+ # That works that way because the involved helpers know whether the resource is a new record or not,
99
+ # and generate HTML accordingly.
100
+ #
101
+ # The controller would receive the form data again in <tt>params[:person]</tt>, ready to be
102
+ # passed to <tt>Person#update</tt>:
103
+ #
104
+ # if @person.update(params[:person])
105
+ # # success
106
+ # else
107
+ # # error handling
108
+ # end
109
+ #
110
+ # That's how you typically work with resources.
111
+ module FormHelper
112
+ extend ActiveSupport::Concern
113
+
114
+ include FormTagHelper
115
+ include UrlHelper
116
+ include ModelNaming
117
+
118
+ # Creates a form that allows the user to create or update the attributes
119
+ # of a specific model object.
120
+ #
121
+ # The method can be used in several slightly different ways, depending on
122
+ # how much you wish to rely on Rails to infer automatically from the model
123
+ # how the form should be constructed. For a generic model object, a form
124
+ # can be created by passing +form_for+ a string or symbol representing
125
+ # the object we are concerned with:
126
+ #
127
+ # <%= form_for :person do |f| %>
128
+ # First name: <%= f.text_field :first_name %><br />
129
+ # Last name : <%= f.text_field :last_name %><br />
130
+ # Biography : <%= f.text_area :biography %><br />
131
+ # Admin? : <%= f.check_box :admin %><br />
132
+ # <%= f.submit %>
133
+ # <% end %>
134
+ #
135
+ # The variable +f+ yielded to the block is a FormBuilder object that
136
+ # incorporates the knowledge about the model object represented by
137
+ # <tt>:person</tt> passed to +form_for+. Methods defined on the FormBuilder
138
+ # are used to generate fields bound to this model. Thus, for example,
139
+ #
140
+ # <%= f.text_field :first_name %>
141
+ #
142
+ # will get expanded to
143
+ #
144
+ # <%= text_field :person, :first_name %>
145
+ # which results in an html <tt><input></tt> tag whose +name+ attribute is
146
+ # <tt>person[first_name]</tt>. This means that when the form is submitted,
147
+ # the value entered by the user will be available in the controller as
148
+ # <tt>params[:person][:first_name]</tt>.
149
+ #
150
+ # For fields generated in this way using the FormBuilder,
151
+ # if <tt>:person</tt> also happens to be the name of an instance variable
152
+ # <tt>@person</tt>, the default value of the field shown when the form is
153
+ # initially displayed (e.g. in the situation where you are editing an
154
+ # existing record) will be the value of the corresponding attribute of
155
+ # <tt>@person</tt>.
156
+ #
157
+ # The rightmost argument to +form_for+ is an
158
+ # optional hash of options -
159
+ #
160
+ # * <tt>:url</tt> - The URL the form is to be submitted to. This may be
161
+ # represented in the same way as values passed to +url_for+ or +link_to+.
162
+ # So for example you may use a named route directly. When the model is
163
+ # represented by a string or symbol, as in the example above, if the
164
+ # <tt>:url</tt> option is not specified, by default the form will be
165
+ # sent back to the current url (We will describe below an alternative
166
+ # resource-oriented usage of +form_for+ in which the URL does not need
167
+ # to be specified explicitly).
168
+ # * <tt>:namespace</tt> - A namespace for your form to ensure uniqueness of
169
+ # id attributes on form elements. The namespace attribute will be prefixed
170
+ # with underscore on the generated HTML id.
171
+ # * <tt>:html</tt> - Optional HTML attributes for the form tag.
172
+ #
173
+ # Also note that +form_for+ doesn't create an exclusive scope. It's still
174
+ # possible to use both the stand-alone FormHelper methods and methods
175
+ # from FormTagHelper. For example:
176
+ #
177
+ # <%= form_for :person do |f| %>
178
+ # First name: <%= f.text_field :first_name %>
179
+ # Last name : <%= f.text_field :last_name %>
180
+ # Biography : <%= text_area :person, :biography %>
181
+ # Admin? : <%= check_box_tag "person[admin]", "1", @person.company.admin? %>
182
+ # <%= f.submit %>
183
+ # <% end %>
184
+ #
185
+ # This also works for the methods in FormOptionHelper and DateHelper that
186
+ # are designed to work with an object as base, like
187
+ # FormOptionHelper#collection_select and DateHelper#datetime_select.
188
+ #
189
+ # === #form_for with a model object
190
+ #
191
+ # In the examples above, the object to be created or edited was
192
+ # represented by a symbol passed to +form_for+, and we noted that
193
+ # a string can also be used equivalently. It is also possible, however,
194
+ # to pass a model object itself to +form_for+. For example, if <tt>@post</tt>
195
+ # is an existing record you wish to edit, you can create the form using
196
+ #
197
+ # <%= form_for @post do |f| %>
198
+ # ...
199
+ # <% end %>
200
+ #
201
+ # This behaves in almost the same way as outlined previously, with a
202
+ # couple of small exceptions. First, the prefix used to name the input
203
+ # elements within the form (hence the key that denotes them in the +params+
204
+ # hash) is actually derived from the object's _class_, e.g. <tt>params[:post]</tt>
205
+ # if the object's class is +Post+. However, this can be overwritten using
206
+ # the <tt>:as</tt> option, e.g. -
207
+ #
208
+ # <%= form_for(@person, as: :client) do |f| %>
209
+ # ...
210
+ # <% end %>
211
+ #
212
+ # would result in <tt>params[:client]</tt>.
213
+ #
214
+ # Secondly, the field values shown when the form is initially displayed
215
+ # are taken from the attributes of the object passed to +form_for+,
216
+ # regardless of whether the object is an instance
217
+ # variable. So, for example, if we had a _local_ variable +post+
218
+ # representing an existing record,
219
+ #
220
+ # <%= form_for post do |f| %>
221
+ # ...
222
+ # <% end %>
223
+ #
224
+ # would produce a form with fields whose initial state reflect the current
225
+ # values of the attributes of +post+.
226
+ #
227
+ # === Resource-oriented style
228
+ #
229
+ # In the examples just shown, although not indicated explicitly, we still
230
+ # need to use the <tt>:url</tt> option in order to specify where the
231
+ # form is going to be sent. However, further simplification is possible
232
+ # if the record passed to +form_for+ is a _resource_, i.e. it corresponds
233
+ # to a set of RESTful routes, e.g. defined using the +resources+ method
234
+ # in <tt>config/routes.rb</tt>. In this case Rails will simply infer the
235
+ # appropriate URL from the record itself. For example,
236
+ #
237
+ # <%= form_for @post do |f| %>
238
+ # ...
239
+ # <% end %>
240
+ #
241
+ # is then equivalent to something like:
242
+ #
243
+ # <%= form_for @post, as: :post, url: post_path(@post), method: :patch, html: { class: "edit_post", id: "edit_post_45" } do |f| %>
244
+ # ...
245
+ # <% end %>
246
+ #
247
+ # And for a new record
248
+ #
249
+ # <%= form_for(Post.new) do |f| %>
250
+ # ...
251
+ # <% end %>
252
+ #
253
+ # is equivalent to something like:
254
+ #
255
+ # <%= form_for @post, as: :post, url: posts_path, html: { class: "new_post", id: "new_post" } do |f| %>
256
+ # ...
257
+ # <% end %>
258
+ #
259
+ # However you can still overwrite individual conventions, such as:
260
+ #
261
+ # <%= form_for(@post, url: super_posts_path) do |f| %>
262
+ # ...
263
+ # <% end %>
264
+ #
265
+ # You can also set the answer format, like this:
266
+ #
267
+ # <%= form_for(@post, format: :json) do |f| %>
268
+ # ...
269
+ # <% end %>
270
+ #
271
+ # For namespaced routes, like +admin_post_url+:
272
+ #
273
+ # <%= form_for([:admin, @post]) do |f| %>
274
+ # ...
275
+ # <% end %>
276
+ #
277
+ # If your resource has associations defined, for example, you want to add comments
278
+ # to the document given that the routes are set correctly:
279
+ #
280
+ # <%= form_for([@document, @comment]) do |f| %>
281
+ # ...
282
+ # <% end %>
283
+ #
284
+ # Where <tt>@document = Document.find(params[:id])</tt> and
285
+ # <tt>@comment = Comment.new</tt>.
286
+ #
287
+ # === Setting the method
288
+ #
289
+ # You can force the form to use the full array of HTTP verbs by setting
290
+ #
291
+ # method: (:get|:post|:patch|:put|:delete)
292
+ #
293
+ # in the options hash. If the verb is not GET or POST, which are natively
294
+ # supported by HTML forms, the form will be set to POST and a hidden input
295
+ # called _method will carry the intended verb for the server to interpret.
296
+ #
297
+ # === Unobtrusive JavaScript
298
+ #
299
+ # Specifying:
300
+ #
301
+ # remote: true
302
+ #
303
+ # in the options hash creates a form that will allow the unobtrusive JavaScript drivers to modify its
304
+ # behavior. The expected default behavior is an XMLHttpRequest in the background instead of the regular
305
+ # POST arrangement, but ultimately the behavior is the choice of the JavaScript driver implementor.
306
+ # Even though it's using JavaScript to serialize the form elements, the form submission will work just like
307
+ # a regular submission as viewed by the receiving side (all elements available in <tt>params</tt>).
308
+ #
309
+ # Example:
310
+ #
311
+ # <%= form_for(@post, remote: true) do |f| %>
312
+ # ...
313
+ # <% end %>
314
+ #
315
+ # The HTML generated for this would be:
316
+ #
317
+ # <form action='http://www.example.com' method='post' data-remote='true'>
318
+ # <div style='margin:0;padding:0;display:inline'>
319
+ # <input name='_method' type='hidden' value='patch' />
320
+ # </div>
321
+ # ...
322
+ # </form>
323
+ #
324
+ # === Setting HTML options
325
+ #
326
+ # You can set data attributes directly by passing in a data hash, but all other HTML options must be wrapped in
327
+ # the HTML key. Example:
328
+ #
329
+ # <%= form_for(@post, data: { behavior: "autosave" }, html: { name: "go" }) do |f| %>
330
+ # ...
331
+ # <% end %>
332
+ #
333
+ # The HTML generated for this would be:
334
+ #
335
+ # <form action='http://www.example.com' method='post' data-behavior='autosave' name='go'>
336
+ # <div style='margin:0;padding:0;display:inline'>
337
+ # <input name='_method' type='hidden' value='patch' />
338
+ # </div>
339
+ # ...
340
+ # </form>
341
+ #
342
+ # === Removing hidden model id's
343
+ #
344
+ # The form_for method automatically includes the model id as a hidden field in the form.
345
+ # This is used to maintain the correlation between the form data and its associated model.
346
+ # Some ORM systems do not use IDs on nested models so in this case you want to be able
347
+ # to disable the hidden id.
348
+ #
349
+ # In the following example the Post model has many Comments stored within it in a NoSQL database,
350
+ # thus there is no primary key for comments.
351
+ #
352
+ # Example:
353
+ #
354
+ # <%= form_for(@post) do |f| %>
355
+ # <%= f.fields_for(:comments, include_id: false) do |cf| %>
356
+ # ...
357
+ # <% end %>
358
+ # <% end %>
359
+ #
360
+ # === Customized form builders
361
+ #
362
+ # You can also build forms using a customized FormBuilder class. Subclass
363
+ # FormBuilder and override or define some more helpers, then use your
364
+ # custom builder. For example, let's say you made a helper to
365
+ # automatically add labels to form inputs.
366
+ #
367
+ # <%= form_for @person, url: { action: "create" }, builder: LabellingFormBuilder do |f| %>
368
+ # <%= f.text_field :first_name %>
369
+ # <%= f.text_field :last_name %>
370
+ # <%= f.text_area :biography %>
371
+ # <%= f.check_box :admin %>
372
+ # <%= f.submit %>
373
+ # <% end %>
374
+ #
375
+ # In this case, if you use this:
376
+ #
377
+ # <%= render f %>
378
+ #
379
+ # The rendered template is <tt>people/_labelling_form</tt> and the local
380
+ # variable referencing the form builder is called
381
+ # <tt>labelling_form</tt>.
382
+ #
383
+ # The custom FormBuilder class is automatically merged with the options
384
+ # of a nested fields_for call, unless it's explicitly set.
385
+ #
386
+ # In many cases you will want to wrap the above in another helper, so you
387
+ # could do something like the following:
388
+ #
389
+ # def labelled_form_for(record_or_name_or_array, *args, &block)
390
+ # options = args.extract_options!
391
+ # form_for(record_or_name_or_array, *(args << options.merge(builder: LabellingFormBuilder)), &block)
392
+ # end
393
+ #
394
+ # If you don't need to attach a form to a model instance, then check out
395
+ # FormTagHelper#form_tag.
396
+ #
397
+ # === Form to external resources
398
+ #
399
+ # When you build forms to external resources sometimes you need to set an authenticity token or just render a form
400
+ # without it, for example when you submit data to a payment gateway number and types of fields could be limited.
401
+ #
402
+ # To set an authenticity token you need to pass an <tt>:authenticity_token</tt> parameter
403
+ #
404
+ # <%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f|
405
+ # ...
406
+ # <% end %>
407
+ #
408
+ # If you don't want to an authenticity token field be rendered at all just pass <tt>false</tt>:
409
+ #
410
+ # <%= form_for @invoice, url: external_url, authenticity_token: false do |f|
411
+ # ...
412
+ # <% end %>
413
+ def form_for(record, options = {}, &block)
414
+ raise ArgumentError, "Missing block" unless block_given?
415
+ html_options = options[:html] ||= {}
416
+
417
+ case record
418
+ when String, Symbol
419
+ object_name = record
420
+ object = nil
421
+ else
422
+ object = record.is_a?(Array) ? record.last : record
423
+ raise ArgumentError, "First argument in form cannot contain nil or be empty" unless object
424
+ object_name = options[:as] || model_name_from_record_or_class(object).param_key
425
+ apply_form_for_options!(record, object, options)
426
+ end
427
+
428
+ html_options[:data] = options.delete(:data) if options.has_key?(:data)
429
+ html_options[:remote] = options.delete(:remote) if options.has_key?(:remote)
430
+ html_options[:method] = options.delete(:method) if options.has_key?(:method)
431
+ html_options[:authenticity_token] = options.delete(:authenticity_token)
432
+
433
+ builder = instantiate_builder(object_name, object, options)
434
+ output = capture(builder, &block)
435
+ html_options[:multipart] ||= builder.multipart?
436
+
437
+ form_tag(options[:url] || {}, html_options) { output }
438
+ end
439
+
440
+ def apply_form_for_options!(record, object, options) #:nodoc:
441
+ object = convert_to_model(object)
442
+
443
+ as = options[:as]
444
+ namespace = options[:namespace]
445
+ action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :patch] : [:new, :post]
446
+ options[:html].reverse_merge!(
447
+ class: as ? "#{action}_#{as}" : dom_class(object, action),
448
+ id: (as ? [namespace, action, as] : [namespace, dom_id(object, action)]).compact.join("_").presence,
449
+ method: method
450
+ )
451
+
452
+ options[:url] ||= polymorphic_path(record, format: options.delete(:format))
453
+ end
454
+ private :apply_form_for_options!
455
+
456
+ # Creates a scope around a specific model object like form_for, but
457
+ # doesn't create the form tags themselves. This makes fields_for suitable
458
+ # for specifying additional model objects in the same form.
459
+ #
460
+ # Although the usage and purpose of +field_for+ is similar to +form_for+'s,
461
+ # its method signature is slightly different. Like +form_for+, it yields
462
+ # a FormBuilder object associated with a particular model object to a block,
463
+ # and within the block allows methods to be called on the builder to
464
+ # generate fields associated with the model object. Fields may reflect
465
+ # a model object in two ways - how they are named (hence how submitted
466
+ # values appear within the +params+ hash in the controller) and what
467
+ # default values are shown when the form the fields appear in is first
468
+ # displayed. In order for both of these features to be specified independently,
469
+ # both an object name (represented by either a symbol or string) and the
470
+ # object itself can be passed to the method separately -
471
+ #
472
+ # <%= form_for @person do |person_form| %>
473
+ # First name: <%= person_form.text_field :first_name %>
474
+ # Last name : <%= person_form.text_field :last_name %>
475
+ #
476
+ # <%= fields_for :permission, @person.permission do |permission_fields| %>
477
+ # Admin? : <%= permission_fields.check_box :admin %>
478
+ # <% end %>
479
+ #
480
+ # <%= f.submit %>
481
+ # <% end %>
482
+ #
483
+ # In this case, the checkbox field will be represented by an HTML +input+
484
+ # tag with the +name+ attribute <tt>permission[admin]</tt>, and the submitted
485
+ # value will appear in the controller as <tt>params[:permission][:admin]</tt>.
486
+ # If <tt>@person.permission</tt> is an existing record with an attribute
487
+ # +admin+, the initial state of the checkbox when first displayed will
488
+ # reflect the value of <tt>@person.permission.admin</tt>.
489
+ #
490
+ # Often this can be simplified by passing just the name of the model
491
+ # object to +fields_for+ -
492
+ #
493
+ # <%= fields_for :permission do |permission_fields| %>
494
+ # Admin?: <%= permission_fields.check_box :admin %>
495
+ # <% end %>
496
+ #
497
+ # ...in which case, if <tt>:permission</tt> also happens to be the name of an
498
+ # instance variable <tt>@permission</tt>, the initial state of the input
499
+ # field will reflect the value of that variable's attribute <tt>@permission.admin</tt>.
500
+ #
501
+ # Alternatively, you can pass just the model object itself (if the first
502
+ # argument isn't a string or symbol +fields_for+ will realize that the
503
+ # name has been omitted) -
504
+ #
505
+ # <%= fields_for @person.permission do |permission_fields| %>
506
+ # Admin?: <%= permission_fields.check_box :admin %>
507
+ # <% end %>
508
+ #
509
+ # and +fields_for+ will derive the required name of the field from the
510
+ # _class_ of the model object, e.g. if <tt>@person.permission</tt>, is
511
+ # of class +Permission+, the field will still be named <tt>permission[admin]</tt>.
512
+ #
513
+ # Note: This also works for the methods in FormOptionHelper and
514
+ # DateHelper that are designed to work with an object as base, like
515
+ # FormOptionHelper#collection_select and DateHelper#datetime_select.
516
+ #
517
+ # === Nested Attributes Examples
518
+ #
519
+ # When the object belonging to the current scope has a nested attribute
520
+ # writer for a certain attribute, fields_for will yield a new scope
521
+ # for that attribute. This allows you to create forms that set or change
522
+ # the attributes of a parent object and its associations in one go.
523
+ #
524
+ # Nested attribute writers are normal setter methods named after an
525
+ # association. The most common way of defining these writers is either
526
+ # with +accepts_nested_attributes_for+ in a model definition or by
527
+ # defining a method with the proper name. For example: the attribute
528
+ # writer for the association <tt>:address</tt> is called
529
+ # <tt>address_attributes=</tt>.
530
+ #
531
+ # Whether a one-to-one or one-to-many style form builder will be yielded
532
+ # depends on whether the normal reader method returns a _single_ object
533
+ # or an _array_ of objects.
534
+ #
535
+ # ==== One-to-one
536
+ #
537
+ # Consider a Person class which returns a _single_ Address from the
538
+ # <tt>address</tt> reader method and responds to the
539
+ # <tt>address_attributes=</tt> writer method:
540
+ #
541
+ # class Person
542
+ # def address
543
+ # @address
544
+ # end
545
+ #
546
+ # def address_attributes=(attributes)
547
+ # # Process the attributes hash
548
+ # end
549
+ # end
550
+ #
551
+ # This model can now be used with a nested fields_for, like so:
552
+ #
553
+ # <%= form_for @person do |person_form| %>
554
+ # ...
555
+ # <%= person_form.fields_for :address do |address_fields| %>
556
+ # Street : <%= address_fields.text_field :street %>
557
+ # Zip code: <%= address_fields.text_field :zip_code %>
558
+ # <% end %>
559
+ # ...
560
+ # <% end %>
561
+ #
562
+ # When address is already an association on a Person you can use
563
+ # +accepts_nested_attributes_for+ to define the writer method for you:
564
+ #
565
+ # class Person < ActiveRecord::Base
566
+ # has_one :address
567
+ # accepts_nested_attributes_for :address
568
+ # end
569
+ #
570
+ # If you want to destroy the associated model through the form, you have
571
+ # to enable it first using the <tt>:allow_destroy</tt> option for
572
+ # +accepts_nested_attributes_for+:
573
+ #
574
+ # class Person < ActiveRecord::Base
575
+ # has_one :address
576
+ # accepts_nested_attributes_for :address, allow_destroy: true
577
+ # end
578
+ #
579
+ # Now, when you use a form element with the <tt>_destroy</tt> parameter,
580
+ # with a value that evaluates to +true+, you will destroy the associated
581
+ # model (eg. 1, '1', true, or 'true'):
582
+ #
583
+ # <%= form_for @person do |person_form| %>
584
+ # ...
585
+ # <%= person_form.fields_for :address do |address_fields| %>
586
+ # ...
587
+ # Delete: <%= address_fields.check_box :_destroy %>
588
+ # <% end %>
589
+ # ...
590
+ # <% end %>
591
+ #
592
+ # ==== One-to-many
593
+ #
594
+ # Consider a Person class which returns an _array_ of Project instances
595
+ # from the <tt>projects</tt> reader method and responds to the
596
+ # <tt>projects_attributes=</tt> writer method:
597
+ #
598
+ # class Person
599
+ # def projects
600
+ # [@project1, @project2]
601
+ # end
602
+ #
603
+ # def projects_attributes=(attributes)
604
+ # # Process the attributes hash
605
+ # end
606
+ # end
607
+ #
608
+ # Note that the <tt>projects_attributes=</tt> writer method is in fact
609
+ # required for fields_for to correctly identify <tt>:projects</tt> as a
610
+ # collection, and the correct indices to be set in the form markup.
611
+ #
612
+ # When projects is already an association on Person you can use
613
+ # +accepts_nested_attributes_for+ to define the writer method for you:
614
+ #
615
+ # class Person < ActiveRecord::Base
616
+ # has_many :projects
617
+ # accepts_nested_attributes_for :projects
618
+ # end
619
+ #
620
+ # This model can now be used with a nested fields_for. The block given to
621
+ # the nested fields_for call will be repeated for each instance in the
622
+ # collection:
623
+ #
624
+ # <%= form_for @person do |person_form| %>
625
+ # ...
626
+ # <%= person_form.fields_for :projects do |project_fields| %>
627
+ # <% if project_fields.object.active? %>
628
+ # Name: <%= project_fields.text_field :name %>
629
+ # <% end %>
630
+ # <% end %>
631
+ # ...
632
+ # <% end %>
633
+ #
634
+ # It's also possible to specify the instance to be used:
635
+ #
636
+ # <%= form_for @person do |person_form| %>
637
+ # ...
638
+ # <% @person.projects.each do |project| %>
639
+ # <% if project.active? %>
640
+ # <%= person_form.fields_for :projects, project do |project_fields| %>
641
+ # Name: <%= project_fields.text_field :name %>
642
+ # <% end %>
643
+ # <% end %>
644
+ # <% end %>
645
+ # ...
646
+ # <% end %>
647
+ #
648
+ # Or a collection to be used:
649
+ #
650
+ # <%= form_for @person do |person_form| %>
651
+ # ...
652
+ # <%= person_form.fields_for :projects, @active_projects do |project_fields| %>
653
+ # Name: <%= project_fields.text_field :name %>
654
+ # <% end %>
655
+ # ...
656
+ # <% end %>
657
+ #
658
+ # If you want to destroy any of the associated models through the
659
+ # form, you have to enable it first using the <tt>:allow_destroy</tt>
660
+ # option for +accepts_nested_attributes_for+:
661
+ #
662
+ # class Person < ActiveRecord::Base
663
+ # has_many :projects
664
+ # accepts_nested_attributes_for :projects, allow_destroy: true
665
+ # end
666
+ #
667
+ # This will allow you to specify which models to destroy in the
668
+ # attributes hash by adding a form element for the <tt>_destroy</tt>
669
+ # parameter with a value that evaluates to +true+
670
+ # (eg. 1, '1', true, or 'true'):
671
+ #
672
+ # <%= form_for @person do |person_form| %>
673
+ # ...
674
+ # <%= person_form.fields_for :projects do |project_fields| %>
675
+ # Delete: <%= project_fields.check_box :_destroy %>
676
+ # <% end %>
677
+ # ...
678
+ # <% end %>
679
+ #
680
+ # When a collection is used you might want to know the index of each
681
+ # object into the array. For this purpose, the <tt>index</tt> method
682
+ # is available in the FormBuilder object.
683
+ #
684
+ # <%= form_for @person do |person_form| %>
685
+ # ...
686
+ # <%= person_form.fields_for :projects do |project_fields| %>
687
+ # Project #<%= project_fields.index %>
688
+ # ...
689
+ # <% end %>
690
+ # ...
691
+ # <% end %>
692
+ #
693
+ # Note that fields_for will automatically generate a hidden field
694
+ # to store the ID of the record. There are circumstances where this
695
+ # hidden field is not needed and you can pass <tt>include_id: false</tt>
696
+ # to prevent fields_for from rendering it automatically.
697
+ def fields_for(record_name, record_object = nil, options = {}, &block)
698
+ builder = instantiate_builder(record_name, record_object, options)
699
+ capture(builder, &block)
700
+ end
701
+
702
+ # Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
703
+ # assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
704
+ # is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
705
+ # Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
706
+ # onto the HTML as an HTML element attribute as in the example shown, except for the <tt>:value</tt> option, which is designed to
707
+ # target labels for radio_button tags (where the value is used in the ID of the input tag).
708
+ #
709
+ # ==== Examples
710
+ # label(:post, :title)
711
+ # # => <label for="post_title">Title</label>
712
+ #
713
+ # You can localize your labels based on model and attribute names.
714
+ # For example you can define the following in your locale (e.g. en.yml)
715
+ #
716
+ # helpers:
717
+ # label:
718
+ # post:
719
+ # body: "Write your entire text here"
720
+ #
721
+ # Which then will result in
722
+ #
723
+ # label(:post, :body)
724
+ # # => <label for="post_body">Write your entire text here</label>
725
+ #
726
+ # Localization can also be based purely on the translation of the attribute-name
727
+ # (if you are using ActiveRecord):
728
+ #
729
+ # activerecord:
730
+ # attributes:
731
+ # post:
732
+ # cost: "Total cost"
733
+ #
734
+ # label(:post, :cost)
735
+ # # => <label for="post_cost">Total cost</label>
736
+ #
737
+ # label(:post, :title, "A short title")
738
+ # # => <label for="post_title">A short title</label>
739
+ #
740
+ # label(:post, :title, "A short title", class: "title_label")
741
+ # # => <label for="post_title" class="title_label">A short title</label>
742
+ #
743
+ # label(:post, :privacy, "Public Post", value: "public")
744
+ # # => <label for="post_privacy_public">Public Post</label>
745
+ #
746
+ # label(:post, :terms) do
747
+ # 'Accept <a href="/terms">Terms</a>.'.html_safe
748
+ # end
749
+ def label(object_name, method, content_or_options = nil, options = nil, &block)
750
+ Tags::Label.new(object_name, method, self, content_or_options, options).render(&block)
751
+ end
752
+
753
+ # Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object
754
+ # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
755
+ # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
756
+ # shown.
757
+ #
758
+ # ==== Examples
759
+ # text_field(:post, :title, size: 20)
760
+ # # => <input type="text" id="post_title" name="post[title]" size="20" value="#{@post.title}" />
761
+ #
762
+ # text_field(:post, :title, class: "create_input")
763
+ # # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />
764
+ #
765
+ # text_field(:session, :user, onchange: "if ($('#session_user').val() === 'admin') { alert('Your login can not be admin!'); }")
766
+ # # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange="if ($('#session_user').val() === 'admin') { alert('Your login can not be admin!'); }"/>
767
+ #
768
+ # text_field(:snippet, :code, size: 20, class: 'code_input')
769
+ # # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" />
770
+ def text_field(object_name, method, options = {})
771
+ Tags::TextField.new(object_name, method, self, options).render
772
+ end
773
+
774
+ # Returns an input tag of the "password" type tailored for accessing a specified attribute (identified by +method+) on an object
775
+ # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
776
+ # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
777
+ # shown. For security reasons this field is blank by default; pass in a value via +options+ if this is not desired.
778
+ #
779
+ # ==== Examples
780
+ # password_field(:login, :pass, size: 20)
781
+ # # => <input type="password" id="login_pass" name="login[pass]" size="20" />
782
+ #
783
+ # password_field(:account, :secret, class: "form_input", value: @account.secret)
784
+ # # => <input type="password" id="account_secret" name="account[secret]" value="#{@account.secret}" class="form_input" />
785
+ #
786
+ # password_field(:user, :password, onchange: "if ($('#user_password').val().length > 30) { alert('Your password needs to be shorter!'); }")
787
+ # # => <input type="password" id="user_password" name="user[password]" onchange="if ($('#user_password').val().length > 30) { alert('Your password needs to be shorter!'); }"/>
788
+ #
789
+ # password_field(:account, :pin, size: 20, class: 'form_input')
790
+ # # => <input type="password" id="account_pin" name="account[pin]" size="20" class="form_input" />
791
+ def password_field(object_name, method, options = {})
792
+ Tags::PasswordField.new(object_name, method, self, options).render
793
+ end
794
+
795
+ # Returns a hidden input tag tailored for accessing a specified attribute (identified by +method+) on an object
796
+ # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
797
+ # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
798
+ # shown.
799
+ #
800
+ # ==== Examples
801
+ # hidden_field(:signup, :pass_confirm)
802
+ # # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" />
803
+ #
804
+ # hidden_field(:post, :tag_list)
805
+ # # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="#{@post.tag_list}" />
806
+ #
807
+ # hidden_field(:user, :token)
808
+ # # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
809
+ def hidden_field(object_name, method, options = {})
810
+ Tags::HiddenField.new(object_name, method, self, options).render
811
+ end
812
+
813
+ # Returns a file upload input tag tailored for accessing a specified attribute (identified by +method+) on an object
814
+ # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
815
+ # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
816
+ # shown.
817
+ #
818
+ # Using this method inside a +form_for+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
819
+ #
820
+ # ==== Options
821
+ # * Creates standard HTML attributes for the tag.
822
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
823
+ # * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files.
824
+ # * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations.
825
+ #
826
+ # ==== Examples
827
+ # file_field(:user, :avatar)
828
+ # # => <input type="file" id="user_avatar" name="user[avatar]" />
829
+ #
830
+ # file_field(:post, :image, :multiple => true)
831
+ # # => <input type="file" id="post_image" name="post[image]" multiple="true" />
832
+ #
833
+ # file_field(:post, :attached, accept: 'text/html')
834
+ # # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
835
+ #
836
+ # file_field(:post, :image, accept: 'image/png,image/gif,image/jpeg')
837
+ # # => <input type="file" id="post_image" name="post[image]" accept="image/png,image/gif,image/jpeg" />
838
+ #
839
+ # file_field(:attachment, :file, class: 'file_input')
840
+ # # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
841
+ def file_field(object_name, method, options = {})
842
+ Tags::FileField.new(object_name, method, self, options).render
843
+ end
844
+
845
+ # Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
846
+ # on an object assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
847
+ # hash with +options+.
848
+ #
849
+ # ==== Examples
850
+ # text_area(:post, :body, cols: 20, rows: 40)
851
+ # # => <textarea cols="20" rows="40" id="post_body" name="post[body]">
852
+ # # #{@post.body}
853
+ # # </textarea>
854
+ #
855
+ # text_area(:comment, :text, size: "20x30")
856
+ # # => <textarea cols="20" rows="30" id="comment_text" name="comment[text]">
857
+ # # #{@comment.text}
858
+ # # </textarea>
859
+ #
860
+ # text_area(:application, :notes, cols: 40, rows: 15, class: 'app_input')
861
+ # # => <textarea cols="40" rows="15" id="application_notes" name="application[notes]" class="app_input">
862
+ # # #{@application.notes}
863
+ # # </textarea>
864
+ #
865
+ # text_area(:entry, :body, size: "20x20", disabled: 'disabled')
866
+ # # => <textarea cols="20" rows="20" id="entry_body" name="entry[body]" disabled="disabled">
867
+ # # #{@entry.body}
868
+ # # </textarea>
869
+ def text_area(object_name, method, options = {})
870
+ Tags::TextArea.new(object_name, method, self, options).render
871
+ end
872
+
873
+ # Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object
874
+ # assigned to the template (identified by +object+). This object must be an instance object (@object) and not a local object.
875
+ # It's intended that +method+ returns an integer and if that integer is above zero, then the checkbox is checked.
876
+ # Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
877
+ # while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
878
+ #
879
+ # ==== Gotcha
880
+ #
881
+ # The HTML specification says unchecked check boxes are not successful, and
882
+ # thus web browsers do not send them. Unfortunately this introduces a gotcha:
883
+ # if an +Invoice+ model has a +paid+ flag, and in the form that edits a paid
884
+ # invoice the user unchecks its check box, no +paid+ parameter is sent. So,
885
+ # any mass-assignment idiom like
886
+ #
887
+ # @invoice.update(params[:invoice])
888
+ #
889
+ # wouldn't update the flag.
890
+ #
891
+ # To prevent this the helper generates an auxiliary hidden field before
892
+ # the very check box. The hidden field has the same name and its
893
+ # attributes mimic an unchecked check box.
894
+ #
895
+ # This way, the client either sends only the hidden field (representing
896
+ # the check box is unchecked), or both fields. Since the HTML specification
897
+ # says key/value pairs have to be sent in the same order they appear in the
898
+ # form, and parameters extraction gets the last occurrence of any repeated
899
+ # key in the query string, that works for ordinary forms.
900
+ #
901
+ # Unfortunately that workaround does not work when the check box goes
902
+ # within an array-like parameter, as in
903
+ #
904
+ # <%= fields_for "project[invoice_attributes][]", invoice, index: nil do |form| %>
905
+ # <%= form.check_box :paid %>
906
+ # ...
907
+ # <% end %>
908
+ #
909
+ # because parameter name repetition is precisely what Rails seeks to distinguish
910
+ # the elements of the array. For each item with a checked check box you
911
+ # get an extra ghost item with only that attribute, assigned to "0".
912
+ #
913
+ # In that case it is preferable to either use +check_box_tag+ or to use
914
+ # hashes instead of arrays.
915
+ #
916
+ # # Let's say that @post.validated? is 1:
917
+ # check_box("post", "validated")
918
+ # # => <input name="post[validated]" type="hidden" value="0" />
919
+ # # <input checked="checked" type="checkbox" id="post_validated" name="post[validated]" value="1" />
920
+ #
921
+ # # Let's say that @puppy.gooddog is "no":
922
+ # check_box("puppy", "gooddog", {}, "yes", "no")
923
+ # # => <input name="puppy[gooddog]" type="hidden" value="no" />
924
+ # # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
925
+ #
926
+ # check_box("eula", "accepted", { class: 'eula_check' }, "yes", "no")
927
+ # # => <input name="eula[accepted]" type="hidden" value="no" />
928
+ # # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
929
+ def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
930
+ Tags::CheckBox.new(object_name, method, self, checked_value, unchecked_value, options).render
931
+ end
932
+
933
+ # Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
934
+ # assigned to the template (identified by +object+). If the current value of +method+ is +tag_value+ the
935
+ # radio button will be checked.
936
+ #
937
+ # To force the radio button to be checked pass <tt>checked: true</tt> in the
938
+ # +options+ hash. You may pass HTML options there as well.
939
+ #
940
+ # # Let's say that @post.category returns "rails":
941
+ # radio_button("post", "category", "rails")
942
+ # radio_button("post", "category", "java")
943
+ # # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
944
+ # # <input type="radio" id="post_category_java" name="post[category]" value="java" />
945
+ #
946
+ # radio_button("user", "receive_newsletter", "yes")
947
+ # radio_button("user", "receive_newsletter", "no")
948
+ # # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
949
+ # # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
950
+ def radio_button(object_name, method, tag_value, options = {})
951
+ Tags::RadioButton.new(object_name, method, self, tag_value, options).render
952
+ end
953
+
954
+ # Returns a text_field of type "color".
955
+ #
956
+ # color_field("car", "color")
957
+ # # => <input id="car_color" name="car[color]" type="color" value="#000000" />
958
+ def color_field(object_name, method, options = {})
959
+ Tags::ColorField.new(object_name, method, self, options).render
960
+ end
961
+
962
+ # Returns an input of type "search" for accessing a specified attribute (identified by +method+) on an object
963
+ # assigned to the template (identified by +object_name+). Inputs of type "search" may be styled differently by
964
+ # some browsers.
965
+ #
966
+ # search_field(:user, :name)
967
+ # # => <input id="user_name" name="user[name]" type="search" />
968
+ # search_field(:user, :name, autosave: false)
969
+ # # => <input autosave="false" id="user_name" name="user[name]" type="search" />
970
+ # search_field(:user, :name, results: 3)
971
+ # # => <input id="user_name" name="user[name]" results="3" type="search" />
972
+ # # Assume request.host returns "www.example.com"
973
+ # search_field(:user, :name, autosave: true)
974
+ # # => <input autosave="com.example.www" id="user_name" name="user[name]" results="10" type="search" />
975
+ # search_field(:user, :name, onsearch: true)
976
+ # # => <input id="user_name" incremental="true" name="user[name]" onsearch="true" type="search" />
977
+ # search_field(:user, :name, autosave: false, onsearch: true)
978
+ # # => <input autosave="false" id="user_name" incremental="true" name="user[name]" onsearch="true" type="search" />
979
+ # search_field(:user, :name, autosave: true, onsearch: true)
980
+ # # => <input autosave="com.example.www" id="user_name" incremental="true" name="user[name]" onsearch="true" results="10" type="search" />
981
+ def search_field(object_name, method, options = {})
982
+ Tags::SearchField.new(object_name, method, self, options).render
983
+ end
984
+
985
+ # Returns a text_field of type "tel".
986
+ #
987
+ # telephone_field("user", "phone")
988
+ # # => <input id="user_phone" name="user[phone]" type="tel" />
989
+ #
990
+ def telephone_field(object_name, method, options = {})
991
+ Tags::TelField.new(object_name, method, self, options).render
992
+ end
993
+ # aliases telephone_field
994
+ alias phone_field telephone_field
995
+
996
+ # Returns a text_field of type "date".
997
+ #
998
+ # date_field("user", "born_on")
999
+ # # => <input id="user_born_on" name="user[born_on]" type="date" />
1000
+ #
1001
+ # The default value is generated by trying to call "to_date"
1002
+ # on the object's value, which makes it behave as expected for instances
1003
+ # of DateTime and ActiveSupport::TimeWithZone. You can still override that
1004
+ # by passing the "value" option explicitly, e.g.
1005
+ #
1006
+ # @user.born_on = Date.new(1984, 1, 27)
1007
+ # date_field("user", "born_on", value: "1984-05-12")
1008
+ # # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-05-12" />
1009
+ #
1010
+ def date_field(object_name, method, options = {})
1011
+ Tags::DateField.new(object_name, method, self, options).render
1012
+ end
1013
+
1014
+ # Returns a text_field of type "time".
1015
+ #
1016
+ # The default value is generated by trying to call +strftime+ with "%T.%L"
1017
+ # on the objects's value. It is still possible to override that
1018
+ # by passing the "value" option.
1019
+ #
1020
+ # === Options
1021
+ # * Accepts same options as time_field_tag
1022
+ #
1023
+ # === Example
1024
+ # time_field("task", "started_at")
1025
+ # # => <input id="task_started_at" name="task[started_at]" type="time" />
1026
+ #
1027
+ def time_field(object_name, method, options = {})
1028
+ Tags::TimeField.new(object_name, method, self, options).render
1029
+ end
1030
+
1031
+ # Returns a text_field of type "datetime".
1032
+ #
1033
+ # datetime_field("user", "born_on")
1034
+ # # => <input id="user_born_on" name="user[born_on]" type="datetime" />
1035
+ #
1036
+ # The default value is generated by trying to call +strftime+ with "%Y-%m-%dT%T.%L%z"
1037
+ # on the object's value, which makes it behave as expected for instances
1038
+ # of DateTime and ActiveSupport::TimeWithZone.
1039
+ #
1040
+ # @user.born_on = Date.new(1984, 1, 12)
1041
+ # datetime_field("user", "born_on")
1042
+ # # => <input id="user_born_on" name="user[born_on]" type="datetime" value="1984-01-12T00:00:00.000+0000" />
1043
+ #
1044
+ def datetime_field(object_name, method, options = {})
1045
+ Tags::DatetimeField.new(object_name, method, self, options).render
1046
+ end
1047
+
1048
+ # Returns a text_field of type "datetime-local".
1049
+ #
1050
+ # datetime_local_field("user", "born_on")
1051
+ # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" />
1052
+ #
1053
+ # The default value is generated by trying to call +strftime+ with "%Y-%m-%dT%T"
1054
+ # on the object's value, which makes it behave as expected for instances
1055
+ # of DateTime and ActiveSupport::TimeWithZone.
1056
+ #
1057
+ # @user.born_on = Date.new(1984, 1, 12)
1058
+ # datetime_local_field("user", "born_on")
1059
+ # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" value="1984-01-12T00:00:00" />
1060
+ #
1061
+ def datetime_local_field(object_name, method, options = {})
1062
+ Tags::DatetimeLocalField.new(object_name, method, self, options).render
1063
+ end
1064
+
1065
+ # Returns a text_field of type "month".
1066
+ #
1067
+ # month_field("user", "born_on")
1068
+ # # => <input id="user_born_on" name="user[born_on]" type="month" />
1069
+ #
1070
+ # The default value is generated by trying to call +strftime+ with "%Y-%m"
1071
+ # on the object's value, which makes it behave as expected for instances
1072
+ # of DateTime and ActiveSupport::TimeWithZone.
1073
+ #
1074
+ # @user.born_on = Date.new(1984, 1, 27)
1075
+ # month_field("user", "born_on")
1076
+ # # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-01" />
1077
+ #
1078
+ def month_field(object_name, method, options = {})
1079
+ Tags::MonthField.new(object_name, method, self, options).render
1080
+ end
1081
+
1082
+ # Returns a text_field of type "week".
1083
+ #
1084
+ # week_field("user", "born_on")
1085
+ # # => <input id="user_born_on" name="user[born_on]" type="week" />
1086
+ #
1087
+ # The default value is generated by trying to call +strftime+ with "%Y-W%W"
1088
+ # on the object's value, which makes it behave as expected for instances
1089
+ # of DateTime and ActiveSupport::TimeWithZone.
1090
+ #
1091
+ # @user.born_on = Date.new(1984, 5, 12)
1092
+ # week_field("user", "born_on")
1093
+ # # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-W19" />
1094
+ #
1095
+ def week_field(object_name, method, options = {})
1096
+ Tags::WeekField.new(object_name, method, self, options).render
1097
+ end
1098
+
1099
+ # Returns a text_field of type "url".
1100
+ #
1101
+ # url_field("user", "homepage")
1102
+ # # => <input id="user_homepage" name="user[homepage]" type="url" />
1103
+ #
1104
+ def url_field(object_name, method, options = {})
1105
+ Tags::UrlField.new(object_name, method, self, options).render
1106
+ end
1107
+
1108
+ # Returns a text_field of type "email".
1109
+ #
1110
+ # email_field("user", "address")
1111
+ # # => <input id="user_address" name="user[address]" type="email" />
1112
+ #
1113
+ def email_field(object_name, method, options = {})
1114
+ Tags::EmailField.new(object_name, method, self, options).render
1115
+ end
1116
+
1117
+ # Returns an input tag of type "number".
1118
+ #
1119
+ # ==== Options
1120
+ # * Accepts same options as number_field_tag
1121
+ def number_field(object_name, method, options = {})
1122
+ Tags::NumberField.new(object_name, method, self, options).render
1123
+ end
1124
+
1125
+ # Returns an input tag of type "range".
1126
+ #
1127
+ # ==== Options
1128
+ # * Accepts same options as range_field_tag
1129
+ def range_field(object_name, method, options = {})
1130
+ Tags::RangeField.new(object_name, method, self, options).render
1131
+ end
1132
+
1133
+ private
1134
+
1135
+ def instantiate_builder(record_name, record_object, options)
1136
+ case record_name
1137
+ when String, Symbol
1138
+ object = record_object
1139
+ object_name = record_name
1140
+ else
1141
+ object = record_name
1142
+ object_name = model_name_from_record_or_class(object).param_key
1143
+ end
1144
+
1145
+ builder = options[:builder] || default_form_builder
1146
+ builder.new(object_name, object, self, options)
1147
+ end
1148
+
1149
+ def default_form_builder
1150
+ builder = ActionView::Base.default_form_builder
1151
+ builder.respond_to?(:constantize) ? builder.constantize : builder
1152
+ end
1153
+ end
1154
+
1155
+ # A +FormBuilder+ object is associated with a particular model object and
1156
+ # allows you to generate fields associated with the model object. The
1157
+ # +FormBuilder+ object is yielded when using +form_for+ or +fields_for+.
1158
+ # For example:
1159
+ #
1160
+ # <%= form_for @person do |person_form| %>
1161
+ # Name: <%= person_form.text_field :name %>
1162
+ # Admin: <%= person_form.check_box :admin %>
1163
+ # <% end %>
1164
+ #
1165
+ # In the above block, the a +FormBuilder+ object is yielded as the
1166
+ # +person_form+ variable. This allows you to generate the +text_field+
1167
+ # and +check_box+ fields by specifying their eponymous methods, which
1168
+ # modify the underlying template and associates the +@person+ model object
1169
+ # with the form.
1170
+ #
1171
+ # The +FormBuilder+ object can be thought of as serving as a proxy for the
1172
+ # methods in the +FormHelper+ module. This class, however, allows you to
1173
+ # call methods with the model object you are building the form for.
1174
+ #
1175
+ # You can create your own custom FormBuilder templates by subclassing this
1176
+ # class. For example:
1177
+ #
1178
+ # class MyFormBuilder < ActionView::Helpers::FormBuilder
1179
+ # def div_radio_button(method, tag_value, options = {})
1180
+ # @template.content_tag(:div,
1181
+ # @template.radio_button(
1182
+ # @object_name, method, tag_value, objectify_options(options)
1183
+ # )
1184
+ # )
1185
+ # end
1186
+ #
1187
+ # The above code creates a new method +div_radio_button+ which wraps a div
1188
+ # around the a new radio button. Note that when options are passed in, you
1189
+ # must called +objectify_options+ in order for the model object to get
1190
+ # correctly passed to the method. If +objectify_options+ is not called,
1191
+ # then the newly created helper will not be linked back to the model.
1192
+ #
1193
+ # The +div_radio_button+ code from above can now be used as follows:
1194
+ #
1195
+ # <%= form_for @person, :builder => MyFormBuilder do |f| %>
1196
+ # I am a child: <%= f.div_radio_button(:admin, "child") %>
1197
+ # I am an adult: <%= f.div_radio_button(:admin, "adult") %>
1198
+ # <% end -%>
1199
+ #
1200
+ # The standard set of helper methods for form building are located in the
1201
+ # +field_helpers+ class attribute.
1202
+ class FormBuilder
1203
+ include ModelNaming
1204
+
1205
+ # The methods which wrap a form helper call.
1206
+ class_attribute :field_helpers
1207
+ self.field_helpers = [:fields_for, :label, :text_field, :password_field,
1208
+ :hidden_field, :file_field, :text_area, :check_box,
1209
+ :radio_button, :color_field, :search_field,
1210
+ :telephone_field, :phone_field, :date_field,
1211
+ :time_field, :datetime_field, :datetime_local_field,
1212
+ :month_field, :week_field, :url_field, :email_field,
1213
+ :number_field, :range_field]
1214
+
1215
+ attr_accessor :object_name, :object, :options
1216
+
1217
+ attr_reader :multipart, :index
1218
+ alias :multipart? :multipart
1219
+
1220
+ def multipart=(multipart)
1221
+ @multipart = multipart
1222
+
1223
+ if parent_builder = @options[:parent_builder]
1224
+ parent_builder.multipart = multipart
1225
+ end
1226
+ end
1227
+
1228
+ def self._to_partial_path
1229
+ @_to_partial_path ||= name.demodulize.underscore.sub!(/_builder$/, '')
1230
+ end
1231
+
1232
+ def to_partial_path
1233
+ self.class._to_partial_path
1234
+ end
1235
+
1236
+ def to_model
1237
+ self
1238
+ end
1239
+
1240
+ def initialize(object_name, object, template, options)
1241
+ @nested_child_index = {}
1242
+ @object_name, @object, @template, @options = object_name, object, template, options
1243
+ @default_options = @options ? @options.slice(:index, :namespace) : {}
1244
+ if @object_name.to_s.match(/\[\]$/)
1245
+ if object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param)
1246
+ @auto_index = object.to_param
1247
+ else
1248
+ raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
1249
+ end
1250
+ end
1251
+ @multipart = nil
1252
+ @index = options[:index] || options[:child_index]
1253
+ end
1254
+
1255
+ (field_helpers - [:label, :check_box, :radio_button, :fields_for, :hidden_field, :file_field]).each do |selector|
1256
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
1257
+ def #{selector}(method, options = {}) # def text_field(method, options = {})
1258
+ @template.send( # @template.send(
1259
+ #{selector.inspect}, # "text_field",
1260
+ @object_name, # @object_name,
1261
+ method, # method,
1262
+ objectify_options(options)) # objectify_options(options))
1263
+ end # end
1264
+ RUBY_EVAL
1265
+ end
1266
+
1267
+ # Creates a scope around a specific model object like form_for, but
1268
+ # doesn't create the form tags themselves. This makes fields_for suitable
1269
+ # for specifying additional model objects in the same form.
1270
+ #
1271
+ # Although the usage and purpose of +field_for+ is similar to +form_for+'s,
1272
+ # its method signature is slightly different. Like +form_for+, it yields
1273
+ # a FormBuilder object associated with a particular model object to a block,
1274
+ # and within the block allows methods to be called on the builder to
1275
+ # generate fields associated with the model object. Fields may reflect
1276
+ # a model object in two ways - how they are named (hence how submitted
1277
+ # values appear within the +params+ hash in the controller) and what
1278
+ # default values are shown when the form the fields appear in is first
1279
+ # displayed. In order for both of these features to be specified independently,
1280
+ # both an object name (represented by either a symbol or string) and the
1281
+ # object itself can be passed to the method separately -
1282
+ #
1283
+ # <%= form_for @person do |person_form| %>
1284
+ # First name: <%= person_form.text_field :first_name %>
1285
+ # Last name : <%= person_form.text_field :last_name %>
1286
+ #
1287
+ # <%= fields_for :permission, @person.permission do |permission_fields| %>
1288
+ # Admin? : <%= permission_fields.check_box :admin %>
1289
+ # <% end %>
1290
+ #
1291
+ # <%= person_form.submit %>
1292
+ # <% end %>
1293
+ #
1294
+ # In this case, the checkbox field will be represented by an HTML +input+
1295
+ # tag with the +name+ attribute <tt>permission[admin]</tt>, and the submitted
1296
+ # value will appear in the controller as <tt>params[:permission][:admin]</tt>.
1297
+ # If <tt>@person.permission</tt> is an existing record with an attribute
1298
+ # +admin+, the initial state of the checkbox when first displayed will
1299
+ # reflect the value of <tt>@person.permission.admin</tt>.
1300
+ #
1301
+ # Often this can be simplified by passing just the name of the model
1302
+ # object to +fields_for+ -
1303
+ #
1304
+ # <%= fields_for :permission do |permission_fields| %>
1305
+ # Admin?: <%= permission_fields.check_box :admin %>
1306
+ # <% end %>
1307
+ #
1308
+ # ...in which case, if <tt>:permission</tt> also happens to be the name of an
1309
+ # instance variable <tt>@permission</tt>, the initial state of the input
1310
+ # field will reflect the value of that variable's attribute <tt>@permission.admin</tt>.
1311
+ #
1312
+ # Alternatively, you can pass just the model object itself (if the first
1313
+ # argument isn't a string or symbol +fields_for+ will realize that the
1314
+ # name has been omitted) -
1315
+ #
1316
+ # <%= fields_for @person.permission do |permission_fields| %>
1317
+ # Admin?: <%= permission_fields.check_box :admin %>
1318
+ # <% end %>
1319
+ #
1320
+ # and +fields_for+ will derive the required name of the field from the
1321
+ # _class_ of the model object, e.g. if <tt>@person.permission</tt>, is
1322
+ # of class +Permission+, the field will still be named <tt>permission[admin]</tt>.
1323
+ #
1324
+ # Note: This also works for the methods in FormOptionHelper and
1325
+ # DateHelper that are designed to work with an object as base, like
1326
+ # FormOptionHelper#collection_select and DateHelper#datetime_select.
1327
+ #
1328
+ # === Nested Attributes Examples
1329
+ #
1330
+ # When the object belonging to the current scope has a nested attribute
1331
+ # writer for a certain attribute, fields_for will yield a new scope
1332
+ # for that attribute. This allows you to create forms that set or change
1333
+ # the attributes of a parent object and its associations in one go.
1334
+ #
1335
+ # Nested attribute writers are normal setter methods named after an
1336
+ # association. The most common way of defining these writers is either
1337
+ # with +accepts_nested_attributes_for+ in a model definition or by
1338
+ # defining a method with the proper name. For example: the attribute
1339
+ # writer for the association <tt>:address</tt> is called
1340
+ # <tt>address_attributes=</tt>.
1341
+ #
1342
+ # Whether a one-to-one or one-to-many style form builder will be yielded
1343
+ # depends on whether the normal reader method returns a _single_ object
1344
+ # or an _array_ of objects.
1345
+ #
1346
+ # ==== One-to-one
1347
+ #
1348
+ # Consider a Person class which returns a _single_ Address from the
1349
+ # <tt>address</tt> reader method and responds to the
1350
+ # <tt>address_attributes=</tt> writer method:
1351
+ #
1352
+ # class Person
1353
+ # def address
1354
+ # @address
1355
+ # end
1356
+ #
1357
+ # def address_attributes=(attributes)
1358
+ # # Process the attributes hash
1359
+ # end
1360
+ # end
1361
+ #
1362
+ # This model can now be used with a nested fields_for, like so:
1363
+ #
1364
+ # <%= form_for @person do |person_form| %>
1365
+ # ...
1366
+ # <%= person_form.fields_for :address do |address_fields| %>
1367
+ # Street : <%= address_fields.text_field :street %>
1368
+ # Zip code: <%= address_fields.text_field :zip_code %>
1369
+ # <% end %>
1370
+ # ...
1371
+ # <% end %>
1372
+ #
1373
+ # When address is already an association on a Person you can use
1374
+ # +accepts_nested_attributes_for+ to define the writer method for you:
1375
+ #
1376
+ # class Person < ActiveRecord::Base
1377
+ # has_one :address
1378
+ # accepts_nested_attributes_for :address
1379
+ # end
1380
+ #
1381
+ # If you want to destroy the associated model through the form, you have
1382
+ # to enable it first using the <tt>:allow_destroy</tt> option for
1383
+ # +accepts_nested_attributes_for+:
1384
+ #
1385
+ # class Person < ActiveRecord::Base
1386
+ # has_one :address
1387
+ # accepts_nested_attributes_for :address, allow_destroy: true
1388
+ # end
1389
+ #
1390
+ # Now, when you use a form element with the <tt>_destroy</tt> parameter,
1391
+ # with a value that evaluates to +true+, you will destroy the associated
1392
+ # model (eg. 1, '1', true, or 'true'):
1393
+ #
1394
+ # <%= form_for @person do |person_form| %>
1395
+ # ...
1396
+ # <%= person_form.fields_for :address do |address_fields| %>
1397
+ # ...
1398
+ # Delete: <%= address_fields.check_box :_destroy %>
1399
+ # <% end %>
1400
+ # ...
1401
+ # <% end %>
1402
+ #
1403
+ # ==== One-to-many
1404
+ #
1405
+ # Consider a Person class which returns an _array_ of Project instances
1406
+ # from the <tt>projects</tt> reader method and responds to the
1407
+ # <tt>projects_attributes=</tt> writer method:
1408
+ #
1409
+ # class Person
1410
+ # def projects
1411
+ # [@project1, @project2]
1412
+ # end
1413
+ #
1414
+ # def projects_attributes=(attributes)
1415
+ # # Process the attributes hash
1416
+ # end
1417
+ # end
1418
+ #
1419
+ # Note that the <tt>projects_attributes=</tt> writer method is in fact
1420
+ # required for fields_for to correctly identify <tt>:projects</tt> as a
1421
+ # collection, and the correct indices to be set in the form markup.
1422
+ #
1423
+ # When projects is already an association on Person you can use
1424
+ # +accepts_nested_attributes_for+ to define the writer method for you:
1425
+ #
1426
+ # class Person < ActiveRecord::Base
1427
+ # has_many :projects
1428
+ # accepts_nested_attributes_for :projects
1429
+ # end
1430
+ #
1431
+ # This model can now be used with a nested fields_for. The block given to
1432
+ # the nested fields_for call will be repeated for each instance in the
1433
+ # collection:
1434
+ #
1435
+ # <%= form_for @person do |person_form| %>
1436
+ # ...
1437
+ # <%= person_form.fields_for :projects do |project_fields| %>
1438
+ # <% if project_fields.object.active? %>
1439
+ # Name: <%= project_fields.text_field :name %>
1440
+ # <% end %>
1441
+ # <% end %>
1442
+ # ...
1443
+ # <% end %>
1444
+ #
1445
+ # It's also possible to specify the instance to be used:
1446
+ #
1447
+ # <%= form_for @person do |person_form| %>
1448
+ # ...
1449
+ # <% @person.projects.each do |project| %>
1450
+ # <% if project.active? %>
1451
+ # <%= person_form.fields_for :projects, project do |project_fields| %>
1452
+ # Name: <%= project_fields.text_field :name %>
1453
+ # <% end %>
1454
+ # <% end %>
1455
+ # <% end %>
1456
+ # ...
1457
+ # <% end %>
1458
+ #
1459
+ # Or a collection to be used:
1460
+ #
1461
+ # <%= form_for @person do |person_form| %>
1462
+ # ...
1463
+ # <%= person_form.fields_for :projects, @active_projects do |project_fields| %>
1464
+ # Name: <%= project_fields.text_field :name %>
1465
+ # <% end %>
1466
+ # ...
1467
+ # <% end %>
1468
+ #
1469
+ # If you want to destroy any of the associated models through the
1470
+ # form, you have to enable it first using the <tt>:allow_destroy</tt>
1471
+ # option for +accepts_nested_attributes_for+:
1472
+ #
1473
+ # class Person < ActiveRecord::Base
1474
+ # has_many :projects
1475
+ # accepts_nested_attributes_for :projects, allow_destroy: true
1476
+ # end
1477
+ #
1478
+ # This will allow you to specify which models to destroy in the
1479
+ # attributes hash by adding a form element for the <tt>_destroy</tt>
1480
+ # parameter with a value that evaluates to +true+
1481
+ # (eg. 1, '1', true, or 'true'):
1482
+ #
1483
+ # <%= form_for @person do |person_form| %>
1484
+ # ...
1485
+ # <%= person_form.fields_for :projects do |project_fields| %>
1486
+ # Delete: <%= project_fields.check_box :_destroy %>
1487
+ # <% end %>
1488
+ # ...
1489
+ # <% end %>
1490
+ #
1491
+ # When a collection is used you might want to know the index of each
1492
+ # object into the array. For this purpose, the <tt>index</tt> method
1493
+ # is available in the FormBuilder object.
1494
+ #
1495
+ # <%= form_for @person do |person_form| %>
1496
+ # ...
1497
+ # <%= person_form.fields_for :projects do |project_fields| %>
1498
+ # Project #<%= project_fields.index %>
1499
+ # ...
1500
+ # <% end %>
1501
+ # ...
1502
+ # <% end %>
1503
+ #
1504
+ # Note that fields_for will automatically generate a hidden field
1505
+ # to store the ID of the record. There are circumstances where this
1506
+ # hidden field is not needed and you can pass <tt>include_id: false</tt>
1507
+ # to prevent fields_for from rendering it automatically.
1508
+ def fields_for(record_name, record_object = nil, fields_options = {}, &block)
1509
+ fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
1510
+ fields_options[:builder] ||= options[:builder]
1511
+ fields_options[:namespace] = options[:namespace]
1512
+ fields_options[:parent_builder] = self
1513
+
1514
+ case record_name
1515
+ when String, Symbol
1516
+ if nested_attributes_association?(record_name)
1517
+ return fields_for_with_nested_attributes(record_name, record_object, fields_options, block)
1518
+ end
1519
+ else
1520
+ record_object = record_name.is_a?(Array) ? record_name.last : record_name
1521
+ record_name = model_name_from_record_or_class(record_object).param_key
1522
+ end
1523
+
1524
+ index = if options.has_key?(:index)
1525
+ options[:index]
1526
+ elsif defined?(@auto_index)
1527
+ self.object_name = @object_name.to_s.sub(/\[\]$/,"")
1528
+ @auto_index
1529
+ end
1530
+
1531
+ record_name = index ? "#{object_name}[#{index}][#{record_name}]" : "#{object_name}[#{record_name}]"
1532
+ fields_options[:child_index] = index
1533
+
1534
+ @template.fields_for(record_name, record_object, fields_options, &block)
1535
+ end
1536
+
1537
+ # Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
1538
+ # assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
1539
+ # is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
1540
+ # Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
1541
+ # onto the HTML as an HTML element attribute as in the example shown, except for the <tt>:value</tt> option, which is designed to
1542
+ # target labels for radio_button tags (where the value is used in the ID of the input tag).
1543
+ #
1544
+ # ==== Examples
1545
+ # label(:post, :title)
1546
+ # # => <label for="post_title">Title</label>
1547
+ #
1548
+ # You can localize your labels based on model and attribute names.
1549
+ # For example you can define the following in your locale (e.g. en.yml)
1550
+ #
1551
+ # helpers:
1552
+ # label:
1553
+ # post:
1554
+ # body: "Write your entire text here"
1555
+ #
1556
+ # Which then will result in
1557
+ #
1558
+ # label(:post, :body)
1559
+ # # => <label for="post_body">Write your entire text here</label>
1560
+ #
1561
+ # Localization can also be based purely on the translation of the attribute-name
1562
+ # (if you are using ActiveRecord):
1563
+ #
1564
+ # activerecord:
1565
+ # attributes:
1566
+ # post:
1567
+ # cost: "Total cost"
1568
+ #
1569
+ # label(:post, :cost)
1570
+ # # => <label for="post_cost">Total cost</label>
1571
+ #
1572
+ # label(:post, :title, "A short title")
1573
+ # # => <label for="post_title">A short title</label>
1574
+ #
1575
+ # label(:post, :title, "A short title", class: "title_label")
1576
+ # # => <label for="post_title" class="title_label">A short title</label>
1577
+ #
1578
+ # label(:post, :privacy, "Public Post", value: "public")
1579
+ # # => <label for="post_privacy_public">Public Post</label>
1580
+ #
1581
+ # label(:post, :terms) do
1582
+ # 'Accept <a href="/terms">Terms</a>.'.html_safe
1583
+ # end
1584
+ def label(method, text = nil, options = {}, &block)
1585
+ @template.label(@object_name, method, text, objectify_options(options), &block)
1586
+ end
1587
+
1588
+ # Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object
1589
+ # assigned to the template (identified by +object+). This object must be an instance object (@object) and not a local object.
1590
+ # It's intended that +method+ returns an integer and if that integer is above zero, then the checkbox is checked.
1591
+ # Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
1592
+ # while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
1593
+ #
1594
+ # ==== Gotcha
1595
+ #
1596
+ # The HTML specification says unchecked check boxes are not successful, and
1597
+ # thus web browsers do not send them. Unfortunately this introduces a gotcha:
1598
+ # if an +Invoice+ model has a +paid+ flag, and in the form that edits a paid
1599
+ # invoice the user unchecks its check box, no +paid+ parameter is sent. So,
1600
+ # any mass-assignment idiom like
1601
+ #
1602
+ # @invoice.update(params[:invoice])
1603
+ #
1604
+ # wouldn't update the flag.
1605
+ #
1606
+ # To prevent this the helper generates an auxiliary hidden field before
1607
+ # the very check box. The hidden field has the same name and its
1608
+ # attributes mimic an unchecked check box.
1609
+ #
1610
+ # This way, the client either sends only the hidden field (representing
1611
+ # the check box is unchecked), or both fields. Since the HTML specification
1612
+ # says key/value pairs have to be sent in the same order they appear in the
1613
+ # form, and parameters extraction gets the last occurrence of any repeated
1614
+ # key in the query string, that works for ordinary forms.
1615
+ #
1616
+ # Unfortunately that workaround does not work when the check box goes
1617
+ # within an array-like parameter, as in
1618
+ #
1619
+ # <%= fields_for "project[invoice_attributes][]", invoice, index: nil do |form| %>
1620
+ # <%= form.check_box :paid %>
1621
+ # ...
1622
+ # <% end %>
1623
+ #
1624
+ # because parameter name repetition is precisely what Rails seeks to distinguish
1625
+ # the elements of the array. For each item with a checked check box you
1626
+ # get an extra ghost item with only that attribute, assigned to "0".
1627
+ #
1628
+ # In that case it is preferable to either use +check_box_tag+ or to use
1629
+ # hashes instead of arrays.
1630
+ #
1631
+ # # Let's say that @post.validated? is 1:
1632
+ # check_box("post", "validated")
1633
+ # # => <input name="post[validated]" type="hidden" value="0" />
1634
+ # # <input checked="checked" type="checkbox" id="post_validated" name="post[validated]" value="1" />
1635
+ #
1636
+ # # Let's say that @puppy.gooddog is "no":
1637
+ # check_box("puppy", "gooddog", {}, "yes", "no")
1638
+ # # => <input name="puppy[gooddog]" type="hidden" value="no" />
1639
+ # # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
1640
+ #
1641
+ # check_box("eula", "accepted", { class: 'eula_check' }, "yes", "no")
1642
+ # # => <input name="eula[accepted]" type="hidden" value="no" />
1643
+ # # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
1644
+ def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
1645
+ @template.check_box(@object_name, method, objectify_options(options), checked_value, unchecked_value)
1646
+ end
1647
+
1648
+ # Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
1649
+ # assigned to the template (identified by +object+). If the current value of +method+ is +tag_value+ the
1650
+ # radio button will be checked.
1651
+ #
1652
+ # To force the radio button to be checked pass <tt>checked: true</tt> in the
1653
+ # +options+ hash. You may pass HTML options there as well.
1654
+ #
1655
+ # # Let's say that @post.category returns "rails":
1656
+ # radio_button("post", "category", "rails")
1657
+ # radio_button("post", "category", "java")
1658
+ # # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
1659
+ # # <input type="radio" id="post_category_java" name="post[category]" value="java" />
1660
+ #
1661
+ # radio_button("user", "receive_newsletter", "yes")
1662
+ # radio_button("user", "receive_newsletter", "no")
1663
+ # # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
1664
+ # # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
1665
+ def radio_button(method, tag_value, options = {})
1666
+ @template.radio_button(@object_name, method, tag_value, objectify_options(options))
1667
+ end
1668
+
1669
+ # Returns a hidden input tag tailored for accessing a specified attribute (identified by +method+) on an object
1670
+ # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
1671
+ # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
1672
+ # shown.
1673
+ #
1674
+ # ==== Examples
1675
+ # hidden_field(:signup, :pass_confirm)
1676
+ # # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" />
1677
+ #
1678
+ # hidden_field(:post, :tag_list)
1679
+ # # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="#{@post.tag_list}" />
1680
+ #
1681
+ # hidden_field(:user, :token)
1682
+ # # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
1683
+ #
1684
+ def hidden_field(method, options = {})
1685
+ @emitted_hidden_id = true if method == :id
1686
+ @template.hidden_field(@object_name, method, objectify_options(options))
1687
+ end
1688
+
1689
+ # Returns a file upload input tag tailored for accessing a specified attribute (identified by +method+) on an object
1690
+ # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
1691
+ # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
1692
+ # shown.
1693
+ #
1694
+ # Using this method inside a +form_for+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
1695
+ #
1696
+ # ==== Options
1697
+ # * Creates standard HTML attributes for the tag.
1698
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
1699
+ # * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files.
1700
+ # * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations.
1701
+ #
1702
+ # ==== Examples
1703
+ # file_field(:user, :avatar)
1704
+ # # => <input type="file" id="user_avatar" name="user[avatar]" />
1705
+ #
1706
+ # file_field(:post, :image, :multiple => true)
1707
+ # # => <input type="file" id="post_image" name="post[image]" multiple="true" />
1708
+ #
1709
+ # file_field(:post, :attached, accept: 'text/html')
1710
+ # # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
1711
+ #
1712
+ # file_field(:post, :image, accept: 'image/png,image/gif,image/jpeg')
1713
+ # # => <input type="file" id="post_image" name="post[image]" accept="image/png,image/gif,image/jpeg" />
1714
+ #
1715
+ # file_field(:attachment, :file, class: 'file_input')
1716
+ # # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
1717
+ def file_field(method, options = {})
1718
+ self.multipart = true
1719
+ @template.file_field(@object_name, method, objectify_options(options))
1720
+ end
1721
+
1722
+ # Add the submit button for the given form. When no value is given, it checks
1723
+ # if the object is a new resource or not to create the proper label:
1724
+ #
1725
+ # <%= form_for @post do |f| %>
1726
+ # <%= f.submit %>
1727
+ # <% end %>
1728
+ #
1729
+ # In the example above, if @post is a new record, it will use "Create Post" as
1730
+ # submit button label, otherwise, it uses "Update Post".
1731
+ #
1732
+ # Those labels can be customized using I18n, under the helpers.submit key and accept
1733
+ # the %{model} as translation interpolation:
1734
+ #
1735
+ # en:
1736
+ # helpers:
1737
+ # submit:
1738
+ # create: "Create a %{model}"
1739
+ # update: "Confirm changes to %{model}"
1740
+ #
1741
+ # It also searches for a key specific for the given object:
1742
+ #
1743
+ # en:
1744
+ # helpers:
1745
+ # submit:
1746
+ # post:
1747
+ # create: "Add %{model}"
1748
+ #
1749
+ def submit(value=nil, options={})
1750
+ value, options = nil, value if value.is_a?(Hash)
1751
+ value ||= submit_default_value
1752
+ @template.submit_tag(value, options)
1753
+ end
1754
+
1755
+ # Add the submit button for the given form. When no value is given, it checks
1756
+ # if the object is a new resource or not to create the proper label:
1757
+ #
1758
+ # <%= form_for @post do |f| %>
1759
+ # <%= f.button %>
1760
+ # <% end %>
1761
+ #
1762
+ # In the example above, if @post is a new record, it will use "Create Post" as
1763
+ # button label, otherwise, it uses "Update Post".
1764
+ #
1765
+ # Those labels can be customized using I18n, under the helpers.submit key
1766
+ # (the same as submit helper) and accept the %{model} as translation interpolation:
1767
+ #
1768
+ # en:
1769
+ # helpers:
1770
+ # submit:
1771
+ # create: "Create a %{model}"
1772
+ # update: "Confirm changes to %{model}"
1773
+ #
1774
+ # It also searches for a key specific for the given object:
1775
+ #
1776
+ # en:
1777
+ # helpers:
1778
+ # submit:
1779
+ # post:
1780
+ # create: "Add %{model}"
1781
+ #
1782
+ # ==== Examples
1783
+ # button("Create a post")
1784
+ # # => <button name='button' type='submit'>Create post</button>
1785
+ #
1786
+ # button do
1787
+ # content_tag(:strong, 'Ask me!')
1788
+ # end
1789
+ # # => <button name='button' type='submit'>
1790
+ # # <strong>Ask me!</strong>
1791
+ # # </button>
1792
+ #
1793
+ def button(value = nil, options = {}, &block)
1794
+ value, options = nil, value if value.is_a?(Hash)
1795
+ value ||= submit_default_value
1796
+ @template.button_tag(value, options, &block)
1797
+ end
1798
+
1799
+ def emitted_hidden_id?
1800
+ @emitted_hidden_id ||= nil
1801
+ end
1802
+
1803
+ private
1804
+ def objectify_options(options)
1805
+ @default_options.merge(options.merge(object: @object))
1806
+ end
1807
+
1808
+ def submit_default_value
1809
+ object = convert_to_model(@object)
1810
+ key = object ? (object.persisted? ? :update : :create) : :submit
1811
+
1812
+ model = if object.class.respond_to?(:model_name)
1813
+ object.class.model_name.human
1814
+ else
1815
+ @object_name.to_s.humanize
1816
+ end
1817
+
1818
+ defaults = []
1819
+ defaults << :"helpers.submit.#{object_name}.#{key}"
1820
+ defaults << :"helpers.submit.#{key}"
1821
+ defaults << "#{key.to_s.humanize} #{model}"
1822
+
1823
+ I18n.t(defaults.shift, model: model, default: defaults)
1824
+ end
1825
+
1826
+ def nested_attributes_association?(association_name)
1827
+ @object.respond_to?("#{association_name}_attributes=")
1828
+ end
1829
+
1830
+ def fields_for_with_nested_attributes(association_name, association, options, block)
1831
+ name = "#{object_name}[#{association_name}_attributes]"
1832
+ association = convert_to_model(association)
1833
+
1834
+ if association.respond_to?(:persisted?)
1835
+ association = [association] if @object.send(association_name).respond_to?(:to_ary)
1836
+ elsif !association.respond_to?(:to_ary)
1837
+ association = @object.send(association_name)
1838
+ end
1839
+
1840
+ if association.respond_to?(:to_ary)
1841
+ explicit_child_index = options[:child_index]
1842
+ output = ActiveSupport::SafeBuffer.new
1843
+ association.each do |child|
1844
+ options[:child_index] = nested_child_index(name) unless explicit_child_index
1845
+ output << fields_for_nested_model("#{name}[#{options[:child_index]}]", child, options, block)
1846
+ end
1847
+ output
1848
+ elsif association
1849
+ fields_for_nested_model(name, association, options, block)
1850
+ end
1851
+ end
1852
+
1853
+ def fields_for_nested_model(name, object, fields_options, block)
1854
+ object = convert_to_model(object)
1855
+ emit_hidden_id = object.persisted? && fields_options.fetch(:include_id) {
1856
+ options.fetch(:include_id, true)
1857
+ }
1858
+
1859
+ @template.fields_for(name, object, fields_options) do |f|
1860
+ output = @template.capture(f, &block)
1861
+ output.concat f.hidden_field(:id) if output && emit_hidden_id && !f.emitted_hidden_id?
1862
+ output
1863
+ end
1864
+ end
1865
+
1866
+ def nested_child_index(name)
1867
+ @nested_child_index[name] ||= -1
1868
+ @nested_child_index[name] += 1
1869
+ end
1870
+ end
1871
+ end
1872
+
1873
+ ActiveSupport.on_load(:action_view) do
1874
+ cattr_accessor(:default_form_builder) { ::ActionView::Helpers::FormBuilder }
1875
+ end
1876
+ end