actionview 4.1.13 → 6.1.3.1

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 +5 -5
  2. data/CHANGELOG.md +181 -359
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +12 -6
  5. data/lib/action_view/base.rb +115 -43
  6. data/lib/action_view/buffers.rb +22 -4
  7. data/lib/action_view/cache_expiry.rb +52 -0
  8. data/lib/action_view/context.rb +8 -12
  9. data/lib/action_view/dependency_tracker.rb +61 -21
  10. data/lib/action_view/digestor.rb +89 -84
  11. data/lib/action_view/flows.rb +12 -13
  12. data/lib/action_view/gem_version.rb +6 -4
  13. data/lib/action_view/helpers/active_model_helper.rb +16 -11
  14. data/lib/action_view/helpers/asset_tag_helper.rb +311 -105
  15. data/lib/action_view/helpers/asset_url_helper.rb +197 -80
  16. data/lib/action_view/helpers/atom_feed_helper.rb +20 -17
  17. data/lib/action_view/helpers/cache_helper.rb +109 -45
  18. data/lib/action_view/helpers/capture_helper.rb +20 -22
  19. data/lib/action_view/helpers/controller_helper.rb +15 -4
  20. data/lib/action_view/helpers/csp_helper.rb +26 -0
  21. data/lib/action_view/helpers/csrf_helper.rb +8 -6
  22. data/lib/action_view/helpers/date_helper.rb +245 -140
  23. data/lib/action_view/helpers/debug_helper.rb +14 -17
  24. data/lib/action_view/helpers/form_helper.rb +875 -148
  25. data/lib/action_view/helpers/form_options_helper.rb +128 -82
  26. data/lib/action_view/helpers/form_tag_helper.rb +253 -91
  27. data/lib/action_view/helpers/javascript_helper.rb +37 -15
  28. data/lib/action_view/helpers/number_helper.rb +100 -77
  29. data/lib/action_view/helpers/output_safety_helper.rb +42 -10
  30. data/lib/action_view/helpers/rendering_helper.rb +26 -15
  31. data/lib/action_view/helpers/sanitize_helper.rb +79 -164
  32. data/lib/action_view/helpers/tag_helper.rb +277 -64
  33. data/lib/action_view/helpers/tags/base.rb +143 -92
  34. data/lib/action_view/helpers/tags/check_box.rb +20 -19
  35. data/lib/action_view/helpers/tags/checkable.rb +4 -2
  36. data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -30
  37. data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
  38. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
  39. data/lib/action_view/helpers/tags/collection_select.rb +4 -2
  40. data/lib/action_view/helpers/tags/color_field.rb +4 -3
  41. data/lib/action_view/helpers/tags/date_field.rb +3 -2
  42. data/lib/action_view/helpers/tags/date_select.rb +38 -37
  43. data/lib/action_view/helpers/tags/datetime_field.rb +14 -5
  44. data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
  45. data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
  46. data/lib/action_view/helpers/tags/email_field.rb +2 -0
  47. data/lib/action_view/helpers/tags/file_field.rb +2 -0
  48. data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
  49. data/lib/action_view/helpers/tags/hidden_field.rb +2 -0
  50. data/lib/action_view/helpers/tags/label.rb +41 -22
  51. data/lib/action_view/helpers/tags/month_field.rb +3 -2
  52. data/lib/action_view/helpers/tags/number_field.rb +2 -0
  53. data/lib/action_view/helpers/tags/password_field.rb +3 -1
  54. data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
  55. data/lib/action_view/helpers/tags/radio_button.rb +7 -6
  56. data/lib/action_view/helpers/tags/range_field.rb +2 -0
  57. data/lib/action_view/helpers/tags/search_field.rb +3 -0
  58. data/lib/action_view/helpers/tags/select.rb +11 -10
  59. data/lib/action_view/helpers/tags/tel_field.rb +2 -0
  60. data/lib/action_view/helpers/tags/text_area.rb +7 -1
  61. data/lib/action_view/helpers/tags/text_field.rb +11 -7
  62. data/lib/action_view/helpers/tags/time_field.rb +3 -2
  63. data/lib/action_view/helpers/tags/time_select.rb +2 -0
  64. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
  65. data/lib/action_view/helpers/tags/translator.rb +39 -0
  66. data/lib/action_view/helpers/tags/url_field.rb +2 -0
  67. data/lib/action_view/helpers/tags/week_field.rb +3 -2
  68. data/lib/action_view/helpers/tags.rb +4 -1
  69. data/lib/action_view/helpers/text_helper.rb +80 -45
  70. data/lib/action_view/helpers/translation_helper.rb +148 -67
  71. data/lib/action_view/helpers/url_helper.rb +289 -147
  72. data/lib/action_view/helpers.rb +5 -3
  73. data/lib/action_view/layouts.rb +68 -63
  74. data/lib/action_view/log_subscriber.rb +80 -13
  75. data/lib/action_view/lookup_context.rb +137 -92
  76. data/lib/action_view/model_naming.rb +4 -2
  77. data/lib/action_view/path_set.rb +30 -16
  78. data/lib/action_view/railtie.rb +62 -13
  79. data/lib/action_view/record_identifier.rb +53 -26
  80. data/lib/action_view/renderer/abstract_renderer.rb +152 -13
  81. data/lib/action_view/renderer/collection_renderer.rb +196 -0
  82. data/lib/action_view/renderer/object_renderer.rb +34 -0
  83. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +102 -0
  84. data/lib/action_view/renderer/partial_renderer.rb +61 -261
  85. data/lib/action_view/renderer/renderer.rb +67 -6
  86. data/lib/action_view/renderer/streaming_template_renderer.rb +58 -54
  87. data/lib/action_view/renderer/template_renderer.rb +83 -75
  88. data/lib/action_view/rendering.rb +73 -46
  89. data/lib/action_view/routing_url_for.rb +54 -17
  90. data/lib/action_view/tasks/cache_digests.rake +25 -0
  91. data/lib/action_view/template/error.rb +44 -29
  92. data/lib/action_view/template/handlers/builder.rb +12 -13
  93. data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
  94. data/lib/action_view/template/handlers/erb.rb +23 -89
  95. data/lib/action_view/template/handlers/html.rb +11 -0
  96. data/lib/action_view/template/handlers/raw.rb +4 -4
  97. data/lib/action_view/template/handlers.rb +22 -9
  98. data/lib/action_view/template/html.rb +10 -11
  99. data/lib/action_view/template/inline.rb +22 -0
  100. data/lib/action_view/template/raw_file.rb +25 -0
  101. data/lib/action_view/template/renderable.rb +24 -0
  102. data/lib/action_view/template/resolver.rb +267 -181
  103. data/lib/action_view/template/sources/file.rb +17 -0
  104. data/lib/action_view/template/sources.rb +13 -0
  105. data/lib/action_view/template/text.rb +8 -10
  106. data/lib/action_view/template/types.rb +18 -18
  107. data/lib/action_view/template.rb +109 -99
  108. data/lib/action_view/test_case.rb +73 -53
  109. data/lib/action_view/testing/resolvers.rb +24 -33
  110. data/lib/action_view/unbound_template.rb +31 -0
  111. data/lib/action_view/version.rb +3 -1
  112. data/lib/action_view/view_paths.rb +74 -44
  113. data/lib/action_view.rb +14 -9
  114. data/lib/assets/compiled/rails-ujs.js +746 -0
  115. metadata +71 -26
  116. data/lib/action_view/helpers/record_tag_helper.rb +0 -108
  117. data/lib/action_view/tasks/dependencies.rake +0 -23
  118. data/lib/action_view/vendor/html-scanner/html/document.rb +0 -68
  119. data/lib/action_view/vendor/html-scanner/html/node.rb +0 -532
  120. data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +0 -188
  121. data/lib/action_view/vendor/html-scanner/html/selector.rb +0 -830
  122. data/lib/action_view/vendor/html-scanner/html/tokenizer.rb +0 -107
  123. data/lib/action_view/vendor/html-scanner/html/version.rb +0 -11
  124. data/lib/action_view/vendor/html-scanner.rb +0 -20
