actionview 4.2.11.1 → 7.0.2.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionview might be problematic. Click here for more details.

Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +229 -215
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +9 -8
  5. data/lib/action_view/base.rb +116 -43
  6. data/lib/action_view/buffers.rb +20 -3
  7. data/lib/action_view/cache_expiry.rb +66 -0
  8. data/lib/action_view/context.rb +8 -12
  9. data/lib/action_view/dependency_tracker/erb_tracker.rb +154 -0
  10. data/lib/action_view/dependency_tracker/ripper_tracker.rb +59 -0
  11. data/lib/action_view/dependency_tracker.rb +21 -122
  12. data/lib/action_view/digestor.rb +92 -85
  13. data/lib/action_view/flows.rb +15 -16
  14. data/lib/action_view/gem_version.rb +6 -4
  15. data/lib/action_view/helpers/active_model_helper.rb +17 -12
  16. data/lib/action_view/helpers/asset_tag_helper.rb +356 -101
  17. data/lib/action_view/helpers/asset_url_helper.rb +180 -74
  18. data/lib/action_view/helpers/atom_feed_helper.rb +21 -19
  19. data/lib/action_view/helpers/cache_helper.rb +156 -43
  20. data/lib/action_view/helpers/capture_helper.rb +21 -14
  21. data/lib/action_view/helpers/controller_helper.rb +16 -5
  22. data/lib/action_view/helpers/csp_helper.rb +26 -0
  23. data/lib/action_view/helpers/csrf_helper.rb +8 -6
  24. data/lib/action_view/helpers/date_helper.rb +288 -132
  25. data/lib/action_view/helpers/debug_helper.rb +9 -6
  26. data/lib/action_view/helpers/form_helper.rb +956 -173
  27. data/lib/action_view/helpers/form_options_helper.rb +178 -97
  28. data/lib/action_view/helpers/form_tag_helper.rb +220 -101
  29. data/lib/action_view/helpers/javascript_helper.rb +33 -19
  30. data/lib/action_view/helpers/number_helper.rb +88 -63
  31. data/lib/action_view/helpers/output_safety_helper.rb +38 -6
  32. data/lib/action_view/helpers/rendering_helper.rb +21 -10
  33. data/lib/action_view/helpers/sanitize_helper.rb +31 -32
  34. data/lib/action_view/helpers/tag_helper.rb +332 -71
  35. data/lib/action_view/helpers/tags/base.rb +123 -99
  36. data/lib/action_view/helpers/tags/check_box.rb +21 -20
  37. data/lib/action_view/helpers/tags/checkable.rb +4 -2
  38. data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -34
  39. data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
  40. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
  41. data/lib/action_view/helpers/tags/collection_select.rb +5 -3
  42. data/lib/action_view/helpers/tags/color_field.rb +4 -3
  43. data/lib/action_view/helpers/tags/date_field.rb +3 -2
  44. data/lib/action_view/helpers/tags/date_select.rb +38 -37
  45. data/lib/action_view/helpers/tags/datetime_field.rb +4 -3
  46. data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
  47. data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
  48. data/lib/action_view/helpers/tags/email_field.rb +2 -0
  49. data/lib/action_view/helpers/tags/file_field.rb +18 -0
  50. data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
  51. data/lib/action_view/helpers/tags/hidden_field.rb +6 -0
  52. data/lib/action_view/helpers/tags/label.rb +7 -2
  53. data/lib/action_view/helpers/tags/month_field.rb +3 -2
  54. data/lib/action_view/helpers/tags/number_field.rb +2 -0
  55. data/lib/action_view/helpers/tags/password_field.rb +3 -1
  56. data/lib/action_view/helpers/tags/placeholderable.rb +3 -1
  57. data/lib/action_view/helpers/tags/radio_button.rb +7 -6
  58. data/lib/action_view/helpers/tags/range_field.rb +2 -0
  59. data/lib/action_view/helpers/tags/search_field.rb +14 -9
  60. data/lib/action_view/helpers/tags/select.rb +11 -10
  61. data/lib/action_view/helpers/tags/tel_field.rb +2 -0
  62. data/lib/action_view/helpers/tags/text_area.rb +4 -2
  63. data/lib/action_view/helpers/tags/text_field.rb +8 -8
  64. data/lib/action_view/helpers/tags/time_field.rb +12 -2
  65. data/lib/action_view/helpers/tags/time_select.rb +2 -0
  66. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
  67. data/lib/action_view/helpers/tags/translator.rb +15 -16
  68. data/lib/action_view/helpers/tags/url_field.rb +2 -0
  69. data/lib/action_view/helpers/tags/week_field.rb +3 -2
  70. data/lib/action_view/helpers/tags/weekday_select.rb +28 -0
  71. data/lib/action_view/helpers/tags.rb +5 -2
  72. data/lib/action_view/helpers/text_helper.rb +80 -51
  73. data/lib/action_view/helpers/translation_helper.rb +120 -69
  74. data/lib/action_view/helpers/url_helper.rb +398 -171
  75. data/lib/action_view/helpers.rb +29 -27
  76. data/lib/action_view/layouts.rb +68 -63
  77. data/lib/action_view/log_subscriber.rb +77 -10
  78. data/lib/action_view/lookup_context.rb +137 -113
  79. data/lib/action_view/model_naming.rb +4 -2
  80. data/lib/action_view/path_set.rb +28 -32
  81. data/lib/action_view/railtie.rb +74 -13
  82. data/lib/action_view/record_identifier.rb +53 -26
  83. data/lib/action_view/render_parser.rb +188 -0
  84. data/lib/action_view/renderer/abstract_renderer.rb +152 -15
  85. data/lib/action_view/renderer/collection_renderer.rb +196 -0
  86. data/lib/action_view/renderer/object_renderer.rb +34 -0
  87. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +102 -0
  88. data/lib/action_view/renderer/partial_renderer.rb +51 -333
  89. data/lib/action_view/renderer/renderer.rb +68 -11
  90. data/lib/action_view/renderer/streaming_template_renderer.rb +60 -56
  91. data/lib/action_view/renderer/template_renderer.rb +87 -74
  92. data/lib/action_view/rendering.rb +73 -47
  93. data/lib/action_view/ripper_ast_parser.rb +198 -0
  94. data/lib/action_view/routing_url_for.rb +35 -24
  95. data/lib/action_view/tasks/cache_digests.rake +25 -0
  96. data/lib/action_view/template/error.rb +151 -41
  97. data/lib/action_view/template/handlers/builder.rb +12 -13
  98. data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
  99. data/lib/action_view/template/handlers/erb.rb +29 -89
  100. data/lib/action_view/template/handlers/html.rb +11 -0
  101. data/lib/action_view/template/handlers/raw.rb +4 -4
  102. data/lib/action_view/template/handlers.rb +14 -10
  103. data/lib/action_view/template/html.rb +12 -13
  104. data/lib/action_view/template/inline.rb +22 -0
  105. data/lib/action_view/template/raw_file.rb +25 -0
  106. data/lib/action_view/template/renderable.rb +24 -0
  107. data/lib/action_view/template/resolver.rb +139 -300
  108. data/lib/action_view/template/sources/file.rb +17 -0
  109. data/lib/action_view/template/sources.rb +13 -0
  110. data/lib/action_view/template/text.rb +10 -12
  111. data/lib/action_view/template/types.rb +28 -26
  112. data/lib/action_view/template.rb +123 -91
  113. data/lib/action_view/template_details.rb +66 -0
  114. data/lib/action_view/template_path.rb +64 -0
  115. data/lib/action_view/test_case.rb +70 -53
  116. data/lib/action_view/testing/resolvers.rb +25 -35
  117. data/lib/action_view/unbound_template.rb +57 -0
  118. data/lib/action_view/version.rb +3 -1
  119. data/lib/action_view/view_paths.rb +73 -58
  120. data/lib/action_view.rb +16 -11
  121. data/lib/assets/compiled/rails-ujs.js +746 -0
  122. metadata +52 -32
  123. data/lib/action_view/helpers/record_tag_helper.rb +0 -108
  124. data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -1,6 +1,7 @@
