omg-actionview 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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,316 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attr_internal"
4
+ require "active_support/core_ext/module/attribute_accessors"
5
+ require "active_support/ordered_options"
6
+ require "action_view/log_subscriber"
7
+ require "action_view/helpers"
8
+ require "action_view/context"
9
+ require "action_view/template"
10
+ require "action_view/lookup_context"
11
+
12
+ module ActionView # :nodoc:
13
+ # = Action View \Base
14
+ #
15
+ # Action View templates can be written in several ways.
16
+ # If the template file has a <tt>.erb</tt> extension, then it uses the erubi[https://rubygems.org/gems/erubi]
17
+ # template system which can embed Ruby into an HTML document.
18
+ # If the template file has a <tt>.builder</tt> extension, then Jim Weirich's Builder::XmlMarkup library is used.
19
+ #
20
+ # == ERB
21
+ #
22
+ # You trigger ERB by using embeddings such as <tt><% %></tt>, <tt><% -%></tt>, and <tt><%= %></tt>. The <tt><%= %></tt> tag set is used when you want output. Consider the
23
+ # following loop for names:
24
+ #
25
+ # <b>Names of all the people</b>
26
+ # <% @people.each do |person| %>
27
+ # Name: <%= person.name %><br/>
28
+ # <% end %>
29
+ #
30
+ # The loop is set up in regular embedding tags <tt><% %></tt>, and the name is written using the output embedding tag <tt><%= %></tt>. Note that this
31
+ # is not just a usage suggestion. Regular output functions like print or puts won't work with ERB templates. So this would be wrong:
32
+ #
33
+ # <%# WRONG %>
34
+ # Hi, Mr. <% puts "Frodo" %>
35
+ #
36
+ # If you absolutely must write from within a function use +concat+.
37
+ #
38
+ # When on a line that only contains whitespaces except for the tag, <tt><% %></tt> suppresses leading and trailing whitespace,
39
+ # including the trailing newline. <tt><% %></tt> and <tt><%- -%></tt> are the same.
40
+ # Note however that <tt><%= %></tt> and <tt><%= -%></tt> are different: only the latter removes trailing whitespaces.
41
+ #
42
+ # === Using sub templates
43
+ #
44
+ # Using sub templates allows you to sidestep tedious replication and extract common display structures in shared templates. The
45
+ # classic example is the use of a header and footer (even though the Action Pack-way would be to use Layouts):
46
+ #
47
+ # <%= render "application/header" %>
48
+ # Something really specific and terrific
49
+ # <%= render "application/footer" %>
50
+ #
51
+ # As you see, we use the output embeddings for the render methods. The render call itself will just return a string holding the
52
+ # result of the rendering. The output embedding writes it to the current template.
53
+ #
54
+ # But you don't have to restrict yourself to static includes. Templates can share variables amongst themselves by using instance
55
+ # variables defined using the regular embedding tags. Like this:
56
+ #
57
+ # <% @page_title = "A Wonderful Hello" %>
58
+ # <%= render "application/header" %>
59
+ #
60
+ # Now the header can pick up on the <tt>@page_title</tt> variable and use it for outputting a title tag:
61
+ #
62
+ # <title><%= @page_title %></title>
63
+ #
64
+ # === Passing local variables to sub templates
65
+ #
66
+ # You can pass local variables to sub templates by using a hash with the variable names as keys and the objects as values:
67
+ #
68
+ # <%= render "application/header", { headline: "Welcome", person: person } %>
69
+ #
70
+ # These can now be accessed in <tt>application/header</tt> with:
71
+ #
72
+ # Headline: <%= headline %>
73
+ # First name: <%= person.first_name %>
74
+ #
75
+ # The local variables passed to sub templates can be accessed as a hash using the <tt>local_assigns</tt> hash. This lets you access the
76
+ # variables as:
77
+ #
78
+ # Headline: <%= local_assigns[:headline] %>
79
+ #
80
+ # This is useful in cases where you aren't sure if the local variable has been assigned. Alternatively, you could also use
81
+ # <tt>defined? headline</tt> to first check if the variable has been assigned before using it.
82
+ #
83
+ # By default, templates will accept any <tt>locals</tt> as keyword arguments. To restrict what <tt>locals</tt> a template accepts, add a <tt>locals:</tt> magic comment:
84
+ #
85
+ # <%# locals: (headline:) %>
86
+ #
87
+ # Headline: <%= headline %>
88
+ #
89
+ # In cases where the local variables are optional, declare the keyword argument with a default value:
90
+ #
91
+ # <%# locals: (headline: nil) %>
92
+ #
93
+ # <% unless headline.nil? %>
94
+ # Headline: <%= headline %>
95
+ # <% end %>
96
+ #
97
+ # Read more about strict locals in {Action View Overview}[https://guides.rubyonrails.org/action_view_overview.html#strict-locals]
98
+ # in the guides.
99
+ #
100
+ # === Template caching
101
+ #
102
+ # By default, \Rails will compile each template to a method in order to render it. When you alter a template,
103
+ # \Rails will check the file's modification time and recompile it in development mode.
104
+ #
105
+ # == Builder
106
+ #
107
+ # Builder templates are a more programmatic alternative to ERB. They are especially useful for generating XML content. An XmlMarkup object
108
+ # named +xml+ is automatically made available to templates with a <tt>.builder</tt> extension.
109
+ #
110
+ # Here are some basic examples:
111
+ #
112
+ # xml.em("emphasized") # => <em>emphasized</em>
113
+ # xml.em { xml.b("emph & bold") } # => <em><b>emph &amp; bold</b></em>
114
+ # xml.a("A Link", "href" => "http://onestepback.org") # => <a href="http://onestepback.org">A Link</a>
115
+ # xml.target("name" => "compile", "option" => "fast") # => <target option="fast" name="compile"\>
116
+ # # NOTE: order of attributes is not specified.
117
+ #
118
+ # Any method with a block will be treated as an XML markup tag with nested markup in the block. For example, the following:
119
+ #
120
+ # xml.div do
121
+ # xml.h1(@person.name)
122
+ # xml.p(@person.bio)
123
+ # end
124
+ #
125
+ # would produce something like:
126
+ #
127
+ # <div>
128
+ # <h1>David Heinemeier Hansson</h1>
129
+ # <p>A product of Danish Design during the Winter of '79...</p>
130
+ # </div>
131
+ #
132
+ # Here is a full-length RSS example actually used on Basecamp:
133
+ #
134
+ # xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
135
+ # xml.channel do
136
+ # xml.title(@feed_title)
137
+ # xml.link(@url)
138
+ # xml.description "Basecamp: Recent items"
139
+ # xml.language "en-us"
140
+ # xml.ttl "40"
141
+ #
142
+ # @recent_items.each do |item|
143
+ # xml.item do
144
+ # xml.title(item_title(item))
145
+ # xml.description(item_description(item)) if item_description(item)
146
+ # xml.pubDate(item_pubDate(item))
147
+ # xml.guid(@person.firm.account.url + @recent_items.url(item))
148
+ # xml.link(@person.firm.account.url + @recent_items.url(item))
149
+ #
150
+ # xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
151
+ # end
152
+ # end
153
+ # end
154
+ # end
155
+ #
156
+ # For more information on Builder please consult the {source code}[https://github.com/rails/builder].
157
+ class Base
158
+ include Helpers, ::ERB::Util, Context
159
+
160
+ # Specify the proc used to decorate input tags that refer to attributes with errors.
161
+ cattr_accessor :field_error_proc, default: Proc.new { |html_tag, instance| content_tag :div, html_tag, class: "field_with_errors" }
162
+
163
+ # How to complete the streaming when an exception occurs.
164
+ # This is our best guess: first try to close the attribute, then the tag.
165
+ cattr_accessor :streaming_completion_on_exception, default: %("><script>window.location = "/500.html"</script></html>)
166
+
167
+ # Specify whether rendering within namespaced controllers should prefix
168
+ # the partial paths for ActiveModel objects with the namespace.
169
+ # (e.g., an Admin::PostsController would render @post using /admin/posts/_post.erb)
170
+ class_attribute :prefix_partial_path_with_controller_namespace, default: true
171
+
172
+ # Specify default_formats that can be rendered.
173
+ cattr_accessor :default_formats
174
+
175
+ # Specify whether submit_tag should automatically disable on click
176
+ cattr_accessor :automatically_disable_submit_tag, default: true
177
+
178
+ # Annotate rendered view with file names
179
+ cattr_accessor :annotate_rendered_view_with_filenames, default: false
180
+
181
+ class_attribute :_routes
182
+ class_attribute :logger
183
+
184
+ class << self
185
+ delegate :erb_trim_mode=, to: "ActionView::Template::Handlers::ERB"
186
+
187
+ def cache_template_loading
188
+ ActionView::Resolver.caching?
189
+ end
190
+
191
+ def cache_template_loading=(value)
192
+ ActionView::Resolver.caching = value
193
+ end
194
+
195
+ def xss_safe? # :nodoc:
196
+ true
197
+ end
198
+
199
+ def with_empty_template_cache # :nodoc:
200
+ subclass = Class.new(self) {
201
+ # We can't implement these as self.class because subclasses will
202
+ # share the same template cache as superclasses, so "changed?" won't work
203
+ # correctly.
204
+ define_method(:compiled_method_container) { subclass }
205
+ define_singleton_method(:compiled_method_container) { subclass }
206
+
207
+ def inspect
208
+ "#<ActionView::Base:#{'%#016x' % (object_id << 1)}>"
209
+ end
210
+ }
211
+ end
212
+
213
+ def changed?(other) # :nodoc:
214
+ compiled_method_container != other.compiled_method_container
215
+ end
216
+ end
217
+
218
+ attr_reader :view_renderer, :lookup_context
219
+ attr_internal :config, :assigns
220
+
221
+ delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, to: :lookup_context
222
+
223
+ def assign(new_assigns) # :nodoc:
224
+ @_assigns = new_assigns
225
+ new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
226
+ end
227
+
228
+ # :stopdoc:
229
+
230
+ def self.empty
231
+ with_view_paths([])
232
+ end
233
+
234
+ def self.with_view_paths(view_paths, assigns = {}, controller = nil)
235
+ with_context ActionView::LookupContext.new(view_paths), assigns, controller
236
+ end
237
+
238
+ def self.with_context(context, assigns = {}, controller = nil)
239
+ new context, assigns, controller
240
+ end
241
+
242
+ # :startdoc:
243
+
244
+ def initialize(lookup_context, assigns, controller) # :nodoc:
245
+ @_config = ActiveSupport::InheritableOptions.new
246
+
247
+ @lookup_context = lookup_context
248
+
249
+ @view_renderer = ActionView::Renderer.new @lookup_context
250
+ @current_template = nil
251
+
252
+ assign_controller(controller)
253
+ _prepare_context
254
+
255
+ super()
256
+
257
+ # Assigns must be called last to minimize the number of shapes
258
+ assign(assigns)
259
+ end
260
+
261
+ def _run(method, template, locals, buffer, add_to_stack: true, has_strict_locals: false, &block)
262
+ _old_output_buffer, _old_virtual_path, _old_template = @output_buffer, @virtual_path, @current_template
263
+ @current_template = template if add_to_stack
264
+ @output_buffer = buffer
265
+
266
+ if has_strict_locals
267
+ begin
268
+ public_send(method, locals, buffer, **locals, &block)
269
+ rescue ArgumentError => argument_error
270
+ raise(
271
+ ArgumentError,
272
+ argument_error.
273
+ message.
274
+ gsub("unknown keyword:", "unknown local:").
275
+ gsub("missing keyword:", "missing local:").
276
+ gsub("no keywords accepted", "no locals accepted").
277
+ concat(" for #{@current_template.short_identifier}")
278
+ )
279
+ end
280
+ else
281
+ public_send(method, locals, buffer, &block)
282
+ end
283
+ ensure
284
+ @output_buffer, @virtual_path, @current_template = _old_output_buffer, _old_virtual_path, _old_template
285
+ end
286
+
287
+ def compiled_method_container
288
+ raise NotImplementedError, <<~msg.squish
289
+ Subclasses of ActionView::Base must implement `compiled_method_container`
290
+ or use the class method `with_empty_template_cache` for constructing
291
+ an ActionView::Base subclass that has an empty cache.
292
+ msg
293
+ end
294
+
295
+ def in_rendering_context(options)
296
+ old_view_renderer = @view_renderer
297
+ old_lookup_context = @lookup_context
298
+
299
+ if !lookup_context.html_fallback_for_js && options[:formats]
300
+ formats = Array(options[:formats])
301
+ if formats == [:js]
302
+ formats << :html
303
+ end
304
+ @lookup_context = lookup_context.with_prepended_formats(formats)
305
+ @view_renderer = ActionView::Renderer.new @lookup_context
306
+ end
307
+
308
+ yield @view_renderer
309
+ ensure
310
+ @view_renderer = old_view_renderer
311
+ @lookup_context = old_lookup_context
312
+ end
313
+
314
+ ActiveSupport.run_load_hooks(:action_view, self)
315
+ end
316
+ end
@@ -0,0 +1,165 @@
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 # :nodoc:
22
+ def initialize(buffer = "")
23
+ @raw_buffer = String.new(buffer)
24
+ @raw_buffer.encode!
25
+ end
26
+
27
+ delegate :length, :empty?, :blank?, :encoding, :encode!, :force_encoding, to: :@raw_buffer
28
+
29
+ def to_s
30
+ @raw_buffer.html_safe
31
+ end
32
+ alias_method :html_safe, :to_s
33
+
34
+ def to_str
35
+ @raw_buffer.dup
36
+ end
37
+
38
+ def html_safe?
39
+ true
40
+ end
41
+
42
+ def <<(value)
43
+ unless value.nil?
44
+ value = value.to_s
45
+ @raw_buffer << if value.html_safe?
46
+ value
47
+ else
48
+ CGI.escapeHTML(value)
49
+ end
50
+ end
51
+ self
52
+ end
53
+ alias :concat :<<
54
+ alias :append= :<<
55
+
56
+ def safe_concat(value)
57
+ @raw_buffer << value
58
+ self
59
+ end
60
+ alias :safe_append= :safe_concat
61
+
62
+ def safe_expr_append=(val)
63
+ return self if val.nil?
64
+ @raw_buffer << val.to_s
65
+ self
66
+ end
67
+
68
+ def initialize_copy(other)
69
+ @raw_buffer = other.to_str
70
+ end
71
+
72
+ def capture(*args)
73
+ new_buffer = +""
74
+ old_buffer, @raw_buffer = @raw_buffer, new_buffer
75
+ yield(*args)
76
+ new_buffer.html_safe
77
+ ensure
78
+ @raw_buffer = old_buffer
79
+ end
80
+
81
+ def ==(other)
82
+ other.class == self.class && @raw_buffer == other.to_str
83
+ end
84
+
85
+ def raw
86
+ RawOutputBuffer.new(self)
87
+ end
88
+
89
+ attr_reader :raw_buffer
90
+ end
91
+
92
+ class RawOutputBuffer # :nodoc:
93
+ def initialize(buffer)
94
+ @buffer = buffer
95
+ end
96
+
97
+ def <<(value)
98
+ unless value.nil?
99
+ @buffer.raw_buffer << value.to_s
100
+ end
101
+ end
102
+
103
+ def raw
104
+ self
105
+ end
106
+ end
107
+
108
+ class StreamingBuffer # :nodoc:
109
+ def initialize(block)
110
+ @block = block
111
+ end
112
+
113
+ def <<(value)
114
+ value = value.to_s
115
+ value = ERB::Util.h(value) unless value.html_safe?
116
+ @block.call(value)
117
+ end
118
+ alias :concat :<<
119
+ alias :append= :<<
120
+
121
+ def safe_concat(value)
122
+ @block.call(value.to_s)
123
+ end
124
+ alias :safe_append= :safe_concat
125
+
126
+ def capture
127
+ buffer = +""
128
+ old_block, @block = @block, ->(value) { buffer << value }
129
+ yield
130
+ buffer.html_safe
131
+ ensure
132
+ @block = old_block
133
+ end
134
+
135
+ def html_safe?
136
+ true
137
+ end
138
+
139
+ def html_safe
140
+ self
141
+ end
142
+
143
+ def raw
144
+ RawStreamingBuffer.new(self)
145
+ end
146
+
147
+ attr_reader :block
148
+ end
149
+
150
+ class RawStreamingBuffer # :nodoc:
151
+ def initialize(buffer)
152
+ @buffer = buffer
153
+ end
154
+
155
+ def <<(value)
156
+ unless value.nil?
157
+ @buffer.block.call(value.to_s)
158
+ end
159
+ end
160
+
161
+ def raw
162
+ self
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module CacheExpiry # :nodoc: all
5
+ class ViewReloader
6
+ def initialize(watcher:, &block)
7
+ @mutex = Mutex.new
8
+ @watcher_class = watcher
9
+ @watched_dirs = nil
10
+ @watcher = nil
11
+ @previous_change = false
12
+
13
+ ActionView::PathRegistry.file_system_resolver_hooks << method(:rebuild_watcher)
14
+ end
15
+
16
+ def updated?
17
+ build_watcher unless @watcher
18
+ @previous_change || @watcher.updated?
19
+ end
20
+
21
+ def execute
22
+ return unless @watcher
23
+
24
+ watcher = nil
25
+ @mutex.synchronize do
26
+ @previous_change = false
27
+ watcher = @watcher
28
+ end
29
+ watcher.execute
30
+ end
31
+
32
+ private
33
+ def reload!
34
+ ActionView::LookupContext::DetailsKey.clear
35
+ end
36
+
37
+ def build_watcher
38
+ @mutex.synchronize do
39
+ old_watcher = @watcher
40
+
41
+ if @watched_dirs != dirs_to_watch
42
+ @watched_dirs = dirs_to_watch
43
+ new_watcher = @watcher_class.new([], @watched_dirs) do
44
+ reload!
45
+ end
46
+ @watcher = new_watcher
47
+
48
+ # We must check the old watcher after initializing the new one to
49
+ # ensure we don't miss any events
50
+ @previous_change ||= old_watcher&.updated?
51
+ end
52
+ end
53
+ end
54
+
55
+ def rebuild_watcher
56
+ return unless @watcher
57
+ build_watcher
58
+ end
59
+
60
+ def dirs_to_watch
61
+ all_view_paths.uniq.sort
62
+ end
63
+
64
+ def all_view_paths
65
+ ActionView::PathRegistry.all_file_system_resolvers.map(&:path)
66
+ end
67
+ end
68
+ end
69
+ 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 = ActionView::OutputBuffer.new
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