actionpack 4.0.1 → 4.2.11.1

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

Potentially problematic release.


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

Files changed (241) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +402 -1173
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +7 -7
  5. data/lib/abstract_controller/base.rb +39 -7
  6. data/lib/abstract_controller/callbacks.rb +32 -53
  7. data/lib/abstract_controller/collector.rb +11 -1
  8. data/lib/abstract_controller/helpers.rb +26 -16
  9. data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
  10. data/lib/abstract_controller/rendering.rb +57 -127
  11. data/lib/abstract_controller/url_for.rb +1 -1
  12. data/lib/abstract_controller.rb +1 -2
  13. data/lib/action_controller/base.rb +19 -10
  14. data/lib/action_controller/caching/fragments.rb +7 -1
  15. data/lib/action_controller/caching.rb +2 -12
  16. data/lib/action_controller/log_subscriber.rb +29 -20
  17. data/lib/action_controller/metal/conditional_get.rb +37 -12
  18. data/lib/action_controller/metal/data_streaming.rb +1 -1
  19. data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
  20. data/lib/action_controller/metal/exceptions.rb +1 -1
  21. data/lib/action_controller/metal/flash.rb +17 -0
  22. data/lib/action_controller/metal/force_ssl.rb +2 -2
  23. data/lib/action_controller/metal/head.rb +8 -6
  24. data/lib/action_controller/metal/helpers.rb +6 -2
  25. data/lib/action_controller/metal/http_authentication.rb +45 -23
  26. data/lib/action_controller/metal/instrumentation.rb +9 -6
  27. data/lib/action_controller/metal/live.rb +173 -20
  28. data/lib/action_controller/metal/mime_responds.rb +127 -232
  29. data/lib/action_controller/metal/params_wrapper.rb +16 -9
  30. data/lib/action_controller/metal/rack_delegation.rb +1 -1
  31. data/lib/action_controller/metal/redirecting.rb +34 -26
  32. data/lib/action_controller/metal/renderers.rb +39 -12
  33. data/lib/action_controller/metal/rendering.rb +41 -14
  34. data/lib/action_controller/metal/request_forgery_protection.rb +147 -19
  35. data/lib/action_controller/metal/streaming.rb +19 -21
  36. data/lib/action_controller/metal/strong_parameters.rb +166 -22
  37. data/lib/action_controller/metal/testing.rb +0 -1
  38. data/lib/action_controller/metal/url_for.rb +11 -12
  39. data/lib/action_controller/metal.rb +14 -8
  40. data/lib/action_controller/model_naming.rb +1 -1
  41. data/lib/action_controller/railtie.rb +5 -1
  42. data/lib/action_controller/test_case.rb +160 -94
  43. data/lib/action_controller.rb +2 -18
  44. data/lib/action_dispatch/http/cache.rb +5 -4
  45. data/lib/action_dispatch/http/filter_parameters.rb +2 -2
  46. data/lib/action_dispatch/http/filter_redirect.rb +5 -4
  47. data/lib/action_dispatch/http/headers.rb +46 -10
  48. data/lib/action_dispatch/http/mime_negotiation.rb +31 -4
  49. data/lib/action_dispatch/http/mime_type.rb +25 -26
  50. data/lib/action_dispatch/http/mime_types.rb +1 -0
  51. data/lib/action_dispatch/http/parameter_filter.rb +1 -1
  52. data/lib/action_dispatch/http/parameters.rb +25 -41
  53. data/lib/action_dispatch/http/request.rb +49 -32
  54. data/lib/action_dispatch/http/response.rb +127 -25
  55. data/lib/action_dispatch/http/upload.rb +9 -21
  56. data/lib/action_dispatch/http/url.rb +97 -70
  57. data/lib/action_dispatch/journey/formatter.rb +35 -19
  58. data/lib/action_dispatch/journey/gtg/builder.rb +3 -3
  59. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -7
  60. data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -33
  61. data/lib/action_dispatch/journey/nfa/dot.rb +2 -2
  62. data/lib/action_dispatch/journey/nfa/simulator.rb +1 -1
  63. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -5
  64. data/lib/action_dispatch/journey/nodes/node.rb +4 -0
  65. data/lib/action_dispatch/journey/parser.rb +51 -59
  66. data/lib/action_dispatch/journey/parser.y +12 -10
  67. data/lib/action_dispatch/journey/path/pattern.rb +16 -19
  68. data/lib/action_dispatch/journey/route.rb +8 -19
  69. data/lib/action_dispatch/journey/router/strexp.rb +9 -6
  70. data/lib/action_dispatch/journey/router/utils.rb +54 -18
  71. data/lib/action_dispatch/journey/router.rb +53 -75
  72. data/lib/action_dispatch/journey/routes.rb +4 -0
  73. data/lib/action_dispatch/journey/scanner.rb +5 -5
  74. data/lib/action_dispatch/journey/visitors.rb +81 -60
  75. data/lib/action_dispatch/journey/visualizer/fsm.css +0 -4
  76. data/lib/action_dispatch/journey/visualizer/index.html.erb +2 -2
  77. data/lib/action_dispatch/middleware/callbacks.rb +7 -7
  78. data/lib/action_dispatch/middleware/cookies.rb +119 -43
  79. data/lib/action_dispatch/middleware/debug_exceptions.rb +32 -13
  80. data/lib/action_dispatch/middleware/exception_wrapper.rb +60 -20
  81. data/lib/action_dispatch/middleware/flash.rb +37 -24
  82. data/lib/action_dispatch/middleware/params_parser.rb +2 -2
  83. data/lib/action_dispatch/middleware/public_exceptions.rb +12 -3
  84. data/lib/action_dispatch/middleware/reloader.rb +11 -2
  85. data/lib/action_dispatch/middleware/remote_ip.rb +40 -54
  86. data/lib/action_dispatch/middleware/request_id.rb +1 -1
  87. data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
  88. data/lib/action_dispatch/middleware/session/cookie_store.rb +8 -7
  89. data/lib/action_dispatch/middleware/show_exceptions.rb +6 -2
  90. data/lib/action_dispatch/middleware/ssl.rb +10 -7
  91. data/lib/action_dispatch/middleware/static.rb +79 -23
  92. data/lib/action_dispatch/middleware/templates/rescues/{_request_and_response.erb → _request_and_response.html.erb} +0 -0
  93. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  94. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +21 -19
  95. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  96. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  97. data/lib/action_dispatch/middleware/templates/rescues/{diagnostics.erb → diagnostics.html.erb} +1 -1
  98. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  99. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +6 -0
  100. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  101. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  102. data/lib/action_dispatch/middleware/templates/rescues/{routing_error.erb → routing_error.html.erb} +3 -1
  103. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  104. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  105. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  106. data/lib/action_dispatch/middleware/templates/rescues/{unknown_action.erb → unknown_action.html.erb} +1 -1
  107. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  108. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +120 -64
  109. data/lib/action_dispatch/railtie.rb +5 -2
  110. data/lib/action_dispatch/request/session.rb +12 -0
  111. data/lib/action_dispatch/request/utils.rb +35 -0
  112. data/lib/action_dispatch/routing/endpoint.rb +10 -0
  113. data/lib/action_dispatch/routing/inspector.rb +11 -17
  114. data/lib/action_dispatch/routing/mapper.rb +519 -312
  115. data/lib/action_dispatch/routing/polymorphic_routes.rb +204 -79
  116. data/lib/action_dispatch/routing/redirection.rb +51 -26
  117. data/lib/action_dispatch/routing/route_set.rb +331 -206
  118. data/lib/action_dispatch/routing/routes_proxy.rb +5 -4
  119. data/lib/action_dispatch/routing/url_for.rb +19 -5
  120. data/lib/action_dispatch/routing.rb +9 -6
  121. data/lib/action_dispatch/testing/assertions/dom.rb +2 -26
  122. data/lib/action_dispatch/testing/assertions/response.rb +9 -15
  123. data/lib/action_dispatch/testing/assertions/routing.rb +22 -22
  124. data/lib/action_dispatch/testing/assertions/selector.rb +2 -429
  125. data/lib/action_dispatch/testing/assertions/tag.rb +2 -134
  126. data/lib/action_dispatch/testing/assertions.rb +11 -7
  127. data/lib/action_dispatch/testing/integration.rb +31 -29
  128. data/lib/action_dispatch/testing/test_request.rb +1 -1
  129. data/lib/action_dispatch/testing/test_response.rb +1 -5
  130. data/lib/action_dispatch.rb +5 -8
  131. data/lib/action_pack/gem_version.rb +15 -0
  132. data/lib/action_pack/version.rb +4 -7
  133. data/lib/action_pack.rb +1 -1
  134. metadata +77 -159
  135. data/lib/abstract_controller/layouts.rb +0 -423
  136. data/lib/abstract_controller/view_paths.rb +0 -96
  137. data/lib/action_controller/deprecated/integration_test.rb +0 -5
  138. data/lib/action_controller/deprecated.rb +0 -7
  139. data/lib/action_controller/metal/responder.rb +0 -287
  140. data/lib/action_controller/record_identifier.rb +0 -31
  141. data/lib/action_controller/vendor/html-scanner.rb +0 -5
  142. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -24
  143. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -7
  144. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -43
  145. data/lib/action_view/base.rb +0 -201
  146. data/lib/action_view/buffers.rb +0 -49
  147. data/lib/action_view/context.rb +0 -36
  148. data/lib/action_view/dependency_tracker.rb +0 -93
  149. data/lib/action_view/digestor.rb +0 -113
  150. data/lib/action_view/flows.rb +0 -76
  151. data/lib/action_view/helpers/active_model_helper.rb +0 -49
  152. data/lib/action_view/helpers/asset_tag_helper.rb +0 -320
  153. data/lib/action_view/helpers/asset_url_helper.rb +0 -355
  154. data/lib/action_view/helpers/atom_feed_helper.rb +0 -203
  155. data/lib/action_view/helpers/cache_helper.rb +0 -196
  156. data/lib/action_view/helpers/capture_helper.rb +0 -216
  157. data/lib/action_view/helpers/controller_helper.rb +0 -25
  158. data/lib/action_view/helpers/csrf_helper.rb +0 -30
  159. data/lib/action_view/helpers/date_helper.rb +0 -1083
  160. data/lib/action_view/helpers/debug_helper.rb +0 -39
  161. data/lib/action_view/helpers/form_helper.rb +0 -1880
  162. data/lib/action_view/helpers/form_options_helper.rb +0 -838
  163. data/lib/action_view/helpers/form_tag_helper.rb +0 -785
  164. data/lib/action_view/helpers/javascript_helper.rb +0 -117
  165. data/lib/action_view/helpers/number_helper.rb +0 -441
  166. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  167. data/lib/action_view/helpers/record_tag_helper.rb +0 -106
  168. data/lib/action_view/helpers/rendering_helper.rb +0 -90
  169. data/lib/action_view/helpers/sanitize_helper.rb +0 -256
  170. data/lib/action_view/helpers/tag_helper.rb +0 -173
  171. data/lib/action_view/helpers/tags/base.rb +0 -148
  172. data/lib/action_view/helpers/tags/check_box.rb +0 -64
  173. data/lib/action_view/helpers/tags/checkable.rb +0 -16
  174. data/lib/action_view/helpers/tags/collection_check_boxes.rb +0 -44
  175. data/lib/action_view/helpers/tags/collection_helpers.rb +0 -84
  176. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +0 -36
  177. data/lib/action_view/helpers/tags/collection_select.rb +0 -28
  178. data/lib/action_view/helpers/tags/color_field.rb +0 -25
  179. data/lib/action_view/helpers/tags/date_field.rb +0 -13
  180. data/lib/action_view/helpers/tags/date_select.rb +0 -72
  181. data/lib/action_view/helpers/tags/datetime_field.rb +0 -22
  182. data/lib/action_view/helpers/tags/datetime_local_field.rb +0 -19
  183. data/lib/action_view/helpers/tags/datetime_select.rb +0 -8
  184. data/lib/action_view/helpers/tags/email_field.rb +0 -8
  185. data/lib/action_view/helpers/tags/file_field.rb +0 -8
  186. data/lib/action_view/helpers/tags/grouped_collection_select.rb +0 -29
  187. data/lib/action_view/helpers/tags/hidden_field.rb +0 -8
  188. data/lib/action_view/helpers/tags/label.rb +0 -66
  189. data/lib/action_view/helpers/tags/month_field.rb +0 -13
  190. data/lib/action_view/helpers/tags/number_field.rb +0 -18
  191. data/lib/action_view/helpers/tags/password_field.rb +0 -12
  192. data/lib/action_view/helpers/tags/radio_button.rb +0 -31
  193. data/lib/action_view/helpers/tags/range_field.rb +0 -8
  194. data/lib/action_view/helpers/tags/search_field.rb +0 -24
  195. data/lib/action_view/helpers/tags/select.rb +0 -40
  196. data/lib/action_view/helpers/tags/tel_field.rb +0 -8
  197. data/lib/action_view/helpers/tags/text_area.rb +0 -18
  198. data/lib/action_view/helpers/tags/text_field.rb +0 -29
  199. data/lib/action_view/helpers/tags/time_field.rb +0 -13
  200. data/lib/action_view/helpers/tags/time_select.rb +0 -8
  201. data/lib/action_view/helpers/tags/time_zone_select.rb +0 -20
  202. data/lib/action_view/helpers/tags/url_field.rb +0 -8
  203. data/lib/action_view/helpers/tags/week_field.rb +0 -13
  204. data/lib/action_view/helpers/tags.rb +0 -39
  205. data/lib/action_view/helpers/text_helper.rb +0 -443
  206. data/lib/action_view/helpers/translation_helper.rb +0 -107
  207. data/lib/action_view/helpers/url_helper.rb +0 -635
  208. data/lib/action_view/helpers.rb +0 -58
  209. data/lib/action_view/locale/en.yml +0 -56
  210. data/lib/action_view/log_subscriber.rb +0 -30
  211. data/lib/action_view/lookup_context.rb +0 -241
  212. data/lib/action_view/model_naming.rb +0 -12
  213. data/lib/action_view/path_set.rb +0 -77
  214. data/lib/action_view/railtie.rb +0 -43
  215. data/lib/action_view/record_identifier.rb +0 -84
  216. data/lib/action_view/renderer/abstract_renderer.rb +0 -47
  217. data/lib/action_view/renderer/partial_renderer.rb +0 -492
  218. data/lib/action_view/renderer/renderer.rb +0 -50
  219. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -103
  220. data/lib/action_view/renderer/template_renderer.rb +0 -96
  221. data/lib/action_view/routing_url_for.rb +0 -107
  222. data/lib/action_view/tasks/dependencies.rake +0 -17
  223. data/lib/action_view/template/error.rb +0 -138
  224. data/lib/action_view/template/handlers/builder.rb +0 -26
  225. data/lib/action_view/template/handlers/erb.rb +0 -146
  226. data/lib/action_view/template/handlers/raw.rb +0 -11
  227. data/lib/action_view/template/handlers.rb +0 -53
  228. data/lib/action_view/template/resolver.rb +0 -326
  229. data/lib/action_view/template/text.rb +0 -34
  230. data/lib/action_view/template/types.rb +0 -57
  231. data/lib/action_view/template.rb +0 -339
  232. data/lib/action_view/test_case.rb +0 -270
  233. data/lib/action_view/testing/resolvers.rb +0 -50
  234. data/lib/action_view/vendor/html-scanner/html/document.rb +0 -68
  235. data/lib/action_view/vendor/html-scanner/html/node.rb +0 -532
  236. data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +0 -188
  237. data/lib/action_view/vendor/html-scanner/html/selector.rb +0 -830
  238. data/lib/action_view/vendor/html-scanner/html/tokenizer.rb +0 -107
  239. data/lib/action_view/vendor/html-scanner/html/version.rb +0 -11
  240. data/lib/action_view/vendor/html-scanner.rb +0 -20
  241. data/lib/action_view.rb +0 -93