@@ -1,12 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionView
2
- module ModelNaming
4
+ module ModelNaming #:nodoc:
3
5
  # Converts the given object to an ActiveModel compliant one.
4
6
  def convert_to_model(object)
5
7
  object.respond_to?(:to_model) ? object.to_model : object
6
8
  end
7
9
 
8
10
  def model_name_from_record_or_class(record_or_class)
9
- (record_or_class.is_a?(Class) ? record_or_class : convert_to_model(record_or_class).class).model_name
11
+ convert_to_model(record_or_class).model_name
10
12
  end
11
13
  end
12
14
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionView #:nodoc:
2
4
  # = Action View PathSet
3
5
  #
@@ -47,31 +49,43 @@ module ActionView #:nodoc:
47
49
  end
48
50
 
49
51
  def find_all(path, prefixes = [], *args)
50
- prefixes = [prefixes] if String === prefixes
51
- prefixes.each do |prefix|
52
- paths.each do |resolver|
53
- templates = resolver.find_all(path, prefix, *args)
54
- return templates unless templates.empty?
55
- end
56
- end
57
- []
52
+ _find_all path, prefixes, args
58
53
  end
59
54
 
60
55
  def exists?(path, prefixes, *args)
61
56
  find_all(path, prefixes, *args).any?
