actionview 4.1.13 → 6.1.3.1
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 +181 -359
- data/MIT-LICENSE +1 -1
- data/README.rdoc +12 -6
- data/lib/action_view/base.rb +115 -43
- data/lib/action_view/buffers.rb +22 -4
- data/lib/action_view/cache_expiry.rb +52 -0
- data/lib/action_view/context.rb +8 -12
- data/lib/action_view/dependency_tracker.rb +61 -21
- data/lib/action_view/digestor.rb +89 -84
- data/lib/action_view/flows.rb +12 -13
- data/lib/action_view/gem_version.rb +6 -4
- data/lib/action_view/helpers/active_model_helper.rb +16 -11
- data/lib/action_view/helpers/asset_tag_helper.rb +311 -105
- data/lib/action_view/helpers/asset_url_helper.rb +197 -80
- data/lib/action_view/helpers/atom_feed_helper.rb +20 -17
- data/lib/action_view/helpers/cache_helper.rb +109 -45
- data/lib/action_view/helpers/capture_helper.rb +20 -22
- data/lib/action_view/helpers/controller_helper.rb +15 -4
- data/lib/action_view/helpers/csp_helper.rb +26 -0
- data/lib/action_view/helpers/csrf_helper.rb +8 -6
- data/lib/action_view/helpers/date_helper.rb +245 -140
- data/lib/action_view/helpers/debug_helper.rb +14 -17
- data/lib/action_view/helpers/form_helper.rb +875 -148
- data/lib/action_view/helpers/form_options_helper.rb +128 -82
- data/lib/action_view/helpers/form_tag_helper.rb +253 -91
- data/lib/action_view/helpers/javascript_helper.rb +37 -15
- data/lib/action_view/helpers/number_helper.rb +100 -77
- data/lib/action_view/helpers/output_safety_helper.rb +42 -10
- data/lib/action_view/helpers/rendering_helper.rb +26 -15
- data/lib/action_view/helpers/sanitize_helper.rb +79 -164
- data/lib/action_view/helpers/tag_helper.rb +277 -64
- data/lib/action_view/helpers/tags/base.rb +143 -92
- data/lib/action_view/helpers/tags/check_box.rb +20 -19
- data/lib/action_view/helpers/tags/checkable.rb +4 -2
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -30
- data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
- data/lib/action_view/helpers/tags/collection_select.rb +4 -2
- data/lib/action_view/helpers/tags/color_field.rb +4 -3
- data/lib/action_view/helpers/tags/date_field.rb +3 -2
- data/lib/action_view/helpers/tags/date_select.rb +38 -37
- data/lib/action_view/helpers/tags/datetime_field.rb +14 -5
- data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
- data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
- data/lib/action_view/helpers/tags/email_field.rb +2 -0
- data/lib/action_view/helpers/tags/file_field.rb +2 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
- data/lib/action_view/helpers/tags/hidden_field.rb +2 -0
- data/lib/action_view/helpers/tags/label.rb +41 -22
- data/lib/action_view/helpers/tags/month_field.rb +3 -2
- data/lib/action_view/helpers/tags/number_field.rb +2 -0
- data/lib/action_view/helpers/tags/password_field.rb +3 -1
- data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
- data/lib/action_view/helpers/tags/radio_button.rb +7 -6
- data/lib/action_view/helpers/tags/range_field.rb +2 -0
- data/lib/action_view/helpers/tags/search_field.rb +3 -0
- data/lib/action_view/helpers/tags/select.rb +11 -10
- data/lib/action_view/helpers/tags/tel_field.rb +2 -0
- data/lib/action_view/helpers/tags/text_area.rb +7 -1
- data/lib/action_view/helpers/tags/text_field.rb +11 -7
- data/lib/action_view/helpers/tags/time_field.rb +3 -2
- data/lib/action_view/helpers/tags/time_select.rb +2 -0
- data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
- data/lib/action_view/helpers/tags/translator.rb +39 -0
- data/lib/action_view/helpers/tags/url_field.rb +2 -0
- data/lib/action_view/helpers/tags/week_field.rb +3 -2
- data/lib/action_view/helpers/tags.rb +4 -1
- data/lib/action_view/helpers/text_helper.rb +80 -45
- data/lib/action_view/helpers/translation_helper.rb +148 -67
- data/lib/action_view/helpers/url_helper.rb +289 -147
- data/lib/action_view/helpers.rb +5 -3
- data/lib/action_view/layouts.rb +68 -63
- data/lib/action_view/log_subscriber.rb +80 -13
- data/lib/action_view/lookup_context.rb +137 -92
- data/lib/action_view/model_naming.rb +4 -2
- data/lib/action_view/path_set.rb +30 -16
- data/lib/action_view/railtie.rb +62 -13
- data/lib/action_view/record_identifier.rb +53 -26
- data/lib/action_view/renderer/abstract_renderer.rb +152 -13
- data/lib/action_view/renderer/collection_renderer.rb +196 -0
- data/lib/action_view/renderer/object_renderer.rb +34 -0
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +102 -0
- data/lib/action_view/renderer/partial_renderer.rb +61 -261
- data/lib/action_view/renderer/renderer.rb +67 -6
- data/lib/action_view/renderer/streaming_template_renderer.rb +58 -54
- data/lib/action_view/renderer/template_renderer.rb +83 -75
- data/lib/action_view/rendering.rb +73 -46
- data/lib/action_view/routing_url_for.rb +54 -17
- data/lib/action_view/tasks/cache_digests.rake +25 -0
- data/lib/action_view/template/error.rb +44 -29
- data/lib/action_view/template/handlers/builder.rb +12 -13
- data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
- data/lib/action_view/template/handlers/erb.rb +23 -89
- data/lib/action_view/template/handlers/html.rb +11 -0
- data/lib/action_view/template/handlers/raw.rb +4 -4
- data/lib/action_view/template/handlers.rb +22 -9
- data/lib/action_view/template/html.rb +10 -11
- data/lib/action_view/template/inline.rb +22 -0
- data/lib/action_view/template/raw_file.rb +25 -0
- data/lib/action_view/template/renderable.rb +24 -0
- data/lib/action_view/template/resolver.rb +267 -181
- data/lib/action_view/template/sources/file.rb +17 -0
- data/lib/action_view/template/sources.rb +13 -0
- data/lib/action_view/template/text.rb +8 -10
- data/lib/action_view/template/types.rb +18 -18
- data/lib/action_view/template.rb +109 -99
- data/lib/action_view/test_case.rb +73 -53
- data/lib/action_view/testing/resolvers.rb +24 -33
- data/lib/action_view/unbound_template.rb +31 -0
- data/lib/action_view/version.rb +3 -1
- data/lib/action_view/view_paths.rb +74 -44
- data/lib/action_view.rb +14 -9
- data/lib/assets/compiled/rails-ujs.js +746 -0
- metadata +71 -26
- data/lib/action_view/helpers/record_tag_helper.rb +0 -108
- data/lib/action_view/tasks/dependencies.rake +0 -23
- data/lib/action_view/vendor/html-scanner/html/document.rb +0 -68
- data/lib/action_view/vendor/html-scanner/html/node.rb +0 -532
- data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +0 -188
- data/lib/action_view/vendor/html-scanner/html/selector.rb +0 -830
- data/lib/action_view/vendor/html-scanner/html/tokenizer.rb +0 -107
- data/lib/action_view/vendor/html-scanner/html/version.rb +0 -11
- data/lib/action_view/vendor/html-scanner.rb +0 -20
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_view/helpers/tags/checkable"
|
2
4
|
|
3
5
|
module ActionView
|
4
6
|
module Helpers
|
@@ -15,16 +17,15 @@ module ActionView
|
|
15
17
|
options = @options.stringify_keys
|
16
18
|
options["type"] = "radio"
|
17
19
|
options["value"] = @tag_value
|
18
|
-
options["checked"] = "checked" if input_checked?(
|
20
|
+
options["checked"] = "checked" if input_checked?(options)
|
19
21
|
add_default_name_and_id_for_value(@tag_value, options)
|
20
22
|
tag("input", options)
|
21
23
|
end
|
22
24
|
|
23
25
|
private
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
26
|
+
def checked?(value)
|
27
|
+
value.to_s == @tag_value.to_s
|
28
|
+
end
|
28
29
|
end
|
29
30
|
end
|
30
31
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionView
|
2
4
|
module Helpers
|
3
5
|
module Tags # :nodoc:
|
@@ -16,6 +18,7 @@ module ActionView
|
|
16
18
|
options["incremental"] = true unless options.has_key?("incremental")
|
17
19
|
end
|
18
20
|
|
21
|
+
@options = options
|
19
22
|
super
|
20
23
|
end
|
21
24
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionView
|
2
4
|
module Helpers
|
3
5
|
module Tags # :nodoc:
|
@@ -13,8 +15,8 @@ module ActionView
|
|
13
15
|
|
14
16
|
def render
|
15
17
|
option_tags_options = {
|
16
|
-
:
|
17
|
-
:
|
18
|
+
selected: @options.fetch(:selected) { value.nil? ? "" : value },
|
19
|
+
disabled: @options[:disabled]
|
18
20
|
}
|
19
21
|
|
20
22
|
option_tags = if grouped_choices?
|
@@ -27,14 +29,13 @@ module ActionView
|
|
27
29
|
end
|
28
30
|
|
29
31
|
private
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
end
|
32
|
+
# Grouped choices look like this:
|
33
|
+
#
|
34
|
+
# [nil, []]
|
35
|
+
# { nil => [] }
|
36
|
+
def grouped_choices?
|
37
|
+
!@choices.blank? && @choices.first.respond_to?(:last) && Array === @choices.first.last
|
38
|
+
end
|
38
39
|
end
|
39
40
|
end
|
40
41
|
end
|
@@ -1,7 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_view/helpers/tags/placeholderable"
|
4
|
+
|
1
5
|
module ActionView
|
2
6
|
module Helpers
|
3
7
|
module Tags # :nodoc:
|
4
8
|
class TextArea < Base # :nodoc:
|
9
|
+
include Placeholderable
|
10
|
+
|
5
11
|
def render
|
6
12
|
options = @options.stringify_keys
|
7
13
|
add_default_name_and_id(options)
|
@@ -10,7 +16,7 @@ module ActionView
|
|
10
16
|
options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
|
11
17
|
end
|
12
18
|
|
13
|
-
content_tag("textarea", options.delete("value") { value_before_type_cast
|
19
|
+
content_tag("textarea", options.delete("value") { value_before_type_cast }, options)
|
14
20
|
end
|
15
21
|
end
|
16
22
|
end
|
@@ -1,28 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_view/helpers/tags/placeholderable"
|
4
|
+
|
1
5
|
module ActionView
|
2
6
|
module Helpers
|
3
7
|
module Tags # :nodoc:
|
4
8
|
class TextField < Base # :nodoc:
|
9
|
+
include Placeholderable
|
10
|
+
|
5
11
|
def render
|
6
12
|
options = @options.stringify_keys
|
7
13
|
options["size"] = options["maxlength"] unless options.key?("size")
|
8
14
|
options["type"] ||= field_type
|
9
|
-
options["value"] = options.fetch("value") { value_before_type_cast
|
10
|
-
options["value"] &&= ERB::Util.html_escape(options["value"])
|
15
|
+
options["value"] = options.fetch("value") { value_before_type_cast } unless field_type == "file"
|
11
16
|
add_default_name_and_id(options)
|
12
17
|
tag("input", options)
|
13
18
|
end
|
14
19
|
|
15
20
|
class << self
|
16
21
|
def field_type
|
17
|
-
@field_type ||=
|
22
|
+
@field_type ||= name.split("::").last.sub("Field", "").downcase
|
18
23
|
end
|
19
24
|
end
|
20
25
|
|
21
26
|
private
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
27
|
+
def field_type
|
28
|
+
self.class.field_type
|
29
|
+
end
|
26
30
|
end
|
27
31
|
end
|
28
32
|
end
|
@@ -1,11 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionView
|
2
4
|
module Helpers
|
3
5
|
module Tags # :nodoc:
|
4
6
|
class TimeField < DatetimeField # :nodoc:
|
5
7
|
private
|
6
|
-
|
7
8
|
def format_date(value)
|
8
|
-
value
|
9
|
+
value&.strftime("%T.%L")
|
9
10
|
end
|
10
11
|
end
|
11
12
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionView
|
2
4
|
module Helpers
|
3
5
|
module Tags # :nodoc:
|
@@ -11,7 +13,7 @@ module ActionView
|
|
11
13
|
|
12
14
|
def render
|
13
15
|
select_content_tag(
|
14
|
-
time_zone_options_for_select(value
|
16
|
+
time_zone_options_for_select(value || @options[:default], @priority_zones, @options[:model] || ActiveSupport::TimeZone), @options, @html_options
|
15
17
|
)
|
16
18
|
end
|
17
19
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionView
|
4
|
+
module Helpers
|
5
|
+
module Tags # :nodoc:
|
6
|
+
class Translator # :nodoc:
|
7
|
+
def initialize(object, object_name, method_and_value, scope:)
|
8
|
+
@object_name = object_name.gsub(/\[(.*)_attributes\]\[\d+\]/, '.\1')
|
9
|
+
@method_and_value = method_and_value
|
10
|
+
@scope = scope
|
11
|
+
@model = object.respond_to?(:to_model) ? object.to_model : nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def translate
|
15
|
+
translated_attribute = I18n.t("#{object_name}.#{method_and_value}", default: i18n_default, scope: scope).presence
|
16
|
+
translated_attribute || human_attribute_name
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
attr_reader :object_name, :method_and_value, :scope, :model
|
21
|
+
|
22
|
+
def i18n_default
|
23
|
+
if model
|
24
|
+
key = model.model_name.i18n_key
|
25
|
+
["#{key}.#{method_and_value}".to_sym, ""]
|
26
|
+
else
|
27
|
+
""
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def human_attribute_name
|
32
|
+
if model && model.class.respond_to?(:human_attribute_name)
|
33
|
+
model.class.human_attribute_name(method_and_value)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,11 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionView
|
2
4
|
module Helpers
|
3
5
|
module Tags # :nodoc:
|
4
6
|
class WeekField < DatetimeField # :nodoc:
|
5
7
|
private
|
6
|
-
|
7
8
|
def format_date(value)
|
8
|
-
value
|
9
|
+
value&.strftime("%Y-W%V")
|
9
10
|
end
|
10
11
|
end
|
11
12
|
end
|
@@ -1,10 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionView
|
2
|
-
module Helpers
|
4
|
+
module Helpers #:nodoc:
|
3
5
|
module Tags #:nodoc:
|
4
6
|
extend ActiveSupport::Autoload
|
5
7
|
|
6
8
|
eager_autoload do
|
7
9
|
autoload :Base
|
10
|
+
autoload :Translator
|
8
11
|
autoload :CheckBox
|
9
12
|
autoload :CollectionCheckBoxes
|
10
13
|
autoload :CollectionRadioButtons
|
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/string/filters"
|
4
|
+
require "active_support/core_ext/array/extract_options"
|
3
5
|
|
4
6
|
module ActionView
|
5
7
|
# = Action View Text Helpers
|
@@ -11,9 +13,9 @@ module ActionView
|
|
11
13
|
#
|
12
14
|
# ==== Sanitization
|
13
15
|
#
|
14
|
-
# Most text helpers
|
15
|
-
# This means HTML tags will appear in the page but all malicious
|
16
|
-
# Let's look at some examples using the +simple_format+ method:
|
16
|
+
# Most text helpers that generate HTML output sanitize the given input by default,
|
17
|
+
# but do not escape it. This means HTML tags will appear in the page but all malicious
|
18
|
+
# code will be removed. Let's look at some examples using the +simple_format+ method:
|
17
19
|
#
|
18
20
|
# simple_format('<a href="http://example.com/">Example</a>')
|
19
21
|
# # => "<p><a href=\"http://example.com/\">Example</a></p>"
|
@@ -103,11 +105,16 @@ module ActionView
|
|
103
105
|
# Highlights one or more +phrases+ everywhere in +text+ by inserting it into
|
104
106
|
# a <tt>:highlighter</tt> string. The highlighter can be specialized by passing <tt>:highlighter</tt>
|
105
107
|
# as a single-quoted string with <tt>\1</tt> where the phrase is to be inserted (defaults to
|
106
|
-
#
|
108
|
+
# <tt><mark>\1</mark></tt>) or passing a block that receives each matched term. By default +text+
|
109
|
+
# is sanitized to prevent possible XSS attacks. If the input is trustworthy, passing false
|
110
|
+
# for <tt>:sanitize</tt> will turn sanitizing off.
|
107
111
|
#
|
108
112
|
# highlight('You searched for: rails', 'rails')
|
109
113
|
# # => You searched for: <mark>rails</mark>
|
110
114
|
#
|
115
|
+
# highlight('You searched for: rails', /for|rails/)
|
116
|
+
# # => You searched <mark>for</mark>: <mark>rails</mark>
|
117
|
+
#
|
111
118
|
# highlight('You searched for: ruby, rails, dhh', 'actionpack')
|
112
119
|
# # => You searched for: ruby, rails, dhh
|
113
120
|
#
|
@@ -116,15 +123,28 @@ module ActionView
|
|
116
123
|
#
|
117
124
|
# highlight('You searched for: rails', 'rails', highlighter: '<a href="search?q=\1">\1</a>')
|
118
125
|
# # => You searched for: <a href="search?q=rails">rails</a>
|
126
|
+
#
|
127
|
+
# highlight('You searched for: rails', 'rails') { |match| link_to(search_path(q: match, match)) }
|
128
|
+
# # => You searched for: <a href="search?q=rails">rails</a>
|
129
|
+
#
|
130
|
+
# highlight('<a href="javascript:alert(\'no!\')">ruby</a> on rails', 'rails', sanitize: false)
|
131
|
+
# # => <a href="javascript:alert('no!')">ruby</a> on <mark>rails</mark>
|
119
132
|
def highlight(text, phrases, options = {})
|
120
133
|
text = sanitize(text) if options.fetch(:sanitize, true)
|
121
134
|
|
122
135
|
if text.blank? || phrases.blank?
|
123
|
-
text
|
136
|
+
text || ""
|
124
137
|
else
|
125
|
-
|
126
|
-
|
127
|
-
|
138
|
+
match = Array(phrases).map do |p|
|
139
|
+
Regexp === p ? p.to_s : Regexp.escape(p)
|
140
|
+
end.join("|")
|
141
|
+
|
142
|
+
if block_given?
|
143
|
+
text.gsub(/(#{match})(?![^<]*?>)/i) { |found| yield found }
|
144
|
+
else
|
145
|
+
highlighter = options.fetch(:highlighter, '<mark>\1</mark>')
|
146
|
+
text.gsub(/(#{match})(?![^<]*?>)/i, highlighter)
|
147
|
+
end
|
128
148
|
end.html_safe
|
129
149
|
end
|
130
150
|
|
@@ -133,7 +153,7 @@ module ActionView
|
|
133
153
|
# defined in <tt>:radius</tt> (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
|
134
154
|
# then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. Use the
|
135
155
|
# <tt>:separator</tt> option to choose the delimitation. The resulting string will be stripped in any case. If the +phrase+
|
136
|
-
# isn't found, nil is returned.
|
156
|
+
# isn't found, +nil+ is returned.
|
137
157
|
#
|
138
158
|
# excerpt('This is an example', 'an', radius: 5)
|
139
159
|
# # => ...s is an exam...
|
@@ -155,23 +175,27 @@ module ActionView
|
|
155
175
|
def excerpt(text, phrase, options = {})
|
156
176
|
return unless text && phrase
|
157
177
|
|
158
|
-
separator = options
|
159
|
-
|
160
|
-
|
178
|
+
separator = options.fetch(:separator, nil) || ""
|
179
|
+
case phrase
|
180
|
+
when Regexp
|
181
|
+
regex = phrase
|
182
|
+
else
|
183
|
+
regex = /#{Regexp.escape(phrase)}/i
|
184
|
+
end
|
161
185
|
|
162
186
|
return unless matches = text.match(regex)
|
163
187
|
phrase = matches[0]
|
164
188
|
|
165
189
|
unless separator.empty?
|
166
190
|
text.split(separator).each do |value|
|
167
|
-
if value.match(regex)
|
168
|
-
|
191
|
+
if value.match?(regex)
|
192
|
+
phrase = value
|
169
193
|
break
|
170
194
|
end
|
171
195
|
end
|
172
196
|
end
|
173
197
|
|
174
|
-
first_part, second_part = text.split(
|
198
|
+
first_part, second_part = text.split(phrase, 2)
|
175
199
|
|
176
200
|
prefix, first_part = cut_excerpt_part(:first, first_part, separator, options)
|
177
201
|
postfix, second_part = cut_excerpt_part(:second, second_part, separator, options)
|
@@ -182,7 +206,12 @@ module ActionView
|
|
182
206
|
|
183
207
|
# Attempts to pluralize the +singular+ word unless +count+ is 1. If
|
184
208
|
# +plural+ is supplied, it will use that when count is > 1, otherwise
|
185
|
-
# it will use the Inflector to determine the plural form
|
209
|
+
# it will use the Inflector to determine the plural form for the given locale,
|
210
|
+
# which defaults to I18n.locale
|
211
|
+
#
|
212
|
+
# The word will be pluralized using rules defined for the locale
|
213
|
+
# (you must define your own inflection rules for languages other than English).
|
214
|
+
# See ActiveSupport::Inflector.pluralize
|
186
215
|
#
|
187
216
|
# pluralize(1, 'person')
|
188
217
|
# # => 1 person
|
@@ -190,16 +219,19 @@ module ActionView
|
|
190
219
|
# pluralize(2, 'person')
|
191
220
|
# # => 2 people
|
192
221
|
#
|
193
|
-
# pluralize(3, 'person', 'users')
|
222
|
+
# pluralize(3, 'person', plural: 'users')
|
194
223
|
# # => 3 users
|
195
224
|
#
|
196
225
|
# pluralize(0, 'person')
|
197
226
|
# # => 0 people
|
198
|
-
|
199
|
-
|
227
|
+
#
|
228
|
+
# pluralize(2, 'Person', locale: :de)
|
229
|
+
# # => 2 Personen
|
230
|
+
def pluralize(count, singular, plural_arg = nil, plural: plural_arg, locale: I18n.locale)
|
231
|
+
word = if count == 1 || count.to_s.match?(/^1(\.0+)?$/)
|
200
232
|
singular
|
201
233
|
else
|
202
|
-
plural || singular.pluralize
|
234
|
+
plural || singular.pluralize(locale)
|
203
235
|
end
|
204
236
|
|
205
237
|
"#{count || 0} #{word}"
|
@@ -220,19 +252,23 @@ module ActionView
|
|
220
252
|
#
|
221
253
|
# word_wrap('Once upon a time', line_width: 1)
|
222
254
|
# # => Once\nupon\na\ntime
|
223
|
-
|
224
|
-
|
225
|
-
|
255
|
+
#
|
256
|
+
# You can also specify a custom +break_sequence+ ("\n" by default)
|
257
|
+
#
|
258
|
+
# word_wrap('Once upon a time', line_width: 1, break_sequence: "\r\n")
|
259
|
+
# # => Once\r\nupon\r\na\r\ntime
|
260
|
+
def word_wrap(text, line_width: 80, break_sequence: "\n")
|
226
261
|
text.split("\n").collect! do |line|
|
227
|
-
line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1
|
228
|
-
end *
|
262
|
+
line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1#{break_sequence}").rstrip : line
|
263
|
+
end * break_sequence
|
229
264
|
end
|
230
265
|
|
231
266
|
# Returns +text+ transformed into HTML using simple formatting rules.
|
232
|
-
# Two or more consecutive newlines(<tt>\n\n</tt>) are
|
233
|
-
# paragraph and wrapped in <tt><p></tt> tags. One newline
|
234
|
-
#
|
235
|
-
# method does not remove the
|
267
|
+
# Two or more consecutive newlines(<tt>\n\n</tt> or <tt>\r\n\r\n</tt>) are
|
268
|
+
# considered a paragraph and wrapped in <tt><p></tt> tags. One newline
|
269
|
+
# (<tt>\n</tt> or <tt>\r\n</tt>) is considered a linebreak and a
|
270
|
+
# <tt><br /></tt> tag is appended. This method does not remove the
|
271
|
+
# newlines from the +text+.
|
236
272
|
#
|
237
273
|
# You can pass any HTML attributes into <tt>html_options</tt>. These
|
238
274
|
# will be added to all created paragraphs.
|
@@ -292,7 +328,7 @@ module ActionView
|
|
292
328
|
# <table>
|
293
329
|
# <% @items.each do |item| %>
|
294
330
|
# <tr class="<%= cycle("odd", "even") -%>">
|
295
|
-
# <td
|
331
|
+
# <td><%= item %></td>
|
296
332
|
# </tr>
|
297
333
|
# <% end %>
|
298
334
|
# </table>
|
@@ -317,7 +353,7 @@ module ActionView
|
|
317
353
|
# <% end %>
|
318
354
|
def cycle(first_value, *values)
|
319
355
|
options = values.extract_options!
|
320
|
-
name = options.fetch(:name,
|
356
|
+
name = options.fetch(:name, "default")
|
321
357
|
|
322
358
|
values.unshift(*first_value)
|
323
359
|
|
@@ -386,22 +422,21 @@ module ActionView
|
|
386
422
|
def to_s
|
387
423
|
value = @values[@index].to_s
|
388
424
|
@index = next_index
|
389
|
-
|
425
|
+
value
|
390
426
|
end
|
391
427
|
|
392
428
|
private
|
429
|
+
def next_index
|
430
|
+
step_index(1)
|
431
|
+
end
|
393
432
|
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
def previous_index
|
399
|
-
step_index(-1)
|
400
|
-
end
|
433
|
+
def previous_index
|
434
|
+
step_index(-1)
|
435
|
+
end
|
401
436
|
|
402
|
-
|
403
|
-
|
404
|
-
|
437
|
+
def step_index(n)
|
438
|
+
(@index + n) % @values.size
|
439
|
+
end
|
405
440
|
end
|
406
441
|
|
407
442
|
private
|
@@ -410,7 +445,7 @@ module ActionView
|
|
410
445
|
# uses an instance variable of ActionView::Base.
|
411
446
|
def get_cycle(name)
|
412
447
|
@_cycles = Hash.new unless defined?(@_cycles)
|
413
|
-
|
448
|
+
@_cycles[name]
|
414
449
|
end
|
415
450
|
|
416
451
|
def set_cycle(name, cycle_object)
|