actionview 5.2.3

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.

Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +142 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +38 -0
  5. data/lib/action_view.rb +97 -0
  6. data/lib/action_view/base.rb +215 -0
  7. data/lib/action_view/buffers.rb +52 -0
  8. data/lib/action_view/context.rb +36 -0
  9. data/lib/action_view/dependency_tracker.rb +175 -0
  10. data/lib/action_view/digestor.rb +134 -0
  11. data/lib/action_view/flows.rb +76 -0
  12. data/lib/action_view/gem_version.rb +17 -0
  13. data/lib/action_view/helpers.rb +68 -0
  14. data/lib/action_view/helpers/active_model_helper.rb +55 -0
  15. data/lib/action_view/helpers/asset_tag_helper.rb +511 -0
  16. data/lib/action_view/helpers/asset_url_helper.rb +469 -0
  17. data/lib/action_view/helpers/atom_feed_helper.rb +205 -0
  18. data/lib/action_view/helpers/cache_helper.rb +263 -0
  19. data/lib/action_view/helpers/capture_helper.rb +212 -0
  20. data/lib/action_view/helpers/controller_helper.rb +36 -0
  21. data/lib/action_view/helpers/csp_helper.rb +24 -0
  22. data/lib/action_view/helpers/csrf_helper.rb +35 -0
  23. data/lib/action_view/helpers/date_helper.rb +1156 -0
  24. data/lib/action_view/helpers/debug_helper.rb +36 -0
  25. data/lib/action_view/helpers/form_helper.rb +2337 -0
  26. data/lib/action_view/helpers/form_options_helper.rb +887 -0
  27. data/lib/action_view/helpers/form_tag_helper.rb +917 -0
  28. data/lib/action_view/helpers/javascript_helper.rb +94 -0
  29. data/lib/action_view/helpers/number_helper.rb +451 -0
  30. data/lib/action_view/helpers/output_safety_helper.rb +70 -0
  31. data/lib/action_view/helpers/record_tag_helper.rb +23 -0
  32. data/lib/action_view/helpers/rendering_helper.rb +99 -0
  33. data/lib/action_view/helpers/sanitize_helper.rb +177 -0
  34. data/lib/action_view/helpers/tag_helper.rb +313 -0
  35. data/lib/action_view/helpers/tags.rb +44 -0
  36. data/lib/action_view/helpers/tags/base.rb +192 -0
  37. data/lib/action_view/helpers/tags/check_box.rb +66 -0
  38. data/lib/action_view/helpers/tags/checkable.rb +18 -0
  39. data/lib/action_view/helpers/tags/collection_check_boxes.rb +36 -0
  40. data/lib/action_view/helpers/tags/collection_helpers.rb +119 -0
  41. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +31 -0
  42. data/lib/action_view/helpers/tags/collection_select.rb +30 -0
  43. data/lib/action_view/helpers/tags/color_field.rb +27 -0
  44. data/lib/action_view/helpers/tags/date_field.rb +15 -0
  45. data/lib/action_view/helpers/tags/date_select.rb +74 -0
  46. data/lib/action_view/helpers/tags/datetime_field.rb +32 -0
  47. data/lib/action_view/helpers/tags/datetime_local_field.rb +21 -0
  48. data/lib/action_view/helpers/tags/datetime_select.rb +10 -0
  49. data/lib/action_view/helpers/tags/email_field.rb +10 -0
  50. data/lib/action_view/helpers/tags/file_field.rb +10 -0
  51. data/lib/action_view/helpers/tags/grouped_collection_select.rb +31 -0
  52. data/lib/action_view/helpers/tags/hidden_field.rb +10 -0
  53. data/lib/action_view/helpers/tags/label.rb +81 -0
  54. data/lib/action_view/helpers/tags/month_field.rb +15 -0
  55. data/lib/action_view/helpers/tags/number_field.rb +20 -0
  56. data/lib/action_view/helpers/tags/password_field.rb +14 -0
  57. data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
  58. data/lib/action_view/helpers/tags/radio_button.rb +33 -0
  59. data/lib/action_view/helpers/tags/range_field.rb +10 -0
  60. data/lib/action_view/helpers/tags/search_field.rb +27 -0
  61. data/lib/action_view/helpers/tags/select.rb +43 -0
  62. data/lib/action_view/helpers/tags/tel_field.rb +10 -0
  63. data/lib/action_view/helpers/tags/text_area.rb +24 -0
  64. data/lib/action_view/helpers/tags/text_field.rb +34 -0
  65. data/lib/action_view/helpers/tags/time_field.rb +15 -0
  66. data/lib/action_view/helpers/tags/time_select.rb +10 -0
  67. data/lib/action_view/helpers/tags/time_zone_select.rb +22 -0
  68. data/lib/action_view/helpers/tags/translator.rb +44 -0
  69. data/lib/action_view/helpers/tags/url_field.rb +10 -0
  70. data/lib/action_view/helpers/tags/week_field.rb +15 -0
  71. data/lib/action_view/helpers/text_helper.rb +486 -0
  72. data/lib/action_view/helpers/translation_helper.rb +141 -0
  73. data/lib/action_view/helpers/url_helper.rb +676 -0
  74. data/lib/action_view/layouts.rb +433 -0
  75. data/lib/action_view/locale/en.yml +56 -0
  76. data/lib/action_view/log_subscriber.rb +96 -0
  77. data/lib/action_view/lookup_context.rb +274 -0
  78. data/lib/action_view/model_naming.rb +14 -0
  79. data/lib/action_view/path_set.rb +100 -0
  80. data/lib/action_view/railtie.rb +82 -0
  81. data/lib/action_view/record_identifier.rb +112 -0
  82. data/lib/action_view/renderer/abstract_renderer.rb +55 -0
  83. data/lib/action_view/renderer/partial_renderer.rb +552 -0
  84. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +57 -0
  85. data/lib/action_view/renderer/renderer.rb +56 -0
  86. data/lib/action_view/renderer/streaming_template_renderer.rb +105 -0
  87. data/lib/action_view/renderer/template_renderer.rb +102 -0
  88. data/lib/action_view/rendering.rb +151 -0
  89. data/lib/action_view/routing_url_for.rb +145 -0
  90. data/lib/action_view/tasks/cache_digests.rake +25 -0
  91. data/lib/action_view/template.rb +361 -0
  92. data/lib/action_view/template/error.rb +141 -0
  93. data/lib/action_view/template/handlers.rb +66 -0
  94. data/lib/action_view/template/handlers/builder.rb +25 -0
  95. data/lib/action_view/template/handlers/erb.rb +74 -0
  96. data/lib/action_view/template/handlers/erb/erubi.rb +83 -0
  97. data/lib/action_view/template/handlers/html.rb +11 -0
  98. data/lib/action_view/template/handlers/raw.rb +11 -0
  99. data/lib/action_view/template/html.rb +34 -0
  100. data/lib/action_view/template/resolver.rb +391 -0
  101. data/lib/action_view/template/text.rb +33 -0
  102. data/lib/action_view/template/types.rb +57 -0
  103. data/lib/action_view/test_case.rb +300 -0
  104. data/lib/action_view/testing/resolvers.rb +54 -0
  105. data/lib/action_view/version.rb +10 -0
  106. data/lib/action_view/view_paths.rb +105 -0
  107. data/lib/assets/compiled/rails-ujs.js +720 -0
  108. metadata +255 -0
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/log_subscriber"
4
+
5
+ module ActionView
6
+ # = Action View Log Subscriber
7
+ #
8
+ # Provides functionality so that Rails can output logs from Action View.
9
+ class LogSubscriber < ActiveSupport::LogSubscriber
10
+ VIEWS_PATTERN = /^app\/views\//
11
+
12
+ def initialize
13
+ @root = nil
14
+ super
15
+ end
16
+
17
+ def render_template(event)
18
+ info do
19
+ message = " Rendered #{from_rails_root(event.payload[:identifier])}".dup
20
+ message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
21
+ message << " (#{event.duration.round(1)}ms)"
22
+ end
23
+ end
24
+
25
+ def render_partial(event)
26
+ info do
27
+ message = " Rendered #{from_rails_root(event.payload[:identifier])}".dup
28
+ message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
29
+ message << " (#{event.duration.round(1)}ms)"
30
+ message << " #{cache_message(event.payload)}" unless event.payload[:cache_hit].nil?
31
+ message
32
+ end
33
+ end
34
+
35
+ def render_collection(event)
36
+ identifier = event.payload[:identifier] || "templates"
37
+
38
+ info do
39
+ " Rendered collection of #{from_rails_root(identifier)}" \
40
+ " #{render_count(event.payload)} (#{event.duration.round(1)}ms)"
41
+ end
42
+ end
43
+
44
+ def start(name, id, payload)
45
+ if name == "render_template.action_view"
46
+ log_rendering_start(payload)
47
+ end
48
+
49
+ super
50
+ end
51
+
52
+ def logger
53
+ ActionView::Base.logger
54
+ end
55
+
56
+ private
57
+
58
+ EMPTY = ""
59
+ def from_rails_root(string) # :doc:
60
+ string = string.sub(rails_root, EMPTY)
61
+ string.sub!(VIEWS_PATTERN, EMPTY)
62
+ string
63
+ end
64
+
65
+ def rails_root # :doc:
66
+ @root ||= "#{Rails.root}/"
67
+ end
68
+
69
+ def render_count(payload) # :doc:
70
+ if payload[:cache_hits]
71
+ "[#{payload[:cache_hits]} / #{payload[:count]} cache hits]"
72
+ else
73
+ "[#{payload[:count]} times]"
74
+ end
75
+ end
76
+
77
+ def cache_message(payload) # :doc:
78
+ case payload[:cache_hit]
79
+ when :hit
80
+ "[cache hit]"
81
+ when :miss
82
+ "[cache miss]"
83
+ end
84
+ end
85
+
86
+ def log_rendering_start(payload)
87
+ info do
88
+ message = " Rendering #{from_rails_root(payload[:identifier])}".dup
89
+ message << " within #{from_rails_root(payload[:layout])}" if payload[:layout]
90
+ message
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ ActionView::LogSubscriber.attach_to :action_view
@@ -0,0 +1,274 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/map"
4
+ require "active_support/core_ext/module/remove_method"
5
+ require "active_support/core_ext/module/attribute_accessors"
6
+ require "action_view/template/resolver"
7
+
8
+ module ActionView
9
+ # = Action View Lookup Context
10
+ #
11
+ # <tt>LookupContext</tt> is the object responsible for holding all information
12
+ # required for looking up templates, i.e. view paths and details.
13
+ # <tt>LookupContext</tt> is also responsible for generating a key, given to
14
+ # view paths, used in the resolver cache lookup. Since this key is generated
15
+ # only once during the request, it speeds up all cache accesses.
16
+ class LookupContext #:nodoc:
17
+ attr_accessor :prefixes, :rendered_format
18
+
19
+ mattr_accessor :fallbacks, default: FallbackFileSystemResolver.instances
20
+
21
+ mattr_accessor :registered_details, default: []
22
+
23
+ def self.register_detail(name, &block)
24
+ registered_details << name
25
+ Accessors::DEFAULT_PROCS[name] = block
26
+
27
+ Accessors.send :define_method, :"default_#{name}", &block
28
+ Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1
29
+ def #{name}
30
+ @details.fetch(:#{name}, [])
31
+ end
32
+
33
+ def #{name}=(value)
34
+ value = value.present? ? Array(value) : default_#{name}
35
+ _set_detail(:#{name}, value) if value != @details[:#{name}]
36
+ end
37
+ METHOD
38
+ end
39
+
40
+ # Holds accessors for the registered details.
41
+ module Accessors #:nodoc:
42
+ DEFAULT_PROCS = {}
43
+ end
44
+
45
+ register_detail(:locale) do
46
+ locales = [I18n.locale]
47
+ locales.concat(I18n.fallbacks[I18n.locale]) if I18n.respond_to? :fallbacks
48
+ locales << I18n.default_locale
49
+ locales.uniq!
50
+ locales
51
+ end
52
+ register_detail(:formats) { ActionView::Base.default_formats || [:html, :text, :js, :css, :xml, :json] }
53
+ register_detail(:variants) { [] }
54
+ register_detail(:handlers) { Template::Handlers.extensions }
55
+
56
+ class DetailsKey #:nodoc:
57
+ alias :eql? :equal?
58
+
59
+ @details_keys = Concurrent::Map.new
60
+
61
+ def self.get(details)
62
+ if details[:formats]
63
+ details = details.dup
64
+ details[:formats] &= Template::Types.symbols
65
+ end
66
+ @details_keys[details] ||= Concurrent::Map.new
67
+ end
68
+
69
+ def self.clear
70
+ @details_keys.clear
71
+ end
72
+
73
+ def self.digest_caches
74
+ @details_keys.values
75
+ end
76
+ end
77
+
78
+ # Add caching behavior on top of Details.
79
+ module DetailsCache
80
+ attr_accessor :cache
81
+
82
+ # Calculate the details key. Remove the handlers from calculation to improve performance
83
+ # since the user cannot modify it explicitly.
84
+ def details_key #:nodoc:
85
+ @details_key ||= DetailsKey.get(@details) if @cache
86
+ end
87
+
88
+ # Temporary skip passing the details_key forward.
89
+ def disable_cache
90
+ old_value, @cache = @cache, false
91
+ yield
92
+ ensure
93
+ @cache = old_value
94
+ end
95
+
96
+ private
97
+
98
+ def _set_detail(key, value) # :doc:
99
+ @details = @details.dup if @details_key
100
+ @details_key = nil
101
+ @details[key] = value
102
+ end
103
+ end
104
+
105
+ # Helpers related to template lookup using the lookup context information.
106
+ module ViewPaths
107
+ attr_reader :view_paths, :html_fallback_for_js
108
+
109
+ # Whenever setting view paths, makes a copy so that we can manipulate them in
110
+ # instance objects as we wish.
111
+ def view_paths=(paths)
112
+ @view_paths = ActionView::PathSet.new(Array(paths))
113
+ end
114
+
115
+ def find(name, prefixes = [], partial = false, keys = [], options = {})
116
+ @view_paths.find(*args_for_lookup(name, prefixes, partial, keys, options))
117
+ end
118
+ alias :find_template :find
119
+
120
+ def find_file(name, prefixes = [], partial = false, keys = [], options = {})
121
+ @view_paths.find_file(*args_for_lookup(name, prefixes, partial, keys, options))
122
+ end
123
+
124
+ def find_all(name, prefixes = [], partial = false, keys = [], options = {})
125
+ @view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys, options))
126
+ end
127
+
128
+ def exists?(name, prefixes = [], partial = false, keys = [], **options)
129
+ @view_paths.exists?(*args_for_lookup(name, prefixes, partial, keys, options))
130
+ end
131
+ alias :template_exists? :exists?
132
+
133
+ def any?(name, prefixes = [], partial = false)
134
+ @view_paths.exists?(*args_for_any(name, prefixes, partial))
135
+ end
136
+ alias :any_templates? :any?
137
+
138
+ # Adds fallbacks to the view paths. Useful in cases when you are rendering
139
+ # a :file.
140
+ def with_fallbacks
141
+ added_resolvers = 0
142
+ self.class.fallbacks.each do |resolver|
143
+ next if view_paths.include?(resolver)
144
+ view_paths.push(resolver)
145
+ added_resolvers += 1
146
+ end
147
+ yield
148
+ ensure
149
+ added_resolvers.times { view_paths.pop }
150
+ end
151
+
152
+ private
153
+
154
+ def args_for_lookup(name, prefixes, partial, keys, details_options)
155
+ name, prefixes = normalize_name(name, prefixes)
156
+ details, details_key = detail_args_for(details_options)
157
+ [name, prefixes, partial || false, details, details_key, keys]
158
+ end
159
+
160
+ # Compute details hash and key according to user options (e.g. passed from #render).
161
+ def detail_args_for(options) # :doc:
162
+ return @details, details_key if options.empty? # most common path.
163
+ user_details = @details.merge(options)
164
+
165
+ if @cache
166
+ details_key = DetailsKey.get(user_details)
167
+ else
168
+ details_key = nil
169
+ end
170
+
171
+ [user_details, details_key]
172
+ end
173
+
174
+ def args_for_any(name, prefixes, partial)
175
+ name, prefixes = normalize_name(name, prefixes)
176
+ details, details_key = detail_args_for_any
177
+ [name, prefixes, partial || false, details, details_key]
178
+ end
179
+
180
+ def detail_args_for_any
181
+ @detail_args_for_any ||= begin
182
+ details = {}
183
+
184
+ registered_details.each do |k|
185
+ if k == :variants
186
+ details[k] = :any
187
+ else
188
+ details[k] = Accessors::DEFAULT_PROCS[k].call
189
+ end
190
+ end
191
+
192
+ if @cache
193
+ [details, DetailsKey.get(details)]
194
+ else
195
+ [details, nil]
196
+ end
197
+ end
198
+ end
199
+
200
+ # Support legacy foo.erb names even though we now ignore .erb
201
+ # as well as incorrectly putting part of the path in the template
202
+ # name instead of the prefix.
203
+ def normalize_name(name, prefixes)
204
+ prefixes = prefixes.presence
205
+ parts = name.to_s.split("/".freeze)
206
+ parts.shift if parts.first.empty?
207
+ name = parts.pop
208
+
209
+ return name, prefixes || [""] if parts.empty?
210
+
211
+ parts = parts.join("/".freeze)
212
+ prefixes = prefixes ? prefixes.map { |p| "#{p}/#{parts}" } : [parts]
213
+
214
+ return name, prefixes
215
+ end
216
+ end
217
+
218
+ include Accessors
219
+ include DetailsCache
220
+ include ViewPaths
221
+
222
+ def initialize(view_paths, details = {}, prefixes = [])
223
+ @details_key = nil
224
+ @cache = true
225
+ @prefixes = prefixes
226
+ @rendered_format = nil
227
+
228
+ @details = initialize_details({}, details)
229
+ self.view_paths = view_paths
230
+ end
231
+
232
+ def digest_cache
233
+ details_key
234
+ end
235
+
236
+ def initialize_details(target, details)
237
+ registered_details.each do |k|
238
+ target[k] = details[k] || Accessors::DEFAULT_PROCS[k].call
239
+ end
240
+ target
241
+ end
242
+ private :initialize_details
243
+
244
+ # Override formats= to expand ["*/*"] values and automatically
245
+ # add :html as fallback to :js.
246
+ def formats=(values)
247
+ if values
248
+ values.concat(default_formats) if values.delete "*/*".freeze
249
+ if values == [:js]
250
+ values << :html
251
+ @html_fallback_for_js = true
252
+ end
253
+ end
254
+ super(values)
255
+ end
256
+
257
+ # Override locale to return a symbol instead of array.
258
+ def locale
259
+ @details[:locale].first
260
+ end
261
+
262
+ # Overload locale= to also set the I18n.locale. If the current I18n.config object responds
263
+ # to original_config, it means that it has a copy of the original I18n configuration and it's
264
+ # acting as proxy, which we need to skip.
265
+ def locale=(value)
266
+ if value
267
+ config = I18n.config.respond_to?(:original_config) ? I18n.config.original_config : I18n.config
268
+ config.locale = value
269
+ end
270
+
271
+ super(default_locale)
272
+ end
273
+ end
274
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module ModelNaming #:nodoc:
5
+ # Converts the given object to an ActiveModel compliant one.
6
+ def convert_to_model(object)
7
+ object.respond_to?(:to_model) ? object.to_model : object
8
+ end
9
+
10
+ def model_name_from_record_or_class(record_or_class)
11
+ convert_to_model(record_or_class).model_name
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView #:nodoc:
4
+ # = Action View PathSet
5
+ #
6
+ # This class is used to store and access paths in Action View. A number of
7
+ # operations are defined so that you can search among the paths in this
8
+ # set and also perform operations on other +PathSet+ objects.
9
+ #
10
+ # A +LookupContext+ will use a +PathSet+ to store the paths in its context.
11
+ class PathSet #:nodoc:
12
+ include Enumerable
13
+
14
+ attr_reader :paths
15
+
16
+ delegate :[], :include?, :pop, :size, :each, to: :paths
17
+
18
+ def initialize(paths = [])
19
+ @paths = typecast paths
20
+ end
21
+
22
+ def initialize_copy(other)
23
+ @paths = other.paths.dup
24
+ self
25
+ end
26
+
27
+ def to_ary
28
+ paths.dup
29
+ end
30
+
31
+ def compact
32
+ PathSet.new paths.compact
33
+ end
34
+
35
+ def +(array)
36
+ PathSet.new(paths + array)
37
+ end
38
+
39
+ %w(<< concat push insert unshift).each do |method|
40
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
41
+ def #{method}(*args)
42
+ paths.#{method}(*typecast(args))
43
+ end
44
+ METHOD
45
+ end
46
+
47
+ def find(*args)
48
+ find_all(*args).first || raise(MissingTemplate.new(self, *args))
49
+ end
50
+
51
+ def find_file(path, prefixes = [], *args)
52
+ _find_all(path, prefixes, args, true).first || raise(MissingTemplate.new(self, path, prefixes, *args))
53
+ end
54
+
55
+ def find_all(path, prefixes = [], *args)
56
+ _find_all path, prefixes, args, false
57
+ end
58
+
59
+ def exists?(path, prefixes, *args)
60
+ find_all(path, prefixes, *args).any?
61
+ end
62
+
63
+ def find_all_with_query(query) # :nodoc:
64
+ paths.each do |resolver|
65
+ templates = resolver.find_all_with_query(query)
66
+ return templates unless templates.empty?
67
+ end
68
+
69
+ []
70
+ end
71
+
72
+ private
73
+
74
+ def _find_all(path, prefixes, args, outside_app)
75
+ prefixes = [prefixes] if String === prefixes
76
+ prefixes.each do |prefix|
77
+ paths.each do |resolver|
78
+ if outside_app
79
+ templates = resolver.find_all_anywhere(path, prefix, *args)
80
+ else
81
+ templates = resolver.find_all(path, prefix, *args)
82
+ end
83
+ return templates unless templates.empty?
84
+ end
85
+ end
86
+ []
87
+ end
88
+
89
+ def typecast(paths)
90
+ paths.map do |path|
91
+ case path
92
+ when Pathname, String
93
+ OptimizedFileSystemResolver.new path.to_s
94
+ else
95
+ path
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end