62
57
  end
63
58
 
59
+ def find_all_with_query(query) # :nodoc:
60
+ paths.each do |resolver|
61
+ templates = resolver.find_all_with_query(query)
62
+ return templates unless templates.empty?
63
+ end
64
+
65
+ []
66
+ end
67
+
64
68
  private
69
+ def _find_all(path, prefixes, args)
70
+ prefixes = [prefixes] if String === prefixes
71
+ prefixes.each do |prefix|
72
+ paths.each do |resolver|
73
+ templates = resolver.find_all(path, prefix, *args)
74
+ return templates unless templates.empty?
75
+ end
76
+ end
77
+ []
78
+ end
65
79
 
66
- def typecast(paths)
67
- paths.map do |path|
68
- case path
69
- when Pathname, String
70
- OptimizedFileSystemResolver.new path.to_s
71
- else
72
- path
80
+ def typecast(paths)
81
+ paths.map do |path|
82
+ case path
83
+ when Pathname, String
84
+ OptimizedFileSystemResolver.new path.to_s
85
+ else
86
+ path
87
+ end
73
88
  end
74
89
  end
75
- end
76
90
  end
77
91
  end
@@ -1,33 +1,64 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "action_view"
2
4
  require "rails"
3
5
 
4
6
  module ActionView
5
7
  # = Action View Railtie
6
- class Railtie < Rails::Railtie # :nodoc:
8
+ class Railtie < Rails::Engine # :nodoc:
7
9
  config.action_view = ActiveSupport::OrderedOptions.new
8
- config.action_view.embed_authenticity_token_in_remote_forms = false
10
+ config.action_view.embed_authenticity_token_in_remote_forms = nil
11
+ config.action_view.debug_missing_translation = true
12
+ config.action_view.default_enforce_utf8 = nil
9
13
 
10
14
  config.eager_load_namespaces << ActionView
11
15
 
