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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +142 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +38 -0
- data/lib/action_view.rb +97 -0
- data/lib/action_view/base.rb +215 -0
- data/lib/action_view/buffers.rb +52 -0
- data/lib/action_view/context.rb +36 -0
- data/lib/action_view/dependency_tracker.rb +175 -0
- data/lib/action_view/digestor.rb +134 -0
- data/lib/action_view/flows.rb +76 -0
- data/lib/action_view/gem_version.rb +17 -0
- data/lib/action_view/helpers.rb +68 -0
- data/lib/action_view/helpers/active_model_helper.rb +55 -0
- data/lib/action_view/helpers/asset_tag_helper.rb +511 -0
- data/lib/action_view/helpers/asset_url_helper.rb +469 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +205 -0
- data/lib/action_view/helpers/cache_helper.rb +263 -0
- data/lib/action_view/helpers/capture_helper.rb +212 -0
- data/lib/action_view/helpers/controller_helper.rb +36 -0
- data/lib/action_view/helpers/csp_helper.rb +24 -0
- data/lib/action_view/helpers/csrf_helper.rb +35 -0
- data/lib/action_view/helpers/date_helper.rb +1156 -0
- data/lib/action_view/helpers/debug_helper.rb +36 -0
- data/lib/action_view/helpers/form_helper.rb +2337 -0
- data/lib/action_view/helpers/form_options_helper.rb +887 -0
- data/lib/action_view/helpers/form_tag_helper.rb +917 -0
- data/lib/action_view/helpers/javascript_helper.rb +94 -0
- data/lib/action_view/helpers/number_helper.rb +451 -0
- data/lib/action_view/helpers/output_safety_helper.rb +70 -0
- data/lib/action_view/helpers/record_tag_helper.rb +23 -0
- data/lib/action_view/helpers/rendering_helper.rb +99 -0
- data/lib/action_view/helpers/sanitize_helper.rb +177 -0
- data/lib/action_view/helpers/tag_helper.rb +313 -0
- data/lib/action_view/helpers/tags.rb +44 -0
- data/lib/action_view/helpers/tags/base.rb +192 -0
- data/lib/action_view/helpers/tags/check_box.rb +66 -0
- data/lib/action_view/helpers/tags/checkable.rb +18 -0
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +36 -0
- data/lib/action_view/helpers/tags/collection_helpers.rb +119 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +31 -0
- data/lib/action_view/helpers/tags/collection_select.rb +30 -0
- data/lib/action_view/helpers/tags/color_field.rb +27 -0
- data/lib/action_view/helpers/tags/date_field.rb +15 -0
- data/lib/action_view/helpers/tags/date_select.rb +74 -0
- data/lib/action_view/helpers/tags/datetime_field.rb +32 -0
- data/lib/action_view/helpers/tags/datetime_local_field.rb +21 -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 +10 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +31 -0
- data/lib/action_view/helpers/tags/hidden_field.rb +10 -0
- data/lib/action_view/helpers/tags/label.rb +81 -0
- data/lib/action_view/helpers/tags/month_field.rb +15 -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 +33 -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 +43 -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 +34 -0
- data/lib/action_view/helpers/tags/time_field.rb +15 -0
- data/lib/action_view/helpers/tags/time_select.rb +10 -0
- data/lib/action_view/helpers/tags/time_zone_select.rb +22 -0
- data/lib/action_view/helpers/tags/translator.rb +44 -0
- data/lib/action_view/helpers/tags/url_field.rb +10 -0
- data/lib/action_view/helpers/tags/week_field.rb +15 -0
- data/lib/action_view/helpers/text_helper.rb +486 -0
- data/lib/action_view/helpers/translation_helper.rb +141 -0
- data/lib/action_view/helpers/url_helper.rb +676 -0
- data/lib/action_view/layouts.rb +433 -0
- data/lib/action_view/locale/en.yml +56 -0
- data/lib/action_view/log_subscriber.rb +96 -0
- data/lib/action_view/lookup_context.rb +274 -0
- data/lib/action_view/model_naming.rb +14 -0
- data/lib/action_view/path_set.rb +100 -0
- data/lib/action_view/railtie.rb +82 -0
- data/lib/action_view/record_identifier.rb +112 -0
- data/lib/action_view/renderer/abstract_renderer.rb +55 -0
- data/lib/action_view/renderer/partial_renderer.rb +552 -0
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +57 -0
- data/lib/action_view/renderer/renderer.rb +56 -0
- data/lib/action_view/renderer/streaming_template_renderer.rb +105 -0
- data/lib/action_view/renderer/template_renderer.rb +102 -0
- data/lib/action_view/rendering.rb +151 -0
- data/lib/action_view/routing_url_for.rb +145 -0
- data/lib/action_view/tasks/cache_digests.rake +25 -0
- data/lib/action_view/template.rb +361 -0
- data/lib/action_view/template/error.rb +141 -0
- data/lib/action_view/template/handlers.rb +66 -0
- data/lib/action_view/template/handlers/builder.rb +25 -0
- data/lib/action_view/template/handlers/erb.rb +74 -0
- data/lib/action_view/template/handlers/erb/erubi.rb +83 -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/html.rb +34 -0
- data/lib/action_view/template/resolver.rb +391 -0
- data/lib/action_view/template/text.rb +33 -0
- data/lib/action_view/template/types.rb +57 -0
- data/lib/action_view/test_case.rb +300 -0
- data/lib/action_view/testing/resolvers.rb +54 -0
- data/lib/action_view/version.rb +10 -0
- data/lib/action_view/view_paths.rb +105 -0
- data/lib/assets/compiled/rails-ujs.js +720 -0
- 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
|