1
- require 'active_support/core_ext/object/try'
2
- require 'active_support/core_ext/kernel/singleton_class'
3
- require 'thread'
1
+ # frozen_string_literal: true
2
+
3
+ require "thread"
4
+ require "delegate"
4
5
 
5
6
  module ActionView
6
7
  # = Action View Template
@@ -65,8 +66,7 @@ module ActionView
65
66
  # If you want to provide an alternate mechanism for
66
67
  # specifying encodings (like ERB does via <%# encoding: ... %>),
67
68
  # you may indicate that you will handle encodings yourself
68
- # by implementing <tt>self.handles_encoding?</tt>
69
- # on your handler.
69
+ # by implementing <tt>handles_encoding?</tt> on your handler.
70
70
  #
71
71
  # If you do, Rails will not try to encode the String
72
72
  # into the default_internal, passing you the unaltered
@@ -87,48 +87,60 @@ module ActionView
87
87
  # expected_encoding
88
88
  # )
89
89
 
90
+ ##
91
+ # :method: local_assigns
92
+ #
93
+ # Returns a hash with the defined local variables.
94
+ #
95
+ # Given this sub template rendering:
96
+ #
97
+ # <%= render "shared/header", { headline: "Welcome", person: person } %>
98
+ #
99
+ # You can use +local_assigns+ in the sub templates to access the local variables:
100
+ #
101
+ # local_assigns[:headline] # => "Welcome"
102
+
90
103
  eager_autoload do