12
- initializer "action_view.embed_authenticity_token_in_remote_forms" do |app|
13
- ActiveSupport.on_load(:action_view) do
14
- ActionView::Helpers::FormTagHelper.embed_authenticity_token_in_remote_forms =
15
- app.config.action_view.delete(:embed_authenticity_token_in_remote_forms)
16
+ config.after_initialize do |app|
17
+ ActionView::Helpers::FormTagHelper.embed_authenticity_token_in_remote_forms =
18
+ app.config.action_view.delete(:embed_authenticity_token_in_remote_forms)
19
+ end
20
+
21
+ config.after_initialize do |app|
22
+ form_with_generates_remote_forms = app.config.action_view.delete(:form_with_generates_remote_forms)
23
+ ActionView::Helpers::FormHelper.form_with_generates_remote_forms = form_with_generates_remote_forms
24
+ end
25
+
26
+ config.after_initialize do |app|
27
+ form_with_generates_ids = app.config.action_view.delete(:form_with_generates_ids)
28
+ unless form_with_generates_ids.nil?
29
+ ActionView::Helpers::FormHelper.form_with_generates_ids = form_with_generates_ids
16
30
  end
17
31
  end
18
32
 
19
- initializer "action_view.logger" do
20
- ActiveSupport.on_load(:action_view) { self.logger ||= Rails.logger }
33
+ config.after_initialize do |app|
34
+ default_enforce_utf8 = app.config.action_view.delete(:default_enforce_utf8)
35
+ unless default_enforce_utf8.nil?
36
+ ActionView::Helpers::FormTagHelper.default_enforce_utf8 = default_enforce_utf8
37
+ end
21
38
  end
22
39
 
23
- initializer "action_view.set_configs" do |app|
40
+ config.after_initialize do |app|
41
+ ActionView::Helpers::AssetTagHelper.preload_links_header = app.config.action_view.delete(:preload_links_header)
42
+ end
43
+
44
+ config.after_initialize do |app|
24
45
  ActiveSupport.on_load(:action_view) do
25
- app.config.action_view.each do |k,v|
46
+ app.config.action_view.each do |k, v|
47
+ if k == :raise_on_missing_translations
48
+ ActiveSupport::Deprecation.warn \
49
+ "action_view.raise_on_missing_translations is deprecated and will be removed in Rails 6.2. " \
50
+ "Set i18n.raise_on_missing_translations instead. " \
51
+ "Note that this new setting also affects how missing translations are handled in controllers."
52
+ end
26
53
  send "#{k}=", v
27
54
  end
28
55
  end
29
56
  end
30
57
 
58
+ initializer "action_view.logger" do
59
+ ActiveSupport.on_load(:action_view) { self.logger ||= Rails.logger }
60
+ end
61
+
31
62
  initializer "action_view.caching" do |app|
32
63
  ActiveSupport.on_load(:action_view) do
33
64
  if app.config.action_view.cache_template_loading.nil?
@@ -38,12 +69,30 @@ module ActionView
38
69
 
39
70
  initializer "action_view.setup_action_pack" do |app|
40
71
  ActiveSupport.on_load(:action_controller) do
41
- ActionView::RoutingUrlFor.send(:include, ActionDispatch::Routing::UrlFor)
72
+ ActionView::RoutingUrlFor.include(ActionDispatch::Routing::UrlFor)
42
73
  end
43
74
  end
44
75
 
45
- rake_tasks do
46
- load "action_view/tasks/dependencies.rake"
76
+ initializer "action_view.collection_caching", after: "action_controller.set_configs" do |app|
77
+ PartialRenderer.collection_cache = app.config.action_controller.cache_store
78
+ end
79
+
80
+ config.after_initialize do |app|
81
+ enable_caching = if app.config.action_view.cache_template_loading.nil?
82
+ app.config.cache_classes
83
+ else
84
+ app.config.action_view.cache_template_loading
85
+ end
86
+
87
+ unless enable_caching
88
+ app.executor.to_run ActionView::CacheExpiry::Executor.new(watcher: app.config.file_watcher)
89
+ end
90
+ end
91
+
92
+ rake_tasks do |app|
93
+ unless app.config.api_only
94
+ load "action_view/tasks/cache_digests.rake"
95
+ end
47
96
  end
