actionpack 3.1.12 → 3.2.0.rc1
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.
- data/CHANGELOG.md +5503 -108
- data/README.rdoc +3 -3
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +102 -18
- data/lib/abstract_controller/helpers.rb +1 -1
- data/lib/abstract_controller/layouts.rb +116 -50
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
- data/lib/abstract_controller/rendering.rb +1 -6
- data/lib/abstract_controller/view_paths.rb +6 -5
- data/lib/action_controller.rb +0 -15
- data/lib/action_controller/caching.rb +0 -1
- data/lib/action_controller/caching/actions.rb +5 -6
- data/lib/action_controller/caching/fragments.rb +18 -18
- data/lib/action_controller/caching/pages.rb +7 -6
- data/lib/action_controller/caching/sweeping.rb +1 -1
- data/lib/action_controller/log_subscriber.rb +8 -4
- data/lib/action_controller/metal.rb +7 -1
- data/lib/action_controller/metal/conditional_get.rb +49 -4
- data/lib/action_controller/metal/data_streaming.rb +17 -5
- data/lib/action_controller/metal/force_ssl.rb +8 -5
- data/lib/action_controller/metal/helpers.rb +7 -4
- data/lib/action_controller/metal/http_authentication.rb +9 -12
- data/lib/action_controller/metal/instrumentation.rb +9 -4
- data/lib/action_controller/metal/mime_responds.rb +4 -4
- data/lib/action_controller/metal/params_wrapper.rb +12 -8
- data/lib/action_controller/metal/redirecting.rb +7 -6
- data/lib/action_controller/metal/renderers.rb +9 -11
- data/lib/action_controller/metal/request_forgery_protection.rb +2 -1
- data/lib/action_controller/metal/rescue.rb +13 -0
- data/lib/action_controller/metal/responder.rb +11 -23
- data/lib/action_controller/metal/streaming.rb +0 -25
- data/lib/action_controller/railtie.rb +1 -0
- data/lib/action_controller/railties/paths.rb +4 -3
- data/lib/action_controller/record_identifier.rb +4 -4
- data/lib/action_controller/test_case.rb +60 -56
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +6 -6
- data/lib/action_dispatch.rb +5 -1
- data/lib/action_dispatch/http/cache.rb +27 -15
- data/lib/action_dispatch/http/filter_parameters.rb +3 -1
- data/lib/action_dispatch/http/headers.rb +3 -5
- data/lib/action_dispatch/http/mime_negotiation.rb +2 -1
- data/lib/action_dispatch/http/mime_type.rb +7 -3
- data/lib/action_dispatch/http/mime_types.rb +12 -0
- data/lib/action_dispatch/http/parameter_filter.rb +3 -1
- data/lib/action_dispatch/http/parameters.rb +0 -4
- data/lib/action_dispatch/http/request.rb +18 -68
- data/lib/action_dispatch/http/response.rb +11 -32
- data/lib/action_dispatch/http/upload.rb +3 -14
- data/lib/action_dispatch/http/url.rb +1 -1
- data/lib/action_dispatch/middleware/callbacks.rb +1 -2
- data/lib/action_dispatch/middleware/cookies.rb +20 -16
- data/lib/action_dispatch/middleware/debug_exceptions.rb +82 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +78 -0
- data/lib/action_dispatch/middleware/flash.rb +6 -9
- data/lib/action_dispatch/middleware/params_parser.rb +6 -11
- data/lib/action_dispatch/middleware/public_exceptions.rb +30 -0
- data/lib/action_dispatch/middleware/reloader.rb +38 -14
- data/lib/action_dispatch/middleware/remote_ip.rb +66 -36
- data/lib/action_dispatch/middleware/request_id.rb +39 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +4 -16
- data/lib/action_dispatch/middleware/session/cache_store.rb +50 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +58 -142
- data/lib/action_dispatch/middleware/static.rb +2 -10
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +13 -8
- data/lib/action_dispatch/railtie.rb +15 -1
- data/lib/action_dispatch/routing.rb +1 -2
- data/lib/action_dispatch/routing/mapper.rb +108 -107
- data/lib/action_dispatch/routing/redirection.rb +63 -69
- data/lib/action_dispatch/routing/route_set.rb +75 -43
- data/lib/action_dispatch/routing/routes_proxy.rb +0 -4
- data/lib/action_dispatch/routing/url_for.rb +3 -3
- data/lib/action_dispatch/testing/assertions/response.rb +5 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +10 -9
- data/lib/action_dispatch/testing/integration.rb +8 -25
- data/lib/action_dispatch/testing/test_process.rb +3 -2
- data/lib/action_dispatch/testing/test_request.rb +4 -23
- data/lib/action_pack/version.rb +3 -3
- data/lib/action_view.rb +1 -5
- data/lib/action_view/asset_paths.rb +7 -8
- data/lib/action_view/base.rb +7 -5
- data/lib/action_view/helpers/asset_paths.rb +1 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +4 -8
- data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +3 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
- data/lib/action_view/helpers/capture_helper.rb +3 -3
- data/lib/action_view/helpers/controller_helper.rb +1 -1
- data/lib/action_view/helpers/date_helper.rb +26 -18
- data/lib/action_view/helpers/debug_helper.rb +1 -1
- data/lib/action_view/helpers/form_helper.rb +71 -13
- data/lib/action_view/helpers/form_options_helper.rb +65 -34
- data/lib/action_view/helpers/form_tag_helper.rb +24 -18
- data/lib/action_view/helpers/javascript_helper.rb +12 -3
- data/lib/action_view/helpers/number_helper.rb +3 -2
- data/lib/action_view/helpers/record_tag_helper.rb +51 -5
- data/lib/action_view/helpers/rendering_helper.rb +2 -2
- data/lib/action_view/helpers/sanitize_helper.rb +6 -7
- data/lib/action_view/helpers/tag_helper.rb +1 -1
- data/lib/action_view/helpers/text_helper.rb +5 -4
- data/lib/action_view/helpers/url_helper.rb +19 -11
- data/lib/action_view/locale/en.yml +6 -0
- data/lib/action_view/log_subscriber.rb +1 -1
- data/lib/action_view/lookup_context.rb +123 -125
- data/lib/action_view/path_set.rb +60 -13
- data/lib/action_view/renderer/abstract_renderer.rb +16 -11
- data/lib/action_view/renderer/partial_renderer.rb +59 -40
- data/lib/action_view/renderer/template_renderer.rb +29 -17
- data/lib/action_view/template.rb +0 -1
- data/lib/action_view/template/error.rb +6 -5
- data/lib/action_view/template/handlers.rb +0 -6
- data/lib/action_view/template/handlers/builder.rb +10 -1
- data/lib/action_view/template/handlers/erb.rb +2 -2
- data/lib/action_view/template/resolver.rb +20 -31
- data/lib/action_view/test_case.rb +7 -10
- data/lib/sprockets/assets.rake +1 -1
- data/lib/sprockets/bootstrap.rb +3 -31
- data/lib/sprockets/compressors.rb +69 -7
- data/lib/sprockets/helpers/rails_helper.rb +6 -11
- data/lib/sprockets/railtie.rb +1 -0
- data/lib/sprockets/static_compiler.rb +0 -3
- metadata +57 -86
- checksums.yaml +0 -7
- data/lib/action_dispatch/middleware/closed_error.rb +0 -7
- data/lib/action_dispatch/routing/route.rb +0 -67
- data/lib/action_view/template/handler.rb +0 -49
@@ -30,7 +30,7 @@ module ActionView
|
|
30
30
|
begin
|
31
31
|
Marshal::dump(object)
|
32
32
|
"<pre class='debug_dump'>#{h(object.to_yaml).gsub(" ", " ")}</pre>".html_safe
|
33
|
-
rescue Exception
|
33
|
+
rescue Exception # errors from Marshal or YAML
|
34
34
|
# Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
|
35
35
|
"<code class='debug_dump'>#{h(object.inspect)}</code>".html_safe
|
36
36
|
end
|
@@ -158,6 +158,9 @@ module ActionView
|
|
158
158
|
# * <tt>:url</tt> - The URL the form is submitted to. It takes the same
|
159
159
|
# fields you pass to +url_for+ or +link_to+. In particular you may pass
|
160
160
|
# here a named route directly as well. Defaults to the current action.
|
161
|
+
# * <tt>:namespace</tt> - A namespace for your form to ensure uniqueness of
|
162
|
+
# id attributes on form elements. The namespace attribute will be prefixed
|
163
|
+
# with underscore on the generated HTML id.
|
161
164
|
# * <tt>:html</tt> - Optional HTML attributes for the form tag.
|
162
165
|
#
|
163
166
|
# Also note that +form_for+ doesn't create an exclusive scope. It's still
|
@@ -384,8 +387,8 @@ module ActionView
|
|
384
387
|
as = options[:as]
|
385
388
|
action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :put] : [:new, :post]
|
386
389
|
options[:html].reverse_merge!(
|
387
|
-
:class => as ? "#{
|
388
|
-
:id => as ? "#{
|
390
|
+
:class => as ? "#{action}_#{as}" : dom_class(object, action),
|
391
|
+
:id => as ? "#{action}_#{as}" : [options[:namespace], dom_id(object, action)].compact.join("_").presence,
|
389
392
|
:method => method
|
390
393
|
)
|
391
394
|
|
@@ -518,6 +521,18 @@ module ActionView
|
|
518
521
|
# end
|
519
522
|
# end
|
520
523
|
#
|
524
|
+
# Note that the <tt>projects_attributes=</tt> writer method is in fact
|
525
|
+
# required for fields_for to correctly identify <tt>:projects</tt> as a
|
526
|
+
# collection, and the correct indices to be set in the form markup.
|
527
|
+
#
|
528
|
+
# When projects is already an association on Person you can use
|
529
|
+
# +accepts_nested_attributes_for+ to define the writer method for you:
|
530
|
+
#
|
531
|
+
# class Person < ActiveRecord::Base
|
532
|
+
# has_many :projects
|
533
|
+
# accepts_nested_attributes_for :projects
|
534
|
+
# end
|
535
|
+
#
|
521
536
|
# This model can now be used with a nested fields_for. The block given to
|
522
537
|
# the nested fields_for call will be repeated for each instance in the
|
523
538
|
# collection:
|
@@ -848,8 +863,8 @@ module ActionView
|
|
848
863
|
end
|
849
864
|
|
850
865
|
# Returns an input of type "search" for accessing a specified attribute (identified by +method+) on an object
|
851
|
-
# assigned to the template (identified by +
|
852
|
-
# some browsers
|
866
|
+
# assigned to the template (identified by +object_name+). Inputs of type "search" may be styled differently by
|
867
|
+
# some browsers.
|
853
868
|
#
|
854
869
|
# ==== Examples
|
855
870
|
#
|
@@ -868,7 +883,7 @@ module ActionView
|
|
868
883
|
# # => <input autosave="false" id="user_name" incremental="true" name="user[name]" onsearch="true" size="30" type="search" />
|
869
884
|
# search_field(:user, :name, :autosave => true, :onsearch => true)
|
870
885
|
# # => <input autosave="com.example.www" id="user_name" incremental="true" name="user[name]" onsearch="true" results="10" size="30" type="search" />
|
871
|
-
|
886
|
+
#
|
872
887
|
def search_field(object_name, method, options = {})
|
873
888
|
options = options.stringify_keys
|
874
889
|
|
@@ -890,7 +905,7 @@ module ActionView
|
|
890
905
|
#
|
891
906
|
# telephone_field("user", "phone")
|
892
907
|
# # => <input id="user_phone" name="user[phone]" size="30" type="tel" />
|
893
|
-
|
908
|
+
#
|
894
909
|
def telephone_field(object_name, method, options = {})
|
895
910
|
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("tel", options)
|
896
911
|
end
|
@@ -900,7 +915,7 @@ module ActionView
|
|
900
915
|
#
|
901
916
|
# url_field("user", "homepage")
|
902
917
|
# # => <input id="user_homepage" size="30" name="user[homepage]" type="url" />
|
903
|
-
|
918
|
+
#
|
904
919
|
def url_field(object_name, method, options = {})
|
905
920
|
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("url", options)
|
906
921
|
end
|
@@ -909,7 +924,7 @@ module ActionView
|
|
909
924
|
#
|
910
925
|
# email_field("user", "address")
|
911
926
|
# # => <input id="user_address" size="30" name="user[address]" type="email" />
|
912
|
-
|
927
|
+
#
|
913
928
|
def email_field(object_name, method, options = {})
|
914
929
|
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("email", options)
|
915
930
|
end
|
@@ -948,7 +963,7 @@ module ActionView
|
|
948
963
|
end
|
949
964
|
|
950
965
|
class InstanceTag
|
951
|
-
include Helpers::
|
966
|
+
include Helpers::TagHelper, Helpers::FormTagHelper
|
952
967
|
|
953
968
|
attr_reader :object, :method_name, :object_name
|
954
969
|
|
@@ -959,6 +974,7 @@ module ActionView
|
|
959
974
|
def initialize(object_name, method_name, template_object, object = nil)
|
960
975
|
@object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
|
961
976
|
@template_object = template_object
|
977
|
+
|
962
978
|
@object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]")
|
963
979
|
@object = retrieve_object(object)
|
964
980
|
@auto_index = retrieve_autoindex(Regexp.last_match.pre_match) if Regexp.last_match
|
@@ -977,10 +993,11 @@ module ActionView
|
|
977
993
|
|
978
994
|
add_default_name_and_id_for_value(tag_value, name_and_id)
|
979
995
|
options.delete("index")
|
996
|
+
options.delete("namespace")
|
980
997
|
options["for"] ||= name_and_id["id"]
|
981
998
|
|
982
999
|
if block_given?
|
983
|
-
label_tag(name_and_id["id"], options, &block)
|
1000
|
+
@template_object.label_tag(name_and_id["id"], options, &block)
|
984
1001
|
else
|
985
1002
|
content = if text.blank?
|
986
1003
|
object_name.gsub!(/\[(.*)_attributes\]\[\d\]/, '.\1')
|
@@ -1023,6 +1040,8 @@ module ActionView
|
|
1023
1040
|
|
1024
1041
|
def to_number_field_tag(field_type, options = {})
|
1025
1042
|
options = options.stringify_keys
|
1043
|
+
options['size'] ||= nil
|
1044
|
+
|
1026
1045
|
if range = options.delete("in") || options.delete("within")
|
1027
1046
|
options.update("min" => range.min, "max" => range.max)
|
1028
1047
|
end
|
@@ -1181,6 +1200,7 @@ module ActionView
|
|
1181
1200
|
options["name"] ||= tag_name + (options['multiple'] ? '[]' : '')
|
1182
1201
|
options["id"] = options.fetch("id"){ tag_id }
|
1183
1202
|
end
|
1203
|
+
options["id"] = [options.delete('namespace'), options["id"]].compact.join("_").presence
|
1184
1204
|
end
|
1185
1205
|
|
1186
1206
|
def tag_name
|
@@ -1223,8 +1243,12 @@ module ActionView
|
|
1223
1243
|
parent_builder.multipart = multipart if parent_builder
|
1224
1244
|
end
|
1225
1245
|
|
1226
|
-
def self.
|
1227
|
-
@
|
1246
|
+
def self._to_partial_path
|
1247
|
+
@_to_partial_path ||= name.demodulize.underscore.sub!(/_builder$/, '')
|
1248
|
+
end
|
1249
|
+
|
1250
|
+
def to_partial_path
|
1251
|
+
self.class._to_partial_path
|
1228
1252
|
end
|
1229
1253
|
|
1230
1254
|
def to_model
|
@@ -1235,7 +1259,7 @@ module ActionView
|
|
1235
1259
|
@nested_child_index = {}
|
1236
1260
|
@object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
|
1237
1261
|
@parent_builder = options[:parent_builder]
|
1238
|
-
@default_options = @options ? @options.slice(:index) : {}
|
1262
|
+
@default_options = @options ? @options.slice(:index, :namespace) : {}
|
1239
1263
|
if @object_name.to_s.match(/\[\]$/)
|
1240
1264
|
if object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param)
|
1241
1265
|
@auto_index = object.to_param
|
@@ -1262,6 +1286,7 @@ module ActionView
|
|
1262
1286
|
fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
|
1263
1287
|
fields_options[:builder] ||= options[:builder]
|
1264
1288
|
fields_options[:parent_builder] = self
|
1289
|
+
fields_options[:namespace] = fields_options[:parent_builder].options[:namespace]
|
1265
1290
|
|
1266
1291
|
case record_name
|
1267
1292
|
when String, Symbol
|
@@ -1339,6 +1364,39 @@ module ActionView
|
|
1339
1364
|
@template.submit_tag(value, options)
|
1340
1365
|
end
|
1341
1366
|
|
1367
|
+
# Add the submit button for the given form. When no value is given, it checks
|
1368
|
+
# if the object is a new resource or not to create the proper label:
|
1369
|
+
#
|
1370
|
+
# <%= form_for @post do |f| %>
|
1371
|
+
# <%= f.button %>
|
1372
|
+
# <% end %>
|
1373
|
+
#
|
1374
|
+
# In the example above, if @post is a new record, it will use "Create Post" as
|
1375
|
+
# submit button label, otherwise, it uses "Update Post".
|
1376
|
+
#
|
1377
|
+
# Those labels can be customized using I18n, under the helpers.submit key and accept
|
1378
|
+
# the %{model} as translation interpolation:
|
1379
|
+
#
|
1380
|
+
# en:
|
1381
|
+
# helpers:
|
1382
|
+
# button:
|
1383
|
+
# create: "Create a %{model}"
|
1384
|
+
# update: "Confirm changes to %{model}"
|
1385
|
+
#
|
1386
|
+
# It also searches for a key specific for the given object:
|
1387
|
+
#
|
1388
|
+
# en:
|
1389
|
+
# helpers:
|
1390
|
+
# button:
|
1391
|
+
# post:
|
1392
|
+
# create: "Add %{model}"
|
1393
|
+
#
|
1394
|
+
def button(value=nil, options={})
|
1395
|
+
value, options = nil, value if value.is_a?(Hash)
|
1396
|
+
value ||= submit_default_value
|
1397
|
+
@template.button_tag(value, options)
|
1398
|
+
end
|
1399
|
+
|
1342
1400
|
def emitted_hidden_id?
|
1343
1401
|
@emitted_hidden_id ||= nil
|
1344
1402
|
end
|
@@ -105,7 +105,10 @@ module ActionView
|
|
105
105
|
|
106
106
|
# Create a select tag and a series of contained option tags for the provided object and method.
|
107
107
|
# The option currently held by the object will be selected, provided that the object is available.
|
108
|
-
#
|
108
|
+
#
|
109
|
+
# There are two possible formats for the choices parameter, corresponding to other helpers' output:
|
110
|
+
# * A flat collection: see options_for_select
|
111
|
+
# * A nested collection: see grouped_options_for_select
|
109
112
|
#
|
110
113
|
# Example with @post.person_id => 1:
|
111
114
|
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, { :include_blank => true })
|
@@ -125,9 +128,31 @@ module ActionView
|
|
125
128
|
# This allows the user to submit a form page more than once with the expected results of creating multiple records.
|
126
129
|
# In addition, this allows a single partial to be used to generate form inputs for both edit and create forms.
|
127
130
|
#
|
128
|
-
# By default, <tt>post.person_id</tt> is the selected option.
|
131
|
+
# By default, <tt>post.person_id</tt> is the selected option. Specify <tt>:selected => value</tt> to use a different selection
|
129
132
|
# or <tt>:selected => nil</tt> to leave all options unselected. Similarly, you can specify values to be disabled in the option
|
130
133
|
# tags by specifying the <tt>:disabled</tt> option. This can either be a single value or an array of values to be disabled.
|
134
|
+
#
|
135
|
+
# ==== Gotcha
|
136
|
+
#
|
137
|
+
# The HTML specification says when +multiple+ parameter passed to select and all options got deselected
|
138
|
+
# web browsers do not send any value to server. Unfortunately this introduces a gotcha:
|
139
|
+
# if an +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user
|
140
|
+
# the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So,
|
141
|
+
# any mass-assignment idiom like
|
142
|
+
#
|
143
|
+
# @user.update_attributes(params[:user])
|
144
|
+
#
|
145
|
+
# wouldn't update roles.
|
146
|
+
#
|
147
|
+
# To prevent this the helper generates an auxiliary hidden field before
|
148
|
+
# every multiple select. The hidden field has the same name as multiple select and blank value.
|
149
|
+
#
|
150
|
+
# This way, the client either sends only the hidden field (representing
|
151
|
+
# the deselected multiple select box), or both fields. Since the HTML specification
|
152
|
+
# says key/value pairs have to be sent in the same order they appear in the
|
153
|
+
# form, and parameters extraction gets the last occurrence of any repeated
|
154
|
+
# key in the query string, that works for ordinary forms.
|
155
|
+
#
|
131
156
|
def select(object, method, choices, options = {}, html_options = {})
|
132
157
|
InstanceTag.new(object, method, self, options.delete(:object)).to_select_tag(choices, options, html_options)
|
133
158
|
end
|
@@ -255,7 +280,7 @@ module ActionView
|
|
255
280
|
# Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
|
256
281
|
# where the elements respond to first and last (such as a two-element array), the "lasts" serve as option values and
|
257
282
|
# the "firsts" as option text. Hashes are turned into this form automatically, so the keys become "firsts" and values
|
258
|
-
# become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag.
|
283
|
+
# become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +selected+
|
259
284
|
# may also be an array of values to be selected when using a multiple select.
|
260
285
|
#
|
261
286
|
# Examples (call, result):
|
@@ -406,13 +431,13 @@ module ActionView
|
|
406
431
|
# wraps them with <tt><optgroup></tt> tags.
|
407
432
|
#
|
408
433
|
# Parameters:
|
409
|
-
# * +grouped_options+ - Accepts a nested array or hash of strings.
|
434
|
+
# * +grouped_options+ - Accepts a nested array or hash of strings. The first value serves as the
|
410
435
|
# <tt><optgroup></tt> label while the second value must be an array of options. The second value can be a
|
411
436
|
# nested array of text-value pairs. See <tt>options_for_select</tt> for more info.
|
412
437
|
# Ex. ["North America",[["United States","US"],["Canada","CA"]]]
|
413
438
|
# * +selected_key+ - A value equal to the +value+ attribute for one of the <tt><option></tt> tags,
|
414
439
|
# which will have the +selected+ attribute set. Note: It is possible for this value to match multiple options
|
415
|
-
# as you might have the same option in multiple groups.
|
440
|
+
# as you might have the same option in multiple groups. Each will then get <tt>selected="selected"</tt>.
|
416
441
|
# * +prompt+ - set to true or a prompt string. When the select element doesn't have a value yet, this
|
417
442
|
# prepends an option with a generic prompt - "Please select" - or the given prompt string.
|
418
443
|
#
|
@@ -552,56 +577,62 @@ module ActionView
|
|
552
577
|
include FormOptionsHelper
|
553
578
|
|
554
579
|
def to_select_tag(choices, options, html_options)
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
580
|
+
selected_value = options.has_key?(:selected) ? options[:selected] : value(object)
|
581
|
+
|
582
|
+
# Grouped choices look like this:
|
583
|
+
#
|
584
|
+
# [nil, []]
|
585
|
+
# { nil => [] }
|
586
|
+
#
|
587
|
+
if !choices.empty? && Array === choices.first.last
|
588
|
+
option_tags = grouped_options_for_select(choices, :selected => selected_value, :disabled => options[:disabled])
|
589
|
+
else
|
590
|
+
option_tags = options_for_select(choices, :selected => selected_value, :disabled => options[:disabled])
|
591
|
+
end
|
592
|
+
|
593
|
+
select_content_tag(option_tags, options, html_options)
|
561
594
|
end
|
562
595
|
|
563
596
|
def to_collection_select_tag(collection, value_method, text_method, options, html_options)
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
disabled_value = options.has_key?(:disabled) ? options[:disabled] : nil
|
568
|
-
selected_value = options.has_key?(:selected) ? options[:selected] : value
|
569
|
-
content_tag(
|
570
|
-
"select", add_options(options_from_collection_for_select(collection, value_method, text_method, :selected => selected_value, :disabled => disabled_value), options, value), html_options
|
597
|
+
selected_value = options.has_key?(:selected) ? options[:selected] : value(object)
|
598
|
+
select_content_tag(
|
599
|
+
options_from_collection_for_select(collection, value_method, text_method, :selected => selected_value, :disabled => options[:disabled]), options, html_options
|
571
600
|
)
|
572
601
|
end
|
573
602
|
|
574
603
|
def to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
|
575
|
-
|
576
|
-
|
577
|
-
value = value(object)
|
578
|
-
content_tag(
|
579
|
-
"select", add_options(option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value), options, value), html_options
|
604
|
+
select_content_tag(
|
605
|
+
option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value(object)), options, html_options
|
580
606
|
)
|
581
607
|
end
|
582
608
|
|
583
609
|
def to_time_zone_select_tag(priority_zones, options, html_options)
|
584
|
-
|
585
|
-
|
586
|
-
value = value(object)
|
587
|
-
content_tag("select",
|
588
|
-
add_options(
|
589
|
-
time_zone_options_for_select(value || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone),
|
590
|
-
options, value
|
591
|
-
), html_options
|
610
|
+
select_content_tag(
|
611
|
+
time_zone_options_for_select(value(object) || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone), options, html_options
|
592
612
|
)
|
593
613
|
end
|
594
614
|
|
595
615
|
private
|
596
616
|
def add_options(option_tags, options, value = nil)
|
597
617
|
if options[:include_blank]
|
598
|
-
option_tags =
|
618
|
+
option_tags = "<option value=\"\">#{ERB::Util.html_escape(options[:include_blank]) if options[:include_blank].kind_of?(String)}</option>\n" + option_tags
|
599
619
|
end
|
600
620
|
if value.blank? && options[:prompt]
|
601
621
|
prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('helpers.select.prompt', :default => 'Please select')
|
602
|
-
option_tags =
|
622
|
+
option_tags = "<option value=\"\">#{ERB::Util.html_escape(prompt)}</option>\n" + option_tags
|
623
|
+
end
|
624
|
+
option_tags.html_safe
|
625
|
+
end
|
626
|
+
|
627
|
+
def select_content_tag(option_tags, options, html_options)
|
628
|
+
html_options = html_options.stringify_keys
|
629
|
+
add_default_name_and_id(html_options)
|
630
|
+
select = content_tag("select", add_options(option_tags, options, value(object)), html_options)
|
631
|
+
if html_options["multiple"]
|
632
|
+
tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select
|
633
|
+
else
|
634
|
+
select
|
603
635
|
end
|
604
|
-
option_tags
|
605
636
|
end
|
606
637
|
end
|
607
638
|
|
@@ -114,11 +114,11 @@ module ActionView
|
|
114
114
|
html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
|
115
115
|
|
116
116
|
if options.delete(:include_blank)
|
117
|
-
option_tags =
|
117
|
+
option_tags = "<option value=\"\"></option>".html_safe + option_tags
|
118
118
|
end
|
119
119
|
|
120
120
|
if prompt = options.delete(:prompt)
|
121
|
-
option_tags =
|
121
|
+
option_tags = "<option value=\"\">#{prompt}</option>".html_safe + option_tags
|
122
122
|
end
|
123
123
|
|
124
124
|
content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
|
@@ -177,9 +177,12 @@ module ActionView
|
|
177
177
|
# label_tag 'name', nil, :class => 'small_label'
|
178
178
|
# # => <label for="name" class="small_label">Name</label>
|
179
179
|
def label_tag(name = nil, content_or_options = nil, options = nil, &block)
|
180
|
-
|
181
|
-
|
182
|
-
|
180
|
+
if block_given? && content_or_options.is_a?(Hash)
|
181
|
+
options = content_or_options = content_or_options.stringify_keys
|
182
|
+
else
|
183
|
+
options ||= {}
|
184
|
+
options = options.stringify_keys
|
185
|
+
end
|
183
186
|
options["for"] = sanitize_to_id(name) unless name.blank? || options.has_key?("for")
|
184
187
|
content_tag :label, content_or_options || name.to_s.humanize, options, &block
|
185
188
|
end
|
@@ -204,7 +207,7 @@ module ActionView
|
|
204
207
|
text_field_tag(name, value, options.stringify_keys.update("type" => "hidden"))
|
205
208
|
end
|
206
209
|
|
207
|
-
# Creates a file upload field.
|
210
|
+
# Creates a file upload field. If you are using file uploads then you will also need
|
208
211
|
# to set the multipart option for the form tag:
|
209
212
|
#
|
210
213
|
# <%= form_tag '/upload', :multipart => true do %>
|
@@ -304,7 +307,7 @@ module ActionView
|
|
304
307
|
# text_area_tag 'comment', nil, :class => 'comment_input'
|
305
308
|
# # => <textarea class="comment_input" id="comment" name="comment"></textarea>
|
306
309
|
def text_area_tag(name, content = nil, options = {})
|
307
|
-
options.stringify_keys
|
310
|
+
options = options.stringify_keys
|
308
311
|
|
309
312
|
if size = options.delete("size")
|
310
313
|
options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
|
@@ -407,7 +410,7 @@ module ActionView
|
|
407
410
|
# data-confirm="Are you sure?" />
|
408
411
|
#
|
409
412
|
def submit_tag(value = "Save changes", options = {})
|
410
|
-
options.stringify_keys
|
413
|
+
options = options.stringify_keys
|
411
414
|
|
412
415
|
if disable_with = options.delete("disable_with")
|
413
416
|
options["data-disable-with"] = disable_with
|
@@ -417,7 +420,7 @@ module ActionView
|
|
417
420
|
options["data-confirm"] = confirm
|
418
421
|
end
|
419
422
|
|
420
|
-
tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options
|
423
|
+
tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options)
|
421
424
|
end
|
422
425
|
|
423
426
|
# Creates a button element that defines a <tt>submit</tt> button,
|
@@ -458,7 +461,7 @@ module ActionView
|
|
458
461
|
def button_tag(content_or_options = nil, options = nil, &block)
|
459
462
|
options = content_or_options if block_given? && content_or_options.is_a?(Hash)
|
460
463
|
options ||= {}
|
461
|
-
options.stringify_keys
|
464
|
+
options = options.stringify_keys
|
462
465
|
|
463
466
|
if disable_with = options.delete("disable_with")
|
464
467
|
options["data-disable-with"] = disable_with
|
@@ -497,13 +500,13 @@ module ActionView
|
|
497
500
|
# image_submit_tag("agree.png", :disabled => true, :class => "agree_disagree_button")
|
498
501
|
# # => <input class="agree_disagree_button" disabled="disabled" src="/images/agree.png" type="image" />
|
499
502
|
def image_submit_tag(source, options = {})
|
500
|
-
options.stringify_keys
|
503
|
+
options = options.stringify_keys
|
501
504
|
|
502
505
|
if confirm = options.delete("confirm")
|
503
506
|
options["data-confirm"] = confirm
|
504
507
|
end
|
505
508
|
|
506
|
-
tag :input, { "type" => "image", "src" => path_to_image(source) }.update(options
|
509
|
+
tag :input, { "type" => "image", "src" => path_to_image(source) }.update(options)
|
507
510
|
end
|
508
511
|
|
509
512
|
# Creates a field set for grouping HTML form elements.
|
@@ -579,7 +582,7 @@ module ActionView
|
|
579
582
|
#
|
580
583
|
# ==== Examples
|
581
584
|
# number_field_tag 'quantity', nil, :in => 1...10
|
582
|
-
# => <input id="quantity" name="quantity" min="1" max="9" />
|
585
|
+
# => <input id="quantity" name="quantity" min="1" max="9" type="number" />
|
583
586
|
def number_field_tag(name, value = nil, options = {})
|
584
587
|
options = options.stringify_keys
|
585
588
|
options["type"] ||= "number"
|
@@ -597,6 +600,12 @@ module ActionView
|
|
597
600
|
number_field_tag(name, value, options.stringify_keys.update("type" => "range"))
|
598
601
|
end
|
599
602
|
|
603
|
+
# Creates the hidden UTF8 enforcer tag. Override this method in a helper
|
604
|
+
# to customize the tag.
|
605
|
+
def utf8_enforcer_tag
|
606
|
+
tag(:input, :type => "hidden", :name => "utf8", :value => "✓".html_safe)
|
607
|
+
end
|
608
|
+
|
600
609
|
private
|
601
610
|
def html_options_for_form(url_for_options, options)
|
602
611
|
options.stringify_keys.tap do |html_options|
|
@@ -611,9 +620,6 @@ module ActionView
|
|
611
620
|
end
|
612
621
|
|
613
622
|
def extra_tags_for_form(html_options)
|
614
|
-
snowman_tag = tag(:input, :type => "hidden",
|
615
|
-
:name => "utf8", :value => "✓".html_safe)
|
616
|
-
|
617
623
|
authenticity_token = html_options.delete("authenticity_token")
|
618
624
|
method = html_options.delete("method").to_s
|
619
625
|
|
@@ -629,7 +635,7 @@ module ActionView
|
|
629
635
|
tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag(authenticity_token)
|
630
636
|
end
|
631
637
|
|
632
|
-
tags =
|
638
|
+
tags = utf8_enforcer_tag << method_tag
|
633
639
|
content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline')
|
634
640
|
end
|
635
641
|
|
@@ -650,7 +656,7 @@ module ActionView
|
|
650
656
|
if token == false || !protect_against_forgery?
|
651
657
|
''
|
652
658
|
else
|
653
|
-
token
|
659
|
+
token ||= form_authenticity_token
|
654
660
|
tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => token)
|
655
661
|
end
|
656
662
|
end
|