actionview 4.2.11 → 5.0.7
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionview might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +304 -184
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -3
- data/lib/action_view.rb +1 -1
- data/lib/action_view/base.rb +14 -2
- data/lib/action_view/dependency_tracker.rb +51 -18
- data/lib/action_view/digestor.rb +83 -81
- data/lib/action_view/flows.rb +4 -5
- data/lib/action_view/gem_version.rb +3 -3
- data/lib/action_view/helpers/asset_tag_helper.rb +15 -5
- data/lib/action_view/helpers/asset_url_helper.rb +51 -12
- data/lib/action_view/helpers/atom_feed_helper.rb +6 -5
- data/lib/action_view/helpers/cache_helper.rb +62 -21
- data/lib/action_view/helpers/capture_helper.rb +5 -4
- data/lib/action_view/helpers/controller_helper.rb +11 -2
- data/lib/action_view/helpers/date_helper.rb +59 -13
- data/lib/action_view/helpers/debug_helper.rb +1 -1
- data/lib/action_view/helpers/form_helper.rb +74 -72
- data/lib/action_view/helpers/form_options_helper.rb +79 -39
- data/lib/action_view/helpers/form_tag_helper.rb +74 -44
- data/lib/action_view/helpers/javascript_helper.rb +4 -4
- data/lib/action_view/helpers/number_helper.rb +28 -13
- data/lib/action_view/helpers/output_safety_helper.rb +32 -2
- data/lib/action_view/helpers/record_tag_helper.rb +12 -99
- data/lib/action_view/helpers/rendering_helper.rb +2 -2
- data/lib/action_view/helpers/sanitize_helper.rb +1 -2
- data/lib/action_view/helpers/tag_helper.rb +19 -11
- data/lib/action_view/helpers/tags/base.rb +45 -29
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +4 -28
- data/lib/action_view/helpers/tags/collection_helpers.rb +32 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -9
- data/lib/action_view/helpers/tags/datetime_field.rb +1 -1
- data/lib/action_view/helpers/tags/label.rb +1 -1
- data/lib/action_view/helpers/tags/placeholderable.rb +1 -1
- data/lib/action_view/helpers/tags/search_field.rb +12 -9
- data/lib/action_view/helpers/tags/text_field.rb +0 -1
- data/lib/action_view/helpers/tags/translator.rb +1 -1
- data/lib/action_view/helpers/text_helper.rb +27 -11
- data/lib/action_view/helpers/translation_helper.rb +56 -26
- data/lib/action_view/helpers/url_helper.rb +108 -79
- data/lib/action_view/layouts.rb +11 -10
- data/lib/action_view/log_subscriber.rb +35 -1
- data/lib/action_view/lookup_context.rb +69 -48
- data/lib/action_view/model_naming.rb +1 -1
- data/lib/action_view/path_set.rb +9 -0
- data/lib/action_view/railtie.rb +18 -3
- data/lib/action_view/record_identifier.rb +45 -19
- data/lib/action_view/renderer/abstract_renderer.rb +7 -3
- data/lib/action_view/renderer/partial_renderer.rb +38 -37
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +49 -0
- data/lib/action_view/renderer/renderer.rb +2 -6
- data/lib/action_view/renderer/streaming_template_renderer.rb +1 -1
- data/lib/action_view/renderer/template_renderer.rb +11 -10
- data/lib/action_view/rendering.rb +15 -7
- data/lib/action_view/routing_url_for.rb +18 -6
- data/lib/action_view/tasks/{dependencies.rake → cache_digests.rake} +2 -2
- data/lib/action_view/template.rb +36 -12
- data/lib/action_view/template/error.rb +20 -9
- data/lib/action_view/template/handlers.rb +6 -4
- data/lib/action_view/template/handlers/html.rb +9 -0
- data/lib/action_view/template/handlers/raw.rb +1 -3
- data/lib/action_view/template/resolver.rb +49 -42
- data/lib/action_view/template/types.rb +14 -16
- data/lib/action_view/test_case.rb +15 -9
- data/lib/action_view/testing/resolvers.rb +1 -2
- data/lib/action_view/view_paths.rb +6 -24
- metadata +16 -20
@@ -17,7 +17,7 @@ module ActionView
|
|
17
17
|
method_and_value = @tag_value.present? ? "#{@method_name}.#{@tag_value}" : @method_name
|
18
18
|
|
19
19
|
content ||= Translator
|
20
|
-
.new(object, @object_name, method_and_value, "helpers.label")
|
20
|
+
.new(object, @object_name, method_and_value, scope: "helpers.label")
|
21
21
|
.translate
|
22
22
|
content ||= @method_name.humanize
|
23
23
|
|
@@ -10,7 +10,7 @@ module ActionView
|
|
10
10
|
method_and_value = tag_value.is_a?(TrueClass) ? @method_name : "#{@method_name}.#{tag_value}"
|
11
11
|
|
12
12
|
placeholder ||= Tags::Translator
|
13
|
-
.new(object, @object_name, method_and_value, "helpers.placeholder")
|
13
|
+
.new(object, @object_name, method_and_value, scope: "helpers.placeholder")
|
14
14
|
.translate
|
15
15
|
placeholder ||= @method_name.humanize
|
16
16
|
@options[:placeholder] = placeholder
|
@@ -3,18 +3,21 @@ module ActionView
|
|
3
3
|
module Tags # :nodoc:
|
4
4
|
class SearchField < TextField # :nodoc:
|
5
5
|
def render
|
6
|
-
|
7
|
-
if options["autosave"]
|
8
|
-
if options["autosave"] == true
|
9
|
-
options["autosave"] = request.host.split(".").reverse.join(".")
|
10
|
-
end
|
11
|
-
options["results"] ||= 10
|
12
|
-
end
|
6
|
+
options = @options.stringify_keys
|
13
7
|
|
14
|
-
|
15
|
-
|
8
|
+
if options["autosave"]
|
9
|
+
if options["autosave"] == true
|
10
|
+
options["autosave"] = request.host.split(".").reverse.join(".")
|
16
11
|
end
|
12
|
+
options["results"] ||= 10
|
13
|
+
end
|
14
|
+
|
15
|
+
if options["onsearch"]
|
16
|
+
options["incremental"] = true unless options.has_key?("incremental")
|
17
17
|
end
|
18
|
+
|
19
|
+
@options = options
|
20
|
+
super
|
18
21
|
end
|
19
22
|
end
|
20
23
|
end
|
@@ -11,7 +11,6 @@ module ActionView
|
|
11
11
|
options["size"] = options["maxlength"] unless options.key?("size")
|
12
12
|
options["type"] ||= field_type
|
13
13
|
options["value"] = options.fetch("value") { value_before_type_cast(object) } unless field_type == "file"
|
14
|
-
yield options if block_given?
|
15
14
|
add_default_name_and_id(options)
|
16
15
|
tag("input", options)
|
17
16
|
end
|
@@ -2,7 +2,7 @@ module ActionView
|
|
2
2
|
module Helpers
|
3
3
|
module Tags # :nodoc:
|
4
4
|
class Translator # :nodoc:
|
5
|
-
def initialize(object, object_name, method_and_value, scope)
|
5
|
+
def initialize(object, object_name, method_and_value, scope:)
|
6
6
|
@object_name = object_name.gsub(/\[(.*)_attributes\]\[\d+\]/, '.\1')
|
7
7
|
@method_and_value = method_and_value
|
8
8
|
@scope = scope
|
@@ -103,7 +103,9 @@ module ActionView
|
|
103
103
|
# Highlights one or more +phrases+ everywhere in +text+ by inserting it into
|
104
104
|
# a <tt>:highlighter</tt> string. The highlighter can be specialized by passing <tt>:highlighter</tt>
|
105
105
|
# as a single-quoted string with <tt>\1</tt> where the phrase is to be inserted (defaults to
|
106
|
-
# '<mark>\1</mark>') or passing a block that receives each matched term.
|
106
|
+
# '<mark>\1</mark>') or passing a block that receives each matched term. By default +text+
|
107
|
+
# is sanitized to prevent possible XSS attacks. If the input is trustworthy, passing false
|
108
|
+
# for <tt>:sanitize</tt> will turn sanitizing off.
|
107
109
|
#
|
108
110
|
# highlight('You searched for: rails', 'rails')
|
109
111
|
# # => You searched for: <mark>rails</mark>
|
@@ -122,6 +124,9 @@ module ActionView
|
|
122
124
|
#
|
123
125
|
# highlight('You searched for: rails', 'rails') { |match| link_to(search_path(q: match, match)) }
|
124
126
|
# # => You searched for: <a href="search?q=rails">rails</a>
|
127
|
+
#
|
128
|
+
# highlight('<a href="javascript:alert(\'no!\')">ruby</a> on rails', 'rails', sanitize: false)
|
129
|
+
# # => "<a>ruby</a> on <mark>rails</mark>"
|
125
130
|
def highlight(text, phrases, options = {})
|
126
131
|
text = sanitize(text) if options.fetch(:sanitize, true)
|
127
132
|
|
@@ -199,7 +204,12 @@ module ActionView
|
|
199
204
|
|
200
205
|
# Attempts to pluralize the +singular+ word unless +count+ is 1. If
|
201
206
|
# +plural+ is supplied, it will use that when count is > 1, otherwise
|
202
|
-
# it will use the Inflector to determine the plural form
|
207
|
+
# it will use the Inflector to determine the plural form for the given locale,
|
208
|
+
# which defaults to I18n.locale
|
209
|
+
#
|
210
|
+
# The word will be pluralized using rules defined for the locale
|
211
|
+
# (you must define your own inflection rules for languages other than English).
|
212
|
+
# See ActiveSupport::Inflector.pluralize
|
203
213
|
#
|
204
214
|
# pluralize(1, 'person')
|
205
215
|
# # => 1 person
|
@@ -207,16 +217,19 @@ module ActionView
|
|
207
217
|
# pluralize(2, 'person')
|
208
218
|
# # => 2 people
|
209
219
|
#
|
210
|
-
# pluralize(3, 'person', 'users')
|
220
|
+
# pluralize(3, 'person', plural: 'users')
|
211
221
|
# # => 3 users
|
212
222
|
#
|
213
223
|
# pluralize(0, 'person')
|
214
224
|
# # => 0 people
|
215
|
-
|
225
|
+
#
|
226
|
+
# pluralize(2, 'Person', locale: :de)
|
227
|
+
# # => 2 Personen
|
228
|
+
def pluralize(count, singular, plural_arg = nil, plural: plural_arg, locale: I18n.locale)
|
216
229
|
word = if (count == 1 || count =~ /^1(\.0+)?$/)
|
217
230
|
singular
|
218
231
|
else
|
219
|
-
plural || singular.pluralize
|
232
|
+
plural || singular.pluralize(locale)
|
220
233
|
end
|
221
234
|
|
222
235
|
"#{count || 0} #{word}"
|
@@ -237,12 +250,15 @@ module ActionView
|
|
237
250
|
#
|
238
251
|
# word_wrap('Once upon a time', line_width: 1)
|
239
252
|
# # => Once\nupon\na\ntime
|
240
|
-
|
241
|
-
|
242
|
-
|
253
|
+
#
|
254
|
+
# You can also specify a custom +break_sequence+ ("\n" by default)
|
255
|
+
#
|
256
|
+
# word_wrap('Once upon a time', line_width: 1, break_sequence: "\r\n")
|
257
|
+
# # => Once\r\nupon\r\na\r\ntime
|
258
|
+
def word_wrap(text, line_width: 80, break_sequence: "\n")
|
243
259
|
text.split("\n").collect! do |line|
|
244
|
-
line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1
|
245
|
-
end *
|
260
|
+
line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1#{break_sequence}").strip : line
|
261
|
+
end * break_sequence
|
246
262
|
end
|
247
263
|
|
248
264
|
# Returns +text+ transformed into HTML using simple formatting rules.
|
@@ -309,7 +325,7 @@ module ActionView
|
|
309
325
|
# <table>
|
310
326
|
# <% @items.each do |item| %>
|
311
327
|
# <tr class="<%= cycle("odd", "even") -%>">
|
312
|
-
# <td
|
328
|
+
# <td><%= item %></td>
|
313
329
|
# </tr>
|
314
330
|
# <% end %>
|
315
331
|
# </table>
|
@@ -6,35 +6,56 @@ module ActionView
|
|
6
6
|
# = Action View Translation Helpers
|
7
7
|
module Helpers
|
8
8
|
module TranslationHelper
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
9
11
|
include TagHelper
|
10
|
-
|
12
|
+
|
13
|
+
included do
|
14
|
+
mattr_accessor :debug_missing_translation
|
15
|
+
self.debug_missing_translation = true
|
16
|
+
end
|
17
|
+
|
18
|
+
# Delegates to <tt>I18n#translate</tt> but also performs three additional
|
19
|
+
# functions.
|
20
|
+
#
|
21
|
+
# First, it will ensure that any thrown +MissingTranslation+ messages will
|
22
|
+
# be rendered as inline spans that:
|
23
|
+
#
|
24
|
+
# * Have a <tt>translation-missing</tt> class applied
|
25
|
+
# * Contain the missing key as the value of the +title+ attribute
|
26
|
+
# * Have a titleized version of the last key segment as text
|
11
27
|
#
|
12
|
-
#
|
13
|
-
#
|
28
|
+
# For example, the value returned for the missing translation key
|
29
|
+
# <tt>"blog.post.title"</tt> will be:
|
14
30
|
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
31
|
+
# <span
|
32
|
+
# class="translation_missing"
|
33
|
+
# title="translation missing: en.blog.post.title">Title</span>
|
18
34
|
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
# This way your views will display rather reasonable strings but it will still
|
22
|
-
# be easy to spot missing translations.
|
35
|
+
# This allows for views to display rather reasonable strings while still
|
36
|
+
# giving developers a way to find missing translations.
|
23
37
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
# <tt>
|
27
|
-
# <tt>
|
28
|
-
#
|
29
|
-
# for scoping them consistently. If you don't prepend the key with a period,
|
30
|
-
# nothing is converted.
|
38
|
+
# If you would prefer missing translations to raise an error, you can
|
39
|
+
# opt out of span-wrapping behavior globally by setting
|
40
|
+
# <tt>ActionView::Base.raise_on_missing_translations = true</tt> or
|
41
|
+
# individually by passing <tt>raise: true</tt> as an option to
|
42
|
+
# <tt>translate</tt>.
|
31
43
|
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
44
|
+
# Second, if the key starts with a period <tt>translate</tt> will scope
|
45
|
+
# the key by the current partial. Calling <tt>translate(".foo")</tt> from
|
46
|
+
# the <tt>people/index.html.erb</tt> template is equivalent to calling
|
47
|
+
# <tt>translate("people.index.foo")</tt>. This makes it less
|
48
|
+
# repetitive to translate many keys within the same partial and provides
|
49
|
+
# a convention to scope keys consistently.
|
50
|
+
#
|
51
|
+
# Third, the translation will be marked as <tt>html_safe</tt> if the key
|
52
|
+
# has the suffix "_html" or the last element of the key is "html". Calling
|
53
|
+
# <tt>translate("footer_html")</tt> or <tt>translate("footer.html")</tt>
|
54
|
+
# will return an HTML safe string that won't be escaped by other HTML
|
55
|
+
# helper methods. This naming convention helps to identify translations
|
56
|
+
# that include HTML tags so that you know what kind of output to expect
|
57
|
+
# when you call translate in a template and translators know which keys
|
58
|
+
# they can provide HTML values for.
|
38
59
|
def translate(key, options = {})
|
39
60
|
options = options.dup
|
40
61
|
has_default = options.has_key?(:default)
|
@@ -47,11 +68,11 @@ module ActionView
|
|
47
68
|
# If the user has explicitly decided to NOT raise errors, pass that option to I18n.
|
48
69
|
# Otherwise, tell I18n to raise an exception, which we rescue further in this method.
|
49
70
|
# Note: `raise_error` refers to us re-raising the error in this method. I18n is forced to raise by default.
|
50
|
-
if options[:raise] == false
|
71
|
+
if options[:raise] == false
|
51
72
|
raise_error = false
|
52
73
|
i18n_raise = false
|
53
74
|
else
|
54
|
-
raise_error = options[:raise] ||
|
75
|
+
raise_error = options[:raise] || ActionView::Base.raise_on_missing_translations
|
55
76
|
i18n_raise = true
|
56
77
|
end
|
57
78
|
|
@@ -75,7 +96,16 @@ module ActionView
|
|
75
96
|
raise e if raise_error
|
76
97
|
|
77
98
|
keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
|
78
|
-
|
99
|
+
title = "translation missing: #{keys.join('.')}"
|
100
|
+
|
101
|
+
interpolations = options.except(:default, :scope)
|
102
|
+
if interpolations.any?
|
103
|
+
title << ", " << interpolations.map { |k, v| "#{k}: #{ERB::Util.html_escape(v)}" }.join(', ')
|
104
|
+
end
|
105
|
+
|
106
|
+
return title unless ActionView::Base.debug_missing_translation
|
107
|
+
|
108
|
+
content_tag('span', keys.last.to_s.titleize, class: 'translation_missing', title: title)
|
79
109
|
end
|
80
110
|
end
|
81
111
|
alias :t :translate
|
@@ -41,14 +41,24 @@ module ActionView
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def _back_url # :nodoc:
|
44
|
-
|
45
|
-
referrer || 'javascript:history.back()'
|
44
|
+
_filtered_referrer || 'javascript:history.back()'
|
46
45
|
end
|
47
46
|
protected :_back_url
|
48
47
|
|
49
|
-
|
48
|
+
def _filtered_referrer # :nodoc:
|
49
|
+
if controller.respond_to?(:request)
|
50
|
+
referrer = controller.request.env["HTTP_REFERER"]
|
51
|
+
if referrer && URI(referrer).scheme != 'javascript'
|
52
|
+
referrer
|
53
|
+
end
|
54
|
+
end
|
55
|
+
rescue URI::InvalidURIError
|
56
|
+
end
|
57
|
+
protected :_filtered_referrer
|
58
|
+
|
59
|
+
# Creates an anchor element of the given +name+ using a URL created by the set of +options+.
|
50
60
|
# See the valid options in the documentation for +url_for+. It's also possible to
|
51
|
-
# pass a String instead of an options hash, which generates
|
61
|
+
# pass a String instead of an options hash, which generates an anchor element that uses the
|
52
62
|
# value of the String as the href for the link. Using a <tt>:back</tt> Symbol instead
|
53
63
|
# of an options hash will generate a link to the referrer (a JavaScript back link
|
54
64
|
# will be used in place of a referrer if none exists). If +nil+ is passed as the name
|
@@ -172,6 +182,11 @@ module ActionView
|
|
172
182
|
#
|
173
183
|
# link_to "Visit Other Site", "http://www.rubyonrails.org/", data: { confirm: "Are you sure?" }
|
174
184
|
# # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?">Visit Other Site</a>
|
185
|
+
#
|
186
|
+
# Also you can set any link attributes such as <tt>target</tt>, <tt>rel</tt>, <tt>type</tt>:
|
187
|
+
#
|
188
|
+
# link_to "External link", "http://www.rubyonrails.org/", target: "_blank", rel: "nofollow"
|
189
|
+
# # => <a href="http://www.rubyonrails.org/" target="_blank" rel="nofollow">External link</a>
|
175
190
|
def link_to(name = nil, options = nil, html_options = nil, &block)
|
176
191
|
html_options, options, name = options, name, block if block_given?
|
177
192
|
options ||= {}
|
@@ -179,9 +194,9 @@ module ActionView
|
|
179
194
|
html_options = convert_options_to_data_attributes(options, html_options)
|
180
195
|
|
181
196
|
url = url_for(options)
|
182
|
-
html_options[
|
197
|
+
html_options["href".freeze] ||= url
|
183
198
|
|
184
|
-
content_tag(
|
199
|
+
content_tag("a".freeze, name || url, html_options, &block)
|
185
200
|
end
|
186
201
|
|
187
202
|
# Generates a form containing a single button that submits to the URL created
|
@@ -280,24 +295,28 @@ module ActionView
|
|
280
295
|
html_options, options = options, name if block_given?
|
281
296
|
options ||= {}
|
282
297
|
html_options ||= {}
|
283
|
-
|
284
298
|
html_options = html_options.stringify_keys
|
285
|
-
convert_boolean_attributes!(html_options, %w(disabled))
|
286
299
|
|
287
300
|
url = options.is_a?(String) ? options : url_for(options)
|
288
301
|
remote = html_options.delete('remote')
|
289
302
|
params = html_options.delete('params')
|
290
303
|
|
291
304
|
method = html_options.delete('method').to_s
|
292
|
-
method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : ''.html_safe
|
305
|
+
method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : ''.freeze.html_safe
|
293
306
|
|
294
307
|
form_method = method == 'get' ? 'get' : 'post'
|
295
308
|
form_options = html_options.delete('form') || {}
|
296
309
|
form_options[:class] ||= html_options.delete('form_class') || 'button_to'
|
297
|
-
form_options
|
298
|
-
form_options
|
310
|
+
form_options[:method] = form_method
|
311
|
+
form_options[:action] = url
|
312
|
+
form_options[:'data-remote'] = true if remote
|
299
313
|
|
300
|
-
request_token_tag = form_method == 'post'
|
314
|
+
request_token_tag = if form_method == 'post'
|
315
|
+
request_method = method.empty? ? 'post' : method
|
316
|
+
token_tag(nil, form_options: { action: url, method: request_method })
|
317
|
+
else
|
318
|
+
''.freeze
|
319
|
+
end
|
301
320
|
|
302
321
|
html_options = convert_options_to_data_attributes(options, html_options)
|
303
322
|
html_options['type'] = 'submit'
|
@@ -311,8 +330,8 @@ module ActionView
|
|
311
330
|
|
312
331
|
inner_tags = method_tag.safe_concat(button).safe_concat(request_token_tag)
|
313
332
|
if params
|
314
|
-
params.each do |
|
315
|
-
inner_tags.safe_concat tag(:input, type: "hidden", name:
|
333
|
+
to_form_params(params).each do |param|
|
334
|
+
inner_tags.safe_concat tag(:input, type: "hidden", name: param[:name], value: param[:value])
|
316
335
|
end
|
317
336
|
end
|
318
337
|
content_tag('form', inner_tags, form_options)
|
@@ -428,6 +447,7 @@ module ActionView
|
|
428
447
|
# * <tt>:body</tt> - Preset the body of the email.
|
429
448
|
# * <tt>:cc</tt> - Carbon Copy additional recipients on the email.
|
430
449
|
# * <tt>:bcc</tt> - Blind Carbon Copy additional recipients on the email.
|
450
|
+
# * <tt>:reply_to</tt> - Preset the Reply-To field of the email.
|
431
451
|
#
|
432
452
|
# ==== Obfuscation
|
433
453
|
# Prior to Rails 4.0, +mail_to+ provided options for encoding the address
|
@@ -457,72 +477,60 @@ module ActionView
|
|
457
477
|
html_options, name = name, nil if block_given?
|
458
478
|
html_options = (html_options || {}).stringify_keys
|
459
479
|
|
460
|
-
extras = %w{ cc bcc body subject }.map! { |item|
|
461
|
-
option = html_options.delete(item) || next
|
462
|
-
"#{item}=#{
|
480
|
+
extras = %w{ cc bcc body subject reply_to }.map! { |item|
|
481
|
+
option = html_options.delete(item).presence || next
|
482
|
+
"#{item.dasherize}=#{ERB::Util.url_encode(option)}"
|
463
483
|
}.compact
|
464
|
-
extras = extras.empty? ? '' : '?' + extras.join('&')
|
484
|
+
extras = extras.empty? ? ''.freeze : '?' + extras.join('&')
|
465
485
|
|
466
|
-
encoded_email_address = ERB::Util.url_encode(email_address
|
486
|
+
encoded_email_address = ERB::Util.url_encode(email_address).gsub("%40", "@")
|
467
487
|
html_options["href"] = "mailto:#{encoded_email_address}#{extras}"
|
468
488
|
|
469
|
-
content_tag(
|
489
|
+
content_tag("a".freeze, name || email_address, html_options, &block)
|
470
490
|
end
|
471
491
|
|
472
492
|
# True if the current request URI was generated by the given +options+.
|
473
493
|
#
|
474
494
|
# ==== Examples
|
475
|
-
# Let's say we're in the <tt>http://www.example.com/shop/checkout?order=desc</tt> action.
|
495
|
+
# Let's say we're in the <tt>http://www.example.com/shop/checkout?order=desc&page=1</tt> action.
|
476
496
|
#
|
477
497
|
# current_page?(action: 'process')
|
478
498
|
# # => false
|
479
499
|
#
|
480
|
-
# current_page?(controller: 'shop', action: 'checkout')
|
481
|
-
# # => true
|
482
|
-
#
|
483
|
-
# current_page?(controller: 'shop', action: 'checkout', order: 'asc')
|
484
|
-
# # => false
|
485
|
-
#
|
486
500
|
# current_page?(action: 'checkout')
|
487
501
|
# # => true
|
488
502
|
#
|
489
503
|
# current_page?(controller: 'library', action: 'checkout')
|
490
504
|
# # => false
|
491
505
|
#
|
492
|
-
# current_page?('
|
493
|
-
# # => true
|
494
|
-
#
|
495
|
-
# current_page?('/shop/checkout')
|
506
|
+
# current_page?(controller: 'shop', action: 'checkout')
|
496
507
|
# # => true
|
497
508
|
#
|
498
|
-
#
|
499
|
-
#
|
500
|
-
# current_page?(action: 'process')
|
509
|
+
# current_page?(controller: 'shop', action: 'checkout', order: 'asc')
|
501
510
|
# # => false
|
502
511
|
#
|
503
|
-
# current_page?(controller: 'shop', action: 'checkout')
|
504
|
-
# # => true
|
505
|
-
#
|
506
512
|
# current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '1')
|
507
513
|
# # => true
|
508
514
|
#
|
509
515
|
# current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '2')
|
510
516
|
# # => false
|
511
517
|
#
|
512
|
-
# current_page?(
|
513
|
-
# # =>
|
518
|
+
# current_page?('http://www.example.com/shop/checkout')
|
519
|
+
# # => true
|
514
520
|
#
|
515
|
-
# current_page?(
|
521
|
+
# current_page?('/shop/checkout')
|
516
522
|
# # => true
|
517
523
|
#
|
518
|
-
# current_page?(
|
519
|
-
# # =>
|
524
|
+
# current_page?('http://www.example.com/shop/checkout?order=desc&page=1')
|
525
|
+
# # => true
|
520
526
|
#
|
521
527
|
# Let's say we're in the <tt>http://www.example.com/products</tt> action with method POST in case of invalid product.
|
522
528
|
#
|
523
529
|
# current_page?(controller: 'product', action: 'index')
|
524
530
|
# # => false
|
525
531
|
#
|
532
|
+
# We can also pass in the symbol arguments instead of strings.
|
533
|
+
#
|
526
534
|
def current_page?(options)
|
527
535
|
unless request
|
528
536
|
raise "You cannot use helpers that need to determine the current " \
|
@@ -540,6 +548,8 @@ module ActionView
|
|
540
548
|
request_uri = url_string.index("?") ? request.fullpath : request.path
|
541
549
|
request_uri = URI.parser.unescape(request_uri).force_encoding(Encoding::BINARY)
|
542
550
|
|
551
|
+
url_string.chomp!("/") if url_string.start_with?("/") && url_string != "/"
|
552
|
+
|
543
553
|
if url_string =~ /^\w+:\/\//
|
544
554
|
url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}"
|
545
555
|
else
|
@@ -551,70 +561,89 @@ module ActionView
|
|
551
561
|
def convert_options_to_data_attributes(options, html_options)
|
552
562
|
if html_options
|
553
563
|
html_options = html_options.stringify_keys
|
554
|
-
html_options['data-remote'] = 'true' if link_to_remote_options?(options) || link_to_remote_options?(html_options)
|
564
|
+
html_options['data-remote'] = 'true'.freeze if link_to_remote_options?(options) || link_to_remote_options?(html_options)
|
555
565
|
|
556
|
-
method = html_options.delete('method')
|
566
|
+
method = html_options.delete('method'.freeze)
|
557
567
|
|
558
568
|
add_method_to_attributes!(html_options, method) if method
|
559
569
|
|
560
570
|
html_options
|
561
571
|
else
|
562
|
-
link_to_remote_options?(options) ? {'data-remote' => 'true'} : {}
|
572
|
+
link_to_remote_options?(options) ? {'data-remote' => 'true'.freeze} : {}
|
563
573
|
end
|
564
574
|
end
|
565
575
|
|
566
576
|
def link_to_remote_options?(options)
|
567
577
|
if options.is_a?(Hash)
|
568
|
-
options.delete('remote') || options.delete(:remote)
|
578
|
+
options.delete('remote'.freeze) || options.delete(:remote)
|
569
579
|
end
|
570
580
|
end
|
571
581
|
|
572
582
|
def add_method_to_attributes!(html_options, method)
|
573
|
-
if method && method.to_s.downcase != "get" && html_options["rel"] !~ /nofollow/
|
574
|
-
html_options["rel"] = "#{html_options["rel"]} nofollow".lstrip
|
583
|
+
if method && method.to_s.downcase != "get".freeze && html_options["rel".freeze] !~ /nofollow/
|
584
|
+
html_options["rel".freeze] = "#{html_options["rel".freeze]} nofollow".lstrip
|
575
585
|
end
|
576
|
-
html_options["data-method"] = method
|
586
|
+
html_options["data-method".freeze] = method
|
577
587
|
end
|
578
588
|
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
589
|
+
def token_tag(token=nil, form_options: {})
|
590
|
+
if token != false && protect_against_forgery?
|
591
|
+
token ||= form_authenticity_token(form_options: form_options)
|
592
|
+
tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token)
|
593
|
+
else
|
594
|
+
''.freeze
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
def method_tag(method)
|
599
|
+
tag('input', type: 'hidden', name: '_method', value: method.to_s)
|
600
|
+
end
|
601
|
+
|
602
|
+
# Returns an array of hashes each containing :name and :value keys
|
603
|
+
# suitable for use as the names and values of form input fields:
|
586
604
|
#
|
587
|
-
#
|
605
|
+
# to_form_params(name: 'David', nationality: 'Danish')
|
606
|
+
# # => [{name: :name, value: 'David'}, {name: 'nationality', value: 'Danish'}]
|
588
607
|
#
|
589
|
-
#
|
590
|
-
#
|
591
|
-
# removed from the +html_options+ hash. (See the XHTML 1.0 spec,
|
592
|
-
# section 4.5 "Attribute Minimization" for more:
|
593
|
-
# http://www.w3.org/TR/xhtml1/#h-4.5)
|
608
|
+
# to_form_params(country: {name: 'Denmark'})
|
609
|
+
# # => [{name: 'country[name]', value: 'Denmark'}]
|
594
610
|
#
|
595
|
-
#
|
596
|
-
#
|
611
|
+
# to_form_params(countries: ['Denmark', 'Sweden']})
|
612
|
+
# # => [{name: 'countries[]', value: 'Denmark'}, {name: 'countries[]', value: 'Sweden'}]
|
597
613
|
#
|
598
|
-
#
|
614
|
+
# An optional namespace can be passed to enclose key names:
|
599
615
|
#
|
600
|
-
#
|
601
|
-
#
|
602
|
-
def
|
603
|
-
|
604
|
-
|
605
|
-
|
616
|
+
# to_form_params({ name: 'Denmark' }, 'country')
|
617
|
+
# # => [{name: 'country[name]', value: 'Denmark'}]
|
618
|
+
def to_form_params(attribute, namespace = nil) # :nodoc:
|
619
|
+
attribute = if attribute.respond_to?(:permitted?)
|
620
|
+
unless attribute.permitted?
|
621
|
+
raise ArgumentError, "Attempting to generate a buttom from non-sanitized request parameters!" \
|
622
|
+
" Whitelist and sanitize passed parameters to be secure."
|
623
|
+
end
|
624
|
+
|
625
|
+
attribute.to_h
|
626
|
+
else
|
627
|
+
attribute
|
628
|
+
end
|
606
629
|
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
630
|
+
params = []
|
631
|
+
case attribute
|
632
|
+
when Hash
|
633
|
+
attribute.each do |key, value|
|
634
|
+
prefix = namespace ? "#{namespace}[#{key}]" : key
|
635
|
+
params.push(*to_form_params(value, prefix))
|
636
|
+
end
|
637
|
+
when Array
|
638
|
+
array_prefix = "#{namespace}[]"
|
639
|
+
attribute.each do |value|
|
640
|
+
params.push(*to_form_params(value, array_prefix))
|
641
|
+
end
|
611
642
|
else
|
612
|
-
|
643
|
+
params << { name: namespace, value: attribute.to_param }
|
613
644
|
end
|
614
|
-
end
|
615
645
|
|
616
|
-
|
617
|
-
tag('input', type: 'hidden', name: '_method', value: method.to_s)
|
646
|
+
params.sort_by { |pair| pair[:name] }
|
618
647
|
end
|
619
648
|
end
|
620
649
|
end
|