48
97
  end
49
98
  end
@@ -1,38 +1,66 @@
1
- require 'active_support/core_ext/module'
2
- require 'action_view/model_naming'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module"
4
+ require "action_view/model_naming"
3
5
 
4
6
  module ActionView
5
- # The record identifier encapsulates a number of naming conventions for dealing with records, like Active Records or
6
- # pretty much any other model type that has an id. These patterns are then used to try elevate the view actions to
7
- # a higher logical level.
7
+ # RecordIdentifier encapsulates methods used by various ActionView helpers
8
+ # to associate records with DOM elements.
8
9
  #
9
- # # routes
10
- # resources :posts
10
+ # Consider for example the following code that form of post:
11
11
  #
12
- # # view
13
- # <%= div_for(post) do %> <div id="post_45" class="post">
14
- # <%= post.body %> What a wonderful world!
15
- # <% end %> </div>
12
+ # <%= form_for(post) do |f| %>
13
+ # <%= f.text_field :body %>
14
+ # <% end %>
16
15
  #
17
- # # controller
18
- # def update
19
- # post = Post.find(params[:id])
20
- # post.update(params[:post])
16
+ # When +post+ is a new, unsaved ActiveRecord::Base instance, the resulting HTML
17
+ # is:
21
18
  #
22
- # redirect_to(post) # Calls polymorphic_url(post) which in turn calls post_url(post)
23
- # end
19
+ # <form class="new_post" id="new_post" action="/posts" accept-charset="UTF-8" method="post">
20
+ # <input type="text" name="post[body]" id="post_body" />
21
+ # </form>
22
+ #
23
+ # When +post+ is a persisted ActiveRecord::Base instance, the resulting HTML
24
+ # is:
25
+ #
26
+ # <form class="edit_post" id="edit_post_42" action="/posts/42" accept-charset="UTF-8" method="post">
27
+ # <input type="text" value="What a wonderful world!" name="post[body]" id="post_body" />
28
+ # </form>
29
+ #
30
+ # In both cases, the +id+ and +class+ of the wrapping DOM element are
31
+ # automatically generated, following naming conventions encapsulated by the
32
+ # RecordIdentifier methods #dom_id and #dom_class:
33
+ #
34
+ # dom_id(Post.new) # => "new_post"
35
+ # dom_class(Post.new) # => "post"
36
+ # dom_id(Post.find 42) # => "post_42"
37
+ # dom_class(Post.find 42) # => "post"
24
38
  #
25
- # As the example above shows, you can stop caring to a large extent what the actual id of the post is.
26
- # You just know that one is being assigned and that the subsequent calls in redirect_to expect that
27
- # same naming convention and allows you to write less code if you follow it.
39
+ # Note that these methods do not strictly require +Post+ to be a subclass of
40
+ # ActiveRecord::Base.
41
+ # Any +Post+ class will work as long as its instances respond to +to_key+
42
+ # and +model_name+, given that +model_name+ responds to +param_key+.
43
+ # For instance:
44
+ #
45
+ # class Post
46
+ # attr_accessor :to_key
47
+ #
48
+ # def model_name
49
+ # OpenStruct.new param_key: 'post'
50
+ # end
51
+ #
52
+ # def self.find(id)
53
+ # new.tap { |post| post.to_key = [id] }
54
+ # end
55
+ # end
28
56
  module RecordIdentifier
29
57
  extend self
30
58
  extend ModelNaming
31
59
 
32
60
  include ModelNaming
33
61
 
34
- JOIN = '_'.freeze
35
- NEW = 'new'.freeze
62
+ JOIN = "_"
63
+ NEW = "new"
36
64
 
37
65
  # The DOM class convention is to use the singular form of an object or class.
38
66
  #
@@ -66,8 +94,7 @@ module ActionView
66
94
  end
67
95
  end