@@ -1,1880 +0,0 @@
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/helpers/tags'
7
- require 'action_view/model_naming'
8
- require 'active_support/core_ext/class/attribute_accessors'
9
- require 'active_support/core_ext/hash/slice'
10
- require 'active_support/core_ext/string/output_safety'
11
- require 'active_support/core_ext/string/inflections'
12
-
13
- module ActionView
14
- # = Action View Form Helpers
15
- module Helpers
16
- # Form helpers are designed to make working with resources much easier
17
- # compared to using vanilla HTML.
18
- #
19
- # Typically, a form designed to create or update a resource reflects the
20
- # identity of the resource in several ways: (i) the url that the form is
21
- # sent to (the form element's +action+ attribute) should result in a request
22
- # being routed to the appropriate controller action (with the appropriate <tt>:id</tt>
23
- # parameter in the case of an existing resource), (ii) input fields should
24
- # be named in such a way that in the controller their values appear in the
25
- # appropriate places within the +params+ hash, and (iii) for an existing record,
26
- # when the form is initially displayed, input fields corresponding to attributes
27
- # of the resource should show the current values of those attributes.
28
- #
29
- # In Rails, this is usually achieved by creating the form using +form_for+ and
30
- # a number of related helper methods. +form_for+ generates an appropriate <tt>form</tt>
31
- # tag and yields a form builder object that knows the model the form is about.
32
- # Input fields are created by calling methods defined on the form builder, which
33
- # means they are able to generate the appropriate names and default values
34
- # corresponding to the model attributes, as well as convenient IDs, etc.
35
- # Conventions in the generated field names allow controllers to receive form data
36
- # nicely structured in +params+ with no effort on your side.
37
- #
38
- # For example, to create a new person you typically set up a new instance of
39
- # +Person+ in the <tt>PeopleController#new</tt> action, <tt>@person</tt>, and
40
- # in the view template pass that object to +form_for+:
41
- #
42
- # <%= form_for @person do |f| %>
43
- # <%= f.label :first_name %>:
44
- # <%= f.text_field :first_name %><br />
45
- #
46
- # <%= f.label :last_name %>:
47
- # <%= f.text_field :last_name %><br />
48
- #
49
- # <%= f.submit %>
50
- # <% end %>
51
- #
52
- # The HTML generated for this would be (modulus formatting):
53
- #
54
- # <form action="/people" class="new_person" id="new_person" method="post">
55
- # <div style="margin:0;padding:0;display:inline">
56
- # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
57
- # </div>
58
- # <label for="person_first_name">First name</label>:
59
- # <input id="person_first_name" name="person[first_name]" type="text" /><br />
60
- #
61
- # <label for="person_last_name">Last name</label>:
62
- # <input id="person_last_name" name="person[last_name]" type="text" /><br />
63
- #
64
- # <input name="commit" type="submit" value="Create Person" />
65
- # </form>
66
- #
67
- # As you see, the HTML reflects knowledge about the resource in several spots,
68
- # like the path the form should be submitted to, or the names of the input fields.
69
- #
70
- # In particular, thanks to the conventions followed in the generated field names, the
71
- # controller gets a nested hash <tt>params[:person]</tt> with the person attributes
72
- # set in the form. That hash is ready to be passed to <tt>Person.create</tt>:
73
- #
74
- # if @person = Person.create(params[:person])
75
- # # success
76
- # else
77
- # # error handling
78
- # end
79
- #
80
- # Interestingly, the exact same view code in the previous example can be used to edit
81
- # a person. If <tt>@person</tt> is an existing record with name "John Smith" and ID 256,
82
- # the code above as is would yield instead:
83
- #
84
- # <form action="/people/256" class="edit_person" id="edit_person_256" method="post">
85
- # <div style="margin:0;padding:0;display:inline">
86
- # <input name="_method" type="hidden" value="patch" />
87
- # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
88
- # </div>
89
- # <label for="person_first_name">First name</label>:
90
- # <input id="person_first_name" name="person[first_name]" type="text" value="John" /><br />
91
- #
92
- # <label for="person_last_name">Last name</label>:
93
- # <input id="person_last_name" name="person[last_name]" type="text" value="Smith" /><br />
94
- #
95
- # <input name="commit" type="submit" value="Update Person" />
96
- # </form>
97
- #
98
- # Note that the endpoint, default values, and submit button label are tailored for <tt>@person</tt>.
99
- # That works that way because the involved helpers know whether the resource is a new record or not,
100
- # and generate HTML accordingly.
101
- #
102
- # The controller would receive the form data again in <tt>params[:person]</tt>, ready to be
103
- # passed to <tt>Person#update</tt>:
104
- #
105
- # if @person.update(params[:person])
106
- # # success
107
- # else
108
- # # error handling
109
- # end
110
- #
111
- # That's how you typically work with resources.
112
- module FormHelper
113
- extend ActiveSupport::Concern
114
-
115
- include FormTagHelper
116
- include UrlHelper
117
- include ModelNaming
118
-
119
- # Creates a form that allows the user to create or update the attributes
120
- # of a specific model object.
121
- #
122
- # The method can be used in several slightly different ways, depending on
123
- # how much you wish to rely on Rails to infer automatically from the model
124
- # how the form should be constructed. For a generic model object, a form
125
- # can be created by passing +form_for+ a string or symbol representing
126
- # the object we are concerned with:
127
- #
128
- # <%= form_for :person do |f| %>
129
- # First name: <%= f.text_field :first_name %><br />
130
- # Last name : <%= f.text_field :last_name %><br />
131
- # Biography : <%= f.text_area :biography %><br />
132
- # Admin? : <%= f.check_box :admin %><br />
133
- # <%= f.submit %>
134
- # <% end %>
135
- #
136
- # The variable +f+ yielded to the block is a FormBuilder object that
137
- # incorporates the knowledge about the model object represented by
138
- # <tt>:person</tt> passed to +form_for+. Methods defined on the FormBuilder
139
- # are used to generate fields bound to this model. Thus, for example,
140
- #
141
- # <%= f.text_field :first_name %>
142
- #
143
- # will get expanded to
144
- #
145
- # <%= text_field :person, :first_name %>
146
- # which results in an html <tt><input></tt> tag whose +name+ attribute is
147
- # <tt>person[first_name]</tt>. This means that when the form is submitted,
148
- # the value entered by the user will be available in the controller as
149
- # <tt>params[:person][:first_name]</tt>.
150
- #
151
- # For fields generated in this way using the FormBuilder,
152
- # if <tt>:person</tt> also happens to be the name of an instance variable
153
- # <tt>@person</tt>, the default value of the field shown when the form is
154
- # initially displayed (e.g. in the situation where you are editing an
155
- # existing record) will be the value of the corresponding attribute of
156
- # <tt>@person</tt>.
157
- #
158
- # The rightmost argument to +form_for+ is an
159
- # optional hash of options -
160
- #
161
- # * <tt>:url</tt> - The URL the form is to be submitted to. This may be
162
- # represented in the same way as values passed to +url_for+ or +link_to+.
163
- # So for example you may use a named route directly. When the model is
164
- # represented by a string or symbol, as in the example above, if the
165
- # <tt>:url</tt> option is not specified, by default the form will be
166
- # sent back to the current url (We will describe below an alternative
167
- # resource-oriented usage of +form_for+ in which the URL does not need
168
- # to be specified explicitly).
169
- # * <tt>:namespace</tt> - A namespace for your form to ensure uniqueness of
170
- # id attributes on form elements. The namespace attribute will be prefixed
171
- # with underscore on the generated HTML id.
172
- # * <tt>:html</tt> - Optional HTML attributes for the form tag.
173
- #
174
- # Also note that +form_for+ doesn't create an exclusive scope. It's still
175
- # possible to use both the stand-alone FormHelper methods and methods
176
- # from FormTagHelper. For example:
177
- #
178
- # <%= form_for :person do |f| %>
179
- # First name: <%= f.text_field :first_name %>
180
- # Last name : <%= f.text_field :last_name %>
181
- # Biography : <%= text_area :person, :biography %>
182
- # Admin? : <%= check_box_tag "person[admin]", "1", @person.company.admin? %>
183
- # <%= f.submit %>
184
- # <% end %>
185
- #
186
- # This also works for the methods in FormOptionHelper and DateHelper that
187
- # are designed to work with an object as base, like
188
- # FormOptionHelper#collection_select and DateHelper#datetime_select.
189
- #
190
- # === #form_for with a model object
191
- #
192
- # In the examples above, the object to be created or edited was
193
- # represented by a symbol passed to +form_for+, and we noted that
194
- # a string can also be used equivalently. It is also possible, however,
195
- # to pass a model object itself to +form_for+. For example, if <tt>@post</tt>
196
- # is an existing record you wish to edit, you can create the form using
197
- #
198
- # <%= form_for @post do |f| %>
199
- # ...
200
- # <% end %>
201
- #
202
- # This behaves in almost the same way as outlined previously, with a
203
- # couple of small exceptions. First, the prefix used to name the input
204
- # elements within the form (hence the key that denotes them in the +params+
205
- # hash) is actually derived from the object's _class_, e.g. <tt>params[:post]</tt>
206
- # if the object's class is +Post+. However, this can be overwritten using
207
- # the <tt>:as</tt> option, e.g. -
208
- #
209
- # <%= form_for(@person, as: :client) do |f| %>
210
- # ...
211
- # <% end %>
212
- #
213
- # would result in <tt>params[:client]</tt>.
214
- #
215
- # Secondly, the field values shown when the form is initially displayed
216
- # are taken from the attributes of the object passed to +form_for+,
217
- # regardless of whether the object is an instance
218
- # variable. So, for example, if we had a _local_ variable +post+
219
- # representing an existing record,
220
- #
221
- # <%= form_for post do |f| %>
222
- # ...
223
- # <% end %>
224
- #
225
- # would produce a form with fields whose initial state reflect the current
226
- # values of the attributes of +post+.
227
- #
228
- # === Resource-oriented style
229
- #
230
- # In the examples just shown, although not indicated explicitly, we still
231
- # need to use the <tt>:url</tt> option in order to specify where the
232
- # form is going to be sent. However, further simplification is possible
233
- # if the record passed to +form_for+ is a _resource_, i.e. it corresponds
234
- # to a set of RESTful routes, e.g. defined using the +resources+ method
235
- # in <tt>config/routes.rb</tt>. In this case Rails will simply infer the
236
- # appropriate URL from the record itself. For example,
237
- #
238
- # <%= form_for @post do |f| %>
239
- # ...
240
- # <% end %>
241
- #
242
- # is then equivalent to something like:
243
- #
244
- # <%= form_for @post, as: :post, url: post_path(@post), method: :patch, html: { class: "edit_post", id: "edit_post_45" } do |f| %>
245
- # ...
246
- # <% end %>
247
- #
248
- # And for a new record
249
- #
250
- # <%= form_for(Post.new) do |f| %>
251
- # ...
252
- # <% end %>
253
- #
254
- # is equivalent to something like:
255
- #
256
- # <%= form_for @post, as: :post, url: posts_path, html: { class: "new_post", id: "new_post" } do |f| %>
257
- # ...
258
- # <% end %>
259
- #
260
- # However you can still overwrite individual conventions, such as:
261
- #
262
- # <%= form_for(@post, url: super_posts_path) do |f| %>
263
- # ...
264
- # <% end %>
265
- #
266
- # You can also set the answer format, like this:
267
- #
268
- # <%= form_for(@post, format: :json) do |f| %>
269
- # ...
270
- # <% end %>
271
- #
272
- # For namespaced routes, like +admin_post_url+:
273
- #
274
- # <%= form_for([:admin, @post]) do |f| %>
275
- # ...
276
- # <% end %>
277
- #
278
- # If your resource has associations defined, for example, you want to add comments
279
- # to the document given that the routes are set correctly:
280
- #
281
- # <%= form_for([@document, @comment]) do |f| %>
282
- # ...
283
- # <% end %>
284
- #
285
- # Where <tt>@document = Document.find(params[:id])</tt> and
286
- # <tt>@comment = Comment.new</tt>.
287
- #
288
- # === Setting the method
289
- #
290
- # You can force the form to use the full array of HTTP verbs by setting
291
- #
292
- # method: (:get|:post|:patch|:put|:delete)
293
- #
294
- # in the options hash. If the verb is not GET or POST, which are natively
295
- # supported by HTML forms, the form will be set to POST and a hidden input
296
- # called _method will carry the intended verb for the server to interpret.
297
- #
298
- # === Unobtrusive JavaScript
299
- #
300
- # Specifying:
301
- #
302
- # remote: true
303
- #
304
- # in the options hash creates a form that will allow the unobtrusive JavaScript drivers to modify its
305
- # behavior. The expected default behavior is an XMLHttpRequest in the background instead of the regular
306
- # POST arrangement, but ultimately the behavior is the choice of the JavaScript driver implementor.
307
- # Even though it's using JavaScript to serialize the form elements, the form submission will work just like
308
- # a regular submission as viewed by the receiving side (all elements available in <tt>params</tt>).
309
- #
310
- # Example:
311
- #
312
- # <%= form_for(@post, remote: true) do |f| %>
313
- # ...
314
- # <% end %>
315
- #
316
- # The HTML generated for this would be:
317
- #
318
- # <form action='http://www.example.com' method='post' data-remote='true'>
319
- # <div style='margin:0;padding:0;display:inline'>
320
- # <input name='_method' type='hidden' value='patch' />
321
- # </div>
322
- # ...
323
- # </form>
324
- #
325
- # === Setting HTML options
326
- #
327
- # You can set data attributes directly by passing in a data hash, but all other HTML options must be wrapped in
328
- # the HTML key. Example:
329
- #
330
- # <%= form_for(@post, data: { behavior: "autosave" }, html: { name: "go" }) do |f| %>
331
- # ...
332
- # <% end %>
333
- #
334
- # The HTML generated for this would be:
335
- #
336
- # <form action='http://www.example.com' method='post' data-behavior='autosave' name='go'>
337
- # <div style='margin:0;padding:0;display:inline'>
338
- # <input name='_method' type='hidden' value='patch' />
339
- # </div>
340
- # ...
341
- # </form>
342
- #
343
- # === Removing hidden model id's
344
- #
345
- # The form_for method automatically includes the model id as a hidden field in the form.
346
- # This is used to maintain the correlation between the form data and its associated model.
347
- # Some ORM systems do not use IDs on nested models so in this case you want to be able
348
- # to disable the hidden id.
349
- #
350
- # In the following example the Post model has many Comments stored within it in a NoSQL database,
351
- # thus there is no primary key for comments.
352
- #
353
- # Example:
354
- #
355
- # <%= form_for(@post) do |f| %>
356
- # <%= f.fields_for(:comments, include_id: false) do |cf| %>
357
- # ...
358
- # <% end %>
359
- # <% end %>
360
- #
361
- # === Customized form builders
362
- #
363
- # You can also build forms using a customized FormBuilder class. Subclass
364
- # FormBuilder and override or define some more helpers, then use your
365
- # custom builder. For example, let's say you made a helper to
366
- # automatically add labels to form inputs.
367
- #
368
- # <%= form_for @person, url: { action: "create" }, builder: LabellingFormBuilder do |f| %>
369
- # <%= f.text_field :first_name %>
370
- # <%= f.text_field :last_name %>
371
- # <%= f.text_area :biography %>
372
- # <%= f.check_box :admin %>
373
- # <%= f.submit %>
374
- # <% end %>
375
- #
376
- # In this case, if you use this:
377
- #
378
- # <%= render f %>
379
- #
380
- # The rendered template is <tt>people/_labelling_form</tt> and the local
381
- # variable referencing the form builder is called
382
- # <tt>labelling_form</tt>.
383
- #
384
- # The custom FormBuilder class is automatically merged with the options
385
- # of a nested fields_for call, unless it's explicitly set.
386
- #
387
- # In many cases you will want to wrap the above in another helper, so you
388
- # could do something like the following:
389
- #
390
- # def labelled_form_for(record_or_name_or_array, *args, &block)
391
- # options = args.extract_options!
392
- # form_for(record_or_name_or_array, *(args << options.merge(builder: LabellingFormBuilder)), &block)
393
- # end
394
- #
395
- # If you don't need to attach a form to a model instance, then check out
396
- # FormTagHelper#form_tag.
397
- #
398
- # === Form to external resources
399
- #
400
- # When you build forms to external resources sometimes you need to set an authenticity token or just render a form
401
- # without it, for example when you submit data to a payment gateway number and types of fields could be limited.
402
- #
403
- # To set an authenticity token you need to pass an <tt>:authenticity_token</tt> parameter
404
- #
405
- # <%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f|
406
- # ...
407
- # <% end %>
408
- #
409
- # If you don't want to an authenticity token field be rendered at all just pass <tt>false</tt>:
410
- #
411
- # <%= form_for @invoice, url: external_url, authenticity_token: false do |f|
412
- # ...
413
- # <% end %>
414
- def form_for(record, options = {}, &block)
415
- raise ArgumentError, "Missing block" unless block_given?
416
- html_options = options[:html] ||= {}
417
-
418
- case record
419
- when String, Symbol
420
- object_name = record
421
- object = nil
422
- else
423
- object = record.is_a?(Array) ? record.last : record
424
- raise ArgumentError, "First argument in form cannot contain nil or be empty" unless object
425
- object_name = options[:as] || model_name_from_record_or_class(object).param_key
426
- apply_form_for_options!(record, object, options)
427
- end
428
-
429
- html_options[:data] = options.delete(:data) if options.has_key?(:data)
430
- html_options[:remote] = options.delete(:remote) if options.has_key?(:remote)
431
- html_options[:method] = options.delete(:method) if options.has_key?(:method)
432
- html_options[:authenticity_token] = options.delete(:authenticity_token)
433
-
434
- builder = instantiate_builder(object_name, object, options)
435
- output = capture(builder, &block)
436
- html_options[:multipart] ||= builder.multipart?
437
-
438
- form_tag(options[:url] || {}, html_options) { output }
439
- end
440
-
441
- def apply_form_for_options!(record, object, options) #:nodoc:
442
- object = convert_to_model(object)
443
-
444
- as = options[:as]
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 ? "#{action}_#{as}" : [options[: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>hidden_field_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, block=nil)
1241
- if block
1242
- ActiveSupport::Deprecation.warn "Giving a block to FormBuilder is deprecated and has no effect anymore."
1243
- end
1244
-
1245
- @nested_child_index = {}
1246
- @object_name, @object, @template, @options = object_name, object, template, options
1247
- @default_options = @options ? @options.slice(:index, :namespace) : {}
1248
- if @object_name.to_s.match(/\[\]$/)
1249
- if object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param)
1250
- @auto_index = object.to_param
1251
- else
1252
- raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
1253
- end
1254
- end
1255
- @multipart = nil
1256
- @index = options[:index] || options[:child_index]
1257
- end
1258
-
1259
- (field_helpers - [:label, :check_box, :radio_button, :fields_for, :hidden_field, :file_field]).each do |selector|
1260
- class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
1261
- def #{selector}(method, options = {}) # def text_field(method, options = {})
1262
- @template.send( # @template.send(
1263
- #{selector.inspect}, # "text_field",
1264
- @object_name, # @object_name,
1265
- method, # method,
1266
- objectify_options(options)) # objectify_options(options))
1267
- end # end
1268
- RUBY_EVAL
1269
- end
1270
-
1271
- # Creates a scope around a specific model object like form_for, but
1272
- # doesn't create the form tags themselves. This makes fields_for suitable
1273
- # for specifying additional model objects in the same form.
1274
- #
1275
- # Although the usage and purpose of +field_for+ is similar to +form_for+'s,
1276
- # its method signature is slightly different. Like +form_for+, it yields
1277
- # a FormBuilder object associated with a particular model object to a block,
1278
- # and within the block allows methods to be called on the builder to
1279
- # generate fields associated with the model object. Fields may reflect
1280
- # a model object in two ways - how they are named (hence how submitted
1281
- # values appear within the +params+ hash in the controller) and what
1282
- # default values are shown when the form the fields appear in is first
1283
- # displayed. In order for both of these features to be specified independently,
1284
- # both an object name (represented by either a symbol or string) and the
1285
- # object itself can be passed to the method separately -
1286
- #
1287
- # <%= form_for @person do |person_form| %>
1288
- # First name: <%= person_form.text_field :first_name %>
1289
- # Last name : <%= person_form.text_field :last_name %>
1290
- #
1291
- # <%= fields_for :permission, @person.permission do |permission_fields| %>
1292
- # Admin? : <%= permission_fields.check_box :admin %>
1293
- # <% end %>
1294
- #
1295
- # <%= person_form.submit %>
1296
- # <% end %>
1297
- #
1298
- # In this case, the checkbox field will be represented by an HTML +input+
1299
- # tag with the +name+ attribute <tt>permission[admin]</tt>, and the submitted
1300
- # value will appear in the controller as <tt>params[:permission][:admin]</tt>.
1301
- # If <tt>@person.permission</tt> is an existing record with an attribute
1302
- # +admin+, the initial state of the checkbox when first displayed will
1303
- # reflect the value of <tt>@person.permission.admin</tt>.
1304
- #
1305
- # Often this can be simplified by passing just the name of the model
1306
- # object to +fields_for+ -
1307
- #
1308
- # <%= fields_for :permission do |permission_fields| %>
1309
- # Admin?: <%= permission_fields.check_box :admin %>
1310
- # <% end %>
1311
- #
1312
- # ...in which case, if <tt>:permission</tt> also happens to be the name of an
1313
- # instance variable <tt>@permission</tt>, the initial state of the input
1314
- # field will reflect the value of that variable's attribute <tt>@permission.admin</tt>.
1315
- #
1316
- # Alternatively, you can pass just the model object itself (if the first
1317
- # argument isn't a string or symbol +fields_for+ will realize that the
1318
- # name has been omitted) -
1319
- #
1320
- # <%= fields_for @person.permission do |permission_fields| %>
1321
- # Admin?: <%= permission_fields.check_box :admin %>
1322
- # <% end %>
1323
- #
1324
- # and +fields_for+ will derive the required name of the field from the
1325
- # _class_ of the model object, e.g. if <tt>@person.permission</tt>, is
1326
- # of class +Permission+, the field will still be named <tt>permission[admin]</tt>.
1327
- #
1328
- # Note: This also works for the methods in FormOptionHelper and
1329
- # DateHelper that are designed to work with an object as base, like
1330
- # FormOptionHelper#collection_select and DateHelper#datetime_select.
1331
- #
1332
- # === Nested Attributes Examples
1333
- #
1334
- # When the object belonging to the current scope has a nested attribute
1335
- # writer for a certain attribute, fields_for will yield a new scope
1336
- # for that attribute. This allows you to create forms that set or change
1337
- # the attributes of a parent object and its associations in one go.
1338
- #
1339
- # Nested attribute writers are normal setter methods named after an
1340
- # association. The most common way of defining these writers is either
1341
- # with +accepts_nested_attributes_for+ in a model definition or by
1342
- # defining a method with the proper name. For example: the attribute
1343
- # writer for the association <tt>:address</tt> is called
1344
- # <tt>address_attributes=</tt>.
1345
- #
1346
- # Whether a one-to-one or one-to-many style form builder will be yielded
1347
- # depends on whether the normal reader method returns a _single_ object
1348
- # or an _array_ of objects.
1349
- #
1350
- # ==== One-to-one
1351
- #
1352
- # Consider a Person class which returns a _single_ Address from the
1353
- # <tt>address</tt> reader method and responds to the
1354
- # <tt>address_attributes=</tt> writer method:
1355
- #
1356
- # class Person
1357
- # def address
1358
- # @address
1359
- # end
1360
- #
1361
- # def address_attributes=(attributes)
1362
- # # Process the attributes hash
1363
- # end
1364
- # end
1365
- #
1366
- # This model can now be used with a nested fields_for, like so:
1367
- #
1368
- # <%= form_for @person do |person_form| %>
1369
- # ...
1370
- # <%= person_form.fields_for :address do |address_fields| %>
1371
- # Street : <%= address_fields.text_field :street %>
1372
- # Zip code: <%= address_fields.text_field :zip_code %>
1373
- # <% end %>
1374
- # ...
1375
- # <% end %>
1376
- #
1377
- # When address is already an association on a Person you can use
1378
- # +accepts_nested_attributes_for+ to define the writer method for you:
1379
- #
1380
- # class Person < ActiveRecord::Base
1381
- # has_one :address
1382
- # accepts_nested_attributes_for :address
1383
- # end
1384
- #
1385
- # If you want to destroy the associated model through the form, you have
1386
- # to enable it first using the <tt>:allow_destroy</tt> option for
1387
- # +accepts_nested_attributes_for+:
1388
- #
1389
- # class Person < ActiveRecord::Base
1390
- # has_one :address
1391
- # accepts_nested_attributes_for :address, allow_destroy: true
1392
- # end
1393
- #
1394
- # Now, when you use a form element with the <tt>_destroy</tt> parameter,
1395
- # with a value that evaluates to +true+, you will destroy the associated
1396
- # model (eg. 1, '1', true, or 'true'):
1397
- #
1398
- # <%= form_for @person do |person_form| %>
1399
- # ...
1400
- # <%= person_form.fields_for :address do |address_fields| %>
1401
- # ...
1402
- # Delete: <%= address_fields.check_box :_destroy %>
1403
- # <% end %>
1404
- # ...
1405
- # <% end %>
1406
- #
1407
- # ==== One-to-many
1408
- #
1409
- # Consider a Person class which returns an _array_ of Project instances
1410
- # from the <tt>projects</tt> reader method and responds to the
1411
- # <tt>projects_attributes=</tt> writer method:
1412
- #
1413
- # class Person
1414
- # def projects
1415
- # [@project1, @project2]
1416
- # end
1417
- #
1418
- # def projects_attributes=(attributes)
1419
- # # Process the attributes hash
1420
- # end
1421
- # end
1422
- #
1423
- # Note that the <tt>projects_attributes=</tt> writer method is in fact
1424
- # required for fields_for to correctly identify <tt>:projects</tt> as a
1425
- # collection, and the correct indices to be set in the form markup.
1426
- #
1427
- # When projects is already an association on Person you can use
1428
- # +accepts_nested_attributes_for+ to define the writer method for you:
1429
- #
1430
- # class Person < ActiveRecord::Base
1431
- # has_many :projects
1432
- # accepts_nested_attributes_for :projects
1433
- # end
1434
- #
1435
- # This model can now be used with a nested fields_for. The block given to
1436
- # the nested fields_for call will be repeated for each instance in the
1437
- # collection:
1438
- #
1439
- # <%= form_for @person do |person_form| %>
1440
- # ...
1441
- # <%= person_form.fields_for :projects do |project_fields| %>
1442
- # <% if project_fields.object.active? %>
1443
- # Name: <%= project_fields.text_field :name %>
1444
- # <% end %>
1445
- # <% end %>
1446
- # ...
1447
- # <% end %>
1448
- #
1449
- # It's also possible to specify the instance to be used:
1450
- #
1451
- # <%= form_for @person do |person_form| %>
1452
- # ...
1453
- # <% @person.projects.each do |project| %>
1454
- # <% if project.active? %>
1455
- # <%= person_form.fields_for :projects, project do |project_fields| %>
1456
- # Name: <%= project_fields.text_field :name %>
1457
- # <% end %>
1458
- # <% end %>
1459
- # <% end %>
1460
- # ...
1461
- # <% end %>
1462
- #
1463
- # Or a collection to be used:
1464
- #
1465
- # <%= form_for @person do |person_form| %>
1466
- # ...
1467
- # <%= person_form.fields_for :projects, @active_projects do |project_fields| %>
1468
- # Name: <%= project_fields.text_field :name %>
1469
- # <% end %>
1470
- # ...
1471
- # <% end %>
1472
- #
1473
- # If you want to destroy any of the associated models through the
1474
- # form, you have to enable it first using the <tt>:allow_destroy</tt>
1475
- # option for +accepts_nested_attributes_for+:
1476
- #
1477
- # class Person < ActiveRecord::Base
1478
- # has_many :projects
1479
- # accepts_nested_attributes_for :projects, allow_destroy: true
1480
- # end
1481
- #
1482
- # This will allow you to specify which models to destroy in the
1483
- # attributes hash by adding a form element for the <tt>_destroy</tt>
1484
- # parameter with a value that evaluates to +true+
1485
- # (eg. 1, '1', true, or 'true'):
1486
- #
1487
- # <%= form_for @person do |person_form| %>
1488
- # ...
1489
- # <%= person_form.fields_for :projects do |project_fields| %>
1490
- # Delete: <%= project_fields.check_box :_destroy %>
1491
- # <% end %>
1492
- # ...
1493
- # <% end %>
1494
- #
1495
- # When a collection is used you might want to know the index of each
1496
- # object into the array. For this purpose, the <tt>index</tt> method
1497
- # is available in the FormBuilder object.
1498
- #
1499
- # <%= form_for @person do |person_form| %>
1500
- # ...
1501
- # <%= person_form.fields_for :projects do |project_fields| %>
1502
- # Project #<%= project_fields.index %>
1503
- # ...
1504
- # <% end %>
1505
- # ...
1506
- # <% end %>
1507
- #
1508
- # Note that fields_for will automatically generate a hidden field
1509
- # to store the ID of the record. There are circumstances where this
1510
- # hidden field is not needed and you can pass <tt>hidden_field_id: false</tt>
1511
- # to prevent fields_for from rendering it automatically.
1512
- def fields_for(record_name, record_object = nil, fields_options = {}, &block)
1513
- fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
1514
- fields_options[:builder] ||= options[:builder]
1515
- fields_options[:namespace] = options[:namespace]
1516
- fields_options[:parent_builder] = self
1517
-
1518
- case record_name
1519
- when String, Symbol
1520
- if nested_attributes_association?(record_name)
1521
- return fields_for_with_nested_attributes(record_name, record_object, fields_options, block)
1522
- end
1523
- else
1524
- record_object = record_name.is_a?(Array) ? record_name.last : record_name
1525
- record_name = model_name_from_record_or_class(record_object).param_key
1526
- end
1527
-
1528
- index = if options.has_key?(:index)
1529
- options[:index]
1530
- elsif defined?(@auto_index)
1531
- self.object_name = @object_name.to_s.sub(/\[\]$/,"")
1532
- @auto_index
1533
- end
1534
-
1535
- record_name = index ? "#{object_name}[#{index}][#{record_name}]" : "#{object_name}[#{record_name}]"
1536
- fields_options[:child_index] = index
1537
-
1538
- @template.fields_for(record_name, record_object, fields_options, &block)
1539
- end
1540
-
1541
- # Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
1542
- # assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
1543
- # is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
1544
- # Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
1545
- # onto the HTML as an HTML element attribute as in the example shown, except for the <tt>:value</tt> option, which is designed to
1546
- # target labels for radio_button tags (where the value is used in the ID of the input tag).
1547
- #
1548
- # ==== Examples
1549
- # label(:post, :title)
1550
- # # => <label for="post_title">Title</label>
1551
- #
1552
- # You can localize your labels based on model and attribute names.
1553
- # For example you can define the following in your locale (e.g. en.yml)
1554
- #
1555
- # helpers:
1556
- # label:
1557
- # post:
1558
- # body: "Write your entire text here"
1559
- #
1560
- # Which then will result in
1561
- #
1562
- # label(:post, :body)
1563
- # # => <label for="post_body">Write your entire text here</label>
1564
- #
1565
- # Localization can also be based purely on the translation of the attribute-name
1566
- # (if you are using ActiveRecord):
1567
- #
1568
- # activerecord:
1569
- # attributes:
1570
- # post:
1571
- # cost: "Total cost"
1572
- #
1573
- # label(:post, :cost)
1574
- # # => <label for="post_cost">Total cost</label>
1575
- #
1576
- # label(:post, :title, "A short title")
1577
- # # => <label for="post_title">A short title</label>
1578
- #
1579
- # label(:post, :title, "A short title", class: "title_label")
1580
- # # => <label for="post_title" class="title_label">A short title</label>
1581
- #
1582
- # label(:post, :privacy, "Public Post", value: "public")
1583
- # # => <label for="post_privacy_public">Public Post</label>
1584
- #
1585
- # label(:post, :terms) do
1586
- # 'Accept <a href="/terms">Terms</a>.'.html_safe
1587
- # end
1588
- def label(method, text = nil, options = {}, &block)
1589
- @template.label(@object_name, method, text, objectify_options(options), &block)
1590
- end
1591
-
1592
- # Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object
1593
- # assigned to the template (identified by +object+). This object must be an instance object (@object) and not a local object.
1594
- # It's intended that +method+ returns an integer and if that integer is above zero, then the checkbox is checked.
1595
- # Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
1596
- # while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
1597
- #
1598
- # ==== Gotcha
1599
- #
1600
- # The HTML specification says unchecked check boxes are not successful, and
1601
- # thus web browsers do not send them. Unfortunately this introduces a gotcha:
1602
- # if an +Invoice+ model has a +paid+ flag, and in the form that edits a paid
1603
- # invoice the user unchecks its check box, no +paid+ parameter is sent. So,
1604
- # any mass-assignment idiom like
1605
- #
1606
- # @invoice.update(params[:invoice])
1607
- #
1608
- # wouldn't update the flag.
1609
- #
1610
- # To prevent this the helper generates an auxiliary hidden field before
1611
- # the very check box. The hidden field has the same name and its
1612
- # attributes mimic an unchecked check box.
1613
- #
1614
- # This way, the client either sends only the hidden field (representing
1615
- # the check box is unchecked), or both fields. Since the HTML specification
1616
- # says key/value pairs have to be sent in the same order they appear in the
1617
- # form, and parameters extraction gets the last occurrence of any repeated
1618
- # key in the query string, that works for ordinary forms.
1619
- #
1620
- # Unfortunately that workaround does not work when the check box goes
1621
- # within an array-like parameter, as in
1622
- #
1623
- # <%= fields_for "project[invoice_attributes][]", invoice, index: nil do |form| %>
1624
- # <%= form.check_box :paid %>
1625
- # ...
1626
- # <% end %>
1627
- #
1628
- # because parameter name repetition is precisely what Rails seeks to distinguish
1629
- # the elements of the array. For each item with a checked check box you
1630
- # get an extra ghost item with only that attribute, assigned to "0".
1631
- #
1632
- # In that case it is preferable to either use +check_box_tag+ or to use
1633
- # hashes instead of arrays.
1634
- #
1635
- # # Let's say that @post.validated? is 1:
1636
- # check_box("post", "validated")
1637
- # # => <input name="post[validated]" type="hidden" value="0" />
1638
- # # <input checked="checked" type="checkbox" id="post_validated" name="post[validated]" value="1" />
1639
- #
1640
- # # Let's say that @puppy.gooddog is "no":
1641
- # check_box("puppy", "gooddog", {}, "yes", "no")
1642
- # # => <input name="puppy[gooddog]" type="hidden" value="no" />
1643
- # # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
1644
- #
1645
- # check_box("eula", "accepted", { class: 'eula_check' }, "yes", "no")
1646
- # # => <input name="eula[accepted]" type="hidden" value="no" />
1647
- # # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
1648
- def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
1649
- @template.check_box(@object_name, method, objectify_options(options), checked_value, unchecked_value)
1650
- end
1651
-
1652
- # Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
1653
- # assigned to the template (identified by +object+). If the current value of +method+ is +tag_value+ the
1654
- # radio button will be checked.
1655
- #
1656
- # To force the radio button to be checked pass <tt>checked: true</tt> in the
1657
- # +options+ hash. You may pass HTML options there as well.
1658
- #
1659
- # # Let's say that @post.category returns "rails":
1660
- # radio_button("post", "category", "rails")
1661
- # radio_button("post", "category", "java")
1662
- # # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
1663
- # # <input type="radio" id="post_category_java" name="post[category]" value="java" />
1664
- #
1665
- # radio_button("user", "receive_newsletter", "yes")
1666
- # radio_button("user", "receive_newsletter", "no")
1667
- # # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
1668
- # # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
1669
- def radio_button(method, tag_value, options = {})
1670
- @template.radio_button(@object_name, method, tag_value, objectify_options(options))
1671
- end
1672
-
1673
- # Returns a hidden input tag tailored for accessing a specified attribute (identified by +method+) on an object
1674
- # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
1675
- # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
1676
- # shown.
1677
- #
1678
- # ==== Examples
1679
- # hidden_field(:signup, :pass_confirm)
1680
- # # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" />
1681
- #
1682
- # hidden_field(:post, :tag_list)
1683
- # # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="#{@post.tag_list}" />
1684
- #
1685
- # hidden_field(:user, :token)
1686
- # # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
1687
- #
1688
- def hidden_field(method, options = {})
1689
- @emitted_hidden_id = true if method == :id
1690
- @template.hidden_field(@object_name, method, objectify_options(options))
1691
- end
1692
-
1693
- # Returns a file upload input tag tailored for accessing a specified attribute (identified by +method+) on an object
1694
- # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
1695
- # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
1696
- # shown.
1697
- #
1698
- # Using this method inside a +form_for+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
1699
- #
1700
- # ==== Options
1701
- # * Creates standard HTML attributes for the tag.
1702
- # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
1703
- # * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files.
1704
- # * <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.
1705
- #
1706
- # ==== Examples
1707
- # file_field(:user, :avatar)
1708
- # # => <input type="file" id="user_avatar" name="user[avatar]" />
1709
- #
1710
- # file_field(:post, :image, :multiple => true)
1711
- # # => <input type="file" id="post_image" name="post[image]" multiple="true" />
1712
- #
1713
- # file_field(:post, :attached, accept: 'text/html')
1714
- # # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
1715
- #
1716
- # file_field(:post, :image, accept: 'image/png,image/gif,image/jpeg')
1717
- # # => <input type="file" id="post_image" name="post[image]" accept="image/png,image/gif,image/jpeg" />
1718
- #
1719
- # file_field(:attachment, :file, class: 'file_input')
1720
- # # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
1721
- def file_field(method, options = {})
1722
- self.multipart = true
1723
- @template.file_field(@object_name, method, objectify_options(options))
1724
- end
1725
-
1726
- # Add the submit button for the given form. When no value is given, it checks
1727
- # if the object is a new resource or not to create the proper label:
1728
- #
1729
- # <%= form_for @post do |f| %>
1730
- # <%= f.submit %>
1731
- # <% end %>
1732
- #
1733
- # In the example above, if @post is a new record, it will use "Create Post" as
1734
- # submit button label, otherwise, it uses "Update Post".
1735
- #
1736
- # Those labels can be customized using I18n, under the helpers.submit key and accept
1737
- # the %{model} as translation interpolation:
1738
- #
1739
- # en:
1740
- # helpers:
1741
- # submit:
1742
- # create: "Create a %{model}"
1743
- # update: "Confirm changes to %{model}"
1744
- #
1745
- # It also searches for a key specific for the given object:
1746
- #
1747
- # en:
1748
- # helpers:
1749
- # submit:
1750
- # post:
1751
- # create: "Add %{model}"
1752
- #
1753
- def submit(value=nil, options={})
1754
- value, options = nil, value if value.is_a?(Hash)
1755
- value ||= submit_default_value
1756
- @template.submit_tag(value, options)
1757
- end
1758
-
1759
- # Add the submit button for the given form. When no value is given, it checks
1760
- # if the object is a new resource or not to create the proper label:
1761
- #
1762
- # <%= form_for @post do |f| %>
1763
- # <%= f.button %>
1764
- # <% end %>
1765
- #
1766
- # In the example above, if @post is a new record, it will use "Create Post" as
1767
- # button label, otherwise, it uses "Update Post".
1768
- #
1769
- # Those labels can be customized using I18n, under the helpers.submit key
1770
- # (the same as submit helper) and accept the %{model} as translation interpolation:
1771
- #
1772
- # en:
1773
- # helpers:
1774
- # submit:
1775
- # create: "Create a %{model}"
1776
- # update: "Confirm changes to %{model}"
1777
- #
1778
- # It also searches for a key specific for the given object:
1779
- #
1780
- # en:
1781
- # helpers:
1782
- # submit:
1783
- # post:
1784
- # create: "Add %{model}"
1785
- #
1786
- # ==== Examples
1787
- # button("Create a post")
1788
- # # => <button name='button' type='submit'>Create post</button>
1789
- #
1790
- # button do
1791
- # content_tag(:strong, 'Ask me!')
1792
- # end
1793
- # # => <button name='button' type='submit'>
1794
- # # <strong>Ask me!</strong>
1795
- # # </button>
1796
- #
1797
- def button(value = nil, options = {}, &block)
1798
- value, options = nil, value if value.is_a?(Hash)
1799
- value ||= submit_default_value
1800
- @template.button_tag(value, options, &block)
1801
- end
1802
-
1803
- def emitted_hidden_id?
1804
- @emitted_hidden_id ||= nil
1805
- end
1806
-
1807
- private
1808
- def objectify_options(options)
1809
- @default_options.merge(options.merge(object: @object))
1810
- end
1811
-
1812
- def submit_default_value
1813
- object = convert_to_model(@object)
1814
- key = object ? (object.persisted? ? :update : :create) : :submit
1815
-
1816
- model = if object.class.respond_to?(:model_name)
1817
- object.class.model_name.human
1818
- else
1819
- @object_name.to_s.humanize
1820
- end
1821
-
1822
- defaults = []
1823
- defaults << :"helpers.submit.#{object_name}.#{key}"
1824
- defaults << :"helpers.submit.#{key}"
1825
- defaults << "#{key.to_s.humanize} #{model}"
1826
-
1827
- I18n.t(defaults.shift, model: model, default: defaults)
1828
- end
1829
-
1830
- def nested_attributes_association?(association_name)
1831
- @object.respond_to?("#{association_name}_attributes=")
1832
- end
1833
-
1834
- def fields_for_with_nested_attributes(association_name, association, options, block)
1835
- name = "#{object_name}[#{association_name}_attributes]"
1836
- association = convert_to_model(association)
1837
-
1838
- if association.respond_to?(:persisted?)
1839
- association = [association] if @object.send(association_name).respond_to?(:to_ary)
1840
- elsif !association.respond_to?(:to_ary)
1841
- association = @object.send(association_name)
1842
- end
1843
-
1844
- if association.respond_to?(:to_ary)
1845
- explicit_child_index = options[:child_index]
1846
- output = ActiveSupport::SafeBuffer.new
1847
- association.each do |child|
1848
- options[:child_index] = nested_child_index(name) unless explicit_child_index
1849
- output << fields_for_nested_model("#{name}[#{options[:child_index]}]", child, options, block)
1850
- end
1851
- output
1852
- elsif association
1853
- fields_for_nested_model(name, association, options, block)
1854
- end
1855
- end
1856
-
1857
- def fields_for_nested_model(name, object, fields_options, block)
1858
- object = convert_to_model(object)
1859
- emit_hidden_id = object.persisted? && fields_options.fetch(:include_id) {
1860
- options.fetch(:include_id, true)
1861
- }
1862
-
1863
- @template.fields_for(name, object, fields_options) do |f|
1864
- output = @template.capture(f, &block)
1865
- output.concat f.hidden_field(:id) if output && emit_hidden_id && !f.emitted_hidden_id?
1866
- output
1867
- end
1868
- end
1869
-
1870
- def nested_child_index(name)
1871
- @nested_child_index[name] ||= -1
1872
- @nested_child_index[name] += 1
1873
- end
1874
- end
1875
- end
1876
-
1877
- ActiveSupport.on_load(:action_view) do
1878
- cattr_accessor(:default_form_builder) { ::ActionView::Helpers::FormBuilder }
1879
- end
1880
- end