omg-actionview 8.0.0.alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +25 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +40 -0
- data/app/assets/javascripts/rails-ujs.esm.js +686 -0
- data/app/assets/javascripts/rails-ujs.js +630 -0
- data/lib/action_view/base.rb +316 -0
- data/lib/action_view/buffers.rb +165 -0
- data/lib/action_view/cache_expiry.rb +69 -0
- data/lib/action_view/context.rb +32 -0
- data/lib/action_view/dependency_tracker/erb_tracker.rb +159 -0
- data/lib/action_view/dependency_tracker/ruby_tracker.rb +43 -0
- data/lib/action_view/dependency_tracker/wildcard_resolver.rb +32 -0
- data/lib/action_view/dependency_tracker.rb +41 -0
- data/lib/action_view/deprecator.rb +7 -0
- data/lib/action_view/digestor.rb +130 -0
- data/lib/action_view/flows.rb +75 -0
- data/lib/action_view/gem_version.rb +17 -0
- data/lib/action_view/helpers/active_model_helper.rb +54 -0
- data/lib/action_view/helpers/asset_tag_helper.rb +680 -0
- data/lib/action_view/helpers/asset_url_helper.rb +473 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +205 -0
- data/lib/action_view/helpers/cache_helper.rb +315 -0
- data/lib/action_view/helpers/capture_helper.rb +236 -0
- data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
- data/lib/action_view/helpers/controller_helper.rb +42 -0
- data/lib/action_view/helpers/csp_helper.rb +26 -0
- data/lib/action_view/helpers/csrf_helper.rb +35 -0
- data/lib/action_view/helpers/date_helper.rb +1266 -0
- data/lib/action_view/helpers/debug_helper.rb +38 -0
- data/lib/action_view/helpers/form_helper.rb +2765 -0
- data/lib/action_view/helpers/form_options_helper.rb +927 -0
- data/lib/action_view/helpers/form_tag_helper.rb +1088 -0
- data/lib/action_view/helpers/javascript_helper.rb +96 -0
- data/lib/action_view/helpers/number_helper.rb +165 -0
- data/lib/action_view/helpers/output_safety_helper.rb +70 -0
- data/lib/action_view/helpers/rendering_helper.rb +218 -0
- data/lib/action_view/helpers/sanitize_helper.rb +201 -0
- data/lib/action_view/helpers/tag_helper.rb +621 -0
- data/lib/action_view/helpers/tags/base.rb +138 -0
- data/lib/action_view/helpers/tags/check_box.rb +65 -0
- data/lib/action_view/helpers/tags/checkable.rb +18 -0
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +37 -0
- data/lib/action_view/helpers/tags/collection_helpers.rb +118 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +31 -0
- data/lib/action_view/helpers/tags/collection_select.rb +33 -0
- data/lib/action_view/helpers/tags/color_field.rb +26 -0
- data/lib/action_view/helpers/tags/date_field.rb +14 -0
- data/lib/action_view/helpers/tags/date_select.rb +75 -0
- data/lib/action_view/helpers/tags/datetime_field.rb +39 -0
- data/lib/action_view/helpers/tags/datetime_local_field.rb +29 -0
- data/lib/action_view/helpers/tags/datetime_select.rb +10 -0
- data/lib/action_view/helpers/tags/email_field.rb +10 -0
- data/lib/action_view/helpers/tags/file_field.rb +26 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +34 -0
- data/lib/action_view/helpers/tags/hidden_field.rb +14 -0
- data/lib/action_view/helpers/tags/label.rb +84 -0
- data/lib/action_view/helpers/tags/month_field.rb +14 -0
- data/lib/action_view/helpers/tags/number_field.rb +20 -0
- data/lib/action_view/helpers/tags/password_field.rb +14 -0
- data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
- data/lib/action_view/helpers/tags/radio_button.rb +32 -0
- data/lib/action_view/helpers/tags/range_field.rb +10 -0
- data/lib/action_view/helpers/tags/search_field.rb +27 -0
- data/lib/action_view/helpers/tags/select.rb +45 -0
- data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
- data/lib/action_view/helpers/tags/tel_field.rb +10 -0
- data/lib/action_view/helpers/tags/text_area.rb +24 -0
- data/lib/action_view/helpers/tags/text_field.rb +33 -0
- data/lib/action_view/helpers/tags/time_field.rb +23 -0
- data/lib/action_view/helpers/tags/time_select.rb +10 -0
- data/lib/action_view/helpers/tags/time_zone_select.rb +25 -0
- data/lib/action_view/helpers/tags/translator.rb +39 -0
- data/lib/action_view/helpers/tags/url_field.rb +10 -0
- data/lib/action_view/helpers/tags/week_field.rb +14 -0
- data/lib/action_view/helpers/tags/weekday_select.rb +31 -0
- data/lib/action_view/helpers/tags.rb +47 -0
- data/lib/action_view/helpers/text_helper.rb +568 -0
- data/lib/action_view/helpers/translation_helper.rb +161 -0
- data/lib/action_view/helpers/url_helper.rb +812 -0
- data/lib/action_view/helpers.rb +68 -0
- data/lib/action_view/layouts.rb +434 -0
- data/lib/action_view/locale/en.yml +56 -0
- data/lib/action_view/log_subscriber.rb +132 -0
- data/lib/action_view/lookup_context.rb +299 -0
- data/lib/action_view/model_naming.rb +14 -0
- data/lib/action_view/path_registry.rb +57 -0
- data/lib/action_view/path_set.rb +84 -0
- data/lib/action_view/railtie.rb +132 -0
- data/lib/action_view/record_identifier.rb +118 -0
- data/lib/action_view/render_parser/prism_render_parser.rb +139 -0
- data/lib/action_view/render_parser/ripper_render_parser.rb +350 -0
- data/lib/action_view/render_parser.rb +40 -0
- data/lib/action_view/renderer/abstract_renderer.rb +186 -0
- data/lib/action_view/renderer/collection_renderer.rb +204 -0
- data/lib/action_view/renderer/object_renderer.rb +34 -0
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +120 -0
- data/lib/action_view/renderer/partial_renderer.rb +267 -0
- data/lib/action_view/renderer/renderer.rb +107 -0
- data/lib/action_view/renderer/streaming_template_renderer.rb +107 -0
- data/lib/action_view/renderer/template_renderer.rb +115 -0
- data/lib/action_view/rendering.rb +190 -0
- data/lib/action_view/routing_url_for.rb +149 -0
- data/lib/action_view/tasks/cache_digests.rake +25 -0
- data/lib/action_view/template/error.rb +264 -0
- data/lib/action_view/template/handlers/builder.rb +25 -0
- data/lib/action_view/template/handlers/erb/erubi.rb +85 -0
- data/lib/action_view/template/handlers/erb.rb +157 -0
- data/lib/action_view/template/handlers/html.rb +11 -0
- data/lib/action_view/template/handlers/raw.rb +11 -0
- data/lib/action_view/template/handlers.rb +66 -0
- data/lib/action_view/template/html.rb +33 -0
- 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 +30 -0
- data/lib/action_view/template/resolver.rb +212 -0
- 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 +32 -0
- data/lib/action_view/template/types.rb +50 -0
- data/lib/action_view/template.rb +580 -0
- data/lib/action_view/template_details.rb +66 -0
- data/lib/action_view/template_path.rb +66 -0
- data/lib/action_view/test_case.rb +449 -0
- data/lib/action_view/testing/resolvers.rb +44 -0
- data/lib/action_view/unbound_template.rb +67 -0
- data/lib/action_view/version.rb +10 -0
- data/lib/action_view/view_paths.rb +117 -0
- data/lib/action_view.rb +104 -0
- metadata +275 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionView
|
|
4
|
+
module Helpers # :nodoc:
|
|
5
|
+
# = Action View JavaScript \Helpers
|
|
6
|
+
module JavaScriptHelper
|
|
7
|
+
JS_ESCAPE_MAP = {
|
|
8
|
+
"\\" => "\\\\",
|
|
9
|
+
"</" => '<\/',
|
|
10
|
+
"\r\n" => '\n',
|
|
11
|
+
"\n" => '\n',
|
|
12
|
+
"\r" => '\n',
|
|
13
|
+
'"' => '\\"',
|
|
14
|
+
"'" => "\\'",
|
|
15
|
+
"`" => "\\`",
|
|
16
|
+
"$" => "\\$"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
JS_ESCAPE_MAP[(+"\342\200\250").force_encoding(Encoding::UTF_8).encode!] = "
"
|
|
20
|
+
JS_ESCAPE_MAP[(+"\342\200\251").force_encoding(Encoding::UTF_8).encode!] = "
"
|
|
21
|
+
|
|
22
|
+
# Escapes carriage returns and single and double quotes for JavaScript segments.
|
|
23
|
+
#
|
|
24
|
+
# Also available through the alias j(). This is particularly helpful in JavaScript
|
|
25
|
+
# responses, like:
|
|
26
|
+
#
|
|
27
|
+
# $('some_element').replaceWith('<%= j render 'some/element_template' %>');
|
|
28
|
+
def escape_javascript(javascript)
|
|
29
|
+
javascript = javascript.to_s
|
|
30
|
+
if javascript.empty?
|
|
31
|
+
result = ""
|
|
32
|
+
else
|
|
33
|
+
result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"']|[`]|[$])/u, JS_ESCAPE_MAP)
|
|
34
|
+
end
|
|
35
|
+
javascript.html_safe? ? result.html_safe : result
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
alias_method :j, :escape_javascript
|
|
39
|
+
|
|
40
|
+
# Returns a JavaScript tag with the +content+ inside. Example:
|
|
41
|
+
# javascript_tag "alert('All is good')"
|
|
42
|
+
#
|
|
43
|
+
# Returns:
|
|
44
|
+
# <script>
|
|
45
|
+
# //<![CDATA[
|
|
46
|
+
# alert('All is good')
|
|
47
|
+
# //]]>
|
|
48
|
+
# </script>
|
|
49
|
+
#
|
|
50
|
+
# +html_options+ may be a hash of attributes for the <tt>\<script></tt>
|
|
51
|
+
# tag.
|
|
52
|
+
#
|
|
53
|
+
# javascript_tag "alert('All is good')", type: 'application/javascript'
|
|
54
|
+
#
|
|
55
|
+
# Returns:
|
|
56
|
+
# <script type="application/javascript">
|
|
57
|
+
# //<![CDATA[
|
|
58
|
+
# alert('All is good')
|
|
59
|
+
# //]]>
|
|
60
|
+
# </script>
|
|
61
|
+
#
|
|
62
|
+
# Instead of passing the content as an argument, you can also use a block
|
|
63
|
+
# in which case, you pass your +html_options+ as the first parameter.
|
|
64
|
+
#
|
|
65
|
+
# <%= javascript_tag type: 'application/javascript' do -%>
|
|
66
|
+
# alert('All is good')
|
|
67
|
+
# <% end -%>
|
|
68
|
+
#
|
|
69
|
+
# If you have a content security policy enabled then you can add an automatic
|
|
70
|
+
# nonce value by passing <tt>nonce: true</tt> as part of +html_options+. Example:
|
|
71
|
+
#
|
|
72
|
+
# <%= javascript_tag nonce: true do -%>
|
|
73
|
+
# alert('All is good')
|
|
74
|
+
# <% end -%>
|
|
75
|
+
def javascript_tag(content_or_options_with_block = nil, html_options = {}, &block)
|
|
76
|
+
content =
|
|
77
|
+
if block_given?
|
|
78
|
+
html_options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
|
|
79
|
+
capture(&block)
|
|
80
|
+
else
|
|
81
|
+
content_or_options_with_block
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
if html_options[:nonce] == true
|
|
85
|
+
html_options[:nonce] = content_security_policy_nonce
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
content_tag("script", javascript_cdata_section(content), html_options)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def javascript_cdata_section(content) # :nodoc:
|
|
92
|
+
"\n//#{cdata_section("\n#{content}\n//")}\n".html_safe
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/core_ext/hash/keys"
|
|
4
|
+
require "active_support/core_ext/string/output_safety"
|
|
5
|
+
require "active_support/number_helper"
|
|
6
|
+
|
|
7
|
+
module ActionView
|
|
8
|
+
module Helpers # :nodoc:
|
|
9
|
+
# = Action View Number \Helpers
|
|
10
|
+
#
|
|
11
|
+
# Provides methods for converting numbers into formatted strings.
|
|
12
|
+
# Methods are provided for phone numbers, currency, percentage,
|
|
13
|
+
# precision, positional notation, file size, and pretty printing.
|
|
14
|
+
#
|
|
15
|
+
# Most methods expect a +number+ argument, and will return it
|
|
16
|
+
# unchanged if can't be converted into a valid number.
|
|
17
|
+
module NumberHelper
|
|
18
|
+
# Raised when argument +number+ param given to the helpers is invalid and
|
|
19
|
+
# the option +:raise+ is set to +true+.
|
|
20
|
+
class InvalidNumberError < StandardError
|
|
21
|
+
attr_accessor :number
|
|
22
|
+
def initialize(number)
|
|
23
|
+
@number = number
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Delegates to ActiveSupport::NumberHelper#number_to_phone.
|
|
28
|
+
#
|
|
29
|
+
# Additionally, supports a +:raise+ option that will cause
|
|
30
|
+
# InvalidNumberError to be raised if +number+ is not a valid number:
|
|
31
|
+
#
|
|
32
|
+
# number_to_phone("12x34") # => "12x34"
|
|
33
|
+
# number_to_phone("12x34", raise: true) # => InvalidNumberError
|
|
34
|
+
#
|
|
35
|
+
def number_to_phone(number, options = {})
|
|
36
|
+
return unless number
|
|
37
|
+
options = options.symbolize_keys
|
|
38
|
+
|
|
39
|
+
parse_float(number, true) if options.delete(:raise)
|
|
40
|
+
ERB::Util.html_escape(ActiveSupport::NumberHelper.number_to_phone(number, options))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Delegates to ActiveSupport::NumberHelper#number_to_currency.
|
|
44
|
+
#
|
|
45
|
+
# Additionally, supports a +:raise+ option that will cause
|
|
46
|
+
# InvalidNumberError to be raised if +number+ is not a valid number:
|
|
47
|
+
#
|
|
48
|
+
# number_to_currency("12x34") # => "$12x34"
|
|
49
|
+
# number_to_currency("12x34", raise: true) # => InvalidNumberError
|
|
50
|
+
#
|
|
51
|
+
def number_to_currency(number, options = {})
|
|
52
|
+
delegate_number_helper_method(:number_to_currency, number, options)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Delegates to ActiveSupport::NumberHelper#number_to_percentage.
|
|
56
|
+
#
|
|
57
|
+
# Additionally, supports a +:raise+ option that will cause
|
|
58
|
+
# InvalidNumberError to be raised if +number+ is not a valid number:
|
|
59
|
+
#
|
|
60
|
+
# number_to_percentage("99x") # => "99x%"
|
|
61
|
+
# number_to_percentage("99x", raise: true) # => InvalidNumberError
|
|
62
|
+
#
|
|
63
|
+
def number_to_percentage(number, options = {})
|
|
64
|
+
delegate_number_helper_method(:number_to_percentage, number, options)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Delegates to ActiveSupport::NumberHelper#number_to_delimited.
|
|
68
|
+
#
|
|
69
|
+
# Additionally, supports a +:raise+ option that will cause
|
|
70
|
+
# InvalidNumberError to be raised if +number+ is not a valid number:
|
|
71
|
+
#
|
|
72
|
+
# number_with_delimiter("12x34") # => "12x34"
|
|
73
|
+
# number_with_delimiter("12x34", raise: true) # => InvalidNumberError
|
|
74
|
+
#
|
|
75
|
+
def number_with_delimiter(number, options = {})
|
|
76
|
+
delegate_number_helper_method(:number_to_delimited, number, options)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Delegates to ActiveSupport::NumberHelper#number_to_rounded.
|
|
80
|
+
#
|
|
81
|
+
# Additionally, supports a +:raise+ option that will cause
|
|
82
|
+
# InvalidNumberError to be raised if +number+ is not a valid number:
|
|
83
|
+
#
|
|
84
|
+
# number_with_precision("12x34") # => "12x34"
|
|
85
|
+
# number_with_precision("12x34", raise: true) # => InvalidNumberError
|
|
86
|
+
#
|
|
87
|
+
def number_with_precision(number, options = {})
|
|
88
|
+
delegate_number_helper_method(:number_to_rounded, number, options)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Delegates to ActiveSupport::NumberHelper#number_to_human_size.
|
|
92
|
+
#
|
|
93
|
+
# Additionally, supports a +:raise+ option that will cause
|
|
94
|
+
# InvalidNumberError to be raised if +number+ is not a valid number:
|
|
95
|
+
#
|
|
96
|
+
# number_to_human_size("12x34") # => "12x34"
|
|
97
|
+
# number_to_human_size("12x34", raise: true) # => InvalidNumberError
|
|
98
|
+
#
|
|
99
|
+
def number_to_human_size(number, options = {})
|
|
100
|
+
delegate_number_helper_method(:number_to_human_size, number, options)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Delegates to ActiveSupport::NumberHelper#number_to_human.
|
|
104
|
+
#
|
|
105
|
+
# Additionally, supports a +:raise+ option that will cause
|
|
106
|
+
# InvalidNumberError to be raised if +number+ is not a valid number:
|
|
107
|
+
#
|
|
108
|
+
# number_to_human("12x34") # => "12x34"
|
|
109
|
+
# number_to_human("12x34", raise: true) # => InvalidNumberError
|
|
110
|
+
#
|
|
111
|
+
def number_to_human(number, options = {})
|
|
112
|
+
delegate_number_helper_method(:number_to_human, number, options)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
def delegate_number_helper_method(method, number, options)
|
|
117
|
+
return unless number
|
|
118
|
+
options = escape_unsafe_options(options.symbolize_keys)
|
|
119
|
+
|
|
120
|
+
wrap_with_output_safety_handling(number, options.delete(:raise)) {
|
|
121
|
+
ActiveSupport::NumberHelper.public_send(method, number, options)
|
|
122
|
+
}
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def escape_unsafe_options(options)
|
|
126
|
+
options[:format] = ERB::Util.html_escape(options[:format]) if options[:format]
|
|
127
|
+
options[:negative_format] = ERB::Util.html_escape(options[:negative_format]) if options[:negative_format]
|
|
128
|
+
options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator]
|
|
129
|
+
options[:delimiter] = ERB::Util.html_escape(options[:delimiter]) if options[:delimiter]
|
|
130
|
+
options[:unit] = ERB::Util.html_escape(options[:unit]) if options[:unit] && !options[:unit].html_safe?
|
|
131
|
+
options[:units] = escape_units(options[:units]) if options[:units] && Hash === options[:units]
|
|
132
|
+
options
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def escape_units(units)
|
|
136
|
+
units.transform_values do |v|
|
|
137
|
+
ERB::Util.html_escape(v)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def wrap_with_output_safety_handling(number, raise_on_invalid, &block)
|
|
142
|
+
valid_float = valid_float?(number)
|
|
143
|
+
raise InvalidNumberError, number if raise_on_invalid && !valid_float
|
|
144
|
+
|
|
145
|
+
formatted_number = yield
|
|
146
|
+
|
|
147
|
+
if valid_float || number.html_safe?
|
|
148
|
+
formatted_number.html_safe
|
|
149
|
+
else
|
|
150
|
+
formatted_number
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def valid_float?(number)
|
|
155
|
+
!parse_float(number, false).nil?
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def parse_float(number, raise_error)
|
|
159
|
+
result = Float(number, exception: false)
|
|
160
|
+
raise InvalidNumberError, number if result.nil? && raise_error
|
|
161
|
+
result
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/core_ext/string/output_safety"
|
|
4
|
+
|
|
5
|
+
module ActionView # :nodoc:
|
|
6
|
+
module Helpers # :nodoc:
|
|
7
|
+
# = Action View Raw Output \Helpers
|
|
8
|
+
module OutputSafetyHelper
|
|
9
|
+
# This method outputs without escaping a string. Since escaping tags is
|
|
10
|
+
# now default, this can be used when you don't want \Rails to automatically
|
|
11
|
+
# escape tags. This is not recommended if the data is coming from the user's
|
|
12
|
+
# input.
|
|
13
|
+
#
|
|
14
|
+
# For example:
|
|
15
|
+
#
|
|
16
|
+
# raw @user.name
|
|
17
|
+
# # => 'Jimmy <alert>Tables</alert>'
|
|
18
|
+
def raw(stringish)
|
|
19
|
+
stringish.to_s.html_safe
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# This method returns an HTML safe string similar to what <tt>Array#join</tt>
|
|
23
|
+
# would return. The array is flattened, and all items, including
|
|
24
|
+
# the supplied separator, are HTML escaped unless they are HTML
|
|
25
|
+
# safe, and the returned string is marked as HTML safe.
|
|
26
|
+
#
|
|
27
|
+
# safe_join([tag.p("foo"), "<p>bar</p>"], "<br>")
|
|
28
|
+
# # => "<p>foo</p><br><p>bar</p>"
|
|
29
|
+
#
|
|
30
|
+
# safe_join([tag.p("foo"), tag.p("bar")], tag.br)
|
|
31
|
+
# # => "<p>foo</p><br><p>bar</p>"
|
|
32
|
+
#
|
|
33
|
+
def safe_join(array, sep = $,)
|
|
34
|
+
sep = ERB::Util.unwrapped_html_escape(sep)
|
|
35
|
+
|
|
36
|
+
array.flatten.map! { |i| ERB::Util.unwrapped_html_escape(i) }.join(sep).html_safe
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Converts the array to a comma-separated sentence where the last element is
|
|
40
|
+
# joined by the connector word. This is the html_safe-aware version of
|
|
41
|
+
# ActiveSupport's {Array#to_sentence}[https://api.rubyonrails.org/classes/Array.html#method-i-to_sentence].
|
|
42
|
+
#
|
|
43
|
+
def to_sentence(array, options = {})
|
|
44
|
+
options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
|
|
45
|
+
|
|
46
|
+
default_connectors = {
|
|
47
|
+
words_connector: ", ",
|
|
48
|
+
two_words_connector: " and ",
|
|
49
|
+
last_word_connector: ", and "
|
|
50
|
+
}
|
|
51
|
+
if defined?(I18n)
|
|
52
|
+
i18n_connectors = I18n.translate(:'support.array', locale: options[:locale], default: {})
|
|
53
|
+
default_connectors.merge!(i18n_connectors)
|
|
54
|
+
end
|
|
55
|
+
options = default_connectors.merge!(options)
|
|
56
|
+
|
|
57
|
+
case array.length
|
|
58
|
+
when 0
|
|
59
|
+
"".html_safe
|
|
60
|
+
when 1
|
|
61
|
+
ERB::Util.html_escape(array[0])
|
|
62
|
+
when 2
|
|
63
|
+
safe_join([array[0], array[1]], options[:two_words_connector])
|
|
64
|
+
else
|
|
65
|
+
safe_join([safe_join(array[0...-1], options[:words_connector]), options[:last_word_connector], array[-1]], nil)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# :markup: markdown
|
|
4
|
+
|
|
5
|
+
module ActionView
|
|
6
|
+
module Helpers # :nodoc:
|
|
7
|
+
# # Action View Rendering Helpers
|
|
8
|
+
#
|
|
9
|
+
# Implements methods that allow rendering from a view context. In order to use
|
|
10
|
+
# this module, all you need is to implement view_renderer that returns an
|
|
11
|
+
# ActionView::Renderer object.
|
|
12
|
+
module RenderingHelper
|
|
13
|
+
# Renders a template and returns the result.
|
|
14
|
+
#
|
|
15
|
+
# Pass the template to render as the first argument. This is shorthand
|
|
16
|
+
# syntax for partial rendering, so the template filename should be
|
|
17
|
+
# prefixed with an underscore. The partial renderer looks for the partial
|
|
18
|
+
# template in the directory of the calling template first.
|
|
19
|
+
#
|
|
20
|
+
# <% # app/views/posts/new.html.erb %>
|
|
21
|
+
# <%= render "form" %>
|
|
22
|
+
# # => renders app/views/posts/_form.html.erb
|
|
23
|
+
#
|
|
24
|
+
# Use the complete view path to render a partial from another directory.
|
|
25
|
+
#
|
|
26
|
+
# <% # app/views/posts/show.html.erb %>
|
|
27
|
+
# <%= render "comments/form" %>
|
|
28
|
+
# # => renders app/views/comments/_form.html.erb
|
|
29
|
+
#
|
|
30
|
+
# Without the rendering mode, the second argument can be a Hash of local
|
|
31
|
+
# variable assignments for the template.
|
|
32
|
+
#
|
|
33
|
+
# <% # app/views/posts/new.html.erb %>
|
|
34
|
+
# <%= render "form", post: Post.new %>
|
|
35
|
+
# # => renders app/views/posts/_form.html.erb
|
|
36
|
+
#
|
|
37
|
+
# If the first argument responds to `render_in`, the template will be rendered
|
|
38
|
+
# by calling `render_in` with the current view context.
|
|
39
|
+
#
|
|
40
|
+
# class Greeting
|
|
41
|
+
# def render_in(view_context)
|
|
42
|
+
# view_context.render html: "<h1>Hello, World</h1>"
|
|
43
|
+
# end
|
|
44
|
+
#
|
|
45
|
+
# def format
|
|
46
|
+
# :html
|
|
47
|
+
# end
|
|
48
|
+
# end
|
|
49
|
+
#
|
|
50
|
+
# <%= render Greeting.new %>
|
|
51
|
+
# # => "<h1>Hello, World</h1>"
|
|
52
|
+
#
|
|
53
|
+
# #### Rendering Mode
|
|
54
|
+
#
|
|
55
|
+
# Pass the rendering mode as first argument to override it.
|
|
56
|
+
#
|
|
57
|
+
# `:partial`
|
|
58
|
+
# : See ActionView::PartialRenderer for details.
|
|
59
|
+
#
|
|
60
|
+
# <%= render partial: "form", locals: { post: Post.new } %>
|
|
61
|
+
# # => renders app/views/posts/_form.html.erb
|
|
62
|
+
#
|
|
63
|
+
# `:file`
|
|
64
|
+
# : Renders the contents of a file. This option should **not** be used with
|
|
65
|
+
# unsanitized user input.
|
|
66
|
+
#
|
|
67
|
+
# <%= render file: "/path/to/some/file" %>
|
|
68
|
+
# # => renders /path/to/some/file
|
|
69
|
+
#
|
|
70
|
+
# `:inline`
|
|
71
|
+
# : Renders an ERB template string.
|
|
72
|
+
#
|
|
73
|
+
# <% name = "World" %>
|
|
74
|
+
# <%= render inline: "<h1>Hello, <%= name %>!</h1>" %>
|
|
75
|
+
# # => renders "<h1>Hello, World!</h1>"
|
|
76
|
+
#
|
|
77
|
+
# `:body`
|
|
78
|
+
# : Renders the provided text, and sets the format as `:text`.
|
|
79
|
+
#
|
|
80
|
+
# <%= render body: "Hello, World!" %>
|
|
81
|
+
# # => renders "Hello, World!"
|
|
82
|
+
#
|
|
83
|
+
# `:plain`
|
|
84
|
+
# : Renders the provided text, and sets the format as `:text`.
|
|
85
|
+
#
|
|
86
|
+
# <%= render plain: "Hello, World!" %>
|
|
87
|
+
# # => renders "Hello, World!"
|
|
88
|
+
#
|
|
89
|
+
# `:html`
|
|
90
|
+
# : Renders the provided HTML string, and sets the format as
|
|
91
|
+
# `:html`. If the string is not `html_safe?`, performs HTML escaping on
|
|
92
|
+
# the string before rendering.
|
|
93
|
+
#
|
|
94
|
+
# <%= render html: "<h1>Hello, World!</h1>".html_safe %>
|
|
95
|
+
# # => renders "<h1>Hello, World!</h1>"
|
|
96
|
+
#
|
|
97
|
+
# <%= render html: "<h1>Hello, World!</h1>" %>
|
|
98
|
+
# # => renders "<h1>Hello, World!</h1>"
|
|
99
|
+
#
|
|
100
|
+
# `:renderable`
|
|
101
|
+
# : Renders the provided object by calling `render_in` with the current view
|
|
102
|
+
# context. The format is determined by calling `format` on the
|
|
103
|
+
# renderable if it responds to `format`, falling back to `:html` by
|
|
104
|
+
# default.
|
|
105
|
+
#
|
|
106
|
+
# <%= render renderable: Greeting.new %>
|
|
107
|
+
# # => renders "<h1>Hello, World</h1>"
|
|
108
|
+
#
|
|
109
|
+
#
|
|
110
|
+
# #### Options
|
|
111
|
+
#
|
|
112
|
+
# `:locals`
|
|
113
|
+
# : Hash of local variable assignments for the template.
|
|
114
|
+
#
|
|
115
|
+
# <%= render inline: "<h1>Hello, <%= name %>!</h1>", locals: { name: "World" } %>
|
|
116
|
+
# # => renders "<h1>Hello, World!</h1>"
|
|
117
|
+
#
|
|
118
|
+
# `:formats`
|
|
119
|
+
# : Override the current format to render a template for a different format.
|
|
120
|
+
#
|
|
121
|
+
# <% # app/views/posts/show.html.erb %>
|
|
122
|
+
# <%= render template: "posts/content", formats: [:text] %>
|
|
123
|
+
# # => renders app/views/posts/content.text.erb
|
|
124
|
+
#
|
|
125
|
+
# `:variants`
|
|
126
|
+
# : Render a template for a different variant.
|
|
127
|
+
#
|
|
128
|
+
# <% # app/views/posts/show.html.erb %>
|
|
129
|
+
# <%= render template: "posts/content", variants: [:tablet] %>
|
|
130
|
+
# # => renders app/views/posts/content.html+tablet.erb
|
|
131
|
+
#
|
|
132
|
+
# `:handlers`
|
|
133
|
+
# : Render a template for a different handler.
|
|
134
|
+
#
|
|
135
|
+
# <% # app/views/posts/show.html.erb %>
|
|
136
|
+
# <%= render template: "posts/content", handlers: [:builder] %>
|
|
137
|
+
# # => renders app/views/posts/content.html.builder
|
|
138
|
+
def render(options = {}, locals = {}, &block)
|
|
139
|
+
case options
|
|
140
|
+
when Hash
|
|
141
|
+
in_rendering_context(options) do |renderer|
|
|
142
|
+
if block_given?
|
|
143
|
+
view_renderer.render_partial(self, options.merge(partial: options[:layout]), &block)
|
|
144
|
+
else
|
|
145
|
+
view_renderer.render(self, options)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
else
|
|
149
|
+
if options.respond_to?(:render_in)
|
|
150
|
+
options.render_in(self, &block)
|
|
151
|
+
else
|
|
152
|
+
view_renderer.render_partial(self, partial: options, locals: locals, &block)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Overrides _layout_for in the context object so it supports the case a block is
|
|
158
|
+
# passed to a partial. Returns the contents that are yielded to a layout, given
|
|
159
|
+
# a name or a block.
|
|
160
|
+
#
|
|
161
|
+
# You can think of a layout as a method that is called with a block. If the user
|
|
162
|
+
# calls `yield :some_name`, the block, by default, returns
|
|
163
|
+
# `content_for(:some_name)`. If the user calls simply `yield`, the default block
|
|
164
|
+
# returns `content_for(:layout)`.
|
|
165
|
+
#
|
|
166
|
+
# The user can override this default by passing a block to the layout:
|
|
167
|
+
#
|
|
168
|
+
# # The template
|
|
169
|
+
# <%= render layout: "my_layout" do %>
|
|
170
|
+
# Content
|
|
171
|
+
# <% end %>
|
|
172
|
+
#
|
|
173
|
+
# # The layout
|
|
174
|
+
# <html>
|
|
175
|
+
# <%= yield %>
|
|
176
|
+
# </html>
|
|
177
|
+
#
|
|
178
|
+
# In this case, instead of the default block, which would return `content_for(:layout)`,
|
|
179
|
+
# this method returns the block that was passed in to `render :layout`, and the response
|
|
180
|
+
# would be
|
|
181
|
+
#
|
|
182
|
+
# <html>
|
|
183
|
+
# Content
|
|
184
|
+
# </html>
|
|
185
|
+
#
|
|
186
|
+
# Finally, the block can take block arguments, which can be passed in by
|
|
187
|
+
# `yield`:
|
|
188
|
+
#
|
|
189
|
+
# # The template
|
|
190
|
+
# <%= render layout: "my_layout" do |customer| %>
|
|
191
|
+
# Hello <%= customer.name %>
|
|
192
|
+
# <% end %>
|
|
193
|
+
#
|
|
194
|
+
# # The layout
|
|
195
|
+
# <html>
|
|
196
|
+
# <%= yield Struct.new(:name).new("David") %>
|
|
197
|
+
# </html>
|
|
198
|
+
#
|
|
199
|
+
# In this case, the layout would receive the block passed into `render :layout`,
|
|
200
|
+
# and the struct specified would be passed into the block as an argument. The result
|
|
201
|
+
# would be
|
|
202
|
+
#
|
|
203
|
+
# <html>
|
|
204
|
+
# Hello David
|
|
205
|
+
# </html>
|
|
206
|
+
#
|
|
207
|
+
def _layout_for(*args, &block)
|
|
208
|
+
name = args.first
|
|
209
|
+
|
|
210
|
+
if block && !name.is_a?(Symbol)
|
|
211
|
+
capture(*args, &block)
|
|
212
|
+
else
|
|
213
|
+
super
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|