68
96
 
69
- protected
70
-
97
+ private
71
98
  # Returns a string representation of the key attribute(s) that is suitable for use in an HTML DOM id.
72
99
  # This can be overwritten to customize the default generated string representation if desired.
73
100
  # If you need to read back a key from a dom_id in order to query for the underlying database record,
@@ -76,9 +103,9 @@ module ActionView
76
103
  # overwritten version of the method. By default, this implementation passes the key string through a
77
104
  # method that replaces all characters that are invalid inside DOM ids, with valid ones. You need to
78
105
  # make sure yourself that your dom ids are valid, in case you overwrite this method.
79
- def record_key_for_dom_id(record)
106
+ def record_key_for_dom_id(record) # :doc:
80
107
  key = convert_to_model(record).to_key
81
- key ? key.join('_') : key
108
+ key ? key.join(JOIN) : key
82
109
  end
83
110
  end
84
111
  end
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/map"
4
+
1
5
  module ActionView
2
6
  # This class defines the interface for a renderer. Each class that
3
7
  # subclasses +AbstractRenderer+ is used by the base +Renderer+ class to
@@ -12,10 +16,10 @@ module ActionView
12
16
  #
13
17
  # Whenever the +render+ method is called on the base +Renderer+ class, a new
14
18
  # renderer object of the correct type is created, and the +render+ method on
15
- # that new object is called in turn. This abstracts the setup and rendering
19
+ # that new object is called in turn. This abstracts the set up and rendering
16
20
  # into a separate classes for partials and templates.
17
21
  class AbstractRenderer #:nodoc:
18
- delegate :find_template, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
22
+ delegate :template_exists?, :any_templates?, :formats, to: :@lookup_context
19
23
 
20
24
  def initialize(lookup_context)
21
25
  @lookup_context = lookup_context
@@ -25,23 +29,158 @@ module ActionView
25
29
  raise NotImplementedError
26
30
  end
27
31
 
28
- protected
32
+ module ObjectRendering # :nodoc:
33
+ PREFIXED_PARTIAL_NAMES = Concurrent::Map.new do |h, k|
34
+ h[k] = Concurrent::Map.new
35
+ end
29
36
 
30
- def extract_details(options)
31
- @lookup_context.registered_details.each_with_object({}) do |key, details|
32
- next unless value = options[key]
33
- details[key] = Array(value)
37
+ def initialize(lookup_context, options)
38
+ super
39
+ @context_prefix = lookup_context.prefixes.first
34
40
  end