91
104
  autoload :Error
105
+ autoload :RawFile
106
+ autoload :Renderable
92
107
  autoload :Handlers
93
108
  autoload :HTML
109
+ autoload :Inline
110
+ autoload :Sources
94
111
  autoload :Text
95
112
  autoload :Types
96
113
  end
97
114
 
98
115
  extend Template::Handlers
99
116
 
100
- attr_accessor :locals, :formats, :variants, :virtual_path
117
+ singleton_class.attr_accessor :frozen_string_literal
118
+ @frozen_string_literal = false
101
119
 
102
- attr_reader :source, :identifier, :handler, :original_encoding, :updated_at
103
-
104
- # This finalizer is needed (and exactly with a proc inside another proc)
105
- # otherwise templates leak in development.
106
- Finalizer = proc do |method_name, mod|
107
- proc do
108
- mod.module_eval do
109
- remove_possible_method method_name
110
- end
111
- end
112
- end
113
-
114
- def initialize(source, identifier, handler, details)
115
- format = details[:format] || (handler.default_format if handler.respond_to?(:default_format))
120
+ attr_reader :identifier, :handler
121
+ attr_reader :variable, :format, :variant, :locals, :virtual_path
116
122
 
123
+ def initialize(source, identifier, handler, locals:, format: nil, variant: nil, virtual_path: nil)
117
124
  @source = source
118
125
  @identifier = identifier
119
126
  @handler = handler
120
127
  @compiled = false
121
- @original_encoding = nil
122
- @locals = details[:locals] || []
123
- @virtual_path = details[:virtual_path]
124
- @updated_at = details[:updated_at] || Time.now
125
- @formats = Array(format).map { |f| f.respond_to?(:ref) ? f.ref : f }
126
- @variants = [details[:variant]]
128
+ @locals = locals
129
+ @virtual_path = virtual_path
130
+
131
+ @variable = if @virtual_path
132
+ base = @virtual_path.end_with?("/") ? "" : ::File.basename(@virtual_path)
133
+ base =~ /\A_?(.*?)(?:\.\w+)*\z/
134
+ $1.to_sym
135
+ end
136
+
137
+ @format = format
138
+ @variant = variant
127
139
  @compile_mutex = Mutex.new
128
140
  end
129
141
 
130
- # Returns if the underlying handler supports streaming. If so,
131
- # a streaming buffer *may* be passed when it start rendering.
142
+ # Returns whether the underlying handler supports streaming. If so,
143
+ # a streaming buffer *may* be passed when it starts rendering.
132
144
  def supports_streaming?
133
145
  handler.respond_to?(:supports_streaming?) && handler.supports_streaming?
134
146
  end
@@ -139,40 +151,29 @@ module ActionView
139
151
  # This method is instrumented as "!render_template.action_view". Notice that
140
152
  # we use a bang in this instrumentation because you don't want to
141
153
  # consume this in production. This is only slow if it's being listened to.
142
- def render(view, locals, buffer=nil, &block)
143
- instrument("!render_template") do
154
+ def render(view, locals, buffer = ActionView::OutputBuffer.new, add_to_stack: true, &block)
155
+ instrument_render_template do
144
156
  compile!(view)
145
- view.send(method_name, locals, buffer, &block)
157
+ view._run(method_name, self, locals, buffer, add_to_stack: add_to_stack, &block)
146
158
  end
147
159
  rescue => e
148
160
  handle_render_error(view, e)
149
161
  end
150
162
 
