actionpack 3.2.22.5 → 4.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +641 -418
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -288
- data/lib/abstract_controller.rb +1 -8
- data/lib/abstract_controller/asset_paths.rb +2 -2
- data/lib/abstract_controller/base.rb +39 -37
- data/lib/abstract_controller/callbacks.rb +101 -82
- data/lib/abstract_controller/collector.rb +7 -3
- data/lib/abstract_controller/helpers.rb +23 -11
- data/lib/abstract_controller/layouts.rb +68 -73
- data/lib/abstract_controller/logger.rb +1 -2
- data/lib/abstract_controller/rendering.rb +22 -13
- data/lib/abstract_controller/translation.rb +16 -1
- data/lib/abstract_controller/url_for.rb +6 -6
- data/lib/abstract_controller/view_paths.rb +1 -1
- data/lib/action_controller.rb +15 -6
- data/lib/action_controller/base.rb +46 -22
- data/lib/action_controller/caching.rb +46 -33
- data/lib/action_controller/caching/fragments.rb +23 -53
- data/lib/action_controller/deprecated.rb +5 -1
- data/lib/action_controller/deprecated/integration_test.rb +3 -0
- data/lib/action_controller/log_subscriber.rb +11 -8
- data/lib/action_controller/metal.rb +16 -30
- data/lib/action_controller/metal/conditional_get.rb +76 -32
- data/lib/action_controller/metal/data_streaming.rb +20 -26
- data/lib/action_controller/metal/exceptions.rb +19 -6
- data/lib/action_controller/metal/flash.rb +24 -9
- data/lib/action_controller/metal/force_ssl.rb +32 -9
- data/lib/action_controller/metal/head.rb +25 -4
- data/lib/action_controller/metal/helpers.rb +6 -9
- data/lib/action_controller/metal/hide_actions.rb +1 -2
- data/lib/action_controller/metal/http_authentication.rb +105 -87
- data/lib/action_controller/metal/implicit_render.rb +1 -1
- data/lib/action_controller/metal/instrumentation.rb +2 -1
- data/lib/action_controller/metal/live.rb +141 -0
- data/lib/action_controller/metal/mime_responds.rb +161 -47
- data/lib/action_controller/metal/params_wrapper.rb +112 -74
- data/lib/action_controller/metal/rack_delegation.rb +9 -3
- data/lib/action_controller/metal/redirecting.rb +15 -20
- data/lib/action_controller/metal/renderers.rb +11 -9
- data/lib/action_controller/metal/rendering.rb +8 -0
- data/lib/action_controller/metal/request_forgery_protection.rb +112 -19
- data/lib/action_controller/metal/responder.rb +20 -19
- data/lib/action_controller/metal/streaming.rb +12 -18
- data/lib/action_controller/metal/strong_parameters.rb +516 -0
- data/lib/action_controller/metal/testing.rb +13 -18
- data/lib/action_controller/metal/url_for.rb +27 -25
- data/lib/action_controller/model_naming.rb +12 -0
- data/lib/action_controller/railtie.rb +33 -17
- data/lib/action_controller/railties/helpers.rb +22 -0
- data/lib/action_controller/record_identifier.rb +18 -72
- data/lib/action_controller/test_case.rb +215 -123
- data/lib/action_controller/vendor/html-scanner.rb +4 -19
- data/lib/action_dispatch.rb +27 -19
- data/lib/action_dispatch/http/cache.rb +63 -11
- data/lib/action_dispatch/http/filter_parameters.rb +18 -8
- data/lib/action_dispatch/http/filter_redirect.rb +37 -0
- data/lib/action_dispatch/http/headers.rb +27 -19
- data/lib/action_dispatch/http/mime_negotiation.rb +25 -2
- data/lib/action_dispatch/http/mime_type.rb +145 -113
- data/lib/action_dispatch/http/mime_types.rb +1 -1
- data/lib/action_dispatch/http/parameter_filter.rb +44 -46
- data/lib/action_dispatch/http/parameters.rb +12 -5
- data/lib/action_dispatch/http/rack_cache.rb +2 -3
- data/lib/action_dispatch/http/request.rb +49 -18
- data/lib/action_dispatch/http/response.rb +129 -35
- data/lib/action_dispatch/http/upload.rb +60 -17
- data/lib/action_dispatch/http/url.rb +53 -31
- data/lib/action_dispatch/journey.rb +5 -0
- data/lib/action_dispatch/journey/backwards.rb +5 -0
- data/lib/action_dispatch/journey/formatter.rb +146 -0
- data/lib/action_dispatch/journey/gtg/builder.rb +162 -0
- data/lib/action_dispatch/journey/gtg/simulator.rb +44 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +156 -0
- data/lib/action_dispatch/journey/nfa/builder.rb +76 -0
- data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
- data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
- data/lib/action_dispatch/journey/nfa/transition_table.rb +163 -0
- data/lib/action_dispatch/journey/nodes/node.rb +124 -0
- data/lib/action_dispatch/journey/parser.rb +206 -0
- data/lib/action_dispatch/journey/parser.y +47 -0
- data/lib/action_dispatch/journey/parser_extras.rb +23 -0
- data/lib/action_dispatch/journey/path/pattern.rb +196 -0
- data/lib/action_dispatch/journey/route.rb +116 -0
- data/lib/action_dispatch/journey/router.rb +164 -0
- data/lib/action_dispatch/journey/router/strexp.rb +24 -0
- data/lib/action_dispatch/journey/router/utils.rb +54 -0
- data/lib/action_dispatch/journey/routes.rb +75 -0
- data/lib/action_dispatch/journey/scanner.rb +61 -0
- data/lib/action_dispatch/journey/visitors.rb +189 -0
- data/lib/action_dispatch/journey/visualizer/fsm.css +34 -0
- data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
- data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
- data/lib/action_dispatch/middleware/callbacks.rb +9 -4
- data/lib/action_dispatch/middleware/cookies.rb +168 -57
- data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -17
- data/lib/action_dispatch/middleware/exception_wrapper.rb +27 -3
- data/lib/action_dispatch/middleware/flash.rb +58 -58
- data/lib/action_dispatch/middleware/params_parser.rb +14 -29
- data/lib/action_dispatch/middleware/public_exceptions.rb +31 -14
- data/lib/action_dispatch/middleware/reloader.rb +6 -6
- data/lib/action_dispatch/middleware/remote_ip.rb +145 -39
- data/lib/action_dispatch/middleware/request_id.rb +2 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +22 -20
- data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
- data/lib/action_dispatch/middleware/session/cookie_store.rb +81 -7
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +12 -45
- data/lib/action_dispatch/middleware/ssl.rb +70 -0
- data/lib/action_dispatch/middleware/stack.rb +6 -1
- data/lib/action_dispatch/middleware/static.rb +5 -24
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +14 -11
- data/lib/action_dispatch/middleware/templates/rescues/_source.erb +25 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +15 -9
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +121 -5
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +7 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +30 -15
- data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +39 -13
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +6 -2
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +144 -0
- data/lib/action_dispatch/railtie.rb +16 -6
- data/lib/action_dispatch/request/session.rb +181 -0
- data/lib/action_dispatch/routing.rb +41 -40
- data/lib/action_dispatch/routing/inspector.rb +240 -0
- data/lib/action_dispatch/routing/mapper.rb +501 -273
- data/lib/action_dispatch/routing/polymorphic_routes.rb +16 -20
- data/lib/action_dispatch/routing/redirection.rb +46 -29
- data/lib/action_dispatch/routing/route_set.rb +203 -164
- data/lib/action_dispatch/routing/routes_proxy.rb +2 -0
- data/lib/action_dispatch/routing/url_for.rb +48 -33
- data/lib/action_dispatch/testing/assertions/dom.rb +3 -13
- data/lib/action_dispatch/testing/assertions/response.rb +32 -40
- data/lib/action_dispatch/testing/assertions/routing.rb +40 -39
- data/lib/action_dispatch/testing/assertions/selector.rb +15 -20
- data/lib/action_dispatch/testing/assertions/tag.rb +20 -23
- data/lib/action_dispatch/testing/integration.rb +41 -22
- data/lib/action_dispatch/testing/test_process.rb +9 -6
- data/lib/action_dispatch/testing/test_request.rb +7 -3
- data/lib/action_pack.rb +1 -1
- data/lib/action_pack/version.rb +4 -4
- data/lib/action_view.rb +17 -8
- data/lib/action_view/base.rb +15 -34
- data/lib/action_view/buffers.rb +1 -1
- data/lib/action_view/context.rb +4 -4
- data/lib/action_view/dependency_tracker.rb +91 -0
- data/lib/action_view/digestor.rb +85 -0
- data/lib/action_view/flows.rb +1 -4
- data/lib/action_view/helpers.rb +2 -4
- data/lib/action_view/helpers/active_model_helper.rb +3 -4
- data/lib/action_view/helpers/asset_tag_helper.rb +211 -353
- data/lib/action_view/helpers/asset_url_helper.rb +354 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +13 -10
- data/lib/action_view/helpers/cache_helper.rb +150 -18
- data/lib/action_view/helpers/capture_helper.rb +42 -29
- data/lib/action_view/helpers/csrf_helper.rb +0 -2
- data/lib/action_view/helpers/date_helper.rb +268 -247
- data/lib/action_view/helpers/debug_helper.rb +10 -11
- data/lib/action_view/helpers/form_helper.rb +904 -547
- data/lib/action_view/helpers/form_options_helper.rb +341 -166
- data/lib/action_view/helpers/form_tag_helper.rb +188 -88
- data/lib/action_view/helpers/javascript_helper.rb +23 -16
- data/lib/action_view/helpers/number_helper.rb +148 -354
- data/lib/action_view/helpers/output_safety_helper.rb +3 -3
- data/lib/action_view/helpers/record_tag_helper.rb +17 -22
- data/lib/action_view/helpers/rendering_helper.rb +2 -4
- data/lib/action_view/helpers/sanitize_helper.rb +3 -6
- data/lib/action_view/helpers/tag_helper.rb +43 -37
- data/lib/action_view/helpers/tags.rb +39 -0
- data/lib/action_view/helpers/tags/base.rb +148 -0
- data/lib/action_view/helpers/tags/check_box.rb +64 -0
- data/lib/action_view/helpers/tags/checkable.rb +16 -0
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +43 -0
- data/lib/action_view/helpers/tags/collection_helpers.rb +83 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +36 -0
- data/lib/action_view/helpers/tags/collection_select.rb +28 -0
- data/lib/action_view/helpers/tags/color_field.rb +25 -0
- data/lib/action_view/helpers/tags/date_field.rb +13 -0
- data/lib/action_view/helpers/tags/date_select.rb +72 -0
- data/lib/action_view/helpers/tags/datetime_field.rb +22 -0
- data/lib/action_view/helpers/tags/datetime_local_field.rb +19 -0
- data/lib/action_view/helpers/tags/datetime_select.rb +8 -0
- data/lib/action_view/helpers/tags/email_field.rb +8 -0
- data/lib/action_view/helpers/tags/file_field.rb +8 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +29 -0
- data/lib/action_view/helpers/tags/hidden_field.rb +8 -0
- data/lib/action_view/helpers/tags/label.rb +65 -0
- data/lib/action_view/helpers/tags/month_field.rb +13 -0
- data/lib/action_view/helpers/tags/number_field.rb +18 -0
- data/lib/action_view/helpers/tags/password_field.rb +12 -0
- data/lib/action_view/helpers/tags/radio_button.rb +31 -0
- data/lib/action_view/helpers/tags/range_field.rb +8 -0
- data/lib/action_view/helpers/tags/search_field.rb +24 -0
- data/lib/action_view/helpers/tags/select.rb +41 -0
- data/lib/action_view/helpers/tags/tel_field.rb +8 -0
- data/lib/action_view/helpers/tags/text_area.rb +18 -0
- data/lib/action_view/helpers/tags/text_field.rb +29 -0
- data/lib/action_view/helpers/tags/time_field.rb +13 -0
- data/lib/action_view/helpers/tags/time_select.rb +8 -0
- data/lib/action_view/helpers/tags/time_zone_select.rb +20 -0
- data/lib/action_view/helpers/tags/url_field.rb +8 -0
- data/lib/action_view/helpers/tags/week_field.rb +13 -0
- data/lib/action_view/helpers/text_helper.rb +126 -113
- data/lib/action_view/helpers/translation_helper.rb +32 -16
- data/lib/action_view/helpers/url_helper.rb +200 -271
- data/lib/action_view/locale/en.yml +1 -105
- data/lib/action_view/log_subscriber.rb +6 -4
- data/lib/action_view/lookup_context.rb +15 -39
- data/lib/action_view/model_naming.rb +12 -0
- data/lib/action_view/path_set.rb +9 -39
- data/lib/action_view/railtie.rb +6 -22
- data/lib/action_view/record_identifier.rb +84 -0
- data/lib/action_view/renderer/abstract_renderer.rb +10 -19
- data/lib/action_view/renderer/partial_renderer.rb +144 -81
- data/lib/action_view/renderer/renderer.rb +2 -19
- data/lib/action_view/renderer/streaming_template_renderer.rb +2 -5
- data/lib/action_view/renderer/template_renderer.rb +14 -13
- data/lib/action_view/routing_url_for.rb +107 -0
- data/lib/action_view/template.rb +22 -21
- data/lib/action_view/template/error.rb +22 -12
- data/lib/action_view/template/handlers.rb +12 -9
- data/lib/action_view/template/handlers/builder.rb +1 -1
- data/lib/action_view/template/handlers/erb.rb +11 -16
- data/lib/action_view/template/handlers/raw.rb +11 -0
- data/lib/action_view/template/resolver.rb +111 -83
- data/lib/action_view/template/text.rb +12 -8
- data/lib/action_view/template/types.rb +57 -0
- data/lib/action_view/test_case.rb +66 -43
- data/lib/action_view/testing/resolvers.rb +3 -2
- data/lib/action_view/vendor/html-scanner.rb +20 -0
- data/lib/{action_controller → action_view}/vendor/html-scanner/html/document.rb +0 -0
- data/lib/{action_controller → action_view}/vendor/html-scanner/html/node.rb +12 -12
- data/lib/{action_controller → action_view}/vendor/html-scanner/html/sanitizer.rb +18 -7
- data/lib/{action_controller → action_view}/vendor/html-scanner/html/selector.rb +1 -1
- data/lib/{action_controller → action_view}/vendor/html-scanner/html/tokenizer.rb +1 -1
- data/lib/{action_controller → action_view}/vendor/html-scanner/html/version.rb +0 -0
- metadata +135 -125
- data/lib/action_controller/caching/actions.rb +0 -185
- data/lib/action_controller/caching/pages.rb +0 -187
- data/lib/action_controller/caching/sweeping.rb +0 -97
- data/lib/action_controller/deprecated/performance_test.rb +0 -1
- data/lib/action_controller/metal/compatibility.rb +0 -65
- data/lib/action_controller/metal/session_management.rb +0 -14
- data/lib/action_controller/railties/paths.rb +0 -25
- data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
- data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
- data/lib/action_dispatch/middleware/head.rb +0 -18
- data/lib/action_dispatch/middleware/rescue.rb +0 -26
- data/lib/action_dispatch/testing/performance_test.rb +0 -10
- data/lib/action_view/asset_paths.rb +0 -142
- data/lib/action_view/helpers/asset_paths.rb +0 -7
- data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
- data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
- data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
- data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
- data/lib/sprockets/assets.rake +0 -99
- data/lib/sprockets/bootstrap.rb +0 -37
- data/lib/sprockets/compressors.rb +0 -83
- data/lib/sprockets/helpers.rb +0 -6
- data/lib/sprockets/helpers/isolated_helper.rb +0 -13
- data/lib/sprockets/helpers/rails_helper.rb +0 -182
- data/lib/sprockets/railtie.rb +0 -62
- data/lib/sprockets/static_compiler.rb +0 -56
@@ -1,21 +1,20 @@
|
|
1
1
|
require 'cgi'
|
2
2
|
require 'erb'
|
3
3
|
require 'action_view/helpers/form_helper'
|
4
|
-
require 'active_support/core_ext/object/blank'
|
5
4
|
require 'active_support/core_ext/string/output_safety'
|
5
|
+
require 'active_support/core_ext/array/extract_options'
|
6
|
+
require 'active_support/core_ext/array/wrap'
|
6
7
|
|
7
8
|
module ActionView
|
8
9
|
# = Action View Form Option Helpers
|
9
10
|
module Helpers
|
10
11
|
# Provides a number of methods for turning different kinds of containers into a set of option tags.
|
11
|
-
#
|
12
|
+
#
|
12
13
|
# The <tt>collection_select</tt>, <tt>select</tt> and <tt>time_zone_select</tt> methods take an <tt>options</tt> parameter, a hash:
|
13
14
|
#
|
14
15
|
# * <tt>:include_blank</tt> - set to true or a prompt string if the first option element of the select element is a blank. Useful if there is not a default value required for the select element.
|
15
16
|
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
# select("post", "category", Post::CATEGORIES, {:include_blank => true})
|
17
|
+
# select("post", "category", Post::CATEGORIES, {include_blank: true})
|
19
18
|
#
|
20
19
|
# could become:
|
21
20
|
#
|
@@ -25,11 +24,11 @@ module ActionView
|
|
25
24
|
# <option>poem</option>
|
26
25
|
# </select>
|
27
26
|
#
|
28
|
-
# Another common case is a select tag for
|
27
|
+
# Another common case is a select tag for a <tt>belongs_to</tt>-associated object.
|
29
28
|
#
|
30
29
|
# Example with @post.person_id => 2:
|
31
30
|
#
|
32
|
-
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {:
|
31
|
+
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {include_blank: 'None'})
|
33
32
|
#
|
34
33
|
# could become:
|
35
34
|
#
|
@@ -42,9 +41,7 @@ module ActionView
|
|
42
41
|
#
|
43
42
|
# * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this prepends an option with a generic prompt -- "Please select" -- or the given prompt string.
|
44
43
|
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {:prompt => 'Select Person'})
|
44
|
+
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {prompt: 'Select Person'})
|
48
45
|
#
|
49
46
|
# could become:
|
50
47
|
#
|
@@ -58,9 +55,7 @@ module ActionView
|
|
58
55
|
# Like the other form helpers, +select+ can accept an <tt>:index</tt> option to manually set the ID used in the resulting output. Unlike other helpers, +select+ expects this
|
59
56
|
# option to be in the +html_options+ parameter.
|
60
57
|
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
# select("album[]", "genre", %w[rap rock country], {}, { :index => nil })
|
58
|
+
# select("album[]", "genre", %w[rap rock country], {}, { index: nil })
|
64
59
|
#
|
65
60
|
# becomes:
|
66
61
|
#
|
@@ -72,9 +67,7 @@ module ActionView
|
|
72
67
|
#
|
73
68
|
# * <tt>:disabled</tt> - can be a single value or an array of values that will be disabled options in the final output.
|
74
69
|
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
# select("post", "category", Post::CATEGORIES, {:disabled => 'restricted'})
|
70
|
+
# select("post", "category", Post::CATEGORIES, {disabled: 'restricted'})
|
78
71
|
#
|
79
72
|
# could become:
|
80
73
|
#
|
@@ -87,9 +80,7 @@ module ActionView
|
|
87
80
|
#
|
88
81
|
# When used with the <tt>collection_select</tt> helper, <tt>:disabled</tt> can also be a Proc that identifies those options that should be disabled.
|
89
82
|
#
|
90
|
-
#
|
91
|
-
#
|
92
|
-
# collection_select(:post, :category_id, Category.all, :id, :name, {:disabled => lambda{|category| category.archived? }})
|
83
|
+
# collection_select(:post, :category_id, Category.all, :id, :name, {disabled: lambda{|category| category.archived? }})
|
93
84
|
#
|
94
85
|
# If the categories "2008 stuff" and "Christmas" return true when the method <tt>archived?</tt> is called, this would return:
|
95
86
|
# <select name="post[category_id]">
|
@@ -111,7 +102,7 @@ module ActionView
|
|
111
102
|
# * A nested collection: see grouped_options_for_select
|
112
103
|
#
|
113
104
|
# Example with @post.person_id => 1:
|
114
|
-
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, { :
|
105
|
+
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, { include_blank: true })
|
115
106
|
#
|
116
107
|
# could become:
|
117
108
|
#
|
@@ -128,8 +119,8 @@ module ActionView
|
|
128
119
|
# This allows the user to submit a form page more than once with the expected results of creating multiple records.
|
129
120
|
# In addition, this allows a single partial to be used to generate form inputs for both edit and create forms.
|
130
121
|
#
|
131
|
-
# By default, <tt>post.person_id</tt> is the selected option. Specify <tt
|
132
|
-
# or <tt
|
122
|
+
# By default, <tt>post.person_id</tt> is the selected option. Specify <tt>selected: value</tt> to use a different selection
|
123
|
+
# or <tt>selected: nil</tt> to leave all options unselected. Similarly, you can specify values to be disabled in the option
|
133
124
|
# tags by specifying the <tt>:disabled</tt> option. This can either be a single value or an array of values to be disabled.
|
134
125
|
#
|
135
126
|
# ==== Gotcha
|
@@ -140,7 +131,7 @@ module ActionView
|
|
140
131
|
# the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So,
|
141
132
|
# any mass-assignment idiom like
|
142
133
|
#
|
143
|
-
# @user.
|
134
|
+
# @user.update(params[:user])
|
144
135
|
#
|
145
136
|
# wouldn't update roles.
|
146
137
|
#
|
@@ -153,8 +144,11 @@ module ActionView
|
|
153
144
|
# form, and parameters extraction gets the last occurrence of any repeated
|
154
145
|
# key in the query string, that works for ordinary forms.
|
155
146
|
#
|
147
|
+
# In case if you don't want the helper to generate this hidden field you can specify
|
148
|
+
# <tt>include_hidden: false</tt> option.
|
149
|
+
#
|
156
150
|
def select(object, method, choices, options = {}, html_options = {})
|
157
|
-
|
151
|
+
Tags::Select.new(object, method, self, choices, options, html_options).render
|
158
152
|
end
|
159
153
|
|
160
154
|
# Returns <tt><select></tt> and <tt><option></tt> tags for the collection of existing return values of
|
@@ -164,12 +158,16 @@ module ActionView
|
|
164
158
|
#
|
165
159
|
# The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are methods to be called on each member
|
166
160
|
# of +collection+. The return values are used as the +value+ attribute and contents of each
|
167
|
-
# <tt><option></tt> tag, respectively.
|
161
|
+
# <tt><option></tt> tag, respectively. They can also be any object that responds to +call+, such
|
162
|
+
# as a +proc+, that will be called for each member of the +collection+ to
|
163
|
+
# retrieve the value/text.
|
168
164
|
#
|
169
165
|
# Example object structure for use with this method:
|
166
|
+
#
|
170
167
|
# class Post < ActiveRecord::Base
|
171
168
|
# belongs_to :author
|
172
169
|
# end
|
170
|
+
#
|
173
171
|
# class Author < ActiveRecord::Base
|
174
172
|
# has_many :posts
|
175
173
|
# def name_with_initial
|
@@ -178,7 +176,8 @@ module ActionView
|
|
178
176
|
# end
|
179
177
|
#
|
180
178
|
# Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
|
181
|
-
#
|
179
|
+
#
|
180
|
+
# collection_select(:post, :author_id, Author.all, :id, :name_with_initial, prompt: true)
|
182
181
|
#
|
183
182
|
# If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
|
184
183
|
# <select name="post[author_id]">
|
@@ -188,10 +187,9 @@ module ActionView
|
|
188
187
|
# <option value="3">M. Clark</option>
|
189
188
|
# </select>
|
190
189
|
def collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
|
191
|
-
|
190
|
+
Tags::CollectionSelect.new(object, method, self, collection, value_method, text_method, options, html_options).render
|
192
191
|
end
|
193
192
|
|
194
|
-
|
195
193
|
# Returns <tt><select></tt>, <tt><optgroup></tt> and <tt><option></tt> tags for the collection of existing return values of
|
196
194
|
# +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will
|
197
195
|
# be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt>
|
@@ -211,23 +209,28 @@ module ActionView
|
|
211
209
|
# +collection+, returns a value to be used as the contents of its <tt><option></tt> tag.
|
212
210
|
#
|
213
211
|
# Example object structure for use with this method:
|
212
|
+
#
|
214
213
|
# class Continent < ActiveRecord::Base
|
215
214
|
# has_many :countries
|
216
215
|
# # attribs: id, name
|
217
216
|
# end
|
217
|
+
#
|
218
218
|
# class Country < ActiveRecord::Base
|
219
219
|
# belongs_to :continent
|
220
220
|
# # attribs: id, name, continent_id
|
221
221
|
# end
|
222
|
+
#
|
222
223
|
# class City < ActiveRecord::Base
|
223
224
|
# belongs_to :country
|
224
225
|
# # attribs: id, name, country_id
|
225
226
|
# end
|
226
227
|
#
|
227
228
|
# Sample usage:
|
229
|
+
#
|
228
230
|
# grouped_collection_select(:city, :country_id, @continents, :countries, :name, :id, :name)
|
229
231
|
#
|
230
232
|
# Possible output:
|
233
|
+
#
|
231
234
|
# <select name="city[country_id]">
|
232
235
|
# <optgroup label="Africa">
|
233
236
|
# <option value="1">South Africa</option>
|
@@ -240,7 +243,7 @@ module ActionView
|
|
240
243
|
# </select>
|
241
244
|
#
|
242
245
|
def grouped_collection_select(object, method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
|
243
|
-
|
246
|
+
Tags::GroupedCollectionSelect.new(object, method, self, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options).render
|
244
247
|
end
|
245
248
|
|
246
249
|
# Return select and option tags for the given object and method, using
|
@@ -261,20 +264,19 @@ module ActionView
|
|
261
264
|
# Finally, this method supports a <tt>:default</tt> option, which selects
|
262
265
|
# a default ActiveSupport::TimeZone if the object's time zone is +nil+.
|
263
266
|
#
|
264
|
-
#
|
265
|
-
# time_zone_select( "user", "time_zone", nil, :include_blank => true)
|
267
|
+
# time_zone_select( "user", "time_zone", nil, include_blank: true)
|
266
268
|
#
|
267
|
-
# time_zone_select( "user", "time_zone", nil, :
|
269
|
+
# time_zone_select( "user", "time_zone", nil, default: "Pacific Time (US & Canada)" )
|
268
270
|
#
|
269
|
-
# time_zone_select( "user", 'time_zone', ActiveSupport::TimeZone.us_zones, :
|
271
|
+
# time_zone_select( "user", 'time_zone', ActiveSupport::TimeZone.us_zones, default: "Pacific Time (US & Canada)")
|
270
272
|
#
|
271
273
|
# time_zone_select( "user", 'time_zone', [ ActiveSupport::TimeZone['Alaska'], ActiveSupport::TimeZone['Hawaii'] ])
|
272
274
|
#
|
273
275
|
# time_zone_select( "user", 'time_zone', /Australia/)
|
274
276
|
#
|
275
|
-
# time_zone_select( "user", "time_zone", ActiveSupport::TimeZone.all.sort, :
|
277
|
+
# time_zone_select( "user", "time_zone", ActiveSupport::TimeZone.all.sort, model: ActiveSupport::TimeZone)
|
276
278
|
def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
|
277
|
-
|
279
|
+
Tags::TimeZoneSelect.new(object, method, self, priority_zones, options, html_options).render
|
278
280
|
end
|
279
281
|
|
280
282
|
# Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
|
@@ -283,67 +285,83 @@ module ActionView
|
|
283
285
|
# become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +selected+
|
284
286
|
# may also be an array of values to be selected when using a multiple select.
|
285
287
|
#
|
286
|
-
# Examples (call, result):
|
287
288
|
# options_for_select([["Dollar", "$"], ["Kroner", "DKK"]])
|
288
|
-
#
|
289
|
+
# # => <option value="$">Dollar</option>
|
290
|
+
# # => <option value="DKK">Kroner</option>
|
289
291
|
#
|
290
292
|
# options_for_select([ "VISA", "MasterCard" ], "MasterCard")
|
291
|
-
#
|
293
|
+
# # => <option>VISA</option>
|
294
|
+
# # => <option selected="selected">MasterCard</option>
|
292
295
|
#
|
293
296
|
# options_for_select({ "Basic" => "$20", "Plus" => "$40" }, "$40")
|
294
|
-
#
|
297
|
+
# # => <option value="$20">Basic</option>
|
298
|
+
# # => <option value="$40" selected="selected">Plus</option>
|
295
299
|
#
|
296
300
|
# options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"])
|
297
|
-
#
|
301
|
+
# # => <option selected="selected">VISA</option>
|
302
|
+
# # => <option>MasterCard</option>
|
303
|
+
# # => <option selected="selected">Discover</option>
|
298
304
|
#
|
299
305
|
# You can optionally provide html attributes as the last element of the array.
|
300
306
|
#
|
301
|
-
#
|
302
|
-
#
|
303
|
-
#
|
307
|
+
# options_for_select([ "Denmark", ["USA", {class: 'bold'}], "Sweden" ], ["USA", "Sweden"])
|
308
|
+
# # => <option value="Denmark">Denmark</option>
|
309
|
+
# # => <option value="USA" class="bold" selected="selected">USA</option>
|
310
|
+
# # => <option value="Sweden" selected="selected">Sweden</option>
|
304
311
|
#
|
305
|
-
# options_for_select([["Dollar", "$", {:
|
306
|
-
#
|
312
|
+
# options_for_select([["Dollar", "$", {class: "bold"}], ["Kroner", "DKK", {onclick: "alert('HI');"}]])
|
313
|
+
# # => <option value="$" class="bold">Dollar</option>
|
314
|
+
# # => <option value="DKK" onclick="alert('HI');">Kroner</option>
|
307
315
|
#
|
308
316
|
# If you wish to specify disabled option tags, set +selected+ to be a hash, with <tt>:disabled</tt> being either a value
|
309
317
|
# or array of values to be disabled. In this case, you can use <tt>:selected</tt> to specify selected option tags.
|
310
318
|
#
|
311
|
-
#
|
312
|
-
#
|
313
|
-
#
|
319
|
+
# options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], disabled: "Super Platinum")
|
320
|
+
# # => <option value="Free">Free</option>
|
321
|
+
# # => <option value="Basic">Basic</option>
|
322
|
+
# # => <option value="Advanced">Advanced</option>
|
323
|
+
# # => <option value="Super Platinum" disabled="disabled">Super Platinum</option>
|
314
324
|
#
|
315
|
-
# options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], :
|
316
|
-
#
|
325
|
+
# options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], disabled: ["Advanced", "Super Platinum"])
|
326
|
+
# # => <option value="Free">Free</option>
|
327
|
+
# # => <option value="Basic">Basic</option>
|
328
|
+
# # => <option value="Advanced" disabled="disabled">Advanced</option>
|
329
|
+
# # => <option value="Super Platinum" disabled="disabled">Super Platinum</option>
|
317
330
|
#
|
318
|
-
# options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], :
|
319
|
-
#
|
331
|
+
# options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], selected: "Free", disabled: "Super Platinum")
|
332
|
+
# # => <option value="Free" selected="selected">Free</option>
|
333
|
+
# # => <option value="Basic">Basic</option>
|
334
|
+
# # => <option value="Advanced">Advanced</option>
|
335
|
+
# # => <option value="Super Platinum" disabled="disabled">Super Platinum</option>
|
320
336
|
#
|
321
337
|
# NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
|
322
338
|
def options_for_select(container, selected = nil)
|
323
339
|
return container if String === container
|
324
340
|
|
325
|
-
selected, disabled = extract_selected_and_disabled(selected).map do |
|
326
|
-
|
341
|
+
selected, disabled = extract_selected_and_disabled(selected).map do |r|
|
342
|
+
Array(r).map { |item| item.to_s }
|
327
343
|
end
|
328
344
|
|
329
345
|
container.map do |element|
|
330
346
|
html_attributes = option_html_attributes(element)
|
331
347
|
text, value = option_text_and_value(element).map { |item| item.to_s }
|
332
|
-
selected_attribute = ' selected="selected"' if option_value_selected?(value, selected)
|
333
|
-
disabled_attribute = ' disabled="disabled"' if disabled && option_value_selected?(value, disabled)
|
334
|
-
%(<option value="#{ERB::Util.html_escape(value)}"#{selected_attribute}#{disabled_attribute}#{html_attributes}>#{ERB::Util.html_escape(text)}</option>)
|
335
|
-
end.join("\n").html_safe
|
336
348
|
|
349
|
+
html_attributes[:selected] = 'selected' if option_value_selected?(value, selected)
|
350
|
+
html_attributes[:disabled] = 'disabled' if disabled && option_value_selected?(value, disabled)
|
351
|
+
html_attributes[:value] = value
|
352
|
+
|
353
|
+
content_tag_string(:option, text, html_attributes)
|
354
|
+
end.join("\n").html_safe
|
337
355
|
end
|
338
356
|
|
339
357
|
# Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning
|
340
358
|
# the result of a call to the +value_method+ as the option value and the +text_method+ as the option text.
|
341
|
-
#
|
359
|
+
#
|
342
360
|
# options_from_collection_for_select(@people, 'id', 'name')
|
343
|
-
#
|
344
|
-
# <option value="#{person.id}">#{person.name}</option>
|
361
|
+
# # => <option value="#{person.id}">#{person.name}</option>
|
345
362
|
#
|
346
363
|
# This is more often than not used inside a #select_tag like this example:
|
364
|
+
#
|
347
365
|
# select_tag 'person', options_from_collection_for_select(@people, 'id', 'name')
|
348
366
|
#
|
349
367
|
# If +selected+ is specified as a value or array of values, the element(s) returning a match on +value_method+
|
@@ -362,12 +380,13 @@ module ActionView
|
|
362
380
|
# should produce the desired results.
|
363
381
|
def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
|
364
382
|
options = collection.map do |element|
|
365
|
-
[element
|
383
|
+
[value_for_collection(element, text_method), value_for_collection(element, value_method)]
|
366
384
|
end
|
367
385
|
selected, disabled = extract_selected_and_disabled(selected)
|
368
|
-
select_deselect = {
|
369
|
-
|
370
|
-
|
386
|
+
select_deselect = {
|
387
|
+
:selected => extract_values_from_collection(collection, value_method, selected),
|
388
|
+
:disabled => extract_values_from_collection(collection, value_method, disabled)
|
389
|
+
}
|
371
390
|
|
372
391
|
options_for_select(options, select_deselect)
|
373
392
|
end
|
@@ -391,10 +410,12 @@ module ActionView
|
|
391
410
|
# to be specified.
|
392
411
|
#
|
393
412
|
# Example object structure for use with this method:
|
413
|
+
#
|
394
414
|
# class Continent < ActiveRecord::Base
|
395
415
|
# has_many :countries
|
396
416
|
# # attribs: id, name
|
397
417
|
# end
|
418
|
+
#
|
398
419
|
# class Country < ActiveRecord::Base
|
399
420
|
# belongs_to :continent
|
400
421
|
# # attribs: id, name, continent_id
|
@@ -420,10 +441,10 @@ module ActionView
|
|
420
441
|
# wrap the output in an appropriate <tt><select></tt> tag.
|
421
442
|
def option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, selected_key = nil)
|
422
443
|
collection.map do |group|
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
444
|
+
option_tags = options_from_collection_for_select(
|
445
|
+
group.send(group_method), option_key_method, option_value_method, selected_key)
|
446
|
+
|
447
|
+
content_tag(:optgroup, option_tags, :label => group.send(group_label_method))
|
427
448
|
end.join.html_safe
|
428
449
|
end
|
429
450
|
|
@@ -438,10 +459,12 @@ module ActionView
|
|
438
459
|
# * +selected_key+ - A value equal to the +value+ attribute for one of the <tt><option></tt> tags,
|
439
460
|
# which will have the +selected+ attribute set. Note: It is possible for this value to match multiple options
|
440
461
|
# as you might have the same option in multiple groups. Each will then get <tt>selected="selected"</tt>.
|
441
|
-
#
|
462
|
+
#
|
463
|
+
# Options:
|
464
|
+
# * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this
|
442
465
|
# prepends an option with a generic prompt - "Please select" - or the given prompt string.
|
466
|
+
# * <tt>:divider</tt> - the divider for the options groups.
|
443
467
|
#
|
444
|
-
# Sample usage (Array):
|
445
468
|
# grouped_options = [
|
446
469
|
# ['North America',
|
447
470
|
# [['United States','US'],'Canada']],
|
@@ -450,37 +473,70 @@ module ActionView
|
|
450
473
|
# ]
|
451
474
|
# grouped_options_for_select(grouped_options)
|
452
475
|
#
|
453
|
-
# Sample usage (Hash):
|
454
476
|
# grouped_options = {
|
455
|
-
#
|
456
|
-
#
|
477
|
+
# 'North America' => [['United States','US'], 'Canada'],
|
478
|
+
# 'Europe' => ['Denmark','Germany','France']
|
457
479
|
# }
|
458
480
|
# grouped_options_for_select(grouped_options)
|
459
481
|
#
|
460
482
|
# Possible output:
|
483
|
+
# <optgroup label="North America">
|
484
|
+
# <option value="US">United States</option>
|
485
|
+
# <option value="Canada">Canada</option>
|
486
|
+
# </optgroup>
|
461
487
|
# <optgroup label="Europe">
|
462
488
|
# <option value="Denmark">Denmark</option>
|
463
489
|
# <option value="Germany">Germany</option>
|
464
490
|
# <option value="France">France</option>
|
465
491
|
# </optgroup>
|
466
|
-
#
|
492
|
+
#
|
493
|
+
# grouped_options = [
|
494
|
+
# [['United States','US'], 'Canada'],
|
495
|
+
# ['Denmark','Germany','France']
|
496
|
+
# ]
|
497
|
+
# grouped_options_for_select(grouped_options, nil, divider: '---------')
|
498
|
+
#
|
499
|
+
# Possible output:
|
500
|
+
# <optgroup label="---------">
|
467
501
|
# <option value="US">United States</option>
|
468
502
|
# <option value="Canada">Canada</option>
|
469
503
|
# </optgroup>
|
504
|
+
# <optgroup label="---------">
|
505
|
+
# <option value="Denmark">Denmark</option>
|
506
|
+
# <option value="Germany">Germany</option>
|
507
|
+
# <option value="France">France</option>
|
508
|
+
# </optgroup>
|
470
509
|
#
|
471
510
|
# <b>Note:</b> Only the <tt><optgroup></tt> and <tt><option></tt> tags are returned, so you still have to
|
472
511
|
# wrap the output in an appropriate <tt><select></tt> tag.
|
473
|
-
def grouped_options_for_select(grouped_options, selected_key = nil,
|
474
|
-
|
475
|
-
|
512
|
+
def grouped_options_for_select(grouped_options, selected_key = nil, options = {})
|
513
|
+
if options.is_a?(Hash)
|
514
|
+
prompt = options[:prompt]
|
515
|
+
divider = options[:divider]
|
516
|
+
else
|
517
|
+
prompt = options
|
518
|
+
options = {}
|
519
|
+
message = "Passing the prompt to grouped_options_for_select as an argument is deprecated. " \
|
520
|
+
"Please use an options hash like `{ prompt: #{prompt.inspect} }`."
|
521
|
+
ActiveSupport::Deprecation.warn message
|
522
|
+
end
|
476
523
|
|
477
|
-
|
524
|
+
body = "".html_safe
|
478
525
|
|
479
|
-
|
480
|
-
body
|
526
|
+
if prompt
|
527
|
+
body.safe_concat content_tag(:option, prompt_text(prompt), :value => "")
|
481
528
|
end
|
482
529
|
|
483
|
-
|
530
|
+
grouped_options.each do |container|
|
531
|
+
if divider
|
532
|
+
label = divider
|
533
|
+
else
|
534
|
+
label, container = container
|
535
|
+
end
|
536
|
+
body.safe_concat content_tag(:optgroup, options_for_select(container, selected_key), :label => label)
|
537
|
+
end
|
538
|
+
|
539
|
+
body
|
484
540
|
end
|
485
541
|
|
486
542
|
# Returns a string of option tags for pretty much any time zone in the
|
@@ -502,42 +558,164 @@ module ActionView
|
|
502
558
|
# NOTE: Only the option tags are returned, you have to wrap this call in
|
503
559
|
# a regular HTML select tag.
|
504
560
|
def time_zone_options_for_select(selected = nil, priority_zones = nil, model = ::ActiveSupport::TimeZone)
|
505
|
-
zone_options = ""
|
561
|
+
zone_options = "".html_safe
|
506
562
|
|
507
563
|
zones = model.all
|
508
564
|
convert_zones = lambda { |list| list.map { |z| [ z.to_s, z.name ] } }
|
509
565
|
|
510
566
|
if priority_zones
|
511
567
|
if priority_zones.is_a?(Regexp)
|
512
|
-
priority_zones =
|
568
|
+
priority_zones = zones.grep(priority_zones)
|
513
569
|
end
|
514
|
-
zone_options += options_for_select(convert_zones[priority_zones], selected)
|
515
|
-
zone_options += "<option value=\"\" disabled=\"disabled\">-------------</option>\n"
|
516
570
|
|
517
|
-
|
571
|
+
zone_options.safe_concat options_for_select(convert_zones[priority_zones], selected)
|
572
|
+
zone_options.safe_concat content_tag(:option, '-------------', :value => '', :disabled => 'disabled')
|
573
|
+
zone_options.safe_concat "\n"
|
574
|
+
|
575
|
+
zones = zones - priority_zones
|
518
576
|
end
|
519
577
|
|
520
|
-
zone_options
|
521
|
-
|
578
|
+
zone_options.safe_concat options_for_select(convert_zones[zones], selected)
|
579
|
+
end
|
580
|
+
|
581
|
+
# Returns radio button tags for the collection of existing return values
|
582
|
+
# of +method+ for +object+'s class. The value returned from calling
|
583
|
+
# +method+ on the instance +object+ will be selected. If calling +method+
|
584
|
+
# returns +nil+, no selection is made.
|
585
|
+
#
|
586
|
+
# The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are
|
587
|
+
# methods to be called on each member of +collection+. The return values
|
588
|
+
# are used as the +value+ attribute and contents of each radio button tag,
|
589
|
+
# respectively. They can also be any object that responds to +call+, such
|
590
|
+
# as a +proc+, that will be called for each member of the +collection+ to
|
591
|
+
# retrieve the value/text.
|
592
|
+
#
|
593
|
+
# Example object structure for use with this method:
|
594
|
+
# class Post < ActiveRecord::Base
|
595
|
+
# belongs_to :author
|
596
|
+
# end
|
597
|
+
# class Author < ActiveRecord::Base
|
598
|
+
# has_many :posts
|
599
|
+
# def name_with_initial
|
600
|
+
# "#{first_name.first}. #{last_name}"
|
601
|
+
# end
|
602
|
+
# end
|
603
|
+
#
|
604
|
+
# Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
|
605
|
+
# collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial)
|
606
|
+
#
|
607
|
+
# If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
|
608
|
+
# <input id="post_author_id_1" name="post[author_id]" type="radio" value="1" checked="checked" />
|
609
|
+
# <label for="post_author_id_1">D. Heinemeier Hansson</label>
|
610
|
+
# <input id="post_author_id_2" name="post[author_id]" type="radio" value="2" />
|
611
|
+
# <label for="post_author_id_2">D. Thomas</label>
|
612
|
+
# <input id="post_author_id_3" name="post[author_id]" type="radio" value="3" />
|
613
|
+
# <label for="post_author_id_3">M. Clark</label>
|
614
|
+
#
|
615
|
+
# It is also possible to customize the way the elements will be shown by
|
616
|
+
# giving a block to the method:
|
617
|
+
# collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
|
618
|
+
# b.label { b.radio_button }
|
619
|
+
# end
|
620
|
+
#
|
621
|
+
# The argument passed to the block is a special kind of builder for this
|
622
|
+
# collection, which has the ability to generate the label and radio button
|
623
|
+
# for the current item in the collection, with proper text and value.
|
624
|
+
# Using it, you can change the label and radio button display order or
|
625
|
+
# even use the label as wrapper, as in the example above.
|
626
|
+
#
|
627
|
+
# The builder methods <tt>label</tt> and <tt>radio_button</tt> also accept
|
628
|
+
# extra html options:
|
629
|
+
# collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
|
630
|
+
# b.label(class: "radio_button") { b.radio_button(class: "radio_button") }
|
631
|
+
# end
|
632
|
+
#
|
633
|
+
# There are also three special methods available: <tt>object</tt>, <tt>text</tt> and
|
634
|
+
# <tt>value</tt>, which are the current item being rendered, its text and value methods,
|
635
|
+
# respectively. You can use them like this:
|
636
|
+
# collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
|
637
|
+
# b.label(:"data-value" => b.value) { b.radio_button + b.text }
|
638
|
+
# end
|
639
|
+
def collection_radio_buttons(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
640
|
+
Tags::CollectionRadioButtons.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
|
641
|
+
end
|
642
|
+
|
643
|
+
# Returns check box tags for the collection of existing return values of
|
644
|
+
# +method+ for +object+'s class. The value returned from calling +method+
|
645
|
+
# on the instance +object+ will be selected. If calling +method+ returns
|
646
|
+
# +nil+, no selection is made.
|
647
|
+
#
|
648
|
+
# The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are
|
649
|
+
# methods to be called on each member of +collection+. The return values
|
650
|
+
# are used as the +value+ attribute and contents of each check box tag,
|
651
|
+
# respectively. They can also be any object that responds to +call+, such
|
652
|
+
# as a +proc+, that will be called for each member of the +collection+ to
|
653
|
+
# retrieve the value/text.
|
654
|
+
#
|
655
|
+
# Example object structure for use with this method:
|
656
|
+
# class Post < ActiveRecord::Base
|
657
|
+
# has_and_belongs_to_many :author
|
658
|
+
# end
|
659
|
+
# class Author < ActiveRecord::Base
|
660
|
+
# has_and_belongs_to_many :posts
|
661
|
+
# def name_with_initial
|
662
|
+
# "#{first_name.first}. #{last_name}"
|
663
|
+
# end
|
664
|
+
# end
|
665
|
+
#
|
666
|
+
# Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
|
667
|
+
# collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial)
|
668
|
+
#
|
669
|
+
# If <tt>@post.author_ids</tt> is already <tt>[1]</tt>, this would return:
|
670
|
+
# <input id="post_author_ids_1" name="post[author_ids][]" type="checkbox" value="1" checked="checked" />
|
671
|
+
# <label for="post_author_ids_1">D. Heinemeier Hansson</label>
|
672
|
+
# <input id="post_author_ids_2" name="post[author_ids][]" type="checkbox" value="2" />
|
673
|
+
# <label for="post_author_ids_2">D. Thomas</label>
|
674
|
+
# <input id="post_author_ids_3" name="post[author_ids][]" type="checkbox" value="3" />
|
675
|
+
# <label for="post_author_ids_3">M. Clark</label>
|
676
|
+
# <input name="post[author_ids][]" type="hidden" value="" />
|
677
|
+
#
|
678
|
+
# It is also possible to customize the way the elements will be shown by
|
679
|
+
# giving a block to the method:
|
680
|
+
# collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
|
681
|
+
# b.label { b.check_box }
|
682
|
+
# end
|
683
|
+
#
|
684
|
+
# The argument passed to the block is a special kind of builder for this
|
685
|
+
# collection, which has the ability to generate the label and check box
|
686
|
+
# for the current item in the collection, with proper text and value.
|
687
|
+
# Using it, you can change the label and check box display order or even
|
688
|
+
# use the label as wrapper, as in the example above.
|
689
|
+
#
|
690
|
+
# The builder methods <tt>label</tt> and <tt>check_box</tt> also accept
|
691
|
+
# extra html options:
|
692
|
+
# collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
|
693
|
+
# b.label(class: "check_box") { b.check_box(class: "check_box") }
|
694
|
+
# end
|
695
|
+
#
|
696
|
+
# There are also three special methods available: <tt>object</tt>, <tt>text</tt> and
|
697
|
+
# <tt>value</tt>, which are the current item being rendered, its text and value methods,
|
698
|
+
# respectively. You can use them like this:
|
699
|
+
# collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
|
700
|
+
# b.label(:"data-value" => b.value) { b.check_box + b.text }
|
701
|
+
# end
|
702
|
+
def collection_check_boxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
703
|
+
Tags::CollectionCheckBoxes.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
|
522
704
|
end
|
523
705
|
|
524
706
|
private
|
525
707
|
def option_html_attributes(element)
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
708
|
+
if Array === element
|
709
|
+
element.select { |e| Hash === e }.reduce({}, :merge!)
|
710
|
+
else
|
711
|
+
{}
|
530
712
|
end
|
531
|
-
html_attributes.join
|
532
713
|
end
|
533
714
|
|
534
715
|
def option_text_and_value(option)
|
535
716
|
# Options are [text, value] pairs or strings used for both.
|
536
|
-
|
537
|
-
|
538
|
-
option = option.reject { |e| Hash === e }
|
539
|
-
[option.first, option.last]
|
540
|
-
when !option.is_a?(String) && option.respond_to?(:first) && option.respond_to?(:last)
|
717
|
+
if !option.is_a?(String) && option.respond_to?(:first) && option.respond_to?(:last)
|
718
|
+
option = option.reject { |e| Hash === e } if Array === option
|
541
719
|
[option.first, option.last]
|
542
720
|
else
|
543
721
|
[option, option]
|
@@ -545,20 +723,17 @@ module ActionView
|
|
545
723
|
end
|
546
724
|
|
547
725
|
def option_value_selected?(value, selected)
|
548
|
-
|
549
|
-
selected.include? value
|
550
|
-
else
|
551
|
-
value == selected
|
552
|
-
end
|
726
|
+
Array(selected).include? value
|
553
727
|
end
|
554
728
|
|
555
729
|
def extract_selected_and_disabled(selected)
|
556
730
|
if selected.is_a?(Proc)
|
557
|
-
[
|
731
|
+
[selected, nil]
|
558
732
|
else
|
559
733
|
selected = Array.wrap(selected)
|
560
734
|
options = selected.extract_options!.symbolize_keys
|
561
|
-
|
735
|
+
selected_items = options.fetch(:selected, selected)
|
736
|
+
[selected_items, options[:disabled]]
|
562
737
|
end
|
563
738
|
end
|
564
739
|
|
@@ -571,88 +746,88 @@ module ActionView
|
|
571
746
|
selected
|
572
747
|
end
|
573
748
|
end
|
574
|
-
end
|
575
|
-
|
576
|
-
class InstanceTag #:nodoc:
|
577
|
-
include FormOptionsHelper
|
578
|
-
|
579
|
-
def to_select_tag(choices, options, html_options)
|
580
|
-
selected_value = options.has_key?(:selected) ? options[:selected] : value(object)
|
581
|
-
choices = choices.to_a if choices.is_a?(Range)
|
582
|
-
|
583
|
-
# Grouped choices look like this:
|
584
|
-
#
|
585
|
-
# [nil, []]
|
586
|
-
# { nil => [] }
|
587
|
-
#
|
588
|
-
if !choices.empty? && choices.first.respond_to?(:last) && Array === choices.first.last
|
589
|
-
option_tags = grouped_options_for_select(choices, :selected => selected_value, :disabled => options[:disabled])
|
590
|
-
else
|
591
|
-
option_tags = options_for_select(choices, :selected => selected_value, :disabled => options[:disabled])
|
592
|
-
end
|
593
|
-
|
594
|
-
select_content_tag(option_tags, options, html_options)
|
595
|
-
end
|
596
|
-
|
597
|
-
def to_collection_select_tag(collection, value_method, text_method, options, html_options)
|
598
|
-
selected_value = options.has_key?(:selected) ? options[:selected] : value(object)
|
599
|
-
select_content_tag(
|
600
|
-
options_from_collection_for_select(collection, value_method, text_method, :selected => selected_value, :disabled => options[:disabled]), options, html_options
|
601
|
-
)
|
602
|
-
end
|
603
|
-
|
604
|
-
def to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
|
605
|
-
select_content_tag(
|
606
|
-
option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value(object)), options, html_options
|
607
|
-
)
|
608
|
-
end
|
609
|
-
|
610
|
-
def to_time_zone_select_tag(priority_zones, options, html_options)
|
611
|
-
select_content_tag(
|
612
|
-
time_zone_options_for_select(value(object) || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone), options, html_options
|
613
|
-
)
|
614
|
-
end
|
615
749
|
|
616
|
-
|
617
|
-
|
618
|
-
if options[:include_blank]
|
619
|
-
option_tags = content_tag_string('option', options[:include_blank].kind_of?(String) ? options[:include_blank] : nil, :value => '') + "\n" + option_tags
|
620
|
-
end
|
621
|
-
if value.blank? && options[:prompt]
|
622
|
-
prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('helpers.select.prompt', :default => 'Please select')
|
623
|
-
option_tags = content_tag_string('option', prompt, :value => '') + "\n" + option_tags
|
624
|
-
end
|
625
|
-
option_tags
|
750
|
+
def value_for_collection(item, value)
|
751
|
+
value.respond_to?(:call) ? value.call(item) : item.send(value)
|
626
752
|
end
|
627
753
|
|
628
|
-
def
|
629
|
-
|
630
|
-
add_default_name_and_id(html_options)
|
631
|
-
select = content_tag("select", add_options(option_tags, options, value(object)), html_options)
|
632
|
-
if html_options["multiple"]
|
633
|
-
tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select
|
634
|
-
else
|
635
|
-
select
|
636
|
-
end
|
754
|
+
def prompt_text(prompt)
|
755
|
+
prompt = prompt.kind_of?(String) ? prompt : I18n.translate('helpers.select.prompt', :default => 'Please select')
|
637
756
|
end
|
638
757
|
end
|
639
758
|
|
640
759
|
class FormBuilder
|
760
|
+
# Wraps ActionView::Helpers::FormOptionsHelper#select for form builders:
|
761
|
+
#
|
762
|
+
# <%= form_for @post do |f| %>
|
763
|
+
# <%= f.select :person_id, Person.all.collect {|p| [ p.name, p.id ] }, { include_blank: true }) %>
|
764
|
+
# <%= f.submit %>
|
765
|
+
# <% end %>
|
766
|
+
#
|
767
|
+
# Please refer to the documentation of the base helper for details.
|
641
768
|
def select(method, choices, options = {}, html_options = {})
|
642
769
|
@template.select(@object_name, method, choices, objectify_options(options), @default_options.merge(html_options))
|
643
770
|
end
|
644
771
|
|
772
|
+
# Wraps ActionView::Helpers::FormOptionsHelper#collection_select for form builders:
|
773
|
+
#
|
774
|
+
# <%= form_for @post do |f| %>
|
775
|
+
# <%= f.collection_select :person_id, Author.all, :id, :name_with_initial, prompt: true %>
|
776
|
+
# <%= f.submit %>
|
777
|
+
# <% end %>
|
778
|
+
#
|
779
|
+
# Please refer to the documentation of the base helper for details.
|
645
780
|
def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
|
646
781
|
@template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
|
647
782
|
end
|
648
783
|
|
784
|
+
# Wraps ActionView::Helpers::FormOptionsHelper#grouped_collection_select for form builders:
|
785
|
+
#
|
786
|
+
# <%= form_for @city do |f| %>
|
787
|
+
# <%= f.grouped_collection_select :country_id, :country_id, @continents, :countries, :name, :id, :name %>
|
788
|
+
# <%= f.submit %>
|
789
|
+
# <% end %>
|
790
|
+
#
|
791
|
+
# Please refer to the documentation of the base helper for details.
|
649
792
|
def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
|
650
793
|
@template.grouped_collection_select(@object_name, method, collection, group_method, group_label_method, option_key_method, option_value_method, objectify_options(options), @default_options.merge(html_options))
|
651
794
|
end
|
652
795
|
|
796
|
+
# Wraps ActionView::Helpers::FormOptionsHelper#time_zone_select for form builders:
|
797
|
+
#
|
798
|
+
# <%= form_for @user do |f| %>
|
799
|
+
# <%= f.time_zone_select :time_zone, nil, include_blank: true %>
|
800
|
+
# <%= f.submit %>
|
801
|
+
# <% end %>
|
802
|
+
#
|
803
|
+
# Please refer to the documentation of the base helper for details.
|
653
804
|
def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
|
654
805
|
@template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options))
|
655
806
|
end
|
807
|
+
|
808
|
+
# Wraps ActionView::Helpers::FormOptionsHelper#collection_check_boxes for form builders:
|
809
|
+
#
|
810
|
+
# <%= form_for @post do |f| %>
|
811
|
+
# <%= f.collection_check_boxes :author_ids, Author.all, :id, :name_with_initial %>
|
812
|
+
# <%= f.submit %>
|
813
|
+
# <% end %>
|
814
|
+
#
|
815
|
+
# Please refer to the documentation of the base helper for details.
|
816
|
+
def collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
817
|
+
@template.collection_check_boxes(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options), &block)
|
818
|
+
end
|
819
|
+
|
820
|
+
# Wraps ActionView::Helpers::FormOptionsHelper#collection_radio_buttons for form builders:
|
821
|
+
#
|
822
|
+
# <%= form_for @post do |f| %>
|
823
|
+
# <%= f.collection_radio_buttons :author_id, Author.all, :id, :name_with_initial %>
|
824
|
+
# <%= f.submit %>
|
825
|
+
# <% end %>
|
826
|
+
#
|
827
|
+
# Please refer to the documentation of the base helper for details.
|
828
|
+
def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
829
|
+
@template.collection_radio_buttons(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options), &block)
|
830
|
+
end
|
656
831
|
end
|
657
832
|
end
|
658
833
|
end
|