41
+
42
+ private
43
+ def local_variable(path)
44
+ if as = @options[:as]
45
+ raise_invalid_option_as(as) unless /\A[a-z_]\w*\z/.match?(as.to_s)
46
+ as.to_sym
47
+ else
48
+ base = path.end_with?("/") ? "" : File.basename(path)
49
+ raise_invalid_identifier(path) unless base =~ /\A_?(.*?)(?:\.\w+)*\z/
50
+ $1.to_sym
51
+ end
52
+ end
53
+
54
+ IDENTIFIER_ERROR_MESSAGE = "The partial name (%s) is not a valid Ruby identifier; " \
55
+ "make sure your partial name starts with underscore."
56
+
57
+ OPTION_AS_ERROR_MESSAGE = "The value (%s) of the option `as` is not a valid Ruby identifier; " \
58
+ "make sure it starts with lowercase letter, " \
59
+ "and is followed by any combination of letters, numbers and underscores."
60
+
61
+ def raise_invalid_identifier(path)
62
+ raise ArgumentError, IDENTIFIER_ERROR_MESSAGE % path
63
+ end
64
+
65
+ def raise_invalid_option_as(as)
66
+ raise ArgumentError, OPTION_AS_ERROR_MESSAGE % as
67
+ end
68
+
69
+ # Obtains the path to where the object's partial is located. If the object
70
+ # responds to +to_partial_path+, then +to_partial_path+ will be called and
71
+ # will provide the path. If the object does not respond to +to_partial_path+,
72
+ # then an +ArgumentError+ is raised.
73
+ #
74
+ # If +prefix_partial_path_with_controller_namespace+ is true, then this
75
+ # method will prefix the partial paths with a namespace.
76
+ def partial_path(object, view)
77
+ object = object.to_model if object.respond_to?(:to_model)
78
+
79
+ path = if object.respond_to?(:to_partial_path)
80
+ object.to_partial_path
81
+ else
82
+ raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object. It must implement :to_partial_path.")
83
+ end
84
+
85
+ if view.prefix_partial_path_with_controller_namespace
86
+ PREFIXED_PARTIAL_NAMES[@context_prefix][path] ||= merge_prefix_into_object_path(@context_prefix, path.dup)
87
+ else
88
+ path
89
+ end
90
+ end
91
+
92
+ def merge_prefix_into_object_path(prefix, object_path)
93
+ if prefix.include?(?/) && object_path.include?(?/)
94
+ prefixes = []
95
+ prefix_array = File.dirname(prefix).split("/")
96
+ object_path_array = object_path.split("/")[0..-3] # skip model dir & partial
97
+
98
+ prefix_array.each_with_index do |dir, index|
99
+ break if dir == object_path_array[index]
100
+ prefixes << dir
101
+ end
102
+
103
+ (prefixes << object_path).join("/")
104
+ else
105
+ object_path
106
+ end
107
+ end
35
108
  end
36
109
 
37
- def instrument(name, options={})
38
- ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield }
110
+ class RenderedCollection # :nodoc:
111
+ def self.empty(format)
112
+ EmptyCollection.new format
113
+ end
114
+
115
+ attr_reader :rendered_templates
116
+
117
+ def initialize(rendered_templates, spacer)
118
+ @rendered_templates = rendered_templates
119
+ @spacer = spacer
120
+ end
121
+
122
+ def body
123
+ @rendered_templates.map(&:body).join(@spacer.body).html_safe
124
+ end
125
+
126
+ def format
127
+ rendered_templates.first.format
128
+ end
129
+
130
+ class EmptyCollection
131
+ attr_reader :format
132
+
133
+ def initialize(format)
134
+ @format = format
135
+ end
136
+
137
+ def body; nil; end
138
+ end
39
139
  end
40
140
 
41
- def prepend_formats(formats)
42
- formats = Array(formats)
43
- return if formats.empty? || @lookup_context.html_fallback_for_js
44
- @lookup_context.formats = formats | @lookup_context.formats
141
+ class RenderedTemplate # :nodoc:
142
+ attr_reader :body, :template
143
+
144
+ def initialize(body, template)
145
+ @body = body
146
+ @template = template
147
+ end
148
+
149
+ def format
150
+ template.format
151
+ end
152
+
153
+ EMPTY_SPACER = Struct.new(:body).new
45
154
  end
155
+
156
+ private
157
+ NO_DETAILS = {}.freeze
158
+
159
+ def extract_details(options) # :doc:
160
+ details = nil
161
+ @lookup_context.registered_details.each do |key|
162
+ value = options[key]
163
+
164
+ if value
165
+ (details ||= {})[key] = Array(value)
166
+ end
167
+ end
168
+ details || NO_DETAILS
169
+ end
170
+
171
+ def prepend_formats(formats) # :doc:
172
+ formats = Array(formats)
173
+ return if formats.empty? || @lookup_context.html_fallback_for_js
174
+
175
+ @lookup_context.formats = formats | @lookup_context.formats
176
+ end
177
+
178
+ def build_rendered_template(content, template)
179
+ RenderedTemplate.new content, template
180
+ end
181
+
182
+ def build_rendered_collection(templates, spacer)
183
+ RenderedCollection.new templates, spacer
184
+ end
46
185
  end
47
186
  end