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.
Files changed (130) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +25 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +40 -0
  5. data/app/assets/javascripts/rails-ujs.esm.js +686 -0
  6. data/app/assets/javascripts/rails-ujs.js +630 -0
  7. data/lib/action_view/base.rb +316 -0
  8. data/lib/action_view/buffers.rb +165 -0
  9. data/lib/action_view/cache_expiry.rb +69 -0
  10. data/lib/action_view/context.rb +32 -0
  11. data/lib/action_view/dependency_tracker/erb_tracker.rb +159 -0
  12. data/lib/action_view/dependency_tracker/ruby_tracker.rb +43 -0
  13. data/lib/action_view/dependency_tracker/wildcard_resolver.rb +32 -0
  14. data/lib/action_view/dependency_tracker.rb +41 -0
  15. data/lib/action_view/deprecator.rb +7 -0
  16. data/lib/action_view/digestor.rb +130 -0
  17. data/lib/action_view/flows.rb +75 -0
  18. data/lib/action_view/gem_version.rb +17 -0
  19. data/lib/action_view/helpers/active_model_helper.rb +54 -0
  20. data/lib/action_view/helpers/asset_tag_helper.rb +680 -0
  21. data/lib/action_view/helpers/asset_url_helper.rb +473 -0
  22. data/lib/action_view/helpers/atom_feed_helper.rb +205 -0
  23. data/lib/action_view/helpers/cache_helper.rb +315 -0
  24. data/lib/action_view/helpers/capture_helper.rb +236 -0
  25. data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
  26. data/lib/action_view/helpers/controller_helper.rb +42 -0
  27. data/lib/action_view/helpers/csp_helper.rb +26 -0
  28. data/lib/action_view/helpers/csrf_helper.rb +35 -0
  29. data/lib/action_view/helpers/date_helper.rb +1266 -0
  30. data/lib/action_view/helpers/debug_helper.rb +38 -0
  31. data/lib/action_view/helpers/form_helper.rb +2765 -0
  32. data/lib/action_view/helpers/form_options_helper.rb +927 -0
  33. data/lib/action_view/helpers/form_tag_helper.rb +1088 -0
  34. data/lib/action_view/helpers/javascript_helper.rb +96 -0
  35. data/lib/action_view/helpers/number_helper.rb +165 -0
  36. data/lib/action_view/helpers/output_safety_helper.rb +70 -0
  37. data/lib/action_view/helpers/rendering_helper.rb +218 -0
  38. data/lib/action_view/helpers/sanitize_helper.rb +201 -0
  39. data/lib/action_view/helpers/tag_helper.rb +621 -0
  40. data/lib/action_view/helpers/tags/base.rb +138 -0
  41. data/lib/action_view/helpers/tags/check_box.rb +65 -0
  42. data/lib/action_view/helpers/tags/checkable.rb +18 -0
  43. data/lib/action_view/helpers/tags/collection_check_boxes.rb +37 -0
  44. data/lib/action_view/helpers/tags/collection_helpers.rb +118 -0
  45. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +31 -0
  46. data/lib/action_view/helpers/tags/collection_select.rb +33 -0
  47. data/lib/action_view/helpers/tags/color_field.rb +26 -0
  48. data/lib/action_view/helpers/tags/date_field.rb +14 -0
  49. data/lib/action_view/helpers/tags/date_select.rb +75 -0
  50. data/lib/action_view/helpers/tags/datetime_field.rb +39 -0
  51. data/lib/action_view/helpers/tags/datetime_local_field.rb +29 -0
  52. data/lib/action_view/helpers/tags/datetime_select.rb +10 -0
  53. data/lib/action_view/helpers/tags/email_field.rb +10 -0
  54. data/lib/action_view/helpers/tags/file_field.rb +26 -0
  55. data/lib/action_view/helpers/tags/grouped_collection_select.rb +34 -0
  56. data/lib/action_view/helpers/tags/hidden_field.rb +14 -0
  57. data/lib/action_view/helpers/tags/label.rb +84 -0
  58. data/lib/action_view/helpers/tags/month_field.rb +14 -0
  59. data/lib/action_view/helpers/tags/number_field.rb +20 -0
  60. data/lib/action_view/helpers/tags/password_field.rb +14 -0
  61. data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
  62. data/lib/action_view/helpers/tags/radio_button.rb +32 -0
  63. data/lib/action_view/helpers/tags/range_field.rb +10 -0
  64. data/lib/action_view/helpers/tags/search_field.rb +27 -0
  65. data/lib/action_view/helpers/tags/select.rb +45 -0
  66. data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
  67. data/lib/action_view/helpers/tags/tel_field.rb +10 -0
  68. data/lib/action_view/helpers/tags/text_area.rb +24 -0
  69. data/lib/action_view/helpers/tags/text_field.rb +33 -0
  70. data/lib/action_view/helpers/tags/time_field.rb +23 -0
  71. data/lib/action_view/helpers/tags/time_select.rb +10 -0
  72. data/lib/action_view/helpers/tags/time_zone_select.rb +25 -0
  73. data/lib/action_view/helpers/tags/translator.rb +39 -0
  74. data/lib/action_view/helpers/tags/url_field.rb +10 -0
  75. data/lib/action_view/helpers/tags/week_field.rb +14 -0
  76. data/lib/action_view/helpers/tags/weekday_select.rb +31 -0
  77. data/lib/action_view/helpers/tags.rb +47 -0
  78. data/lib/action_view/helpers/text_helper.rb +568 -0
  79. data/lib/action_view/helpers/translation_helper.rb +161 -0
  80. data/lib/action_view/helpers/url_helper.rb +812 -0
  81. data/lib/action_view/helpers.rb +68 -0
  82. data/lib/action_view/layouts.rb +434 -0
  83. data/lib/action_view/locale/en.yml +56 -0
  84. data/lib/action_view/log_subscriber.rb +132 -0
  85. data/lib/action_view/lookup_context.rb +299 -0
  86. data/lib/action_view/model_naming.rb +14 -0
  87. data/lib/action_view/path_registry.rb +57 -0
  88. data/lib/action_view/path_set.rb +84 -0
  89. data/lib/action_view/railtie.rb +132 -0
  90. data/lib/action_view/record_identifier.rb +118 -0
  91. data/lib/action_view/render_parser/prism_render_parser.rb +139 -0
  92. data/lib/action_view/render_parser/ripper_render_parser.rb +350 -0
  93. data/lib/action_view/render_parser.rb +40 -0
  94. data/lib/action_view/renderer/abstract_renderer.rb +186 -0
  95. data/lib/action_view/renderer/collection_renderer.rb +204 -0
  96. data/lib/action_view/renderer/object_renderer.rb +34 -0
  97. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +120 -0
  98. data/lib/action_view/renderer/partial_renderer.rb +267 -0
  99. data/lib/action_view/renderer/renderer.rb +107 -0
  100. data/lib/action_view/renderer/streaming_template_renderer.rb +107 -0
  101. data/lib/action_view/renderer/template_renderer.rb +115 -0
  102. data/lib/action_view/rendering.rb +190 -0
  103. data/lib/action_view/routing_url_for.rb +149 -0
  104. data/lib/action_view/tasks/cache_digests.rake +25 -0
  105. data/lib/action_view/template/error.rb +264 -0
  106. data/lib/action_view/template/handlers/builder.rb +25 -0
  107. data/lib/action_view/template/handlers/erb/erubi.rb +85 -0
  108. data/lib/action_view/template/handlers/erb.rb +157 -0
  109. data/lib/action_view/template/handlers/html.rb +11 -0
  110. data/lib/action_view/template/handlers/raw.rb +11 -0
  111. data/lib/action_view/template/handlers.rb +66 -0
  112. data/lib/action_view/template/html.rb +33 -0
  113. data/lib/action_view/template/inline.rb +22 -0
  114. data/lib/action_view/template/raw_file.rb +25 -0
  115. data/lib/action_view/template/renderable.rb +30 -0
  116. data/lib/action_view/template/resolver.rb +212 -0
  117. data/lib/action_view/template/sources/file.rb +17 -0
  118. data/lib/action_view/template/sources.rb +13 -0
  119. data/lib/action_view/template/text.rb +32 -0
  120. data/lib/action_view/template/types.rb +50 -0
  121. data/lib/action_view/template.rb +580 -0
  122. data/lib/action_view/template_details.rb +66 -0
  123. data/lib/action_view/template_path.rb +66 -0
  124. data/lib/action_view/test_case.rb +449 -0
  125. data/lib/action_view/testing/resolvers.rb +44 -0
  126. data/lib/action_view/unbound_template.rb +67 -0
  127. data/lib/action_view/version.rb +10 -0
  128. data/lib/action_view/view_paths.rb +117 -0
  129. data/lib/action_view.rb +104 -0
  130. metadata +275 -0
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ class DependencyTracker # :nodoc:
5
+ class ERBTracker # :nodoc:
6
+ EXPLICIT_DEPENDENCY = /# Template Dependency: (\S+)/
7
+
8
+ # A valid ruby identifier - suitable for class, method and specially variable names
9
+ IDENTIFIER = /
10
+ [[:alpha:]_] # at least one uppercase letter, lowercase letter or underscore
11
+ [[:word:]]* # followed by optional letters, numbers or underscores
12
+ /x
13
+
14
+ # Any kind of variable name. e.g. @instance, @@class, $global or local.
15
+ # Possibly following a method call chain
16
+ VARIABLE_OR_METHOD_CHAIN = /
17
+ (?:\$|@{1,2})? # optional global, instance or class variable indicator
18
+ (?:#{IDENTIFIER}\.)* # followed by an optional chain of zero-argument method calls
19
+ (?<dynamic>#{IDENTIFIER}) # and a final valid identifier, captured as DYNAMIC
20
+ /x
21
+
22
+ # A simple string literal. e.g. "School's out!"
23
+ STRING = /
24
+ (?<quote>['"]) # an opening quote
25
+ (?<static>.*?) # with anything inside, captured as STATIC
26
+ \k<quote> # and a matching closing quote
27
+ /x
28
+
29
+ # Part of any hash containing the :partial key
30
+ PARTIAL_HASH_KEY = /
31
+ (?:\bpartial:|:partial\s*=>) # partial key in either old or new style hash syntax
32
+ \s* # followed by optional spaces
33
+ /x
34
+
35
+ # Part of any hash containing the :layout key
36
+ LAYOUT_HASH_KEY = /
37
+ (?:\blayout:|:layout\s*=>) # layout key in either old or new style hash syntax
38
+ \s* # followed by optional spaces
39
+ /x
40
+
41
+ # Matches:
42
+ # partial: "comments/comment", collection: @all_comments => "comments/comment"
43
+ # (object: @single_comment, partial: "comments/comment") => "comments/comment"
44
+ #
45
+ # "comments/comments"
46
+ # 'comments/comments'
47
+ # ('comments/comments')
48
+ #
49
+ # (@topic) => "topics/topic"
50
+ # topics => "topics/topic"
51
+ # (message.topics) => "topics/topic"
52
+ RENDER_ARGUMENTS = /\A
53
+ (?:\s*\(?\s*) # optional opening paren surrounded by spaces
54
+ (?:.*?#{PARTIAL_HASH_KEY}|#{LAYOUT_HASH_KEY})? # optional hash, up to the partial or layout key declaration
55
+ (?:#{STRING}|#{VARIABLE_OR_METHOD_CHAIN}) # finally, the dependency name of interest
56
+ /xm
57
+
58
+ LAYOUT_DEPENDENCY = /\A
59
+ (?:\s*\(?\s*) # optional opening paren surrounded by spaces
60
+ (?:.*?#{LAYOUT_HASH_KEY}) # check if the line has layout key declaration
61
+ (?:#{STRING}|#{VARIABLE_OR_METHOD_CHAIN}) # finally, the dependency name of interest
62
+ /xm
63
+
64
+ def self.supports_view_paths? # :nodoc:
65
+ true
66
+ end
67
+
68
+ def self.call(name, template, view_paths = nil)
69
+ new(name, template, view_paths).dependencies
70
+ end
71
+
72
+ def initialize(name, template, view_paths = nil)
73
+ @name, @template, @view_paths = name, template, view_paths
74
+ end
75
+
76
+ def dependencies
77
+ WildcardResolver.new(@view_paths, render_dependencies + explicit_dependencies).resolve
78
+ end
79
+
80
+ attr_reader :name, :template
81
+ private :name, :template
82
+
83
+ private
84
+ def source
85
+ template.source
86
+ end
87
+
88
+ def directory
89
+ name.split("/")[0..-2].join("/")
90
+ end
91
+
92
+ def render_dependencies
93
+ dependencies = []
94
+ render_calls = source.split(/\brender\b/).drop(1)
95
+
96
+ render_calls.each do |arguments|
97
+ add_dependencies(dependencies, arguments, LAYOUT_DEPENDENCY)
98
+ add_dependencies(dependencies, arguments, RENDER_ARGUMENTS)
99
+ end
100
+
101
+ dependencies
102
+ end
103
+
104
+ def add_dependencies(render_dependencies, arguments, pattern)
105
+ arguments.scan(pattern) do
106
+ match = Regexp.last_match
107
+ add_dynamic_dependency(render_dependencies, match[:dynamic])
108
+ add_static_dependency(render_dependencies, match[:static], match[:quote])
109
+ end
110
+ end
111
+
112
+ def add_dynamic_dependency(dependencies, dependency)
113
+ if dependency
114
+ dependencies << "#{dependency.pluralize}/#{dependency.singularize}"
115
+ end
116
+ end
117
+
118
+ def add_static_dependency(dependencies, dependency, quote_type)
119
+ if quote_type == '"' && dependency.include?('#{')
120
+ scanner = StringScanner.new(dependency)
121
+
122
+ wildcard_dependency = +""
123
+
124
+ while !scanner.eos?
125
+ next unless scanner.scan_until(/\#{/)
126
+
127
+ unmatched_brackets = 1
128
+ wildcard_dependency << scanner.pre_match
129
+
130
+ while unmatched_brackets > 0 && !scanner.eos?
131
+ scanner.scan_until(/[{}]/)
132
+
133
+ case scanner.matched
134
+ when "{"
135
+ unmatched_brackets += 1
136
+ when "}"
137
+ unmatched_brackets -= 1
138
+ end
139
+ end
140
+
141
+ wildcard_dependency << "*"
142
+ end
143
+
144
+ dependencies << wildcard_dependency
145
+ elsif dependency
146
+ if dependency.include?("/")
147
+ dependencies << dependency
148
+ else
149
+ dependencies << "#{directory}/#{dependency}"
150
+ end
151
+ end
152
+ end
153
+
154
+ def explicit_dependencies
155
+ source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ class DependencyTracker # :nodoc:
5
+ class RubyTracker # :nodoc:
6
+ EXPLICIT_DEPENDENCY = /# Template Dependency: (\S+)/
7
+
8
+ def self.call(name, template, view_paths = nil)
9
+ new(name, template, view_paths).dependencies
10
+ end
11
+
12
+ def dependencies
13
+ WildcardResolver.new(view_paths, render_dependencies + explicit_dependencies).resolve
14
+ end
15
+
16
+ def self.supports_view_paths? # :nodoc:
17
+ true
18
+ end
19
+
20
+ def initialize(name, template, view_paths = nil, parser_class: RenderParser::Default)
21
+ @name, @template, @view_paths = name, template, view_paths
22
+ @parser_class = parser_class
23
+ end
24
+
25
+ private
26
+ attr_reader :template, :name, :view_paths
27
+
28
+ def render_dependencies
29
+ return [] unless template.source.include?("render")
30
+
31
+ compiled_source = template.handler.call(template, template.source)
32
+
33
+ @parser_class.new(@name, compiled_source).render_calls.filter_map do |render_call|
34
+ render_call.gsub(%r|/_|, "/")
35
+ end
36
+ end
37
+
38
+ def explicit_dependencies
39
+ template.source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ class DependencyTracker # :nodoc:
5
+ class WildcardResolver # :nodoc:
6
+ def initialize(view_paths, dependencies)
7
+ @view_paths = view_paths
8
+
9
+ @wildcard_dependencies, @explicit_dependencies =
10
+ dependencies.partition { |dependency| dependency.end_with?("/*") }
11
+ end
12
+
13
+ def resolve
14
+ return explicit_dependencies.uniq if !view_paths || wildcard_dependencies.empty?
15
+
16
+ (explicit_dependencies + resolved_wildcard_dependencies).uniq
17
+ end
18
+
19
+ private
20
+ attr_reader :explicit_dependencies, :wildcard_dependencies, :view_paths
21
+
22
+ def resolved_wildcard_dependencies
23
+ # Remove trailing "/*"
24
+ prefixes = wildcard_dependencies.map { |query| query[0..-3] }
25
+
26
+ view_paths.flat_map(&:all_template_paths).uniq.filter_map { |path|
27
+ path.to_s if prefixes.include?(path.prefix)
28
+ }.sort
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/map"
4
+ require "action_view/path_set"
5
+ require "action_view/render_parser"
6
+
7
+ module ActionView
8
+ class DependencyTracker # :nodoc:
9
+ extend ActiveSupport::Autoload
10
+
11
+ autoload :ERBTracker
12
+ autoload :RubyTracker
13
+ autoload :WildcardResolver
14
+
15
+ @trackers = Concurrent::Map.new
16
+
17
+ def self.find_dependencies(name, template, view_paths = nil)
18
+ tracker = @trackers[template.handler]
19
+ return [] unless tracker
20
+
21
+ tracker.call(name, template, view_paths)
22
+ end
23
+
24
+ def self.register_tracker(extension, tracker)
25
+ handler = Template.handler_for_extension(extension)
26
+ if tracker.respond_to?(:supports_view_paths?)
27
+ @trackers[handler] = tracker
28
+ else
29
+ @trackers[handler] = lambda { |name, template, _|
30
+ tracker.call(name, template)
31
+ }
32
+ end
33
+ end
34
+
35
+ def self.remove_tracker(handler)
36
+ @trackers.delete(handler)
37
+ end
38
+
39
+ register_tracker :erb, ERBTracker
40
+ end
41
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ def self.deprecator # :nodoc:
5
+ @deprecator ||= ActiveSupport::Deprecation.new
6
+ end
7
+ end
@@ -0,0 +1,130 @@
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
+ # * +finder+ - An instance of ActionView::LookupContext
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
+ dependencies_suffix = dependencies.flatten.tap(&:compact!).join(".")
21
+ cache_key = "#{name}.#{format}.#{dependencies_suffix}"
22
+ end
23
+
24
+ # this is a correctly done double-checked locking idiom
25
+ # (Concurrent::Map's lookups have volatile semantics)
26
+ finder.digest_cache[cache_key] || @@digest_mutex.synchronize do
27
+ finder.digest_cache.fetch(cache_key) do # re-check under lock
28
+ path = TemplatePath.parse(name)
29
+ root = tree(path.to_s, finder, path.partial?)
30
+ dependencies.each do |injected_dep|
31
+ root.children << Injected.new(injected_dep, nil, nil)
32
+ end if dependencies
33
+ finder.digest_cache[cache_key] = root.digest(finder)
34
+ end
35
+ end
36
+ end
37
+
38
+ def logger
39
+ ActionView::Base.logger || NullLogger
40
+ end
41
+
42
+ # Create a dependency tree for template named +name+.
43
+ def tree(name, finder, partial = false, seen = {})
44
+ logical_name = name.gsub(%r|/_|, "/")
45
+ interpolated = name.include?("#")
46
+
47
+ path = TemplatePath.parse(name)
48
+
49
+ if !interpolated && (template = find_template(finder, path.name, [path.prefix], partial, []))
50
+ if node = seen[template.identifier] # handle cycles in the tree
51
+ node
52
+ else
53
+ node = seen[template.identifier] = Node.create(name, logical_name, template, partial)
54
+
55
+ deps = DependencyTracker.find_dependencies(name, template, finder.view_paths)
56
+ deps.uniq { |n| n.gsub(%r|/_|, "/") }.each do |dep_file|
57
+ node.children << tree(dep_file, finder, true, seen)
58
+ end
59
+ node
60
+ end
61
+ else
62
+ unless interpolated # Dynamic template partial names can never be tracked
63
+ logger.error " Couldn't find template for digesting: #{name}"
64
+ end
65
+
66
+ seen[name] ||= Missing.new(name, logical_name, nil)
67
+ end
68
+ end
69
+
70
+ private
71
+ def find_template(finder, name, prefixes, partial, keys)
72
+ finder.disable_cache do
73
+ finder.find_all(name, prefixes, partial, keys).first
74
+ end
75
+ end
76
+ end
77
+
78
+ class Node
79
+ attr_reader :name, :logical_name, :template, :children
80
+
81
+ def self.create(name, logical_name, template, partial)
82
+ klass = partial ? Partial : Node
83
+ klass.new(name, logical_name, template, [])
84
+ end
85
+
86
+ def initialize(name, logical_name, template, children = [])
87
+ @name = name
88
+ @logical_name = logical_name
89
+ @template = template
90
+ @children = children
91
+ end
92
+
93
+ def digest(finder, stack = [])
94
+ ActiveSupport::Digest.hexdigest("#{template.source}-#{dependency_digest(finder, stack)}")
95
+ end
96
+
97
+ def dependency_digest(finder, stack)
98
+ children.map do |node|
99
+ if stack.include?(node)
100
+ false
101
+ else
102
+ finder.digest_cache[node.name] ||= begin
103
+ stack.push node
104
+ node.digest(finder, stack).tap { stack.pop }
105
+ end
106
+ end
107
+ end.join("-")
108
+ end
109
+
110
+ def to_dep_map
111
+ children.any? ? { name => children.map(&:to_dep_map) } : name
112
+ end
113
+ end
114
+
115
+ class Partial < Node; end
116
+
117
+ class Missing < Node
118
+ def digest(finder, _ = []) "" end
119
+ end
120
+
121
+ class Injected < Node
122
+ def digest(finder, _ = []) name end
123
+ end
124
+
125
+ class NullLogger
126
+ def self.debug(_); end
127
+ def self.error(_); end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,75 @@
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.to_s)
21
+ end
22
+
23
+ # Called by content_for
24
+ def append(key, value)
25
+ @content[key] << value.to_s
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
+ def inside_fiber?
72
+ Fiber.current.object_id != @root
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ # Returns the currently loaded version of Action View as a +Gem::Version+.
5
+ def self.gem_version
6
+ Gem::Version.new VERSION::STRING
7
+ end
8
+
9
+ module VERSION
10
+ MAJOR = 8
11
+ MINOR = 0
12
+ TINY = 0
13
+ PRE = "alpha1"
14
+
15
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
+ end
17
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attribute_accessors"
4
+ require "active_support/core_ext/enumerable"
5
+
6
+ module ActionView
7
+ module Helpers # :nodoc:
8
+ module ActiveModelHelper
9
+ end
10
+
11
+ # = Active \Model Instance Tag \Helpers
12
+ module ActiveModelInstanceTag
13
+ def object
14
+ @active_model_object ||= begin
15
+ object = super
16
+ object.respond_to?(:to_model) ? object.to_model : object
17
+ end
18
+ end
19
+
20
+ def content_tag(type, options, *)
21
+ select_markup_helper?(type) ? super : error_wrapping(super)
22
+ end
23
+
24
+ def tag(type, options, *)
25
+ tag_generate_errors?(options) ? error_wrapping(super) : super
26
+ end
27
+
28
+ def error_wrapping(html_tag)
29
+ if object_has_errors?
30
+ @template_object.instance_exec(html_tag, self, &Base.field_error_proc)
31
+ else
32
+ html_tag
33
+ end
34
+ end
35
+
36
+ def error_message
37
+ object.errors[@method_name]
38
+ end
39
+
40
+ private
41
+ def object_has_errors?
42
+ object.respond_to?(:errors) && object.errors.respond_to?(:[]) && error_message.present?
43
+ end
44
+
45
+ def select_markup_helper?(type)
46
+ ["optgroup", "option"].include?(type)
47
+ end
48
+
49
+ def tag_generate_errors?(options)
50
+ options["type"] != "hidden"
51
+ end
52
+ end
53
+ end
54
+ end