151
163
  def type
152
- @type ||= Types[@formats.first] if @formats.first
164
+ @type ||= Types[format]
153
165
  end
154
166
 
155
- # Receives a view object and return a template similar to self by using @virtual_path.
156
- #
157
- # This method is useful if you have a template object but it does not contain its source
158
- # anymore since it was already compiled. In such cases, all you need to do is to call
159
- # refresh passing in the view object.
160
- #
161
- # Notice this method raises an error if the template to be refreshed does not have a
162
- # virtual path set (true just for inline templates).
163
- def refresh(view)
164
- raise "A template needs to have a virtual path in order to be refreshed" unless @virtual_path
165
- lookup = view.lookup_context
166
- pieces = @virtual_path.split("/")
167
- name = pieces.pop
168
- partial = !!name.sub!(/^_/, "")
169
- lookup.disable_cache do
170
- lookup.find_template(name, [ pieces.join('/') ], partial, @locals)
171
- end
167
+ def short_identifier
168
+ @short_identifier ||= defined?(Rails.root) ? identifier.delete_prefix("#{Rails.root}/") : identifier
172
169
  end
173
170
 
174
171
  def inspect
175
- @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", '') : identifier
172
+ "#<#{self.class.name} #{short_identifier} locals=#{@locals.inspect}>"
173
+ end
174
+
175
+ def source
176
+ @source.to_s
176
177
  end
177
178
 
178
179
  # This method is responsible for properly setting the encoding of the
@@ -186,12 +187,14 @@ module ActionView
186
187
  # before passing the source on to the template engine, leaving a
187
188
  # blank line in its stead.
188
189
  def encode!
189
- return unless source.encoding == Encoding::BINARY
190
+ source = self.source
191
+
192
+ return source unless source.encoding == Encoding::BINARY
190
193
 
191
194
  # Look for # encoding: *. If we find one, we'll encode the
192
195
  # String in that encoding, otherwise, we'll use the
193
196
  # default external encoding.
