omg-actionview 8.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +25 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +40 -0
- data/app/assets/javascripts/rails-ujs.esm.js +686 -0
- data/app/assets/javascripts/rails-ujs.js +630 -0
- data/lib/action_view/base.rb +316 -0
- data/lib/action_view/buffers.rb +165 -0
- data/lib/action_view/cache_expiry.rb +69 -0
- data/lib/action_view/context.rb +32 -0
- data/lib/action_view/dependency_tracker/erb_tracker.rb +159 -0
- data/lib/action_view/dependency_tracker/ruby_tracker.rb +43 -0
- data/lib/action_view/dependency_tracker/wildcard_resolver.rb +32 -0
- data/lib/action_view/dependency_tracker.rb +41 -0
- data/lib/action_view/deprecator.rb +7 -0
- data/lib/action_view/digestor.rb +130 -0
- data/lib/action_view/flows.rb +75 -0
- data/lib/action_view/gem_version.rb +17 -0
- data/lib/action_view/helpers/active_model_helper.rb +54 -0
- data/lib/action_view/helpers/asset_tag_helper.rb +680 -0
- data/lib/action_view/helpers/asset_url_helper.rb +473 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +205 -0
- data/lib/action_view/helpers/cache_helper.rb +315 -0
- data/lib/action_view/helpers/capture_helper.rb +236 -0
- data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
- data/lib/action_view/helpers/controller_helper.rb +42 -0
- data/lib/action_view/helpers/csp_helper.rb +26 -0
- data/lib/action_view/helpers/csrf_helper.rb +35 -0
- data/lib/action_view/helpers/date_helper.rb +1266 -0
- data/lib/action_view/helpers/debug_helper.rb +38 -0
- data/lib/action_view/helpers/form_helper.rb +2765 -0
- data/lib/action_view/helpers/form_options_helper.rb +927 -0
- data/lib/action_view/helpers/form_tag_helper.rb +1088 -0
- data/lib/action_view/helpers/javascript_helper.rb +96 -0
- data/lib/action_view/helpers/number_helper.rb +165 -0
- data/lib/action_view/helpers/output_safety_helper.rb +70 -0
- data/lib/action_view/helpers/rendering_helper.rb +218 -0
- data/lib/action_view/helpers/sanitize_helper.rb +201 -0
- data/lib/action_view/helpers/tag_helper.rb +621 -0
- data/lib/action_view/helpers/tags/base.rb +138 -0
- data/lib/action_view/helpers/tags/check_box.rb +65 -0
- data/lib/action_view/helpers/tags/checkable.rb +18 -0
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +37 -0
- data/lib/action_view/helpers/tags/collection_helpers.rb +118 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +31 -0
- data/lib/action_view/helpers/tags/collection_select.rb +33 -0
- data/lib/action_view/helpers/tags/color_field.rb +26 -0
- data/lib/action_view/helpers/tags/date_field.rb +14 -0
- data/lib/action_view/helpers/tags/date_select.rb +75 -0
- data/lib/action_view/helpers/tags/datetime_field.rb +39 -0
- data/lib/action_view/helpers/tags/datetime_local_field.rb +29 -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 +26 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +34 -0
- data/lib/action_view/helpers/tags/hidden_field.rb +14 -0
- data/lib/action_view/helpers/tags/label.rb +84 -0
- data/lib/action_view/helpers/tags/month_field.rb +14 -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 +32 -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 +45 -0
- data/lib/action_view/helpers/tags/select_renderer.rb +56 -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 +33 -0
- data/lib/action_view/helpers/tags/time_field.rb +23 -0
- data/lib/action_view/helpers/tags/time_select.rb +10 -0
- data/lib/action_view/helpers/tags/time_zone_select.rb +25 -0
- data/lib/action_view/helpers/tags/translator.rb +39 -0
- data/lib/action_view/helpers/tags/url_field.rb +10 -0
- data/lib/action_view/helpers/tags/week_field.rb +14 -0
- data/lib/action_view/helpers/tags/weekday_select.rb +31 -0
- data/lib/action_view/helpers/tags.rb +47 -0
- data/lib/action_view/helpers/text_helper.rb +568 -0
- data/lib/action_view/helpers/translation_helper.rb +161 -0
- data/lib/action_view/helpers/url_helper.rb +812 -0
- data/lib/action_view/helpers.rb +68 -0
- data/lib/action_view/layouts.rb +434 -0
- data/lib/action_view/locale/en.yml +56 -0
- data/lib/action_view/log_subscriber.rb +132 -0
- data/lib/action_view/lookup_context.rb +299 -0
- data/lib/action_view/model_naming.rb +14 -0
- data/lib/action_view/path_registry.rb +57 -0
- data/lib/action_view/path_set.rb +84 -0
- data/lib/action_view/railtie.rb +132 -0
- data/lib/action_view/record_identifier.rb +118 -0
- data/lib/action_view/render_parser/prism_render_parser.rb +139 -0
- data/lib/action_view/render_parser/ripper_render_parser.rb +350 -0
- data/lib/action_view/render_parser.rb +40 -0
- data/lib/action_view/renderer/abstract_renderer.rb +186 -0
- data/lib/action_view/renderer/collection_renderer.rb +204 -0
- data/lib/action_view/renderer/object_renderer.rb +34 -0
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +120 -0
- data/lib/action_view/renderer/partial_renderer.rb +267 -0
- data/lib/action_view/renderer/renderer.rb +107 -0
- data/lib/action_view/renderer/streaming_template_renderer.rb +107 -0
- data/lib/action_view/renderer/template_renderer.rb +115 -0
- data/lib/action_view/rendering.rb +190 -0
- data/lib/action_view/routing_url_for.rb +149 -0
- data/lib/action_view/tasks/cache_digests.rake +25 -0
- data/lib/action_view/template/error.rb +264 -0
- data/lib/action_view/template/handlers/builder.rb +25 -0
- data/lib/action_view/template/handlers/erb/erubi.rb +85 -0
- data/lib/action_view/template/handlers/erb.rb +157 -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/handlers.rb +66 -0
- data/lib/action_view/template/html.rb +33 -0
- data/lib/action_view/template/inline.rb +22 -0
- data/lib/action_view/template/raw_file.rb +25 -0
- data/lib/action_view/template/renderable.rb +30 -0
- data/lib/action_view/template/resolver.rb +212 -0
- data/lib/action_view/template/sources/file.rb +17 -0
- data/lib/action_view/template/sources.rb +13 -0
- data/lib/action_view/template/text.rb +32 -0
- data/lib/action_view/template/types.rb +50 -0
- data/lib/action_view/template.rb +580 -0
- data/lib/action_view/template_details.rb +66 -0
- data/lib/action_view/template_path.rb +66 -0
- data/lib/action_view/test_case.rb +449 -0
- data/lib/action_view/testing/resolvers.rb +44 -0
- data/lib/action_view/unbound_template.rb +67 -0
- data/lib/action_view/version.rb +10 -0
- data/lib/action_view/view_paths.rb +117 -0
- data/lib/action_view.rb +104 -0
- 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 & 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
|