Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +115 -66
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/action_view.rb +1 -1
  6. data/lib/action_view/buffers.rb +15 -0
  7. data/lib/action_view/context.rb +5 -4
  8. data/lib/action_view/digestor.rb +7 -6
  9. data/lib/action_view/gem_version.rb +4 -4
  10. data/lib/action_view/helpers.rb +0 -2
  11. data/lib/action_view/helpers/asset_tag_helper.rb +4 -27
  12. data/lib/action_view/helpers/asset_url_helper.rb +4 -3
  13. data/lib/action_view/helpers/cache_helper.rb +18 -10
  14. data/lib/action_view/helpers/capture_helper.rb +4 -0
  15. data/lib/action_view/helpers/csrf_helper.rb +1 -1
  16. data/lib/action_view/helpers/date_helper.rb +69 -25
  17. data/lib/action_view/helpers/form_helper.rb +238 -6
  18. data/lib/action_view/helpers/form_options_helper.rb +23 -15
  19. data/lib/action_view/helpers/form_tag_helper.rb +9 -7
  20. data/lib/action_view/helpers/javascript_helper.rb +9 -8
  21. data/lib/action_view/helpers/number_helper.rb +5 -0
  22. data/lib/action_view/helpers/record_tag_helper.rb +0 -23
  23. data/lib/action_view/helpers/sanitize_helper.rb +3 -3
  24. data/lib/action_view/helpers/tag_helper.rb +7 -6
  25. data/lib/action_view/helpers/tags/base.rb +8 -4
  26. data/lib/action_view/helpers/tags/color_field.rb +1 -1
  27. data/lib/action_view/helpers/tags/translator.rb +1 -6
  28. data/lib/action_view/helpers/text_helper.rb +3 -3
  29. data/lib/action_view/helpers/translation_helper.rb +14 -10
  30. data/lib/action_view/helpers/url_helper.rb +13 -13
  31. data/lib/action_view/log_subscriber.rb +6 -6
  32. data/lib/action_view/lookup_context.rb +4 -4
  33. data/lib/action_view/railtie.rb +18 -0
  34. data/lib/action_view/record_identifier.rb +2 -2
  35. data/lib/action_view/renderer/partial_renderer.rb +2 -2
  36. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +40 -1
  37. data/lib/action_view/renderer/streaming_template_renderer.rb +1 -1
  38. data/lib/action_view/rendering.rb +5 -4
  39. data/lib/action_view/routing_url_for.rb +12 -11
  40. data/lib/action_view/template.rb +25 -8
  41. data/lib/action_view/template/handlers/erb.rb +12 -2
  42. data/lib/action_view/template/resolver.rb +56 -16
  43. data/lib/action_view/test_case.rb +1 -1
  44. data/lib/action_view/testing/resolvers.rb +1 -1
  45. data/lib/assets/compiled/rails-ujs.js +34 -17
  46. metadata +11 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
