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,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/output_safety"
4
+
5
+ module ActionView
6
+ class OutputBuffer < ActiveSupport::SafeBuffer #:nodoc:
7
+ def initialize(*)
8
+ super
9
+ encode!
10
+ end
11
+
12
+ def <<(value)
13
+ return self if value.nil?
14
+ super(value.to_s)
15
+ end
16
+ alias :append= :<<
17
+
18
+ def safe_expr_append=(val)
19
+ return self if val.nil?
20
+ safe_concat val.to_s
21
+ end
22
+
23
+ alias :safe_append= :safe_concat
24
+ end
25
+
26
+ class StreamingBuffer #:nodoc:
27
+ def initialize(block)
28
+ @block = block
29
+ end
30
+
31
+ def <<(value)
32
+ value = value.to_s
33
+ value = ERB::Util.h(value) unless value.html_safe?
34
+ @block.call(value)
35
+ end
36
+ alias :concat :<<
37
+ alias :append= :<<
38
+
39
+ def safe_concat(value)
40
+ @block.call(value.to_s)
41
+ end
42
+ alias :safe_append= :safe_concat
43
+
44
+ def html_safe?
45
+ true
46
+ end
47
+
48
+ def html_safe
49
+ self
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module CompiledTemplates #:nodoc:
5
+ # holds compiled template code
6
+ end
7
+
8
+ # = Action View Context
9
+ #
10
+ # Action View contexts are supplied to Action Controller to render a template.
11
+ # The default Action View context is ActionView::Base.
12
+ #
13
+ # In order to work with ActionController, a Context must just include this module.
14
+ # The initialization of the variables used by the context (@output_buffer, @view_flow,
15
+ # and @virtual_path) is responsibility of the object that includes this module
16
+ # (although you can call _prepare_context defined below).
17
+ module Context
18
+ include CompiledTemplates
19
+ attr_accessor :output_buffer, :view_flow
20
+
21
+ # Prepares the context by setting the appropriate instance variables.
22
+ def _prepare_context
23
+ @view_flow = OutputFlow.new
24
+ @output_buffer = nil
25
+ @virtual_path = nil
26
+ end
27
+
28
+ # Encapsulates the interaction with the view flow so it
29
+ # returns the correct buffer on +yield+. This is usually
30
+ # overwritten by helpers to add more behavior.
31
+ def _layout_for(name = nil)
32
+ name ||= :layout
33
+ view_flow.get(name).html_safe
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,175 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/map"
4
+ require "action_view/path_set"
5
+
6
+ module ActionView
7
+ class DependencyTracker # :nodoc:
8
+ @trackers = Concurrent::Map.new
9
+
10
+ def self.find_dependencies(name, template, view_paths = nil)
11
+ tracker = @trackers[template.handler]
12
+ return [] unless tracker
13
+
14
+ tracker.call(name, template, view_paths)
15
+ end
16
+
17
+ def self.register_tracker(extension, tracker)
18
+ handler = Template.handler_for_extension(extension)
19
+ if tracker.respond_to?(:supports_view_paths?)
20
+ @trackers[handler] = tracker
21
+ else
22
+ @trackers[handler] = lambda { |name, template, _|
23
+ tracker.call(name, template)
24
+ }
25
+ end
26
+ end
27
+
28
+ def self.remove_tracker(handler)
29
+ @trackers.delete(handler)
30
+ end
31
+
32
+ class ERBTracker # :nodoc:
33
+ EXPLICIT_DEPENDENCY = /# Template Dependency: (\S+)/
34
+
35
+ # A valid ruby identifier - suitable for class, method and specially variable names
36
+ IDENTIFIER = /
37
+ [[:alpha:]_] # at least one uppercase letter, lowercase letter or underscore
38
+ [[:word:]]* # followed by optional letters, numbers or underscores
39
+ /x
40
+
41
+ # Any kind of variable name. e.g. @instance, @@class, $global or local.
42
+ # Possibly following a method call chain
43
+ VARIABLE_OR_METHOD_CHAIN = /
44
+ (?:\$|@{1,2})? # optional global, instance or class variable indicator
45
+ (?:#{IDENTIFIER}\.)* # followed by an optional chain of zero-argument method calls
46
+ (?<dynamic>#{IDENTIFIER}) # and a final valid identifier, captured as DYNAMIC
47
+ /x
48
+
49
+ # A simple string literal. e.g. "School's out!"
50
+ STRING = /
51
+ (?<quote>['"]) # an opening quote
52
+ (?<static>.*?) # with anything inside, captured as STATIC
53
+ \k<quote> # and a matching closing quote
54
+ /x
55
+
56
+ # Part of any hash containing the :partial key
57
+ PARTIAL_HASH_KEY = /
58
+ (?:\bpartial:|:partial\s*=>) # partial key in either old or new style hash syntax
59
+ \s* # followed by optional spaces
60
+ /x
61
+
62
+ # Part of any hash containing the :layout key
63
+ LAYOUT_HASH_KEY = /
64
+ (?:\blayout:|:layout\s*=>) # layout key in either old or new style hash syntax
65
+ \s* # followed by optional spaces
66
+ /x
67
+
68
+ # Matches:
69
+ # partial: "comments/comment", collection: @all_comments => "comments/comment"
70
+ # (object: @single_comment, partial: "comments/comment") => "comments/comment"
71
+ #
72
+ # "comments/comments"
73
+ # 'comments/comments'
74
+ # ('comments/comments')
75
+ #
76
+ # (@topic) => "topics/topic"
77
+ # topics => "topics/topic"
78
+ # (message.topics) => "topics/topic"
79
+ RENDER_ARGUMENTS = /\A
80
+ (?:\s*\(?\s*) # optional opening paren surrounded by spaces
81
+ (?:.*?#{PARTIAL_HASH_KEY}|#{LAYOUT_HASH_KEY})? # optional hash, up to the partial or layout key declaration
82
+ (?:#{STRING}|#{VARIABLE_OR_METHOD_CHAIN}) # finally, the dependency name of interest
83
+ /xm
84
+
85
+ LAYOUT_DEPENDENCY = /\A
86
+ (?:\s*\(?\s*) # optional opening paren surrounded by spaces
87
+ (?:.*?#{LAYOUT_HASH_KEY}) # check if the line has layout key declaration
88
+ (?:#{STRING}|#{VARIABLE_OR_METHOD_CHAIN}) # finally, the dependency name of interest
89
+ /xm
90
+
91
+ def self.supports_view_paths? # :nodoc:
92
+ true
93
+ end
94
+
95
+ def self.call(name, template, view_paths = nil)
96
+ new(name, template, view_paths).dependencies
97
+ end
98
+
99
+ def initialize(name, template, view_paths = nil)
100
+ @name, @template, @view_paths = name, template, view_paths
101
+ end
102
+
103
+ def dependencies
104
+ render_dependencies + explicit_dependencies
105
+ end
106
+
107
+ attr_reader :name, :template
108
+ private :name, :template
109
+
110
+ private
111
+ def source
112
+ template.source
113
+ end
114
+
115
+ def directory
116
+ name.split("/")[0..-2].join("/")
117
+ end
118
+
119
+ def render_dependencies
120
+ render_dependencies = []
121
+ render_calls = source.split(/\brender\b/).drop(1)
122
+
123
+ render_calls.each do |arguments|
124
+ add_dependencies(render_dependencies, arguments, LAYOUT_DEPENDENCY)
125
+ add_dependencies(render_dependencies, arguments, RENDER_ARGUMENTS)
126
+ end
127
+
128
+ render_dependencies.uniq
129
+ end
130
+
131
+ def add_dependencies(render_dependencies, arguments, pattern)
132
+ arguments.scan(pattern) do
133
+ add_dynamic_dependency(render_dependencies, Regexp.last_match[:dynamic])
134
+ add_static_dependency(render_dependencies, Regexp.last_match[:static])
135
+ end
136
+ end
137
+
138
+ def add_dynamic_dependency(dependencies, dependency)
139
+ if dependency
140
+ dependencies << "#{dependency.pluralize}/#{dependency.singularize}"
141
+ end
142
+ end
143
+
144
+ def add_static_dependency(dependencies, dependency)
145
+ if dependency
146
+ if dependency.include?("/")
147
+ dependencies << dependency
148
+ else
149
+ dependencies << "#{directory}/#{dependency}"
150
+ end
151
+ end
152
+ end
153
+
154
+ def resolve_directories(wildcard_dependencies)
155
+ return [] unless @view_paths
156
+
157
+ wildcard_dependencies.flat_map { |query, templates|
158
+ @view_paths.find_all_with_query(query).map do |template|
159
+ "#{File.dirname(query)}/#{File.basename(template).split('.').first}"
160
+ end
161
+ }.sort
162
+ end
163
+
164
+ def explicit_dependencies
165
+ dependencies = source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
166
+
167
+ wildcards, explicits = dependencies.partition { |dependency| dependency[-1] == "*" }
168
+
169
+ (explicits + resolve_directories(wildcards)).uniq
170
+ end
171
+ end
172
+
173
+ register_tracker :erb, ERBTracker
174
+ end
175
+ end
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/map"
4
+ require "action_view/dependency_tracker"
5
+ require "monitor"
6
+
7
+ module ActionView
8
+ class Digestor
9
+ @@digest_mutex = Mutex.new
10
+
11
+ module PerExecutionDigestCacheExpiry
12
+ def self.before(target)
13
+ ActionView::LookupContext::DetailsKey.clear
14
+ end
15
+ end
16
+
17
+ class << self
18
+ # Supported options:
19
+ #
20
+ # * <tt>name</tt> - Template name
21
+ # * <tt>finder</tt> - An instance of <tt>ActionView::LookupContext</tt>
22
+ # * <tt>dependencies</tt> - An array of dependent views
23
+ def digest(name:, finder:, dependencies: [])
24
+ dependencies ||= []
25
+ cache_key = [ name, finder.rendered_format, dependencies ].flatten.compact.join(".")
26
+
27
+ # this is a correctly done double-checked locking idiom
28
+ # (Concurrent::Map's lookups have volatile semantics)
29
+ finder.digest_cache[cache_key] || @@digest_mutex.synchronize do
30
+ finder.digest_cache.fetch(cache_key) do # re-check under lock
31
+ partial = name.include?("/_")
32
+ root = tree(name, finder, partial)
33
+ dependencies.each do |injected_dep|
34
+ root.children << Injected.new(injected_dep, nil, nil)
35
+ end
36
+ finder.digest_cache[cache_key] = root.digest(finder)
37
+ end
38
+ end
39
+ end
40
+
41
+ def logger
42
+ ActionView::Base.logger || NullLogger
43
+ end
44
+
45
+ # Create a dependency tree for template named +name+.
46
+ def tree(name, finder, partial = false, seen = {})
47
+ logical_name = name.gsub(%r|/_|, "/")
48
+
49
+ if template = find_template(finder, logical_name, [], partial, [])
50
+ finder.rendered_format ||= template.formats.first
51
+
52
+ if node = seen[template.identifier] # handle cycles in the tree
53
+ node
54
+ else
55
+ node = seen[template.identifier] = Node.create(name, logical_name, template, partial)
56
+
57
+ deps = DependencyTracker.find_dependencies(name, template, finder.view_paths)
58
+ deps.uniq { |n| n.gsub(%r|/_|, "/") }.each do |dep_file|
59
+ node.children << tree(dep_file, finder, true, seen)
60
+ end
61
+ node
62
+ end
63
+ else
64
+ unless name.include?("#") # Dynamic template partial names can never be tracked
65
+ logger.error " Couldn't find template for digesting: #{name}"
66
+ end
67
+
68
+ seen[name] ||= Missing.new(name, logical_name, nil)
69
+ end
70
+ end
71
+
72
+ private
73
+ def find_template(finder, name, prefixes, partial, keys)
74
+ finder.disable_cache do
75
+ format = finder.rendered_format
76
+ result = finder.find_all(name, prefixes, partial, keys, formats: [format]).first if format
77
+ result || finder.find_all(name, prefixes, partial, keys).first
78
+ end
79
+ end
80
+ end
81
+
82
+ class Node
83
+ attr_reader :name, :logical_name, :template, :children
84
+
85
+ def self.create(name, logical_name, template, partial)
86
+ klass = partial ? Partial : Node
87
+ klass.new(name, logical_name, template, [])
88
+ end
89
+
90
+ def initialize(name, logical_name, template, children = [])
91
+ @name = name
92
+ @logical_name = logical_name
93
+ @template = template
94
+ @children = children
95
+ end
96
+
97
+ def digest(finder, stack = [])
98
+ ActiveSupport::Digest.hexdigest("#{template.source}-#{dependency_digest(finder, stack)}")
99
+ end
100
+
101
+ def dependency_digest(finder, stack)
102
+ children.map do |node|
103
+ if stack.include?(node)
104
+ false
105
+ else
106
+ finder.digest_cache[node.name] ||= begin
107
+ stack.push node
108
+ node.digest(finder, stack).tap { stack.pop }
109
+ end
110
+ end
111
+ end.join("-")
112
+ end
113
+
114
+ def to_dep_map
115
+ children.any? ? { name => children.map(&:to_dep_map) } : name
116
+ end
117
+ end
118
+
119
+ class Partial < Node; end
120
+
121
+ class Missing < Node
122
+ def digest(finder, _ = []) "" end
123
+ end
124
+
125
+ class Injected < Node
126
+ def digest(finder, _ = []) name end
127
+ end
128
+
129
+ class NullLogger
130
+ def self.debug(_); end
131
+ def self.error(_); end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/output_safety"
4
+
5
+ module ActionView
6
+ class OutputFlow #:nodoc:
7
+ attr_reader :content
8
+
9
+ def initialize
10
+ @content = Hash.new { |h, k| h[k] = ActiveSupport::SafeBuffer.new }
11
+ end
12
+
13
+ # Called by _layout_for to read stored values.
14
+ def get(key)
15
+ @content[key]
16
+ end
17
+
18
+ # Called by each renderer object to set the layout contents.
19
+ def set(key, value)
20
+ @content[key] = ActiveSupport::SafeBuffer.new(value)
21
+ end
22
+
23
+ # Called by content_for
24
+ def append(key, value)
25
+ @content[key] << value
26
+ end
27
+ alias_method :append!, :append
28
+ end
29
+
30
+ class StreamingFlow < OutputFlow #:nodoc:
31
+ def initialize(view, fiber)
32
+ @view = view
33
+ @parent = nil
34
+ @child = view.output_buffer
35
+ @content = view.view_flow.content
36
+ @fiber = fiber
37
+ @root = Fiber.current.object_id
38
+ end
39
+
40
+ # Try to get stored content. If the content
41
+ # is not available and we're inside the layout fiber,
42
+ # then it will begin waiting for the given key and yield.
43
+ def get(key)
44
+ return super if @content.key?(key)
45
+
46
+ if inside_fiber?
47
+ view = @view
48
+
49
+ begin
50
+ @waiting_for = key
51
+ view.output_buffer, @parent = @child, view.output_buffer
52
+ Fiber.yield
53
+ ensure
54
+ @waiting_for = nil
55
+ view.output_buffer, @child = @parent, view.output_buffer
56
+ end
57
+ end
58
+
59
+ super
60
+ end
61
+
62
+ # Appends the contents for the given key. This is called
63
+ # by providing and resuming back to the fiber,
64
+ # if that's the key it's waiting for.
65
+ def append!(key, value)
66
+ super
67
+ @fiber.resume if @waiting_for == key
68
+ end
69
+
70
+ private
71
+
72
+ def inside_fiber?
73
+ Fiber.current.object_id != @root
74
+ end
75
+ end
76
+ end