194
- if source.sub!(/\A#{ENCODING_FLAG}/, '')
197
+ if source.sub!(/\A#{ENCODING_FLAG}/, "")
195
198
  encoding = magic_encoding = $1
196
199
  else
197
200
  encoding = Encoding.default_external
@@ -219,11 +222,23 @@ module ActionView
219
222
  end
220
223
  end
221
224
 
222
- protected
223
225
 
226
+ # Exceptions are marshalled when using the parallel test runner with DRb, so we need
227
+ # to ensure that references to the template object can be marshalled as well. This means forgoing
228
+ # the marshalling of the compiler mutex and instantiating that again on unmarshalling.
229
+ def marshal_dump # :nodoc:
230
+ [ @source, @identifier, @handler, @compiled, @locals, @virtual_path, @format, @variant ]
231
+ end
232
+
233
+ def marshal_load(array) # :nodoc:
234
+ @source, @identifier, @handler, @compiled, @locals, @virtual_path, @format, @variant = *array
235
+ @compile_mutex = Mutex.new
236
+ end
237
+
238
+ private
224
239
  # Compile a template. This method ensures a template is compiled
225
240
  # just once and removes the source after it is compiled.
226
- def compile!(view) #:nodoc:
241
+ def compile!(view)
227
242
  return if @compiled
228
243
 
229
244
  # Templates can be used concurrently in threaded environments
@@ -235,19 +250,12 @@ module ActionView
235
250
  # re-compilation
236
251
  return if @compiled
237
252
 
238
- if view.is_a?(ActionView::CompiledTemplates)
239
- mod = ActionView::CompiledTemplates
240
- else
241
- mod = view.singleton_class
242
- end
253
+ mod = view.compiled_method_container
243
254
 
244
255
  instrument("!compile_template") do
245
256
  compile(mod)
246
257
  end
247
258
 
248
- # Just discard the source if we have a virtual path. This
249
- # means we can get the template back.
250
- @source = nil if @virtual_path
251
259
  @compiled = true
252
260
  end
253
261
  end
@@ -264,18 +272,16 @@ module ActionView
264
272
  # encode the source into <tt>Encoding.default_internal</tt>.
265
273
  # In general, this means that templates will be UTF-8 inside of Rails,
266
274
  # regardless of the original source encoding.
267
- def compile(mod) #:nodoc:
268
- encode!
269
- method_name = self.method_name
270
- code = @handler.call(self)
275
+ def compile(mod)
276
+ source = encode!
277
+ code = @handler.call(self, source)
271
278
 
272
279
  # Make sure that the resulting String to be eval'd is in the
273
280
  # encoding of the code
274
- source = <<-end_src
281
+ original_source = source
282
+ source = +<<-end_src
275
283
  def #{method_name}(local_assigns, output_buffer)
276
- _old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code}
277
- ensure
278
- @virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer
284
+ @virtual_path = #{@virtual_path.inspect};#{locals_code};#{code}
279
285
  end
280
286
  end_src
281
287
 
@@ -290,47 +296,73 @@ module ActionView
290
296
  # handler is valid in the default_internal. This is for handlers
291
297
  # that handle encoding but screw up
292
298
  unless source.valid_encoding?
293
- raise WrongEncodingError.new(@source, Encoding.default_internal)
299
+ raise WrongEncodingError.new(source, Encoding.default_internal)
294
300
  end
295
301
 
296
- mod.module_eval(source, identifier, 0)
297
- ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
302
+ begin
303
+ if Template.frozen_string_literal
304
+ mod.module_eval("# frozen_string_literal: true\n#{source}", identifier, -1)
305
+ else
306
+ mod.module_eval(source, identifier, 0)
307
+ end
308
+ rescue SyntaxError
309
+ # Account for when code in the template is not syntactically valid; e.g. if we're using
310
+ # ERB and the user writes <%= foo( %>, attempting to call a helper `foo` and interpolate
311
+ # the result into the template, but missing an end parenthesis.
312
+ raise SyntaxErrorInTemplate.new(self, original_source)
313
+ end
298
314
  end
299
315
 
300
- def handle_render_error(view, e) #:nodoc:
316
+ def handle_render_error(view, e)
301
317
  if e.is_a?(Template::Error)
302
318
  e.sub_template_of(self)
303
319
  raise e
304
320
  else
305
- template = self
306
- unless template.source
307
- template = refresh(view)
308
- template.encode!
309
- end
310
- raise Template::Error.new(template, e)
321
+ raise Template::Error.new(self)
311
322
  end
312
323
  end
313
324
 
314
- def locals_code #:nodoc:
315
- # Double assign to suppress the dreaded 'assigned but unused variable' warning
316
- @locals.each_with_object('') { |key, code| code << "#{key} = #{key} = local_assigns[:#{key}];" }
325
+ def locals_code
326
+ # Only locals with valid variable names get set directly. Others will
327
+ # still be available in local_assigns.
328
+ locals = @locals - Module::RUBY_RESERVED_KEYWORDS
329
+ deprecated_locals = locals.grep(/\A@+/)
330
+ if deprecated_locals.any?
331
+ ActiveSupport::Deprecation.warn(<<~MSG)
332
+ Passing instance variables to `render` is deprecated.
333
+ In Rails 7.1, #{deprecated_locals.to_sentence} will be ignored.
334
+ MSG
335
+ locals = locals.grep(/\A@?(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
336
+ else
337
+ locals = locals.grep(/\A(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
338
+ end
339
+
340
+ # Assign for the same variable is to suppress unused variable warning
341
+ locals.each_with_object(+"") { |key, code| code << "#{key} = local_assigns[:#{key}]; #{key} = #{key};" }
317
342
  end
318
343
 
319
- def method_name #:nodoc:
344
+ def method_name
320
345
  @method_name ||= begin
321
- m = "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}"
322
- m.tr!('-', '_')
346
+ m = +"_#{identifier_method_name}__#{@identifier.hash}_#{__id__}"
347
+ m.tr!("-", "_")
323
348
  m
324
349
  end
325
350
  end
326
351
 
327
- def identifier_method_name #:nodoc:
328
- inspect.tr('^a-z_', '_')
352
+ def identifier_method_name
353
+ short_identifier.tr("^a-z_", "_")
354
+ end
355
+
356
+ def instrument(action, &block) # :doc:
357
+ ActiveSupport::Notifications.instrument("#{action}.action_view", instrument_payload, &block)
358
+ end
359
+
360
+ def instrument_render_template(&block)
361
+ ActiveSupport::Notifications.instrument("!render_template.action_view", instrument_payload, &block)
329
362
  end
330
363
 
331
- def instrument(action, &block)
332
- payload = { virtual_path: @virtual_path, identifier: @identifier }
333
- ActiveSupport::Notifications.instrument("#{action}.action_view", payload, &block)
364
+ def instrument_payload
365
+ { virtual_path: @virtual_path, identifier: @identifier }
334
366
  end
335
367
  end
336
368
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ class TemplateDetails # :nodoc:
5
+ class Requested
6
+ attr_reader :locale, :handlers, :formats, :variants
7
+ attr_reader :locale_idx, :handlers_idx, :formats_idx, :variants_idx
8
+
9
+ ANY_HASH = Hash.new(1).merge(nil => 0).freeze
10
+
11
+ def initialize(locale:, handlers:, formats:, variants:)
12
+ @locale = locale
13
+ @handlers = handlers
14
+ @formats = formats
15
+ @variants = variants
16
+
17
+ @locale_idx = build_idx_hash(locale)
18
+ @handlers_idx = build_idx_hash(handlers)
19
+ @formats_idx = build_idx_hash(formats)
20
+ if variants == :any
21
+ @variants_idx = ANY_HASH
22
+ else
23
+ @variants_idx = build_idx_hash(variants)
24
+ end
25
+ end
26
+
27
+ private
28
+ def build_idx_hash(arr)
29
+ [*arr, nil].each_with_index.to_h.freeze
30
+ end
31
+ end
32
+
33
+ attr_reader :locale, :handler, :format, :variant
34
+
35
+ def initialize(locale, handler, format, variant)
36
+ @locale = locale
37
+ @handler = handler
38
+ @format = format
39
+ @variant = variant
40
+ end
41
+
42
+ def matches?(requested)
43
+ requested.formats_idx[@format] &&
44
+ requested.locale_idx[@locale] &&
45
+ requested.variants_idx[@variant] &&
46
+ requested.handlers_idx[@handler]
47
+ end
48
+
49
+ def sort_key_for(requested)
50
+ [
51
+ requested.formats_idx[@format],
52
+ requested.locale_idx[@locale],
53
+ requested.variants_idx[@variant],
54
+ requested.handlers_idx[@handler]
55
+ ]
56
+ end
57
+
58
+ def handler_class
59
+ Template.handler_for_extension(handler)
60
+ end
61
+
62
+ def format_or_default
63
+ format || handler_class.try(:default_format)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ # Represents a template path within ActionView's lookup and rendering system,
5
+ # like "users/show"
6
+ #
7
+ # TemplatePath makes it convenient to convert between separate name, prefix,
8
+ # partial arguments and the virtual path.
9
+ class TemplatePath
10
+ attr_reader :name, :prefix, :partial, :virtual
11
+ alias_method :partial?, :partial
12
+ alias_method :virtual_path, :virtual
13
+
14
+ # Convert name, prefix, and partial into a virtual path string
15
+ def self.virtual(name, prefix, partial)
16
+ if prefix.empty?
17
+ "#{partial ? "_" : ""}#{name}"
18
+ elsif partial
19
+ "#{prefix}/_#{name}"
20
+ else
21
+ "#{prefix}/#{name}"
22
+ end
23
+ end
24
+
25
+ # Build a TemplatePath form a virtual path
26
+ def self.parse(virtual)
27
+ if nameidx = virtual.rindex("/")
28
+ prefix = virtual[0, nameidx]
29
+ name = virtual.from(nameidx + 1)
30
+ prefix = prefix[1..] if prefix.start_with?("/")
31
+ else
32
+ prefix = ""
33
+ name = virtual
34
+ end
35
+ partial = name.start_with?("_")
36
+ name = name[1..] if partial
37
+ new name, prefix, partial, virtual
38
+ end
39
+
40
+ # Convert name, prefix, and partial into a TemplatePath
41
+ def self.build(name, prefix, partial)
42
+ new name, prefix, partial, virtual(name, prefix, partial)
43
+ end
44
+
45
+ def initialize(name, prefix, partial, virtual)
46
+ @name = name
47
+ @prefix = prefix
48
+ @partial = partial
49
+ @virtual = virtual
50
+ end
51
+
52
+ alias :to_str :virtual
53
+ alias :to_s :virtual
54
+
55
+ def hash # :nodoc:
56
+ @virtual.hash
57
+ end
58
+
59
+ def eql?(other) # :nodoc:
60
+ @virtual == other.virtual
61
+ end
62
+ alias :== :eql? # :nodoc:
63
+ end
64
+ end