---
2
2
SHA256:
3
- metadata.gz: 20b55c23000cc907e0aed85f5811bec79a4d7f818e37d070acdee9e070be6424
4
- data.tar.gz: 70224fb23e260c9fd5187402ea18c6a5ea89cdb147413135be64e0e514985b13
3
+ metadata.gz: 58d19af0e853c217ca89f2167775195c7a44ebba26e4d9a682aabeb6a25b6af4
4
+ data.tar.gz: 15d2c9faa90c17d33772df3ce0eacccecb090e7310af47ab1d82bca6448a2a11
5
5
SHA512:
6
- metadata.gz: a1b2b7522e79efb0c107d5d7b79b11eea20b7c72567aa6ed39e24766b47f4efd681ac3dad0f056e43036e4e960faa7e007f158cad1c00a5bd355d18a6edb721f
7
- data.tar.gz: 1c774076688b21de0faf5e4de096637c0e0b05af77432c84aa9ac7082a6d2152e2e2610dbe298b99b5a89704fa2a9ea9297e29304efcf80c9fbdaf2f80f657f5
6
+ metadata.gz: dc169a10649b5f6cdfb8488bd16cbb7cab049081e7a6c53821cfab497e8035c7843c690e3bffcffc571bf2f610e5f2b7eee80283f622262d5ff9f1f20c8ef210
7
+ data.tar.gz: ed371d7bec363bafe775d5a1ed3d11a1002f96589c142c662afabc1862c71314427fc07004d0bfa4bc2ac5078f0c0b38410ccca1ee738594223a1a3a336ffa14
data/CHANGELOG.md CHANGED
@@ -1,4 +1,18 @@
1
- ## Rails 5.2.3.rc1 (March 21, 2019) ##
1
+ ## Rails 6.0.0.beta1 (January 18, 2019) ##
2
+
3
+ * Remove deprecated `image_alt` helper.
4
+
5
+ *Rafael Mendonça França*
6
+
7
+ * Fix the need of `#protect_against_forgery?` method defined in
8
+ `ActionView::Base` subclasses. This prevents the use of forms and buttons.
9
+
10
+ *Genadi Samokovarov*
11
+
12
+ * Fix UJS permanently showing disabled text in a[data-remote][data-disable-with] elements within forms.
13
+ Fixes #33889
14
+
15
+ *Wolfgang Hobmaier*
2
16
3
17
* Prevent non-primary mouse keys from triggering Rails UJS click handlers.
4
18
Firefox fires click events even if the click was triggered by non-primary mouse keys such as right- or scroll-wheel-clicks.
@@ -12,28 +26,50 @@
12
26
13
27
*Wolfgang Hobmaier*
14
28
29
+ * Prevent `ActionView::TextHelper#word_wrap` from unexpectedly stripping white space from the _left_ side of lines.
30
+
31
+ For example, given input like this:
32
+
33
+ ```
34
+ This is a paragraph with an initial indent,
35
+ followed by additional lines that are not indented,
36
+ and finally terminated with a blockquote:
37
+ "A pithy saying"
38
+ ```
39
+
40
+ Calling `word_wrap` should not trim the indents on the first and last lines.
15
41
16
- ## Rails 5.2.2.1 (March 11, 2019) ##
42
+ Fixes #34487
17
43
18
- * No changes.
44
+ *Lyle Mullican*
19
45
46
+ * Add allocations to template rendering instrumentation.
20
47
21
- ## Rails 5.2.2 (December 04, 2018) ##
48
+ Adds the allocations for template and partial rendering to the server output on render.
49
+
50
+ ```
51
+ Rendered posts/_form.html.erb (Duration: 7.1ms | Allocations: 6004)
52
+ Rendered posts/new.html.erb within layouts/application (Duration: 8.3ms | Allocations: 6654)
53
+ Completed 200 OK in 858ms (Views: 848.4ms | ActiveRecord: 0.4ms | Allocations: 1539564)
54
+ ```
22
55
23
- * No changes.
56
+ *Eileen M. Uchitelle*, *Aaron Patterson*
24
57
58
+ * Respect the `only_path` option passed to `url_for` when the options are passed in as an array
25
59
26
- ## Rails 5.2.1.1 (November 27, 2018) ##
60
+ Fixes #33237.
27
61
28
- * No changes.
62
+ *Joel Ambass*
29
63
64
+ * Deprecate calling private model methods from view helpers.
30
65
31
- ## Rails 5.2.1 (August 07, 2018) ##
66
+ For example, in methods like `options_from_collection_for_select`
67
+ and `collection_select` it is possible to call private methods from
68
+ the objects used.
32
69
33
- * Fix leak of `skip_default_ids` and `allow_method_names_outside_object` options
70
+ Fixes #33546.
34
- to HTML attributes.
35
71
36
- *Yurii Cherniavskyi*
72
+ *Ana María Martínez Gómez*
37
73
38
74
* Fix issue with `button_to`'s `to_form_params`
39
75
@@ -46,97 +82,110 @@
46
82
47
83
*Georgi Georgiev*
48
84
49
- * Fix JavaScript views rendering does not work with Firefox when using
85
+ * Mark arrays of translations as trusted safe by using the `_html` suffix.
50
- Content Security Policy.
51
86
52
- Fixes #32577.
87
+ Example:
53
88
54
- *Yuji Yaginuma*
89
+ en:
90
+ foo_html:
91
+ - "One"
92
+ - "<strong>Two</strong>"
93
+ - "Three &#128075; &#128578;"
55
94
56
- * Add the `nonce: true` option for `javascript_include_tag` helper to
95
+ *Juan Broullon*
57
- support automatic nonce generation for Content Security Policy.
58
- Works the same way as `javascript_tag nonce: true` does.
59
-
60
- *Yaroslav Markin*
61
96
97
+ * Add `year_format` option to date_select tag. This option makes it possible to customize year
98
+ names. Lambda should be passed to use this option.
62
99
63
- ## Rails 5.2.0 (April 09, 2018) ##
100
+ Example:
64
101
65
- * Pass the `:skip_pipeline` option in `image_submit_tag` when calling `path_to_image`.
102
+ date_select('user_birthday', '', start_year: 1998, end_year: 2000, year_format: ->year { "Heisei #{year - 1988}" })
66
103
67
- Fixes #32248.
104
+ The HTML produced:
68
105
69
- *Andrew White*
70
-
71
- * Allow the use of callable objects as group methods for grouped selects.
72
-
73
- Until now, the `option_groups_from_collection_for_select` method was only able to
74
- handle method names as `group_method` and `group_label_method` parameters,
106
+ <select id="user_birthday__1i" name="user_birthday[(1i)]">
107
+ <option value="1998">Heisei 10</option>
108
+ <option value="1999">Heisei 11</option>
109
+ <option value="2000">Heisei 12</option>
110
+ </select>
111
+ /* The rest is omitted */
75
- it is now able to receive procs and other callable objects too.
76
112
77
- *Jérémie Bonal*
113
+ *Koki Ryu*
78
114
79
- * Add `preload_link_tag` helper.
115
+ * Fix JavaScript views rendering does not work with Firefox when using
116
+ Content Security Policy.
80
117
81
- This helper that allows to the browser to initiate early fetch of resources
118
+ Fixes #32577.
82
- (different to the specified in `javascript_include_tag` and `stylesheet_link_tag`).
83
- Additionally, this sends Early Hints if supported by browser.
84
119
85
- *Guillermo Iguaran*
120
+ *Yuji Yaginuma*
86
121
87
- * Change `form_with` to generates ids by default.
122
+ * Add the `nonce: true` option for `javascript_include_tag` helper to
123
+ support automatic nonce generation for Content Security Policy.
124
+ Works the same way as `javascript_tag nonce: true` does.
88
125
89
- When `form_with` was introduced we disabled the automatic generation of ids
126
+ *Yaroslav Markin*
90
- that was enabled in `form_for`. This usually is not an good idea since labels don't work
91
- when the input doesn't have an id and it made harder to test with Capybara.
92
127
93
- You can still disable the automatic generation of ids setting `config.action_view.form_with_generates_ids`
128
+ * Remove `ActionView::Helpers::RecordTagHelper`.
94
- to `false.`
95
129
96
- *Nick Pezza*
130
+ *Yoshiyuki Hirano*
97
131
98
- * Fix issues with `field_error_proc` wrapping `optgroup` and select divider `option`.
132
+ * Disable `ActionView::Template` finalizers in test environment.
99
133
100
- Fixes #31088
134
+ Template finalization can be expensive in large view test suites.
135
+ Add a configuration option,
136
+ `action_view.finalize_compiled_template_methods`, and turn it off in
137
+ the test environment.
101
138
102
- *Matthias Neumayr*
139
+ *Simon Coffey*
103
140
104
- * Remove deprecated Erubis ERB handler.
141
+ * Extract the `confirm` call in its own, overridable method in `rails_ujs`.
105
142
106
- *Rafael Mendonça França*
143
+ Example:
107
144
108
- * Remove default `alt` text generation.
145
+ Rails.confirm = function(message, element) {
146
+ return (my_bootstrap_modal_confirm(message));
147
+ }
109
148
110
- Fixes #30096
149
+ *Mathieu Mahé*
111
150
112
- *Cameron Cundiff*
151
+ * Enable select tag helper to mark `prompt` option as `selected` and/or `disabled` for `required`
152
+ field.
113
153
114
- * Add `srcset` option to `image_tag` helper.
154
+ Example:
115
155
116
- *Roberto Miranda*
156
+ select :post,
157
+ :category,
158
+ ["lifestyle", "programming", "spiritual"],
159
+ { selected: "", disabled: "", prompt: "Choose one" },
160
+ { required: true }
117
161
118
- * Fix issues with scopes and engine on `current_page?` method.
162
+ Placeholder option would be selected and disabled.
119
163
120
- Fixes #29401.
164
+ The HTML produced:
121
165
122
- *Nikita Savrov*
166
+ <select required="required" name="post[category]" id="post_category">
167
+ <option disabled="disabled" selected="selected" value="">Choose one</option>
168
+ <option value="lifestyle">lifestyle</option>
169
+ <option value="programming">programming</option>
170
+ <option value="spiritual">spiritual</option></select>
123
171
124
- * Generate field ids in `collection_check_boxes` and `collection_radio_buttons`.
172
+ *Sergey Prikhodko*
125
173
126
- This makes sure that the labels are linked up with the fields.
174
+ * Don't enforce UTF-8 by default.
127
175
128
- Fixes #29014.
176
+ With the disabling of TLS 1.0 by most major websites, continuing to run
177
+ IE8 or lower becomes increasingly difficult so default to not enforcing
178
+ UTF-8 encoding as it's not relevant to other browsers.
129
179
130
- *Yuji Yaginuma*
180
+ *Andrew White*
131
181
132
- * Add `:json` type to `auto_discovery_link_tag` to support [JSON Feeds](https://jsonfeed.org/version/1).
182
+ * Change translation key of `submit_tag` from `module_name_class_name` to `module_name/class_name`.
133
183
134
- *Mike Gunderloy*
184
+ *Rui Onodera*
135
185
136
- * Update `distance_of_time_in_words` helper to display better error messages
186
+ * Rails 6 requires Ruby 2.5.0 or newer.
137
- for bad input.
138
187
139
- *Jay Hayes*
188
+ *Jeremy Daer*, *Kasper Timm Hansen*
140
189
141
190
142
- Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/actionview/CHANGELOG.md) for previous changes.
191
+ Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/actionview/CHANGELOG.md) for previous changes.
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2004-2018 David Heinemeier Hansson
1
+ Copyright (c) 2004-2019 David Heinemeier Hansson
2
2
3
3
Permission is hereby granted, free of charge, to any person obtaining
4
4
a copy of this software and associated documentation files (the
data/README.rdoc CHANGED
@@ -13,7 +13,7 @@ The latest version of Action View can be installed with RubyGems:
13
13
14
14
Source code can be downloaded as part of the Rails project on GitHub:
15
15
16
- * https://github.com/rails/rails/tree/5-2-stable/actionview
16
+ * https://github.com/rails/rails/tree/master/actionview
17
17
18
18
19
19
== License
data/lib/action_view.rb CHANGED
@@ -1,7 +1,7 @@
1
1
# frozen_string_literal: true
2
2
3
3
#--
4
- # Copyright (c) 2004-2018 David Heinemeier Hansson
4
+ # Copyright (c) 2004-2019 David Heinemeier Hansson
5
5
#
6
6
# Permission is hereby granted, free of charge, to any person obtaining
7
7
# a copy of this software and associated documentation files (the
data/lib/action_view/buffers.rb CHANGED
@@ -3,6 +3,21 @@
3
3
require "active_support/core_ext/string/output_safety"
4
4
5
5
module ActionView
6
+ # Used as a buffer for views
7
+ #
8
+ # The main difference between this and ActiveSupport::SafeBuffer
9
+ # is for the methods `<<` and `safe_expr_append=` the inputs are
10
+ # checked for nil before they are assigned and `to_s` is called on
11
+ # the input. For example:
12
+ #
13
+ # obuf = ActionView::OutputBuffer.new "hello"
14
+ # obuf << 5
15
+ # puts obuf # => "hello5"
16
+ #
17
+ # sbuf = ActiveSupport::SafeBuffer.new "hello"
18
+ # sbuf << 5
19
+ # puts sbuf # => "hello\u0005"
20
+ #
6
21
class OutputBuffer < ActiveSupport::SafeBuffer #:nodoc:
7
22
def initialize(*)
8
23
super
data/lib/action_view/context.rb CHANGED
@@ -10,10 +10,11 @@ module ActionView
10
10
# Action View contexts are supplied to Action Controller to render a template.
11
11
# The default Action View context is ActionView::Base.
12
12
#
13
- # In order to work with ActionController, a Context must just include this module.
14
- # The initialization of the variables used by the context (@output_buffer, @view_flow,
15
- # and @virtual_path) is responsibility of the object that includes this module
16
- # (although you can call _prepare_context defined below).
13
+ # In order to work with Action Controller, a Context must just include this
14
+ # module. The initialization of the variables used by the context
15
+ # (@output_buffer, @view_flow, and @virtual_path) is responsibility of the
16
+ # object that includes this module (although you can call _prepare_context
17
+ # defined below).
17
18
module Context
18
19
include CompiledTemplates
19
20
attr_accessor :output_buffer, :view_flow
data/lib/action_view/digestor.rb CHANGED
@@ -1,8 +1,6 @@
1
1
# frozen_string_literal: true
2
2
3
- require "concurrent/map"
4
3
require "action_view/dependency_tracker"
5
- require "monitor"
6
4
7
5
module ActionView
8
6
class Digestor
@@ -20,9 +18,12 @@ module ActionView
20
18
# * <tt>name</tt> - Template name
21
19
# * <tt>finder</tt> - An instance of <tt>ActionView::LookupContext</tt>
22
20
# * <tt>dependencies</tt> - An array of dependent views
23
- def digest(name:, finder:, dependencies: [])
24
- dependencies ||= []
25
- cache_key = [ name, finder.rendered_format, dependencies ].flatten.compact.join(".")
21
+ def digest(name:, finder:, dependencies: nil)
22
+ if dependencies.nil? || dependencies.empty?
23
+ cache_key = "#{name}.#{finder.rendered_format}"
24
+ else
25
+ cache_key = [ name, finder.rendered_format, dependencies ].flatten.compact.join(".")
26
+ end
26
27
27
28
# this is a correctly done double-checked locking idiom
28
29
# (Concurrent::Map's lookups have volatile semantics)
@@ -32,7 +33,7 @@ module ActionView
32
33
root = tree(name, finder, partial)
33
34
dependencies.each do |injected_dep|
34
35
root.children << Injected.new(injected_dep, nil, nil)
35
- end
36
+ end if dependencies
36
37
finder.digest_cache[cache_key] = root.digest(finder)
37
38
end
38
39
end
data/lib/action_view/gem_version.rb CHANGED
@@ -7,10 +7,10 @@ module ActionView
7
7
end
8
8
9
9
module VERSION
10
- MAJOR = 5
11
- MINOR = 2
12
- TINY = 3
13
- PRE = "rc1"
10
+ MAJOR = 6
11
+ MINOR = 0
12
+ TINY = 0
13
+ PRE = "beta1"
14
14
15
15
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
end
data/lib/action_view/helpers.rb CHANGED
@@ -23,7 +23,6 @@ module ActionView #:nodoc:
23
23
autoload :JavaScriptHelper, "action_view/helpers/javascript_helper"
24
24
autoload :NumberHelper
25
25
autoload :OutputSafetyHelper
26
- autoload :RecordTagHelper
27
26
autoload :RenderingHelper
28
27
autoload :SanitizeHelper
29
28
autoload :TagHelper
@@ -57,7 +56,6 @@ module ActionView #:nodoc:
57
56
include JavaScriptHelper
58
57
include NumberHelper
59
58
include OutputSafetyHelper
60
- include RecordTagHelper
61
59
include RenderingHelper
62
60
include SanitizeHelper
63
61
include TagHelper
data/lib/action_view/helpers/asset_tag_helper.rb CHANGED
@@ -55,7 +55,7 @@ module ActionView
55
55
# that path.
56
56
# * <tt>:skip_pipeline</tt> - This option is used to bypass the asset pipeline
57
57
# when it is set to true.
58
- # * <tt>:nonce<tt> - When set to true, adds an automatic nonce value if
58
+ # * <tt>:nonce</tt> - When set to true, adds an automatic nonce value if
59
59
# you have Content Security Policy enabled.
60
60
#
61
61
# ==== Examples
@@ -98,7 +98,7 @@ module ActionView
98
98
if tag_options["nonce"] == true
99
99
tag_options["nonce"] = content_security_policy_nonce
100
100
end
101
- content_tag("script".freeze, "", tag_options)
101
+ content_tag("script", "", tag_options)
102
102
}.join("\n").html_safe
103
103
104
104
request.send_early_hints("Link" => early_hints_links.join("\n")) if respond_to?(:request) && request
@@ -333,9 +333,9 @@ module ActionView
333
333
#
334
334
# image_tag(user.avatar)
335
335
# # => <img src="/rails/active_storage/blobs/.../tiger.jpg" />
336
- # image_tag(user.avatar.variant(resize: "100x100"))
336
+ # image_tag(user.avatar.variant(resize_to_fit: [100, 100]))
337
337
# # => <img src="/rails/active_storage/variants/.../tiger.jpg" />
338
- # image_tag(user.avatar.variant(resize: "100x100"), size: '100')
338
+ # image_tag(user.avatar.variant(resize_to_fit: [100, 100]), size: '100')
339
339
# # => <img width="100" height="100" src="/rails/active_storage/variants/.../tiger.jpg" />
340
340
def image_tag(source, options = {})
341
341
options = options.symbolize_keys
@@ -355,29 +355,6 @@ module ActionView
355
355
tag("img", options)
356
356
end
357
357
358
- # Returns a string suitable for an HTML image tag alt attribute.
359
- # The +src+ argument is meant to be an image file path.
360
- # The method removes the basename of the file path and the digest,
361
- # if any. It also removes hyphens and underscores from file names and
362
- # replaces them with spaces, returning a space-separated, titleized
363
- # string.
364
- #
365
- # ==== Examples
366
- #
367
- # image_alt('rails.png')
368
- # # => Rails
369
- #
370
- # image_alt('hyphenated-file-name.png')
371
- # # => Hyphenated file name
372
- #
373
- # image_alt('underscored_file_name.png')
374
- # # => Underscored file name
375
- def image_alt(src)
376
- ActiveSupport::Deprecation.warn("image_alt is deprecated and will be removed from Rails 6.0. You must explicitly set alt text on images.")
377
-
378
- File.basename(src, ".*".freeze).sub(/-[[:xdigit:]]{32,64}\z/, "".freeze).tr("-_".freeze, " ".freeze).capitalize
379
- end
380
-
381
358
# Returns an HTML video tag for the +sources+. If +sources+ is a string,
382
359
# a single video tag will be returned. If +sources+ is an array, a video
383
360
# tag with nested source tags for each source will be returned. The
data/lib/action_view/helpers/asset_url_helper.rb CHANGED
@@ -98,8 +98,9 @@ module ActionView
98
98
# have SSL certificates for each of the asset hosts this technique allows you
99
99
# to avoid warnings in the client about mixed media.
100
100
# Note that the +request+ parameter might not be supplied, e.g. when the assets
101
- # are precompiled via a Rake task. Make sure to use a +Proc+ instead of a lambda,
102
- # since a +Proc+ allows missing parameters and sets them to +nil+.
101
+ # are precompiled with the command `rails assets:precompile`. Make sure to use a
102
+ # +Proc+ instead of a lambda, since a +Proc+ allows missing parameters and sets them
103
+ # to +nil+.
103
104
#
104
105
# config.action_controller.asset_host = Proc.new { |source, request|
105
106
# if request && request.ssl?
@@ -187,7 +188,7 @@ module ActionView
187
188
return "" if source.blank?
188
189
return source if URI_REGEXP.match?(source)
189
190
190
- tail, source = source[/([\?#].+)#x2F;], source.sub(/([\?#].+)#x2F;, "".freeze)
191
+ tail, source = source[/([\?#].+)#x2F;], source.sub(/([\?#].+)#x2F;, "")
191
192
192
193
if extname = compute_asset_extname(source, options)
193
194
source = "#{source}#{extname}"
data/lib/action_view/helpers/cache_helper.rb CHANGED
@@ -201,34 +201,42 @@ module ActionView
201
201
end
202
202
203
203
# This helper returns the name of a cache key for a given fragment cache
204
- # call. By supplying +skip_digest:+ true to cache, the digestion of cache
204
+ # call. By supplying <tt>skip_digest: true</tt> to cache, the digestion of cache
205
205
# fragments can be manually bypassed. This is useful when cache fragments
206
206
# cannot be manually expired unless you know the exact key which is the
207
207
# case when using memcached.
208
208
#
209
209
# The digest will be generated using +virtual_path:+ if it is provided.
210
210
#
211
- def cache_fragment_name(name = {}, skip_digest: nil, virtual_path: nil)
211
+ def cache_fragment_name(name = {}, skip_digest: nil, virtual_path: nil, digest_path: nil)
212
212
if skip_digest
213
213
name
214
214
else
215
- fragment_name_with_digest(name, virtual_path)
215
+ fragment_name_with_digest(name, virtual_path, digest_path)
216
+ end
217
+ end
218
+
219
+ def digest_path_from_virtual(virtual_path) # :nodoc:
220
+ digest = Digestor.digest(name: virtual_path, finder: lookup_context, dependencies: view_cache_dependencies)
221
+
222
+ if digest.present?
223
+ "#{virtual_path}:#{digest}"
224
+ else
225
+ virtual_path
216
226
end
217
227
end
218
228
219
229
private
220
230
221
- def fragment_name_with_digest(name, virtual_path)
231
+ def fragment_name_with_digest(name, virtual_path, digest_path)
222
232
virtual_path ||= @virtual_path
223
233
224
- if virtual_path
234
+ if virtual_path || digest_path
225
235
name = controller.url_for(name).split("://").last if name.is_a?(Hash)
226
236
227
- if digest = Digestor.digest(name: virtual_path, finder: lookup_context, dependencies: view_cache_dependencies).presence
228
- [ "#{virtual_path}:#{digest}", name ]
229
- else
237
+ digest_path ||= digest_path_from_virtual(virtual_path)
238
+
239
+ [ digest_path, name ]
230
- [ virtual_path, name ]
231
- end
232
240
else
233
241
name
234
242
end
data/lib/action_view/helpers/capture_helper.rb CHANGED
@@ -36,6 +36,10 @@ module ActionView
36
36
# </body>
37
37
# </html>
38
38
#
39
+ # The return of capture is the string generated by the block. For Example:
40
+ #
41
+ # @greeting # => "Welcome to my shiny new web page! The date and time is 2018-09-06 11:09:16 -0500"
42
+ #
39
43
def capture(*args)
40
44
value = nil
41
45
buffer = with_output_buffer { value = yield(*args) }
data/lib/action_view/helpers/csrf_helper.rb CHANGED
@@ -20,7 +20,7 @@ module ActionView
20
20
# "X-CSRF-Token" HTTP header. If you are using rails-ujs this happens automatically.
21
21
#
22
22
def csrf_meta_tags
23
- if protect_against_forgery?
23
+ if defined?(protect_against_forgery?) && protect_against_forgery?
24
24
[
25
25
tag("meta", name: "csrf-param", content: request_forgery_protection_token),
26
26
tag("meta", name: "csrf-token", content: form_authenticity_token)
data/lib/action_view/helpers/date_helper.rb CHANGED
@@ -205,6 +205,7 @@ module ActionView
205
205
# * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Date.today.year + 5</tt> if
206
206
# you are creating new record. While editing existing record, <tt>:end_year</tt> defaults to
207
207
# the current selected year plus 5.
208
+ # * <tt>:year_format</tt> - Set format of years for year select. Lambda should be passed.
208
209
# * <tt>:discard_day</tt> - Set to true if you don't want to show a day select. This includes the day
209
210
# as a hidden field instead of showing a select field. Also note that this implicitly sets the day to be the
210
211
# first of the given month in order to not create invalid dates like 31 February.
@@ -275,6 +276,9 @@ module ActionView
275
276
# # Generates a date select with custom prompts.
276
277
# date_select("article", "written_on", prompt: { day: 'Select day', month: 'Select month', year: 'Select year' })
277
278
#
279
+ # # Generates a date select with custom year format.
280
+ # date_select("article", "written_on", year_format: ->(year) { "Heisei #{year - 1988}" })
281
+ #
278
282
# The selects are prepared for multi-parameter assignment to an Active Record object.
279
283
#
280
284
# Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
@@ -302,15 +306,15 @@ module ActionView
302
306
# time_select("article", "start_time", include_seconds: true)
303
307
#
304
308
# # You can set the <tt>:minute_step</tt> to 15 which will give you: 00, 15, 30, and 45.
305
- # time_select 'game', 'game_time', {minute_step: 15}
309
+ # time_select 'game', 'game_time', { minute_step: 15 }
306
310
#
307
311
# # Creates a time select tag with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
308
- # time_select("article", "written_on", prompt: {hour: 'Choose hour', minute: 'Choose minute', second: 'Choose seconds'})
309
- # time_select("article", "written_on", prompt: {hour: true}) # generic prompt for hours
312
+ # time_select("article", "written_on", prompt: { hour: 'Choose hour', minute: 'Choose minute', second: 'Choose seconds' })
313
+ # time_select("article", "written_on", prompt: { hour: true }) # generic prompt for hours
310
314
# time_select("article", "written_on", prompt: true) # generic prompts for all
311
315
#
312
316
# # You can set :ampm option to true which will show the hours as: 12 PM, 01 AM .. 11 PM.
313
- # time_select 'game', 'game_time', {ampm: true}
317
+ # time_select 'game', 'game_time', { ampm: true }
314
318
#
315
319
# The selects are prepared for multi-parameter assignment to an Active Record object.
316
320
#
@@ -346,8 +350,8 @@ module ActionView
346
350
# datetime_select("article", "written_on", discard_type: true)
347
351
#
348
352
# # Generates a datetime select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
349
- # datetime_select("article", "written_on", prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
350
- # datetime_select("article", "written_on", prompt: {hour: true}) # generic prompt for hours
353
+ # datetime_select("article", "written_on", prompt: { day: 'Choose day', month: 'Choose month', year: 'Choose year' })
354
+ # datetime_select("article", "written_on", prompt: { hour: true }) # generic prompt for hours
351
355
# datetime_select("article", "written_on", prompt: true) # generic prompts for all
352
356
#
353
357
# The selects are prepared for multi-parameter assignment to an Active Record object.
@@ -397,8 +401,8 @@ module ActionView
397
401
# select_datetime(my_date_time, prefix: 'payday')
398
402
#
399
403
# # Generates a datetime select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
400
- # select_datetime(my_date_time, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
401
- # select_datetime(my_date_time, prompt: {hour: true}) # generic prompt for hours
404
+ # select_datetime(my_date_time, prompt: { day: 'Choose day', month: 'Choose month', year: 'Choose year' })
405
+ # select_datetime(my_date_time, prompt: { hour: true }) # generic prompt for hours
402
406
# select_datetime(my_date_time, prompt: true) # generic prompts for all
403
407
def select_datetime(datetime = Time.current, options = {}, html_options = {})
404
408
DateTimeSelector.new(datetime, options, html_options).select_datetime
@@ -436,8 +440,8 @@ module ActionView
436
440
# select_date(my_date, prefix: 'payday')
437
441
#
438
442
# # Generates a date select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
439
- # select_date(my_date, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
440
- # select_date(my_date, prompt: {hour: true}) # generic prompt for hours
443
+ # select_date(my_date, prompt: { day: 'Choose day', month: 'Choose month', year: 'Choose year' })
444
+ # select_date(my_date, prompt: { hour: true }) # generic prompt for hours
441
445
# select_date(my_date, prompt: true) # generic prompts for all
442
446
def select_date(date = Date.current, options = {}, html_options = {})
443
447
DateTimeSelector.new(date, options, html_options).select_date
@@ -476,8 +480,8 @@ module ActionView
476
480
# select_time(my_time, start_hour: 2, end_hour: 14)
477
481
#
478
482
# # Generates a time select with a custom prompt. Use <tt>:prompt</tt> to true for generic prompts.
479
- # select_time(my_time, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
480
- # select_time(my_time, prompt: {hour: true}) # generic prompt for hours
483
+ # select_time(my_time, prompt: { day: 'Choose day', month: 'Choose month', year: 'Choose year' })
484
+ # select_time(my_time, prompt: { hour: true }) # generic prompt for hours
481
485
# select_time(my_time, prompt: true) # generic prompts for all
482
486
def select_time(datetime = Time.current, options = {}, html_options = {})
483
487
DateTimeSelector.new(datetime, options, html_options).select_time
@@ -668,8 +672,6 @@ module ActionView
668
672
# <time datetime="2010-11-04T17:55:45+01:00">November 04, 2010 17:55</time>
669
673
# time_tag Date.yesterday, 'Yesterday' # =>
670
674
# <time datetime="2010-11-03">Yesterday</time>
671
- # time_tag Date.today, pubdate: true # =>
672
- # <time datetime="2010-11-04" pubdate="pubdate">November 04, 2010</time>
673
675
# time_tag Date.today, datetime: Date.today.strftime('%G-W%V') # =>
674
676
# <time datetime="2010-W44">November 04, 2010</time>
675
677
#
@@ -681,9 +683,8 @@ module ActionView
681
683
options = args.extract_options!
682
684
format = options.delete(:format) || :long
683
685
content = args.first || I18n.l(date_or_time, format: format)
684
- datetime = date_or_time.acts_like?(:time) ? date_or_time.xmlschema : date_or_time.iso8601
685
686
686
- content_tag("time".freeze, content, options.reverse_merge(datetime: datetime), &block)
687
+ content_tag("time", content, options.reverse_merge(datetime: date_or_time.iso8601), &block)
687
688
end
688
689
689
690
private
@@ -702,7 +703,7 @@ module ActionView
702
703
class DateTimeSelector #:nodoc:
703
704
include ActionView::Helpers::TagHelper
704
705
705
- DEFAULT_PREFIX = "date".freeze
706
+ DEFAULT_PREFIX = "date"
706
707
POSITION = {
707
708
year: 1, month: 2, day: 3, hour: 4, minute: 5, second: 6
708
709
}.freeze
@@ -823,7 +824,7 @@ module ActionView
823
824
1.upto(12) do |month_number|
824
825
options = { value: month_number }
825
826
options[:selected] = "selected" if month == month_number
826
- month_options << content_tag("option".freeze, month_name(month_number), options) + "\n"
827
+ month_options << content_tag("option", month_name(month_number), options) + "\n"
827
828
end
828
829
build_select(:month, month_options.join)
829
830
end
@@ -851,7 +852,7 @@ module ActionView
851
852
raise ArgumentError, "There are too many years options to be built. Are you sure you haven't mistyped something? You can provide the :max_years_allowed parameter."
852
853
end
853
854
854
- build_options_and_select(:year, val, options)
855
+ build_select(:year, build_year_options(val, options))
855
856
end
856
857
end
857
858
@@ -934,6 +935,21 @@ module ActionView
934
935
end
935
936
end
936
937
938
+ # Looks up year names by number.
939
+ #
940
+ # year_name(1998) # => 1998
941
+ #
942
+ # If the <tt>:year_format</tt> option is passed:
943
+ #
944
+ # year_name(1998) # => "Heisei 10"
945
+ def year_name(number)
946
+ if year_format_lambda = @options[:year_format]
947
+ year_format_lambda.call(number)
948
+ else
949
+ number
950
+ end
951
+ end
952
+
937
953
def date_order
938
954
@date_order ||= @options[:order] || translated_date_order
939
955
end
@@ -990,7 +1006,35 @@ module ActionView
990
1006
tag_options[:selected] = "selected" if selected == i
991
1007
text = options[:use_two_digit_numbers] ? sprintf("%02d", i) : value
992
1008
text = options[:ampm] ? AMPM_TRANSLATION[i] : text
993
- select_options << content_tag("option".freeze, text, tag_options)
1009
+ select_options << content_tag("option", text, tag_options)
1010
+ end
1011
+
1012
+ (select_options.join("\n") + "\n").html_safe
1013
+ end
1014
+
1015
+ # Build select option HTML for year.
1016
+ # If <tt>year_format</tt> option is not passed
1017
+ # build_year_options(1998, start: 1998, end: 2000)
1018
+ # => "<option value="1998" selected="selected">1998</option>
1019
+ # <option value="1999">1999</option>
1020
+ # <option value="2000">2000</option>"
1021
+ #
1022
+ # If <tt>year_format</tt> option is passed
1023
+ # build_year_options(1998, start: 1998, end: 2000, year_format: ->year { "Heisei #{ year - 1988 }" })
1024
+ # => "<option value="1998" selected="selected">Heisei 10</option>
1025
+ # <option value="1999">Heisei 11</option>
1026
+ # <option value="2000">Heisei 12</option>"
1027
+ def build_year_options(selected, options = {})
1028
+ start = options.delete(:start)
1029
+ stop = options.delete(:end)
1030
+ step = options.delete(:step)
1031
+
1032
+ select_options = []
1033
+ start.step(stop, step) do |value|
1034
+ tag_options = { value: value }
1035
+ tag_options[:selected] = "selected" if selected == value
1036
+ text = year_name(value)
1037
+ select_options << content_tag("option", text, tag_options)
994
1038
end
995
1039
996
1040
(select_options.join("\n") + "\n").html_safe
@@ -1009,12 +1053,12 @@ module ActionView
1009
1053
select_options[:disabled] = "disabled" if @options[:disabled]
1010
1054
select_options[:class] = css_class_attribute(type, select_options[:class], @options[:with_css_classes]) if @options[:with_css_classes]
1011
1055
1012
- select_html = "\n".dup
1013
- select_html << content_tag("option".freeze, "", value: "") + "\n" if @options[:include_blank]
1056
+ select_html = +"\n"
1057
+ select_html << content_tag("option", "", value: "") + "\n" if @options[:include_blank]
1014
1058
select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
1015
1059
select_html << select_options_as_html
1016
1060
1017
- (content_tag("select".freeze, select_html.html_safe, select_options) + "\n").html_safe
1061
+ (content_tag("select", select_html.html_safe, select_options) + "\n").html_safe
1018
1062
end
1019
1063
1020
1064
# Builds the css class value for the select element
@@ -1047,7 +1091,7 @@ module ActionView
1047
1091
I18n.translate(:"datetime.prompts.#{type}", locale: @options[:locale])
1048
1092
end
1049
1093
1050
- prompt ? content_tag("option".freeze, prompt, value: "") : ""
1094
+ prompt ? content_tag("option", prompt, value: "") : ""
1051
1095
end
1052
1096
1053
1097
# Builds hidden input tag for date part and value.
@@ -1091,7 +1135,7 @@ module ActionView
1091
1135
# Given an ordering of datetime components, create the selection HTML
1092
1136
# and join them with their appropriate separators.
1093
1137
def build_selects_from_types(order)
1094
- select = "".dup
1138
+ select = +""
1095
1139
first_visible = order.find { |type| !@options[:"discard_#{type}"] }
1096
1140
order.reverse_each do |type|
1097
1141
separator = separator(type) unless type == first_visible # don't add before first visible field
data/lib/action_view/helpers/form_helper.rb CHANGED
@@ -590,6 +590,9 @@ module ActionView
590
590
# Skipped if a <tt>:url</tt> is passed.
591
591
# * <tt>:scope</tt> - The scope to prefix input field names with and
592
592
# thereby how the submitted parameters are grouped in controllers.
593
+ # * <tt>:namespace</tt> - A namespace for your form to ensure uniqueness of
594
+ # id attributes on form elements. The namespace attribute will be prefixed
595
+ # with underscore on the generated HTML id.
593
596
# * <tt>:model</tt> - A model object to infer the <tt>:url</tt> and
594
597
# <tt>:scope</tt> by, plus fill out input field values.
595
598
# So if a +title+ attribute is set to "Ahoy!" then a +title+ input
@@ -610,8 +613,8 @@ module ActionView
610
613
# unnecessary unless you support browsers without JavaScript.
611
614
# * <tt>:local</tt> - By default form submits are remote and unobtrusive XHRs.
612
615
# Disable remote submits with <tt>local: true</tt>.
613
- # * <tt>:skip_enforcing_utf8</tt> - By default a hidden field named +utf8+
614
- # is output to enforce UTF-8 submits. Set to true to skip the field.
616
+ # * <tt>:skip_enforcing_utf8</tt> - If set to true, a hidden input with name
617
+ # utf8 is not output.
615
618
# * <tt>:builder</tt> - Override the object used to build the form.
616
619
# * <tt>:id</tt> - Optional HTML id attribute.
617
620
# * <tt>:class</tt> - Optional HTML class attribute.
@@ -1127,6 +1130,9 @@ module ActionView
1127
1130
# text_field(:post, :title, class: "create_input")
1128
1131
# # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />
1129
1132
#
1133
+ # text_field(:post, :title, maxlength: 30, class: "title_input")
1134
+ # # => <input type="text" id="post_title" name="post[title]" maxlength="30" size="30" value="#{@post.title}" class="title_input" />
1135
+ #
1130
1136
# text_field(:session, :user, onchange: "if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }")
1131
1137
# # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange="if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }"/>
1132
1138
#
@@ -1519,10 +1525,10 @@ module ActionView
1519
1525
1520
1526
private
1521
1527
def html_options_for_form_with(url_for_options = nil, model = nil, html: {}, local: !form_with_generates_remote_forms,
1522
- skip_enforcing_utf8: false, **options)
1528
+ skip_enforcing_utf8: nil, **options)
1523
1529
html_options = options.slice(:id, :class, :multipart, :method, :data).merge(html)
1524
1530
html_options[:method] ||= :patch if model.respond_to?(:persisted?) && model.persisted?
1525
- html_options[:enforce_utf8] = !skip_enforcing_utf8
1531
+ html_options[:enforce_utf8] = !skip_enforcing_utf8 unless skip_enforcing_utf8.nil?
1526
1532
1527
1533
html_options[:enctype] = "multipart/form-data" if html_options.delete(:multipart)
1528
1534
@@ -1674,6 +1680,227 @@ module ActionView
1674
1680
@index = options[:index] || options[:child_index]
1675
1681
end
1676
1682
1683
+ ##
1684
+ # :method: text_field
1685
+ #
1686
+ # :call-seq: text_field(method, options = {})
1687
+ #
1688
+ # Wraps ActionView::Helpers::FormHelper#text_field for form builders:
1689
+ #
1690
+ # <%= form_with model: @user do |f| %>
1691
+ # <%= f.text_field :name %>
1692
+ # <% end %>
1693
+ #
1694
+ # Please refer to the documentation of the base helper for details.
1695
+
1696
+ ##
1697
+ # :method: password_field
1698
+ #
1699
+ # :call-seq: password_field(method, options = {})
1700
+ #
1701
+ # Wraps ActionView::Helpers::FormHelper#password_field for form builders:
1702
+ #
1703
+ # <%= form_with model: @user do |f| %>
1704
+ # <%= f.password_field :password %>
1705
+ # <% end %>
1706
+ #
1707
+ # Please refer to the documentation of the base helper for details.
1708
+
1709
+ ##
1710
+ # :method: text_area
1711
+ #
1712
+ # :call-seq: text_area(method, options = {})
1713
+ #
1714
+ # Wraps ActionView::Helpers::FormHelper#text_area for form builders:
1715
+ #
1716
+ # <%= form_with model: @user do |f| %>
1717
+ # <%= f.text_area :detail %>
1718
+ # <% end %>
1719
+ #
1720
+ # Please refer to the documentation of the base helper for details.
1721
+
1722
+ ##
1723
+ # :method: color_field
1724
+ #
1725
+ # :call-seq: color_field(method, options = {})
1726
+ #
1727
+ # Wraps ActionView::Helpers::FormHelper#color_field for form builders:
1728
+ #
1729
+ # <%= form_with model: @user do |f| %>
1730
+ # <%= f.color_field :favorite_color %>
1731
+ # <% end %>
1732
+ #
1733
+ # Please refer to the documentation of the base helper for details.
1734
+
1735
+ ##
1736
+ # :method: search_field
1737
+ #
1738
+ # :call-seq: search_field(method, options = {})
1739
+ #
1740
+ # Wraps ActionView::Helpers::FormHelper#search_field for form builders:
1741
+ #
1742
+ # <%= form_with model: @user do |f| %>
1743
+ # <%= f.search_field :name %>
1744
+ # <% end %>
1745
+ #
1746
+ # Please refer to the documentation of the base helper for details.
1747
+
1748
+ ##
1749
+ # :method: telephone_field
1750
+ #
1751
+ # :call-seq: telephone_field(method, options = {})
1752
+ #
1753
+ # Wraps ActionView::Helpers::FormHelper#telephone_field for form builders:
1754
+ #
1755
+ # <%= form_with model: @user do |f| %>
1756
+ # <%= f.telephone_field :phone %>
1757
+ # <% end %>
1758
+ #
1759
+ # Please refer to the documentation of the base helper for details.
1760
+
1761
+ ##
1762
+ # :method: phone_field
1763
+ #
1764
+ # :call-seq: phone_field(method, options = {})
1765
+ #
1766
+ # Wraps ActionView::Helpers::FormHelper#phone_field for form builders:
1767
+ #
1768
+ # <%= form_with model: @user do |f| %>
1769
+ # <%= f.phone_field :phone %>
1770
+ # <% end %>
1771
+ #
1772
+ # Please refer to the documentation of the base helper for details.
1773
+
1774
+ ##
1775
+ # :method: date_field
1776
+ #
1777
+ # :call-seq: date_field(method, options = {})
1778
+ #
1779
+ # Wraps ActionView::Helpers::FormHelper#date_field for form builders:
1780
+ #
1781
+ # <%= form_with model: @user do |f| %>
1782
+ # <%= f.date_field :born_on %>
1783
+ # <% end %>
1784
+ #
1785
+ # Please refer to the documentation of the base helper for details.
1786
+
1787
+ ##
1788
+ # :method: time_field
1789
+ #
1790
+ # :call-seq: time_field(method, options = {})
1791
+ #
1792
+ # Wraps ActionView::Helpers::FormHelper#time_field for form builders:
1793
+ #
1794
+ # <%= form_with model: @user do |f| %>
1795
+ # <%= f.time_field :borned_at %>
1796
+ # <% end %>
1797
+ #
1798
+ # Please refer to the documentation of the base helper for details.
1799
+
1800
+ ##
1801
+ # :method: datetime_field
1802
+ #
1803
+ # :call-seq: datetime_field(method, options = {})
1804
+ #
1805
+ # Wraps ActionView::Helpers::FormHelper#datetime_field for form builders:
1806
+ #
1807
+ # <%= form_with model: @user do |f| %>
1808
+ # <%= f.datetime_field :graduation_day %>
1809
+ # <% end %>
1810
+ #
1811
+ # Please refer to the documentation of the base helper for details.
1812
+
1813
+ ##
1814
+ # :method: datetime_local_field
1815
+ #
1816
+ # :call-seq: datetime_local_field(method, options = {})
1817
+ #
1818
+ # Wraps ActionView::Helpers::FormHelper#datetime_local_field for form builders:
1819
+ #
1820
+ # <%= form_with model: @user do |f| %>
1821
+ # <%= f.datetime_local_field :graduation_day %>
1822
+ # <% end %>
1823
+ #
1824
+ # Please refer to the documentation of the base helper for details.
1825
+
1826
+ ##
1827
+ # :method: month_field
1828
+ #
1829
+ # :call-seq: month_field(method, options = {})
1830
+ #
1831
+ # Wraps ActionView::Helpers::FormHelper#month_field for form builders:
1832
+ #
1833
+ # <%= form_with model: @user do |f| %>
1834
+ # <%= f.month_field :birthday_month %>
1835
+ # <% end %>
1836
+ #
1837
+ # Please refer to the documentation of the base helper for details.
1838
+
1839
+ ##
1840
+ # :method: week_field
1841
+ #
1842
+ # :call-seq: week_field(method, options = {})
1843
+ #
1844
+ # Wraps ActionView::Helpers::FormHelper#week_field for form builders:
1845
+ #
1846
+ # <%= form_with model: @user do |f| %>
1847
+ # <%= f.week_field :birthday_week %>
1848
+ # <% end %>
1849
+ #
1850
+ # Please refer to the documentation of the base helper for details.
1851
+
1852
+ ##
1853
+ # :method: url_field
1854
+ #
1855
+ # :call-seq: url_field(method, options = {})
1856
+ #
1857
+ # Wraps ActionView::Helpers::FormHelper#url_field for form builders:
1858
+ #
1859
+ # <%= form_with model: @user do |f| %>
1860
+ # <%= f.url_field :homepage %>
1861
+ # <% end %>
1862
+ #
1863
+ # Please refer to the documentation of the base helper for details.
1864
+
1865
+ ##
1866
+ # :method: email_field
1867
+ #
1868
+ # :call-seq: email_field(method, options = {})
1869
+ #
1870
+ # Wraps ActionView::Helpers::FormHelper#email_field for form builders:
1871
+ #
1872
+ # <%= form_with model: @user do |f| %>
1873
+ # <%= f.email_field :address %>
1874
+ # <% end %>
1875
+ #
1876
+ # Please refer to the documentation of the base helper for details.
1877
+
1878
+ ##
1879
+ # :method: number_field
1880
+ #
1881
+ # :call-seq: number_field(method, options = {})
1882
+ #
1883
+ # Wraps ActionView::Helpers::FormHelper#number_field for form builders:
1884
+ #
1885
+ # <%= form_with model: @user do |f| %>
1886
+ # <%= f.number_field :age %>
1887
+ # <% end %>
1888
+ #
1889
+ # Please refer to the documentation of the base helper for details.
1890
+
1891
+ ##
1892
+ # :method: range_field
1893
+ #
1894
+ # :call-seq: range_field(method, options = {})
1895
+ #
1896
+ # Wraps ActionView::Helpers::FormHelper#range_field for form builders:
1897
+ #
1898
+ # <%= form_with model: @user do |f| %>
1899
+ # <%= f.range_field :age %>
1900
+ # <% end %>
1901
+ #
1902
+ # Please refer to the documentation of the base helper for details.
1903
+
1677
1904
(field_helpers - [:label, :check_box, :radio_button, :fields_for, :fields, :hidden_field, :file_field]).each do |selector|
1678
1905
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
1679
1906
def #{selector}(method, options = {}) # def text_field(method, options = {})
@@ -2247,7 +2474,7 @@ module ActionView
2247
2474
@template.button_tag(value, options, &block)
2248
2475
end
2249
2476
2250
- def emitted_hidden_id?
2477
+ def emitted_hidden_id? # :nodoc:
2251
2478
@emitted_hidden_id ||= nil
2252
2479
end
2253
2480
@@ -2267,7 +2494,12 @@ module ActionView
2267
2494
end
2268
2495
2269
2496
defaults = []
2270
- defaults << :"helpers.submit.#{object_name}.#{key}"
2497
+ # Object is a model and it is not overwritten by as and scope option.
2498
+ if object.respond_to?(:model_name) && object_name.to_s == model.downcase
2499
+ defaults << :"helpers.submit.#{object.model_name.i18n_key}.#{key}"
2500
+ else
2501
+ defaults << :"helpers.submit.#{object_name}.#{key}"
2502
+ end
2271
2503
defaults << :"helpers.submit.#{key}"
2272
2504
defaults << "#{key.to_s.humanize} #{model}"
2273
2505
data/lib/action_view/helpers/form_options_helper.rb CHANGED
@@ -16,7 +16,7 @@ module ActionView
16
16
#
17
17
# * <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.
18
18
#
19
- # select("post", "category", Post::CATEGORIES, {include_blank: true})
19
+ # select("post", "category", Post::CATEGORIES, { include_blank: true })
20
20
#
21
21
# could become:
22
22
#
@@ -30,7 +30,7 @@ module ActionView
30
30
#
31
31
# Example with <tt>@post.person_id => 2</tt>:
32
32
#
33
- # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {include_blank: 'None'})
33
+ # select("post", "person_id", Person.all.collect { |p| [ p.name, p.id ] }, { include_blank: 'None' })
34
34
#
35
35
# could become:
36
36
#
@@ -43,7 +43,7 @@ module ActionView
43
43
#
44
44
# * <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.
45
45
#
46
- # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {prompt: 'Select Person'})
46
+ # select("post", "person_id", Person.all.collect { |p| [ p.name, p.id ] }, { prompt: 'Select Person' })
47
47
#
48
48
# could become:
49
49
#
@@ -69,7 +69,7 @@ module ActionView
69
69
#
70
70
# * <tt>:disabled</tt> - can be a single value or an array of values that will be disabled options in the final output.
71
71
#
72
- # select("post", "category", Post::CATEGORIES, {disabled: 'restricted'})
72
+ # select("post", "category", Post::CATEGORIES, { disabled: 'restricted' })
73
73
#
74
74
# could become:
75
75
#
@@ -82,7 +82,7 @@ module ActionView
82
82
#
83
83
# 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.
84
84
#
85
- # collection_select(:post, :category_id, Category.all, :id, :name, {disabled: -> (category) { category.archived? }})
85
+ # collection_select(:post, :category_id, Category.all, :id, :name, { disabled: -> (category) { category.archived? } })
86
86
#
87
87
# If the categories "2008 stuff" and "Christmas" return true when the method <tt>archived?</tt> is called, this would return:
88
88
# <select name="post[category_id]" id="post_category_id">
@@ -107,7 +107,7 @@ module ActionView
107
107
#
108
108
# For example:
109
109
#
110
- # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, { include_blank: true })
110
+ # select("post", "person_id", Person.all.collect { |p| [ p.name, p.id ] }, { include_blank: true })
111
111
#
112
112
# would become:
113
113
#
@@ -323,12 +323,12 @@ module ActionView
323
323
#
324
324
# You can optionally provide HTML attributes as the last element of the array.
325
325
#
326
- # options_for_select([ "Denmark", ["USA", {class: 'bold'}], "Sweden" ], ["USA", "Sweden"])
326
+ # options_for_select([ "Denmark", ["USA", { class: 'bold' }], "Sweden" ], ["USA", "Sweden"])
327
327
# # => <option value="Denmark">Denmark</option>
328
328
# # => <option value="USA" class="bold" selected="selected">USA</option>
329
329
# # => <option value="Sweden" selected="selected">Sweden</option>
330
330
#
331
- # options_for_select([["Dollar", "quot;, {class: "bold"}], ["Kroner", "DKK", {onclick: "alert('HI');"}]])
331
+ # options_for_select([["Dollar", "quot;, { class: "bold" }], ["Kroner", "DKK", { onclick: "alert('HI');" }]])
332
332
# # => <option value="quot; class="bold">Dollar</option>
333
333
# # => <option value="DKK" onclick="alert('HI');">Kroner</option>
334
334
#
@@ -463,7 +463,7 @@ module ActionView
463
463
option_tags = options_from_collection_for_select(
464
464
value_for_collection(group, group_method), option_key_method, option_value_method, selected_key)
465
465
466
- content_tag("optgroup".freeze, option_tags, label: value_for_collection(group, group_label_method))
466
+ content_tag("optgroup", option_tags, label: value_for_collection(group, group_label_method))
467
467
end.join.html_safe
468
468
end
469
469
@@ -535,7 +535,7 @@ module ActionView
535
535
body = "".html_safe
536
536
537
537
if prompt
538
- body.safe_concat content_tag("option".freeze, prompt_text(prompt), value: "")
538
+ body.safe_concat content_tag("option", prompt_text(prompt), value: "")
539
539
end
540
540
541
541
grouped_options.each do |container|
@@ -548,7 +548,7 @@ module ActionView
548
548
end
549
549
550
550
html_attributes = { label: label }.merge!(html_attributes)
551
- body.safe_concat content_tag("optgroup".freeze, options_for_select(container, selected_key), html_attributes)
551
+ body.safe_concat content_tag("optgroup", options_for_select(container, selected_key), html_attributes)
552
552
end
553
553
554
554
body
@@ -584,7 +584,7 @@ module ActionView
584
584
end
585
585
586
586
zone_options.safe_concat options_for_select(convert_zones[priority_zones], selected)
587
- zone_options.safe_concat content_tag("option".freeze, "-------------", value: "", disabled: true)
587
+ zone_options.safe_concat content_tag("option", "-------------", value: "", disabled: true)
588
588
zone_options.safe_concat "\n"
589
589
590
590
zones = zones - priority_zones
@@ -654,7 +654,7 @@ module ActionView
654
654
#
655
655
# ==== Gotcha
656
656
#
657
- # The HTML specification says when nothing is select on a collection of radio buttons
657
+ # The HTML specification says when nothing is selected on a collection of radio buttons
658
658
# web browsers do not send any value to server.
659
659
# Unfortunately this introduces a gotcha:
660
660
# if a +User+ model has a +category_id+ field and in the form no category is selected, no +category_id+ parameter is sent. So,
@@ -794,7 +794,7 @@ module ActionView
794
794
def extract_values_from_collection(collection, value_method, selected)
795
795
if selected.is_a?(Proc)
796
796
collection.map do |element|
797
- element.send(value_method) if selected.call(element)
797
+ public_or_deprecated_send(element, value_method) if selected.call(element)
798
798
end.compact
799
799
else
800
800
selected
@@ -802,7 +802,15 @@ module ActionView
802
802
end
803
803
804
804
def value_for_collection(item, value)
805
- value.respond_to?(:call) ? value.call(item) : item.send(value)
805
+ value.respond_to?(:call) ? value.call(item) : public_or_deprecated_send(item, value)
806
+ end
807
+
808
+ def public_or_deprecated_send(item, value)
809
+ item.public_send(value)
810
+ rescue NoMethodError
811
+ raise unless item.respond_to?(value, true) && !item.respond_to?(value)
812
+ ActiveSupport::Deprecation.warn "Using private methods from view helpers is deprecated (calling private #{item.class}##{value})"
813
+ item.send(value)
806
814
end
807
815
808
816
def prompt_text(prompt)
data/lib/action_view/helpers/form_tag_helper.rb CHANGED
@@ -22,6 +22,8 @@ module ActionView
22
22
mattr_accessor :embed_authenticity_token_in_remote_forms
23
23
self.embed_authenticity_token_in_remote_forms = nil
24
24
25
+ mattr_accessor :default_enforce_utf8, default: true
26
+
25
27
# Starts a form tag that points the action to a url configured with <tt>url_for_options</tt> just like
26
28
# ActionController::Base#url_for. The method for the form defaults to POST.
27
29
#
@@ -144,15 +146,15 @@ module ActionView
144
146
end
145
147
146
148
if include_blank
147
- option_tags = content_tag("option".freeze, include_blank, options_for_blank_options_tag).safe_concat(option_tags)
149
+ option_tags = content_tag("option", include_blank, options_for_blank_options_tag).safe_concat(option_tags)
148
150
end
149
151
end
150
152
151
153
if prompt = options.delete(:prompt)
152
- option_tags = content_tag("option".freeze, prompt, value: "").safe_concat(option_tags)
154
+ option_tags = content_tag("option", prompt, value: "").safe_concat(option_tags)
153
155
end
154
156
155
- content_tag "select".freeze, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
157
+ content_tag "select", option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
156
158
end
157
159
158
160
# Creates a standard text field; use these text fields to input smaller chunks of text like a username
@@ -387,8 +389,8 @@ module ActionView
387
389
# * Any other key creates standard HTML options for the tag.
388
390
#
389
391
# ==== Examples
390
- # radio_button_tag 'gender', 'male'
391
- # # => <input id="gender_male" name="gender" type="radio" value="male" />
392
+ # radio_button_tag 'favorite_color', 'maroon'
393
+ # # => <input id="favorite_color_maroon" name="favorite_color" type="radio" value="maroon" />
392
394
#
393
395
# radio_button_tag 'receive_updates', 'no', true
394
396
# # => <input checked="checked" id="receive_updates_no" name="receive_updates" type="radio" value="no" />
@@ -575,7 +577,7 @@ module ActionView
575
577
# # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
576
578
def field_set_tag(legend = nil, options = nil, &block)
577
579
output = tag(:fieldset, options, true)
578
- output.safe_concat(content_tag("legend".freeze, legend)) unless legend.blank?
580
+ output.safe_concat(content_tag("legend", legend)) unless legend.blank?
579
581
output.concat(capture(&block)) if block_given?
580
582
output.safe_concat("</fieldset>")
581
583
end
@@ -867,7 +869,7 @@ module ActionView
867
869
})
868
870
end
869
871
870
- if html_options.delete("enforce_utf8") { true }
872
+ if html_options.delete("enforce_utf8") { default_enforce_utf8 }
871
873
utf8_enforcer_tag + method_tag
872
874
else
873
875
method_tag
data/lib/action_view/helpers/javascript_helper.rb CHANGED
@@ -15,8 +15,8 @@ module ActionView
15
15
"'" => "\\'"
16
16
}
17
17
18
- JS_ESCAPE_MAP["\342\200\250".dup.force_encoding(Encoding::UTF_8).encode!] = "&#x2028;"
19
- JS_ESCAPE_MAP["\342\200\251".dup.force_encoding(Encoding::UTF_8).encode!] = "&#x2029;"
18
+ JS_ESCAPE_MAP[(+"\342\200\250").force_encoding(Encoding::UTF_8).encode!] = "&#x2028;"
19
+ JS_ESCAPE_MAP[(+"\342\200\251").force_encoding(Encoding::UTF_8).encode!] = "&#x2029;"
20
20
21
21
# Escapes carriage returns and single and double quotes for JavaScript segments.
22
22
#
@@ -25,12 +25,13 @@ module ActionView
25
25
#
26
26
# $('some_element').replaceWith('<%= j render 'some/element_template' %>');
27
27
def escape_javascript(javascript)
28
- if javascript
29
- result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u) { |match| JS_ESCAPE_MAP[match] }
30
- javascript.html_safe? ? result.html_safe : result
28
+ javascript = javascript.to_s
29
+ if javascript.empty?
30
+ result = ""
31
31
else
32
- ""
32
+ result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u) { |match| JS_ESCAPE_MAP[match] }
33
33
end
34
+ javascript.html_safe? ? result.html_safe : result
34
35
end
35
36
36
37
alias_method :j, :escape_javascript
@@ -65,7 +66,7 @@ module ActionView
65
66
# <% end -%>
66
67
#
67
68
# If you have a content security policy enabled then you can add an automatic
68
- # nonce value by passing +nonce: true+ as part of +html_options+. Example:
69
+ # nonce value by passing <tt>nonce: true</tt> as part of +html_options+. Example:
69
70
#
70
71
# <%= javascript_tag nonce: true do -%>
71
72
# alert('All is good')
@@ -83,7 +84,7 @@ module ActionView
83
84
html_options[:nonce] = content_security_policy_nonce
84
85
end
85
86
86
- content_tag("script".freeze, javascript_cdata_section(content), html_options)
87
+ content_tag("script", javascript_cdata_section(content), html_options)
87
88
end
88
89
89
90
def javascript_cdata_section(content) #:nodoc:
data/lib/action_view/helpers/number_helper.rb CHANGED
@@ -100,6 +100,9 @@ module ActionView
100
100
# absolute value of the number.
101
101
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
102
102
# the argument is invalid.
103
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
104
+ # insignificant zeros after the decimal separator (defaults to
105
+ # +false+).
103
106
#
104
107
# ==== Examples
105
108
#
@@ -117,6 +120,8 @@ module ActionView
117
120
# # => R$1234567890,50
118
121
# number_to_currency(1234567890.50, unit: "Rquot;, separator: ",", delimiter: "", format: "%n %u")
119
122
# # => 1234567890,50 R$
123
+ # number_to_currency(1234567890.50, strip_insignificant_zeros: true)
124
+ # # => "$1,234,567,890.5"
120
125
def number_to_currency(number, options = {})
121
126
delegate_number_helper_method(:number_to_currency, number, options)
122
127
end
data/lib/action_view/helpers/record_tag_helper.rb DELETED
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActionView
4
- module Helpers #:nodoc:
5
- module RecordTagHelper
6
- def div_for(*) # :nodoc:
7
- raise NoMethodError, "The `div_for` method has been removed from " \
8
- "Rails. To continue using it, add the `record_tag_helper` gem to " \
9
- "your Gemfile:\n" \
10
- " gem 'record_tag_helper', '~> 1.0'\n" \
11
- "Consult the Rails upgrade guide for details."
12
- end
13
-
14
- def content_tag_for(*) # :nodoc:
15
- raise NoMethodError, "The `content_tag_for` method has been removed from " \
16
- "Rails. To continue using it, add the `record_tag_helper` gem to " \
17
- "your Gemfile:\n" \
18
- " gem 'record_tag_helper', '~> 1.0'\n" \
19
- "Consult the Rails upgrade guide for details."
20
- end
21
- end
22
- end
23
- end
data/lib/action_view/helpers/sanitize_helper.rb CHANGED
@@ -10,7 +10,7 @@ module ActionView
10
10
# These helper methods extend Action View making them callable within your template files.
11
11
module SanitizeHelper
12
12
extend ActiveSupport::Concern
13
- # Sanitizes HTML input, stripping all tags and attributes that aren't whitelisted.
13
+ # Sanitizes HTML input, stripping all but known-safe tags and attributes.
14
14
#
15
15
# It also strips href/src attributes with unsafe protocols like
16
16
# <tt>javascript:</tt>, while also protecting against attempts to use Unicode,
@@ -40,7 +40,7 @@ module ActionView
40
40
#
41
41
# <%= sanitize @comment.body %>
42
42
#
43
- # Providing custom whitelisted tags and attributes:
43
+ # Providing custom lists of permitted tags and attributes:
44
44
#
45
45
# <%= sanitize @comment.body, tags: %w(strong em a), attributes: %w(href) %>
46
46
#
@@ -126,7 +126,7 @@ module ActionView
126
126
attr_writer :full_sanitizer, :link_sanitizer, :white_list_sanitizer
127
127
128
128
# Vendors the full, link and white list sanitizers.
129
- # Provided strictly for compatibility and can be removed in Rails 5.1.
129
+ # Provided strictly for compatibility and can be removed in Rails 6.
130
130
def sanitizer_vendor
131
131
Rails::Html::Sanitizer
132
132
end
data/lib/action_view/helpers/tag_helper.rb CHANGED
@@ -58,7 +58,7 @@ module ActionView
58
58
59
59
def tag_options(options, escape = true)
60
60
return if options.blank?
61
- output = "".dup
61
+ output = +""
62
62
sep = " "
63
63
options.each_pair do |key, value|
64
64
if TAG_PREFIXES.include?(key) && value.is_a?(Hash)
@@ -86,11 +86,12 @@ module ActionView
86
86
87
87
def tag_option(key, value, escape)
88
88
if value.is_a?(Array)
89
- value = escape ? safe_join(value, " ".freeze) : value.join(" ".freeze)
89
+ value = escape ? safe_join(value, " ") : value.join(" ")
90
90
else
91
- value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s
91
+ value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s.dup
92
92
end
93
- %(#{key}="#{value.gsub('"'.freeze, '&quot;'.freeze)}")
93
+ value.gsub!('"', "&quot;")
94
+ %(#{key}="#{value}")
94
95
end
95
96
96
97
private
@@ -227,10 +228,10 @@ module ActionView
227
228
# tag("img", src: "open & shut.png")
228
229
# # => <img src="open &amp; shut.png" />
229
230
#
230
- # tag("img", {src: "open &amp; shut.png"}, false, false)
231
+ # tag("img", { src: "open &amp; shut.png" }, false, false)
231
232
# # => <img src="open &amp; shut.png" />
232
233
#
233
- # tag("div", data: {name: 'Stephen', city_state: %w(Chicago IL)})
234
+ # tag("div", data: { name: 'Stephen', city_state: %w(Chicago IL) })
234
235
# # => <div data-name="Stephen" data-city-state="[&quot;Chicago&quot;,&quot;IL&quot;]" />
235
236
def tag(name = nil, options = nil, open = false, escape = true)
236
237
if name.nil?
data/lib/action_view/helpers/tags/base.rb CHANGED
@@ -109,11 +109,11 @@ module ActionView
109
109
# a little duplication to construct less strings
110
110
case
111
111
when @object_name.empty?
112
- "#{sanitized_method_name}#{"[]" if multiple}"
112
+ "#{sanitized_method_name}#{multiple ? "[]" : ""}"
113
113
when index
114
- "#{@object_name}[#{index}][#{sanitized_method_name}]#{"[]" if multiple}"
114
+ "#{@object_name}[#{index}][#{sanitized_method_name}]#{multiple ? "[]" : ""}"
115
115
else
116
- "#{@object_name}[#{sanitized_method_name}]#{"[]" if multiple}"
116
+ "#{@object_name}[#{sanitized_method_name}]#{multiple ? "[]" : ""}"
117
117
end
118
118
end
119
119
@@ -170,7 +170,11 @@ module ActionView
170
170
option_tags = tag_builder.content_tag_string("option", options[:include_blank].kind_of?(String) ? options[:include_blank] : nil, value: "") + "\n" + option_tags
171
171
end
172
172
if value.blank? && options[:prompt]
173
- option_tags = tag_builder.content_tag_string("option", prompt_text(options[:prompt]), value: "") + "\n" + option_tags
173
+ tag_options = { value: "" }.tap do |prompt_opts|
174
+ prompt_opts[:disabled] = true if options[:disabled] == ""
175
+ prompt_opts[:selected] = true if options[:selected] == ""
176
+ end
177
+ option_tags = tag_builder.content_tag_string("option", prompt_text(options[:prompt]), tag_options) + "\n" + option_tags
174
178
end
175
179
option_tags
176
180
end
data/lib/action_view/helpers/tags/color_field.rb CHANGED
@@ -15,7 +15,7 @@ module ActionView
15
15
16
16
def validate_color_string(string)
17
17
regex = /#[0-9a-fA-F]{6}/
18
- if regex.match(string)
18
+ if regex.match?(string)
19
19
string.downcase
20
20
else
21
21
"#000000"
data/lib/action_view/helpers/tags/translator.rb CHANGED
@@ -16,13 +16,8 @@ module ActionView
16
16
translated_attribute || human_attribute_name
17
17
end
18
18
19
- # TODO Change this to private once we've dropped Ruby 2.2 support.
20
- # Workaround for Ruby 2.2 "private attribute?" warning.
21
- protected
22
-
23
- attr_reader :object_name, :method_and_value, :scope, :model
24
-
25
19
private
20
+ attr_reader :object_name, :method_and_value, :scope, :model
26
21
27
22
def i18n_default
28
23
if model
data/lib/action_view/helpers/text_helper.rb CHANGED
@@ -188,7 +188,7 @@ module ActionView
188
188
189
189
unless separator.empty?
190
190
text.split(separator).each do |value|
191
- if value.match(regex)
191
+ if value.match?(regex)
192
192
phrase = value
193
193
break
194
194
end
@@ -228,7 +228,7 @@ module ActionView
228
228
# pluralize(2, 'Person', locale: :de)
229
229
# # => 2 Personen
230
230
def pluralize(count, singular, plural_arg = nil, plural: plural_arg, locale: I18n.locale)
231
- word = if (count == 1 || count.to_s =~ /^1(\.0+)?#x2F;)
231
+ word = if count == 1 || count.to_s =~ /^1(\.0+)?#x2F;
232
232
singular
233
233
else
234
234
plural || singular.pluralize(locale)
@@ -259,7 +259,7 @@ module ActionView
259
259
# # => Once\r\nupon\r\na\r\ntime
260
260
def word_wrap(text, line_width: 80, break_sequence: "\n")
261
261
text.split("\n").collect! do |line|
262
- line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1#{break_sequence}").strip : line
262
+ line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1#{break_sequence}").rstrip : line
263
263
end * break_sequence
264
264
end
265
265
data/lib/action_view/helpers/translation_helper.rb CHANGED
@@ -59,11 +59,9 @@ module ActionView
59
59
# they can provide HTML values for.
60
60
def translate(key, options = {})
61
61
options = options.dup
62
- has_default = options.has_key?(:default)
63
- remaining_defaults = Array(options.delete(:default)).compact
64
-
62
+ if options.has_key?(:default)
63
+ remaining_defaults = Array(options.delete(:default)).compact
64
+ options[:default] = remaining_defaults unless remaining_defaults.first.kind_of?(Symbol)
65
- if has_default && !remaining_defaults.first.kind_of?(Symbol)
66
- options[:default] = remaining_defaults
67
65
end
68
66
69
67
# If the user has explicitly decided to NOT raise errors, pass that option to I18n.
@@ -85,8 +83,11 @@ module ActionView
85
83
end
86
84
end
87
85
translation = I18n.translate(scope_key_by_partial(key), html_safe_options.merge(raise: i18n_raise))
88
-
89
- translation.respond_to?(:html_safe) ? translation.html_safe : translation
86
+ if translation.respond_to?(:map)
87
+ translation.map { |element| element.respond_to?(:html_safe) ? element.html_safe : element }
88
+ else
89
+ translation.respond_to?(:html_safe) ? translation.html_safe : translation
90
+ end
90
91
else
91
92
I18n.translate(scope_key_by_partial(key), options.merge(raise: i18n_raise))
92
93
end
@@ -97,7 +98,7 @@ module ActionView
97
98
raise e if raise_error
98
99
99
100
keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
100
- title = "translation missing: #{keys.join('.')}".dup
101
+ title = +"translation missing: #{keys.join('.')}"
101
102
102
103
interpolations = options.except(:default, :scope)
103
104
if interpolations.any?
@@ -122,9 +123,12 @@ module ActionView
122
123
123
124
private
124
125
def scope_key_by_partial(key)
125
- if key.to_s.first == "."
126
+ stringified_key = key.to_s
127
+ if stringified_key.first == "."
126
128
if @virtual_path
127
- @virtual_path.gsub(%r{/_?}, ".") + key.to_s
129
+ @_scope_key_by_partial_cache ||= {}
130
+ @_scope_key_by_partial_cache[@virtual_path] ||= @virtual_path.gsub(%r{/_?}, ".")
131
+ "#{@_scope_key_by_partial_cache[@virtual_path]}#{stringified_key}"
128
132
else
129
133
raise "Cannot use t(#{key.inspect}) shortcut because path is not available"
130
134
end
data/lib/action_view/helpers/url_helper.rb CHANGED
@@ -200,9 +200,9 @@ module ActionView
200
200
html_options = convert_options_to_data_attributes(options, html_options)
201
201
202
202
url = url_for(options)
203
- html_options["href".freeze] ||= url
203
+ html_options["href"] ||= url
204
204
205
- content_tag("a".freeze, name || url, html_options, &block)
205
+ content_tag("a", name || url, html_options, &block)
206
206
end
207
207
208
208
# Generates a form containing a single button that submits to the URL created
@@ -308,7 +308,7 @@ module ActionView
308
308
params = html_options.delete("params")
309
309
310
310
method = html_options.delete("method").to_s
311
- method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : "".freeze.html_safe
311
+ method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : "".html_safe
312
312
313
313
form_method = method == "get" ? "get" : "post"
314
314
form_options = html_options.delete("form") || {}
@@ -321,7 +321,7 @@ module ActionView
321
321
request_method = method.empty? ? "post" : method
322
322
token_tag(nil, form_options: { action: url, method: request_method })
323
323
else
324
- "".freeze
324
+ ""
325
325
end
326
326
327
327
html_options = convert_options_to_data_attributes(options, html_options)
@@ -487,12 +487,12 @@ module ActionView
487
487
option = html_options.delete(item).presence || next
488
488
"#{item.dasherize}=#{ERB::Util.url_encode(option)}"
489
489
}.compact
490
- extras = extras.empty? ? "".freeze : "?" + extras.join("&")
490
+ extras = extras.empty? ? "" : "?" + extras.join("&")
491
491
492
492
encoded_email_address = ERB::Util.url_encode(email_address).gsub("%40", "@")
493
493
html_options["href"] = "mailto:#{encoded_email_address}#{extras}"
494
494
495
- content_tag("a".freeze, name || email_address, html_options, &block)
495
+ content_tag("a", name || email_address, html_options, &block)
496
496
end
497
497
498
498
# True if the current request URI was generated by the given +options+.
@@ -575,21 +575,21 @@ module ActionView
575
575
def convert_options_to_data_attributes(options, html_options)
576
576
if html_options
577
577
html_options = html_options.stringify_keys
578
- html_options["data-remote"] = "true".freeze if link_to_remote_options?(options) || link_to_remote_options?(html_options)
578
+ html_options["data-remote"] = "true" if link_to_remote_options?(options) || link_to_remote_options?(html_options)
579
579
580
- method = html_options.delete("method".freeze)
580
+ method = html_options.delete("method")
581
581
582
582
add_method_to_attributes!(html_options, method) if method
583
583
584
584
html_options
585
585
else
586
- link_to_remote_options?(options) ? { "data-remote" => "true".freeze } : {}
586
+ link_to_remote_options?(options) ? { "data-remote" => "true" } : {}
587
587
end
588
588
end
589
589
590
590
def link_to_remote_options?(options)
591
591
if options.is_a?(Hash)
592
- options.delete("remote".freeze) || options.delete(:remote)
592
+ options.delete("remote") || options.delete(:remote)
593
593
end
594
594
end
595
595
@@ -618,11 +618,11 @@ module ActionView
618
618
end
619
619
620
620
def token_tag(token = nil, form_options: {})
621
- if token != false && protect_against_forgery?
621
+ if token != false && defined?(protect_against_forgery?) && protect_against_forgery?
622
622
token ||= form_authenticity_token(form_options: form_options)
623
623
tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token)
624
624
else
625
- "".freeze
625
+ ""
626
626
end
627
627
end
628
628
@@ -636,7 +636,7 @@ module ActionView
636
636
# to_form_params(name: 'David', nationality: 'Danish')
637
637
# # => [{name: 'name', value: 'David'}, {name: 'nationality', value: 'Danish'}]
638
638
#
639
- # to_form_params(country: {name: 'Denmark'})
639
+ # to_form_params(country: { name: 'Denmark' })
640
640
# # => [{name: 'country[name]', value: 'Denmark'}]
641
641
#
642
642
# to_form_params(countries: ['Denmark', 'Sweden']})
data/lib/action_view/log_subscriber.rb CHANGED
@@ -16,17 +16,17 @@ module ActionView
16
16
17
17
def render_template(event)
18
18
info do
19
- message = " Rendered #{from_rails_root(event.payload[:identifier])}".dup
19
+ message = +" Rendered #{from_rails_root(event.payload[:identifier])}"
20
20
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
21
- message << " (#{event.duration.round(1)}ms)"
21
+ message << " (Duration: #{event.duration.round(1)}ms | Allocations: #{event.allocations})"
22
22
end
23
23
end
24
24
25
25
def render_partial(event)
26
26
info do
27
- message = " Rendered #{from_rails_root(event.payload[:identifier])}".dup
27
+ message = +" Rendered #{from_rails_root(event.payload[:identifier])}"
28
28
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
29
- message << " (#{event.duration.round(1)}ms)"
29
+ message << " (Duration: #{event.duration.round(1)}ms | Allocations: #{event.allocations})"
30
30
message << " #{cache_message(event.payload)}" unless event.payload[:cache_hit].nil?
31
31
message
32
32
end
@@ -37,7 +37,7 @@ module ActionView
37
37
38
38
info do
39
39
" Rendered collection of #{from_rails_root(identifier)}" \
40
- " #{render_count(event.payload)} (#{event.duration.round(1)}ms)"
40
+ " #{render_count(event.payload)} (Duration: #{event.duration.round(1)}ms | Allocations: #{event.allocations})"
41
41
end
42
42
end
43
43
@@ -85,7 +85,7 @@ module ActionView
85
85
86
86
def log_rendering_start(payload)
87
87
info do
88
- message = " Rendering #{from_rails_root(payload[:identifier])}".dup
88
+ message = +" Rendering #{from_rails_root(payload[:identifier])}"
89
89
message << " within #{from_rails_root(payload[:layout])}" if payload[:layout]
90
90
message
91
91
end
data/lib/action_view/lookup_context.rb CHANGED
@@ -24,7 +24,7 @@ module ActionView
24
24
registered_details << name
25
25
Accessors::DEFAULT_PROCS[name] = block
26
26
27
- Accessors.send :define_method, :"default_#{name}", &block
27
+ Accessors.define_method(:"default_#{name}", &block)
28
28
Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1
29
29
def #{name}
30
30
@details.fetch(:#{name}, [])
@@ -202,13 +202,13 @@ module ActionView
202
202
# name instead of the prefix.
203
203
def normalize_name(name, prefixes)
204
204
prefixes = prefixes.presence
205
- parts = name.to_s.split("/".freeze)
205
+ parts = name.to_s.split("/")
206
206
parts.shift if parts.first.empty?
207
207
name = parts.pop
208
208
209
209
return name, prefixes || [""] if parts.empty?
210
210
211
- parts = parts.join("/".freeze)
211
+ parts = parts.join("/")
212
212
prefixes = prefixes ? prefixes.map { |p| "#{p}/#{parts}" } : [parts]
213
213
214
214
return name, prefixes
@@ -245,7 +245,7 @@ module ActionView
245
245
# add :html as fallback to :js.
246
246
def formats=(values)
247
247
if values
248
- values.concat(default_formats) if values.delete "*/*".freeze
248
+ values.concat(default_formats) if values.delete "*/*"
249
249
if values == [:js]
250
250
values << :html
251
251
@html_fallback_for_js = true
data/lib/action_view/railtie.rb CHANGED
@@ -9,6 +9,8 @@ module ActionView
9
9
config.action_view = ActiveSupport::OrderedOptions.new
10
10
config.action_view.embed_authenticity_token_in_remote_forms = nil
11
11
config.action_view.debug_missing_translation = true
12
+ config.action_view.default_enforce_utf8 = nil
13
+ config.action_view.finalize_compiled_template_methods = true
12
14
13
15
config.eager_load_namespaces << ActionView
14
16
@@ -35,6 +37,22 @@ module ActionView
35
37
end
36
38
end
37
39
40
+ initializer "action_view.default_enforce_utf8" do |app|
41
+ ActiveSupport.on_load(:action_view) do
42
+ default_enforce_utf8 = app.config.action_view.delete(:default_enforce_utf8)
43
+ unless default_enforce_utf8.nil?
44
+ ActionView::Helpers::FormTagHelper.default_enforce_utf8 = default_enforce_utf8
45
+ end
46
+ end
47
+ end
48
+
49
+ initializer "action_view.finalize_compiled_template_methods" do |app|
50
+ ActiveSupport.on_load(:action_view) do
51
+ ActionView::Template.finalize_compiled_template_methods =
52
+ app.config.action_view.delete(:finalize_compiled_template_methods)
53
+ end
54
+ end
55
+
38
56
initializer "action_view.logger" do
39
57
ActiveSupport.on_load(:action_view) { self.logger ||= Rails.logger }
40
58
end
data/lib/action_view/record_identifier.rb CHANGED
@@ -59,8 +59,8 @@ module ActionView
59
59
60
60
include ModelNaming
61
61
62
- JOIN = "_".freeze
63
- NEW = "new".freeze
62
+ JOIN = "_"
63
+ NEW = "new"
64
64
65
65
# The DOM class convention is to use the singular form of an object or class.
66
66
#
data/lib/action_view/renderer/partial_renderer.rb CHANGED
@@ -363,7 +363,7 @@ module ActionView
363
363
@options = options
364
364
@block = block
365
365
366
- @locals = options[:locals] || {}
366
+ @locals = options[:locals] ? options[:locals].symbolize_keys : {}
367
367
@details = extract_details(options)
368
368
369
369
prepend_formats(options[:formats])
@@ -523,7 +523,7 @@ module ActionView
523
523
524
524
def retrieve_variable(path, as)
525
525
variable = as || begin
526
- base = path[-1] == "/".freeze ? "".freeze : File.basename(path)
526
+ base = path[-1] == "/" ? "" : File.basename(path)
527
527
raise_invalid_identifier(path) unless base =~ /\A_?(.*?)(?:\.\w+)*\z/
528
528
$1.to_sym
529
529
end
data/lib/action_view/renderer/partial_renderer/collection_caching.rb CHANGED
@@ -14,15 +14,35 @@ module ActionView
14
14
def cache_collection_render(instrumentation_payload)
15
15
return yield unless @options[:cached]
16
16
17
+ # Result is a hash with the key represents the
18
+ # key used for cache lookup and the value is the item
19
+ # on which the partial is being rendered
17
20
keyed_collection = collection_by_cache_keys
21
+
22
+ # Pull all partials from cache
23
+ # Result is a hash, key matches the entry in
24
+ # `keyed_collection` where the cache was retrieved and the
25
+ # value is the value that was present in the cache
18
26
cached_partials = collection_cache.read_multi(*keyed_collection.keys)
19
27
instrumentation_payload[:cache_hits] = cached_partials.size
20
28
29
+ # Extract the items for the keys that are not found
30
+ # Set the uncached values to instance variable @collection
31
+ # which is used by the caller
21
32
@collection = keyed_collection.reject { |key, _| cached_partials.key?(key) }.values
33
+
34
+ # If all elements are already in cache then
35
+ # rendered partials will be an empty array
36
+ #
37
+ # If the cache is missing elements then
38
+ # the block will be called against the remaining items
39
+ # in the @collection.
22
40
rendered_partials = @collection.empty? ? [] : yield
23
41
24
42
index = 0
25
43
fetch_or_cache_partial(cached_partials, order_by: keyed_collection.each_key) do
44
+ # This block is called once
45
+ # for every cache miss while preserving order.
26
46
rendered_partials[index].tap { index += 1 }
27
47
end
28
48
end
@@ -40,10 +60,29 @@ module ActionView
40
60
end
41
61
42
62
def expanded_cache_key(key)
43
- key = @view.combined_fragment_cache_key(@view.cache_fragment_name(key, virtual_path: @template.virtual_path))
63
+ key = @view.combined_fragment_cache_key(@view.cache_fragment_name(key, virtual_path: @template.virtual_path, digest_path: digest_path))
44
64
key.frozen? ? key.dup : key # #read_multi & #write may require mutability, Dalli 2.6.0.
45
65
end
46
66
67
+ def digest_path
68
+ @digest_path ||= @view.digest_path_from_virtual(@template.virtual_path)
69
+ end
70
+
71
+ # `order_by` is an enumerable object containing keys of the cache,
72
+ # all keys are passed in whether found already or not.
73
+ #
74
+ # `cached_partials` is a hash. If the value exists
75
+ # it represents the rendered partial from the cache
76
+ # otherwise `Hash#fetch` will take the value of its block.
77
+ #
78
+ # This method expects a block that will return the rendered
79
+ # partial. An example is to render all results
80
+ # for each element that was not found in the cache and store it as an array.
81
+ # Order it so that the first empty cache element in `cached_partials`
82
+ # corresponds to the first element in `rendered_partials`.
83
+ #
84
+ # If the partial is not already cached it will also be
85
+ # written back to the underlying cache store.
47
86
def fetch_or_cache_partial(cached_partials, order_by:)
48
87
order_by.map do |cache_key|
49
88
cached_partials.fetch(cache_key) do
data/lib/action_view/renderer/streaming_template_renderer.rb CHANGED
@@ -33,7 +33,7 @@ module ActionView
33
33
logger = ActionView::Base.logger
34
34
return unless logger
35
35
36
- message = "\n#{exception.class} (#{exception.message}):\n".dup
36
+ message = +"\n#{exception.class} (#{exception.message}):\n"
37
37
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
38
38
message << " " << exception.backtrace.join("\n ")
39
39
logger.fatal("#{message}\n\n")
data/lib/action_view/rendering.rb CHANGED
@@ -64,10 +64,11 @@ module ActionView
64
64
# An instance of a view class. The default view class is ActionView::Base.
65
65
#
66
66
# The view class must have the following methods:
67
- # View.new[lookup_context, assigns, controller]
68
- # Create a new ActionView instance for a controller and we can also pass the arguments.
69
- # View#render(option)
70
- # Returns String with the rendered template
67
+ #
68
+ # * <tt>View.new(lookup_context, assigns, controller)</tt> Create a new
69
+ # ActionView instance for a controller and we can also pass the arguments.
70
+ #
71
+ # * <tt>View#render(option)</tt> — Returns String with the rendered template.
71
72
#
72
73
# Override this method in a module to change the default behavior.
73
74
def view_context
data/lib/action_view/routing_url_for.rb CHANGED
@@ -84,25 +84,24 @@ module ActionView
84
84
super(only_path: _generate_paths_by_default)
85
85
when Hash
86
86
options = options.symbolize_keys
87
- unless options.key?(:only_path)
87
+ ensure_only_path_option(options)
88
- options[:only_path] = only_path?(options[:host])
89
- end
90
88
91
89
super(options)
92
90
when ActionController::Parameters
93
- unless options.key?(:only_path)
91
+ ensure_only_path_option(options)
94
- options[:only_path] = only_path?(options[:host])
95
- end
96
92
97
93
super(options)
98
94
when :back
99
95
_back_url
100
96
when Array
101
97
components = options.dup
102
- if _generate_paths_by_default
103
- polymorphic_path(components, components.extract_options!)
98
+ options = components.extract_options!
99
+ ensure_only_path_option(options)
100
+
101
+ if options[:only_path]
102
+ polymorphic_path(components, options)
104
103
else
105
- polymorphic_url(components, components.extract_options!)
104
+ polymorphic_url(components, options)
106
105
end
107
106
else
108
107
method = _generate_paths_by_default ? :path : :url
@@ -138,8 +137,10 @@ module ActionView
138
137
true
139
138
end
140
139
141
- def only_path?(host)
142
- _generate_paths_by_default unless host
140
+ def ensure_only_path_option(options)
141
+ unless options.key?(:only_path)
142
+ options[:only_path] = _generate_paths_by_default unless options[:host]
143
+ end
143
144
end
144
145
end
145
146
end
data/lib/action_view/template.rb CHANGED
@@ -9,6 +9,8 @@ module ActionView
9
9
class Template
10
10
extend ActiveSupport::Autoload
11
11
12
+ mattr_accessor :finalize_compiled_template_methods, default: true
13
+
12
14
# === Encodings in ActionView::Template
13
15
#
14
16
# ActionView::Template is one of a few sources of potential
@@ -186,7 +188,7 @@ module ActionView
186
188
end
187
189
188
190
def inspect
189
- @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", "".freeze) : identifier
191
+ @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", "") : identifier
190
192
end
191
193
192
194
# This method is responsible for properly setting the encoding of the
@@ -233,6 +235,19 @@ module ActionView
233
235
end
234
236
end
235
237
238
+
239
+ # Exceptions are marshalled when using the parallel test runner with DRb, so we need
240
+ # to ensure that references to the template object can be marshalled as well. This means forgoing
241
+ # the marshalling of the compiler mutex and instantiating that again on unmarshalling.
242
+ def marshal_dump # :nodoc:
243
+ [ @source, @identifier, @handler, @compiled, @original_encoding, @locals, @virtual_path, @updated_at, @formats, @variants ]
244
+ end
245
+
246
+ def marshal_load(array) # :nodoc:
247
+ @source, @identifier, @handler, @compiled, @original_encoding, @locals, @virtual_path, @updated_at, @formats, @variants = *array
248
+ @compile_mutex = Mutex.new
249
+ end
250
+
236
251
private
237
252
238
253
# Compile a template. This method ensures a template is compiled
@@ -284,7 +299,7 @@ module ActionView
284
299
285
300
# Make sure that the resulting String to be eval'd is in the
286
301
# encoding of the code
287
- source = <<-end_src.dup
302
+ source = +<<-end_src
288
303
def #{method_name}(local_assigns, output_buffer)
289
304
_old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code}
290
305
ensure
@@ -307,7 +322,9 @@ module ActionView
307
322
end
308
323
309
324
mod.module_eval(source, identifier, 0)
310
- ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
325
+ if finalize_compiled_template_methods
326
+ ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
327
+ end
311
328
end
312
329
313
330
def handle_render_error(view, e)
@@ -331,19 +348,19 @@ module ActionView
331
348
locals = locals.grep(/\A@?(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
332
349
333
350
# Assign for the same variable is to suppress unused variable warning
334
- locals.each_with_object("".dup) { |key, code| code << "#{key} = local_assigns[:#{key}]; #{key} = #{key};" }
351
+ locals.each_with_object(+"") { |key, code| code << "#{key} = local_assigns[:#{key}]; #{key} = #{key};" }
335
352
end
336
353
337
354
def method_name
338
355
@method_name ||= begin
339
- m = "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}".dup
340
- m.tr!("-".freeze, "_".freeze)
356
+ m = +"_#{identifier_method_name}__#{@identifier.hash}_#{__id__}"
357
+ m.tr!("-", "_")
341
358
m
342
359
end
343
360
end
344
361
345
362
def identifier_method_name
346
- inspect.tr("^a-z_".freeze, "_".freeze)
363
+ inspect.tr("^a-z_", "_")
347
364
end
348
365
349
366
def instrument(action, &block) # :doc:
@@ -351,7 +368,7 @@ module ActionView
351
368
end
352
369
353
370
def instrument_render_template(&block)
354
- ActiveSupport::Notifications.instrument("!render_template.action_view".freeze, instrument_payload, &block)
371
+ ActiveSupport::Notifications.instrument("!render_template.action_view", instrument_payload, &block)
355
372
end
356
373
357
374
def instrument_payload
data/lib/action_view/template/handlers/erb.rb CHANGED
@@ -14,7 +14,17 @@ module ActionView
14
14
class_attribute :erb_implementation, default: Erubi
15
15
16
16
# Do not escape templates of these mime types.
17
- class_attribute :escape_whitelist, default: ["text/plain"]
17
+ class_attribute :escape_ignore_list, default: ["text/plain"]
18
+
19
+ [self, singleton_class].each do |base|
20
+ base.alias_method :escape_whitelist, :escape_ignore_list
21
+ base.alias_method :escape_whitelist=, :escape_ignore_list=
22
+
23
+ base.deprecate(
24
+ escape_whitelist: "use #escape_ignore_list instead",
25
+ :escape_whitelist= => "use #escape_ignore_list= instead"
26
+ )
27
+ end
18
28
19
29
ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
20
30
@@ -47,7 +57,7 @@ module ActionView
47
57
48
58
self.class.erb_implementation.new(
49
59
erb,
50
- escape: (self.class.escape_whitelist.include? template.type),
60
+ escape: (self.class.escape_ignore_list.include? template.type),
51
61
trim: (self.class.erb_trim_mode == "-")
52
62
).src
53
63
end
data/lib/action_view/template/resolver.rb CHANGED
@@ -16,7 +16,7 @@ module ActionView
16
16
alias_method :partial?, :partial
17
17
18
18
def self.build(name, prefix, partial)
19
- virtual = "".dup
19
+ virtual = +""
20
20
virtual << "#{prefix}/" unless prefix.empty?
21
21
virtual << (partial ? "_#{name}" : name)
22
22
new name, prefix, partial, virtual
@@ -221,9 +221,7 @@ module ActionView
221
221
end
222
222
223
223
def query(path, details, formats, outside_app_allowed)
224
- query = build_query(path, details)
224
+ template_paths = find_template_paths_from_details(path, details)
225
-
226
- template_paths = find_template_paths(query)
227
225
template_paths = reject_files_external_to_app(template_paths) unless outside_app_allowed
228
226
229
227
template_paths.map do |template|
@@ -243,6 +241,11 @@ module ActionView
243
241
files.reject { |filename| !inside_path?(@path, filename) }
244
242
end
245
243
244
+ def find_template_paths_from_details(path, details)
245
+ query = build_query(path, details)
246
+ find_template_paths(query)
247
+ end
248
+
246
249
def find_template_paths(query)
247
250
Dir[query].uniq.reject do |filename|
248
251
File.directory?(filename) ||
@@ -279,7 +282,7 @@ module ActionView
279
282
end
280
283
281
284
def escape_entry(entry)
282
- entry.gsub(/[*?{}\[\]]/, '\\\\\\&'.freeze)
285
+ entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
283
286
end
284
287
285
288
# Returns the file mtime from the filesystem.
@@ -291,7 +294,7 @@ module ActionView
291
294
# from the path, or the handler, we should return the array of formats given
292
295
# to the resolver.
293
296
def extract_handler_and_format_and_variant(path)
294
- pieces = File.basename(path).split(".".freeze)
297
+ pieces = File.basename(path).split(".")
295
298
pieces.shift
296
299
297
300
extension = pieces.pop
@@ -362,19 +365,56 @@ module ActionView
362
365
363
366
# An Optimized resolver for Rails' most common case.
364
367
class OptimizedFileSystemResolver < FileSystemResolver #:nodoc:
365
- def build_query(path, details)
368
+ private
366
- query = escape_entry(File.join(@path, path))
367
369
368
- exts = EXTENSIONS.map do |ext, prefix|
369
- if ext == :variants && details[ext] == :any
370
- "{#{prefix}*,}"
371
- else
372
- "{#{details[ext].compact.uniq.map { |e| "#{prefix}#{e}," }.join}}"
370
+ def find_template_paths_from_details(path, details)
371
+ # Instead of checking for every possible path, as our other globs would
372
+ # do, scan the directory for files with the right prefix.
373
+ query = "#{escape_entry(File.join(@path, path))}*"
374
+
375
+ regex = build_regex(path, details)
376
+
377
+ Dir[query].uniq.reject do |filename|
378
+ # This regex match does double duty of finding only files which match
379
+ # details (instead of just matching the prefix) and also filtering for
380
+ # case-insensitive file systems.
381
+ !regex.match?(filename) ||
382
+ File.directory?(filename)
383
+ end.sort_by do |filename|
384
+ # Because we scanned the directory, instead of checking for files
385
+ # one-by-one, they will be returned in an arbitrary order.
386
+ # We can use the matches found by the regex and sort by their index in
387
+ # details.
388
+ match = filename.match(regex)
389
+ EXTENSIONS.keys.reverse.map do |ext|
390
+ if ext == :variants && details[ext] == :any
391
+ match[ext].nil? ? 0 : 1
392
+ elsif match[ext].nil?
393
+ # No match should be last
394
+ details[ext].length
395
+ else
396
+ found = match[ext].to_sym
397
+ details[ext].index(found)
398
+ end
399
+ end
373
400
end
374
- end.join
401
+ end
375
402
376
- query + exts
377
- end
403
+ def build_regex(path, details)
404
+ query = escape_entry(File.join(@path, path))
405
+ exts = EXTENSIONS.map do |ext, prefix|
406
+ match =
407
+ if ext == :variants && details[ext] == :any
408
+ ".*?"
409
+ else
410
+ details[ext].compact.uniq.map { |e| Regexp.escape(e) }.join("|")
411
+ end
412
+ prefix = Regexp.escape(prefix)
413
+ "(#{prefix}(?<#{ext}>#{match}))?"
414
+ end.join
415
+
416
+ %r{\A#{query}#{exts}\z}
417
+ end
378
418
end
379
419
380
420
# The same as FileSystemResolver but does not allow templates to store
data/lib/action_view/test_case.rb CHANGED
@@ -107,7 +107,7 @@ module ActionView
107
107
# empty string ensures buffer has UTF-8 encoding as
108
108
# new without arguments returns ASCII-8BIT encoded buffer like String#new
109
109
@output_buffer = ActiveSupport::SafeBuffer.new ""
110
- @rendered = "".dup
110
+ @rendered = +""
111
111
112
112
make_test_case_available_to_view!
113
113
say_no_to_protect_against_forgery!
data/lib/action_view/testing/resolvers.rb CHANGED
@@ -22,7 +22,7 @@ module ActionView #:nodoc:
22
22
private
23
23
24
24
def query(path, exts, _, _)
25
- query = "".dup
25
+ query = +""
26
26
EXTENSIONS.each_key do |ext|
27
27
query << "(" << exts[ext].map { |e| e && Regexp.escape(".#{e}") }.join("|") << "|)"
28
28
end
data/lib/assets/compiled/rails-ujs.js CHANGED
@@ -32,17 +32,12 @@ Released under the MIT license
32
32
33
33
(function() {
34
34
(function() {
35
- var nonce;
35
+ var cspNonce;
36
36
37
- nonce = null;
38
-
39
- Rails.loadCSPNonce = function() {
40
- var ref;
37
+ cspNonce = Rails.cspNonce = function() {
38
+ var meta;
39
+ meta = document.querySelector('meta[name=csp-nonce]');
40
+ return meta && meta.content;
41
- return nonce = (ref = document.querySelector("meta[name=csp-nonce]")) != null ? ref.content : void 0;
42
- };
43
-
44
- Rails.cspNonce = function() {
45
- return nonce != null ? nonce : Rails.loadCSPNonce();
46
41
};
47
42
48
43
}).call(this);
@@ -270,7 +265,7 @@ Released under the MIT license
270
265
script.setAttribute('nonce', cspNonce());
271
266
script.text = response;
272
267
document.head.appendChild(script).parentNode.removeChild(script);
273
- } else if (type.match(/\b(xml|html|svg)\b/)) {
268
+ } else if (type.match(/\bxml\b/)) {
274
269
parser = new DOMParser();
275
270
type = type.replace(/;.+/, '');
276
271
try {
@@ -370,6 +365,10 @@ Released under the MIT license
370
365
}
371
366
};
372
367
368
+ Rails.confirm = function(message, element) {
369
+ return confirm(message);
370
+ };
371
+
373
372
allowAction = function(element) {
374
373
var answer, callback, message;
375
374
message = element.getAttribute('data-confirm');
@@ -379,7 +378,7 @@ Released under the MIT license
379
378
answer = false;
380
379
if (fire(element, 'confirm')) {
381
380
try {
382
- answer = confirm(message);
381
+ answer = Rails.confirm(message, element);
383
382
} catch (error) {}
384
383
callback = fire(element, 'confirm:complete', [answer]);
385
384
}
@@ -388,7 +387,7 @@ Released under the MIT license
388
387
389
388
}).call(this);
390
389
(function() {
391
- var disableFormElement, disableFormElements, disableLinkElement, enableFormElement, enableFormElements, enableLinkElement, formElements, getData, matches, setData, stopEverything;
390
+ var disableFormElement, disableFormElements, disableLinkElement, enableFormElement, enableFormElements, enableLinkElement, formElements, getData, isXhrRedirect, matches, setData, stopEverything;
392
391
393
392
matches = Rails.matches, getData = Rails.getData, setData = Rails.setData, stopEverything = Rails.stopEverything, formElements = Rails.formElements;
394
393
@@ -402,7 +401,14 @@ Released under the MIT license
402
401
403
402
Rails.enableElement = function(e) {
404
403
var element;
405
- element = e instanceof Event ? e.target : e;
404
+ if (e instanceof Event) {
405
+ if (isXhrRedirect(e)) {
406
+ return;
407
+ }
408
+ element = e.target;
409
+ } else {
410
+ element = e;
411
+ }
406
412
if (matches(element, Rails.linkDisableSelector)) {
407
413
return enableLinkElement(element);
408
414
} else if (matches(element, Rails.buttonDisableSelector) || matches(element, Rails.formEnableSelector)) {
@@ -426,6 +432,9 @@ Released under the MIT license
426
432
427
433
disableLinkElement = function(element) {
428
434
var replacement;
435
+ if (getData(element, 'ujs:disabled')) {
436
+ return;
437
+ }
429
438
replacement = element.getAttribute('data-disable-with');
430
439
if (replacement != null) {
431
440
setData(element, 'ujs:enable-with', element.innerHTML);
@@ -452,6 +461,9 @@ Released under the MIT license
452
461
453
462
disableFormElement = function(element) {
454
463
var replacement;
464
+ if (getData(element, 'ujs:disabled')) {
465
+ return;
466
+ }
455
467
replacement = element.getAttribute('data-disable-with');
456
468
if (replacement != null) {
457
469
if (matches(element, 'button')) {
@@ -485,6 +497,12 @@ Released under the MIT license
485
497
return setData(element, 'ujs:disabled', null);
486
498
};
487
499
500
+ isXhrRedirect = function(event) {
501
+ var ref, xhr;
502
+ xhr = (ref = event.detail) != null ? ref[0] : void 0;
503
+ return (xhr != null ? xhr.getResponseHeader("X-Xhr-Redirect") : void 0) != null;
504
+ };
505
+
488
506
}).call(this);
489
507
(function() {
490
508
var stopEverything;
@@ -636,9 +654,9 @@ Released under the MIT license
636
654
637
655
}).call(this);
638
656
(function() {
639
- var $, CSRFProtection, delegate, disableElement, enableElement, fire, formSubmitButtonClick, getData, handleConfirm, handleDisabledElement, handleMethod, handleRemote, loadCSPNonce, preventInsignificantClick, refreshCSRFTokens;
657
+ var $, CSRFProtection, delegate, disableElement, enableElement, fire, formSubmitButtonClick, getData, handleConfirm, handleDisabledElement, handleMethod, handleRemote, preventInsignificantClick, refreshCSRFTokens;
640
658
641
- fire = Rails.fire, delegate = Rails.delegate, getData = Rails.getData, $ = Rails.$, refreshCSRFTokens = Rails.refreshCSRFTokens, CSRFProtection = Rails.CSRFProtection, loadCSPNonce = Rails.loadCSPNonce, enableElement = Rails.enableElement, disableElement = Rails.disableElement, handleDisabledElement = Rails.handleDisabledElement, handleConfirm = Rails.handleConfirm, preventInsignificantClick = Rails.preventInsignificantClick, handleRemote = Rails.handleRemote, formSubmitButtonClick = Rails.formSubmitButtonClick, handleMethod = Rails.handleMethod;
659
+ fire = Rails.fire, delegate = Rails.delegate, getData = Rails.getData, $ = Rails.$, refreshCSRFTokens = Rails.refreshCSRFTokens, CSRFProtection = Rails.CSRFProtection, enableElement = Rails.enableElement, disableElement = Rails.disableElement, handleDisabledElement = Rails.handleDisabledElement, handleConfirm = Rails.handleConfirm, preventInsignificantClick = Rails.preventInsignificantClick, handleRemote = Rails.handleRemote, formSubmitButtonClick = Rails.formSubmitButtonClick, handleMethod = Rails.handleMethod;
642
660
643
661
if ((typeof jQuery !== "undefined" && jQuery !== null) && (jQuery.ajax != null)) {
644
662
if (jQuery.rails) {
@@ -701,7 +719,6 @@ Released under the MIT license
701
719
delegate(document, Rails.formInputClickSelector, 'click', handleConfirm);
702
720
delegate(document, Rails.formInputClickSelector, 'click', formSubmitButtonClick);
703
721
document.addEventListener('DOMContentLoaded', refreshCSRFTokens);
704
- document.addEventListener('DOMContentLoaded', loadCSPNonce);
705
722
return window._rails_loaded = true;
706
723
};
707
724
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
--- !ruby/object:Gem::Specification
2
2
name: actionview
3
3
version: !ruby/object:Gem::Version
4
- version: 5.2.3.rc1
4
+ version: 6.0.0.beta1
5
5
platform: ruby
6
6
authors:
7
7
- David Heinemeier Hansson
8
8
autorequire:
9
9
bindir: bin
10
10
cert_chain: []
11
- date: 2019-03-22 00:00:00.000000000 Z
11
+ date: 2019-01-18 00:00:00.000000000 Z
12
12
dependencies:
13
13
- !ruby/object:Gem::Dependency
14
14
name: activesupport
@@ -16,14 +16,14 @@ dependencies:
16
16
requirements:
17
17
- - '='
18
18
- !ruby/object:Gem::Version
19
- version: 5.2.3.rc1
19
+ version: 6.0.0.beta1
20
20
type: :runtime
21
21
prerelease: false
22
22
version_requirements: !ruby/object:Gem::Requirement
23
23
requirements:
24
24
- - '='
25
25
- !ruby/object:Gem::Version
26
- version: 5.2.3.rc1
26
+ version: 6.0.0.beta1
27
27
- !ruby/object:Gem::Dependency
28
28
name: builder
29
29
requirement: !ruby/object:Gem::Requirement
@@ -92,28 +92,28 @@ dependencies:
92
92
requirements:
93
93
- - '='
94
94
- !ruby/object:Gem::Version
95
- version: 5.2.3.rc1
95
+ version: 6.0.0.beta1
96
96
type: :development
97
97
prerelease: false
98
98
version_requirements: !ruby/object:Gem::Requirement
99
99
requirements:
100
100
- - '='
101
101
- !ruby/object:Gem::Version
102
- version: 5.2.3.rc1
102
+ version: 6.0.0.beta1
103
103
- !ruby/object:Gem::Dependency
104
104
name: activemodel
105
105
requirement: !ruby/object:Gem::Requirement
106
106
requirements:
107
107
- - '='
108
108
- !ruby/object:Gem::Version
109
- version: 5.2.3.rc1
109
+ version: 6.0.0.beta1
110
110
type: :development
111
111
prerelease: false
112
112
version_requirements: !ruby/object:Gem::Requirement
113
113
requirements:
114
114
- - '='
115
115
- !ruby/object:Gem::Version
116
- version: 5.2.3.rc1
116
+ version: 6.0.0.beta1
117
117
description: Simple, battle-tested conventions and helpers for building web pages.
118
118
email: david@loudthinking.com
119
119
executables: []
@@ -149,7 +149,6 @@ files:
149
149
- lib/action_view/helpers/javascript_helper.rb
150
150
- lib/action_view/helpers/number_helper.rb
151
151
- lib/action_view/helpers/output_safety_helper.rb
152
- - lib/action_view/helpers/record_tag_helper.rb
153
152
- lib/action_view/helpers/rendering_helper.rb
154
153
- lib/action_view/helpers/sanitize_helper.rb
155
154
- lib/action_view/helpers/tag_helper.rb
@@ -230,8 +229,8 @@ homepage: http://rubyonrails.org
230
229
licenses:
231
230
- MIT
232
231
metadata:
233
- source_code_uri: https://github.com/rails/rails/tree/v5.2.3.rc1/actionview
234
- changelog_uri: https://github.com/rails/rails/blob/v5.2.3.rc1/actionview/CHANGELOG.md
232
+ source_code_uri: https://github.com/rails/rails/tree/v6.0.0.beta1/actionview
233
+ changelog_uri: https://github.com/rails/rails/blob/v6.0.0.beta1/actionview/CHANGELOG.md
235
234
post_install_message:
236
235
rdoc_options: []
237
236
require_paths:
@@ -240,7 +239,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
240
239
requirements:
241
240
- - ">="
242
241
- !ruby/object:Gem::Version
243
- version: 2.2.2
242
+ version: 2.5.0
244
243
required_rubygems_version: !ruby/object:Gem::Requirement
245
244
requirements:
246
245
- - ">"