actionview 6.0.0

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 (113) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +271 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +40 -0
  5. data/lib/action_view.rb +98 -0
  6. data/lib/action_view/base.rb +312 -0
  7. data/lib/action_view/buffers.rb +67 -0
  8. data/lib/action_view/cache_expiry.rb +54 -0
  9. data/lib/action_view/context.rb +32 -0
  10. data/lib/action_view/dependency_tracker.rb +175 -0
  11. data/lib/action_view/digestor.rb +126 -0
  12. data/lib/action_view/flows.rb +76 -0
  13. data/lib/action_view/gem_version.rb +17 -0
  14. data/lib/action_view/helpers.rb +66 -0
  15. data/lib/action_view/helpers/active_model_helper.rb +55 -0
  16. data/lib/action_view/helpers/asset_tag_helper.rb +488 -0
  17. data/lib/action_view/helpers/asset_url_helper.rb +470 -0
  18. data/lib/action_view/helpers/atom_feed_helper.rb +205 -0
  19. data/lib/action_view/helpers/cache_helper.rb +271 -0
  20. data/lib/action_view/helpers/capture_helper.rb +216 -0
  21. data/lib/action_view/helpers/controller_helper.rb +36 -0
  22. data/lib/action_view/helpers/csp_helper.rb +26 -0
  23. data/lib/action_view/helpers/csrf_helper.rb +35 -0
  24. data/lib/action_view/helpers/date_helper.rb +1200 -0
  25. data/lib/action_view/helpers/debug_helper.rb +36 -0
  26. data/lib/action_view/helpers/form_helper.rb +2569 -0
  27. data/lib/action_view/helpers/form_options_helper.rb +896 -0
  28. data/lib/action_view/helpers/form_tag_helper.rb +920 -0
  29. data/lib/action_view/helpers/javascript_helper.rb +95 -0
  30. data/lib/action_view/helpers/number_helper.rb +456 -0
  31. data/lib/action_view/helpers/output_safety_helper.rb +70 -0
  32. data/lib/action_view/helpers/rendering_helper.rb +101 -0
  33. data/lib/action_view/helpers/sanitize_helper.rb +171 -0
  34. data/lib/action_view/helpers/tag_helper.rb +314 -0
  35. data/lib/action_view/helpers/tags.rb +44 -0
  36. data/lib/action_view/helpers/tags/base.rb +196 -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 +39 -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 +145 -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 +316 -0
  78. data/lib/action_view/model_naming.rb +14 -0
  79. data/lib/action_view/path_set.rb +95 -0
  80. data/lib/action_view/railtie.rb +105 -0
  81. data/lib/action_view/record_identifier.rb +112 -0
  82. data/lib/action_view/renderer/abstract_renderer.rb +108 -0
  83. data/lib/action_view/renderer/partial_renderer.rb +563 -0
  84. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +103 -0
  85. data/lib/action_view/renderer/renderer.rb +68 -0
  86. data/lib/action_view/renderer/streaming_template_renderer.rb +105 -0
  87. data/lib/action_view/renderer/template_renderer.rb +108 -0
  88. data/lib/action_view/rendering.rb +171 -0
  89. data/lib/action_view/routing_url_for.rb +146 -0
  90. data/lib/action_view/tasks/cache_digests.rake +25 -0
  91. data/lib/action_view/template.rb +393 -0
  92. data/lib/action_view/template/error.rb +161 -0
  93. data/lib/action_view/template/handlers.rb +92 -0
  94. data/lib/action_view/template/handlers/builder.rb +25 -0
  95. data/lib/action_view/template/handlers/erb.rb +84 -0
  96. data/lib/action_view/template/handlers/erb/erubi.rb +87 -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 +43 -0
  100. data/lib/action_view/template/inline.rb +22 -0
  101. data/lib/action_view/template/raw_file.rb +28 -0
  102. data/lib/action_view/template/resolver.rb +394 -0
  103. data/lib/action_view/template/sources.rb +13 -0
  104. data/lib/action_view/template/sources/file.rb +17 -0
  105. data/lib/action_view/template/text.rb +35 -0
  106. data/lib/action_view/template/types.rb +57 -0
  107. data/lib/action_view/test_case.rb +300 -0
  108. data/lib/action_view/testing/resolvers.rb +67 -0
  109. data/lib/action_view/unbound_template.rb +32 -0
  110. data/lib/action_view/version.rb +10 -0
  111. data/lib/action_view/view_paths.rb +129 -0
  112. data/lib/assets/compiled/rails-ujs.js +746 -0
  113. metadata +260 -0
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/output_safety"
4
+
5
+ module ActionView
6
+ # Used as a buffer for views
7
+ #
8
+ # The main difference between this and ActiveSupport::SafeBuffer
9
+ # is for the methods `<<` and `safe_expr_append=` the inputs are
10
+ # checked for nil before they are assigned and `to_s` is called on
11
+ # the input. For example:
12
+ #
13
+ # obuf = ActionView::OutputBuffer.new "hello"
14
+ # obuf << 5
15
+ # puts obuf # => "hello5"
16
+ #
17
+ # sbuf = ActiveSupport::SafeBuffer.new "hello"
18
+ # sbuf << 5
19
+ # puts sbuf # => "hello\u0005"
20
+ #
21
+ class OutputBuffer < ActiveSupport::SafeBuffer #:nodoc:
22
+ def initialize(*)
23
+ super
24
+ encode!
25
+ end
26
+
27
+ def <<(value)
28
+ return self if value.nil?
29
+ super(value.to_s)
30
+ end
31
+ alias :append= :<<
32
+
33
+ def safe_expr_append=(val)
34
+ return self if val.nil?
35
+ safe_concat val.to_s
36
+ end
37
+
38
+ alias :safe_append= :safe_concat
39
+ end
40
+
41
+ class StreamingBuffer #:nodoc:
42
+ def initialize(block)
43
+ @block = block
44
+ end
45
+
46
+ def <<(value)
47
+ value = value.to_s
48
+ value = ERB::Util.h(value) unless value.html_safe?
49
+ @block.call(value)
50
+ end
51
+ alias :concat :<<
52
+ alias :append= :<<
53
+
54
+ def safe_concat(value)
55
+ @block.call(value.to_s)
56
+ end
57
+ alias :safe_append= :safe_concat
58
+
59
+ def html_safe?
60
+ true
61
+ end
62
+
63
+ def html_safe
64
+ self
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ class CacheExpiry
5
+ class Executor
6
+ def initialize(watcher:)
7
+ @cache_expiry = CacheExpiry.new(watcher: watcher)
8
+ end
9
+
10
+ def before(target)
11
+ @cache_expiry.clear_cache_if_necessary
12
+ end
13
+ end
14
+
15
+ def initialize(watcher:)
16
+ @watched_dirs = nil
17
+ @watcher_class = watcher
18
+ @watcher = nil
19
+ @mutex = Mutex.new
20
+ end
21
+
22
+ def clear_cache_if_necessary
23
+ @mutex.synchronize do
24
+ watched_dirs = dirs_to_watch
25
+ return if watched_dirs.empty?
26
+
27
+ if watched_dirs != @watched_dirs
28
+ @watched_dirs = watched_dirs
29
+ @watcher = @watcher_class.new([], watched_dirs) do
30
+ clear_cache
31
+ end
32
+ @watcher.execute
33
+ else
34
+ @watcher.execute_if_updated
35
+ end
36
+ end
37
+ end
38
+
39
+ def clear_cache
40
+ ActionView::LookupContext::DetailsKey.clear
41
+ end
42
+
43
+ private
44
+
45
+ def dirs_to_watch
46
+ fs_paths = all_view_paths.grep(FileSystemResolver)
47
+ fs_paths.map(&:path).sort.uniq
48
+ end
49
+
50
+ def all_view_paths
51
+ ActionView::ViewPaths.all_view_paths.flat_map(&:paths)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ # = Action View Context
5
+ #
6
+ # Action View contexts are supplied to Action Controller to render a template.
7
+ # The default Action View context is ActionView::Base.
8
+ #
9
+ # In order to work with Action Controller, a Context must just include this
10
+ # module. The initialization of the variables used by the context
11
+ # (@output_buffer, @view_flow, and @virtual_path) is responsibility of the
12
+ # object that includes this module (although you can call _prepare_context
13
+ # defined below).
14
+ module Context
15
+ attr_accessor :output_buffer, :view_flow
16
+
17
+ # Prepares the context by setting the appropriate instance variables.
18
+ def _prepare_context
19
+ @view_flow = OutputFlow.new
20
+ @output_buffer = nil
21
+ @virtual_path = nil
22
+ end
23
+
24
+ # Encapsulates the interaction with the view flow so it
25
+ # returns the correct buffer on +yield+. This is usually
26
+ # overwritten by helpers to add more behavior.
27
+ def _layout_for(name = nil)
28
+ name ||= :layout
29
+ view_flow.get(name).html_safe
30
+ end
31
+ end
32
+ 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,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_view/dependency_tracker"
4
+
5
+ module ActionView
6
+ class Digestor
7
+ @@digest_mutex = Mutex.new
8
+
9
+ class << self
10
+ # Supported options:
11
+ #
12
+ # * <tt>name</tt> - Template name
13
+ # * <tt>format</tt> - Template format
14
+ # * <tt>finder</tt> - An instance of <tt>ActionView::LookupContext</tt>
15
+ # * <tt>dependencies</tt> - An array of dependent views
16
+ def digest(name:, format: nil, finder:, dependencies: nil)
17
+ if dependencies.nil? || dependencies.empty?
18
+ cache_key = "#{name}.#{format}"
19
+ else
20
+ cache_key = [ name, format, dependencies ].flatten.compact.join(".")
21
+ end
22
+
23
+ # this is a correctly done double-checked locking idiom
24
+ # (Concurrent::Map's lookups have volatile semantics)
25
+ finder.digest_cache[cache_key] || @@digest_mutex.synchronize do
26
+ finder.digest_cache.fetch(cache_key) do # re-check under lock
27
+ partial = name.include?("/_")
28
+ root = tree(name, finder, partial)
29
+ dependencies.each do |injected_dep|
30
+ root.children << Injected.new(injected_dep, nil, nil)
31
+ end if dependencies
32
+ finder.digest_cache[cache_key] = root.digest(finder)
33
+ end
34
+ end
35
+ end
36
+
37
+ def logger
38
+ ActionView::Base.logger || NullLogger
39
+ end
40
+
41
+ # Create a dependency tree for template named +name+.
42
+ def tree(name, finder, partial = false, seen = {})
43
+ logical_name = name.gsub(%r|/_|, "/")
44
+
45
+ if template = find_template(finder, logical_name, [], partial, [])
46
+ if node = seen[template.identifier] # handle cycles in the tree
47
+ node
48
+ else
49
+ node = seen[template.identifier] = Node.create(name, logical_name, template, partial)
50
+
51
+ deps = DependencyTracker.find_dependencies(name, template, finder.view_paths)
52
+ deps.uniq { |n| n.gsub(%r|/_|, "/") }.each do |dep_file|
53
+ node.children << tree(dep_file, finder, true, seen)
54
+ end
55
+ node
56
+ end
57
+ else
58
+ unless name.include?("#") # Dynamic template partial names can never be tracked
59
+ logger.error " Couldn't find template for digesting: #{name}"
60
+ end
61
+
62
+ seen[name] ||= Missing.new(name, logical_name, nil)
63
+ end
64
+ end
65
+
66
+ private
67
+ def find_template(finder, name, prefixes, partial, keys)
68
+ finder.disable_cache do
69
+ finder.find_all(name, prefixes, partial, keys).first
70
+ end
71
+ end
72
+ end
73
+
74
+ class Node
75
+ attr_reader :name, :logical_name, :template, :children
76
+
77
+ def self.create(name, logical_name, template, partial)
78
+ klass = partial ? Partial : Node
79
+ klass.new(name, logical_name, template, [])
80
+ end
81
+
82
+ def initialize(name, logical_name, template, children = [])
83
+ @name = name
84
+ @logical_name = logical_name
85
+ @template = template
86
+ @children = children
87
+ end
88
+
89
+ def digest(finder, stack = [])
90
+ ActiveSupport::Digest.hexdigest("#{template.source}-#{dependency_digest(finder, stack)}")
91
+ end
92
+
93
+ def dependency_digest(finder, stack)
94
+ children.map do |node|
95
+ if stack.include?(node)
96
+ false
97
+ else
98
+ finder.digest_cache[node.name] ||= begin
99
+ stack.push node
100
+ node.digest(finder, stack).tap { stack.pop }
101
+ end
102
+ end
103
+ end.join("-")
104
+ end
105
+
106
+ def to_dep_map
107
+ children.any? ? { name => children.map(&:to_dep_map) } : name
108
+ end
109
+ end
110
+
111
+ class Partial < Node; end
112
+
113
+ class Missing < Node
114
+ def digest(finder, _ = []) "" end
115
+ end
116
+
117
+ class Injected < Node
118
+ def digest(finder, _ = []) name end
119
+ end
120
+
121
+ class NullLogger
122
+ def self.debug(_); end
123
+ def self.error(_); end
124
+ end
125
+ end
126
+ end