# frozen_string_literal: true
require "action_view/view_paths"
module ActionView
# This is a class to fix I18n global state. Whenever you provide I18n.locale during a request,
# it will trigger the lookup_context and consequently expire the cache.
class I18nProxy < ::I18n::Config # :nodoc:
attr_reader :original_config, :lookup_context
def initialize(original_config, lookup_context)
original_config = original_config.original_config if original_config.respond_to?(:original_config)
@original_config = original_config
@lookup_context = lookup_context
end
def locale
@original_config.locale
end
def locale=(value)
@lookup_context.locale = value
end
end
module Rendering
extend ActiveSupport::Concern
include ActionView::ViewPaths
attr_internal_reader :rendered_format
def initialize
@_rendered_format = nil
super
end
# Override process to set up I18n proxy.
def process(...) # :nodoc:
old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
super
ensure
I18n.config = old_config
end
module ClassMethods
def _routes
end
def _helpers
end
def inherit_view_context_class?
superclass.respond_to?(:view_context_class) &&
supports_path? == superclass.supports_path? &&
_routes.equal?(superclass._routes) &&
_helpers.equal?(superclass._helpers)
end
def build_view_context_class(klass, supports_path, routes, helpers)
if inherit_view_context_class?
return superclass.view_context_class
end
Class.new(klass) do
if routes
include routes.url_helpers(supports_path)
include routes.mounted_helpers
end
if helpers
include helpers
end
end
end
def eager_load!
super
view_context_class
nil
end
def view_context_class
klass = ActionView::LookupContext::DetailsKey.view_context_class
@view_context_class ||= build_view_context_class(klass, supports_path?, _routes, _helpers)
if klass.changed?(@view_context_class)
@view_context_class = build_view_context_class(klass, supports_path?, _routes, _helpers)
end
@view_context_class
end
end
def view_context_class
self.class.view_context_class
end
# An instance of a view class. The default view class is ActionView::Base.
#
# The view class must have the following methods:
#
# * View.new(lookup_context, assigns, controller) — Create a new
# ActionView instance for a controller and we can also pass the arguments.
#
# * View#render(option) — Returns String with the rendered template.
#
# Override this method in a module to change the default behavior.
def view_context
view_context_class.new(lookup_context, view_assigns, self)
end
# Returns an object that is able to render templates.
def view_renderer # :nodoc:
# Lifespan: Per controller
@_view_renderer ||= ActionView::Renderer.new(lookup_context)
end
def render_to_body(options = {})
_process_options(options)
_process_render_template_options(options)
_render_template(options)
end
private
# Find and render a template based on the options given.
def _render_template(options)
variant = options.delete(:variant)
assigns = options.delete(:assigns)
context = view_context
context.assign assigns if assigns
lookup_context.variants = variant if variant
rendered_template = context.in_rendering_context(options) do |renderer|
renderer.render_to_object(context, options)
end
rendered_format = rendered_template.format || lookup_context.formats.first
@_rendered_format = Template::Types[rendered_format]
rendered_template.body
end
# Assign the rendered format to look up context.
def _process_format(format)
super
lookup_context.formats = [format.to_sym] if format.to_sym
end
# Normalize args by converting render "foo" to render action: "foo" and
# render "foo/bar" to render template: "foo/bar".
def _normalize_args(action = nil, options = {})
options = super(action, options)
case action
when NilClass
when Hash
options = action
when String, Symbol
action = action.to_s
key = action.include?(?/) ? :template : :action
options[key] = action
else
if action.respond_to?(:permitted?) && action.permitted?
options = action
elsif action.respond_to?(:render_in)
options[:renderable] = action
else
options[:partial] = action
end
end
options
end
# Normalize options.
def _process_render_template_options(options)
if options[:partial] == true
options[:partial] = action_name
end
if !options.keys.intersect?([:partial, :file, :template])
options[:prefixes] ||= _prefixes
end
options[:template] ||= (options[:action] || action_name).to_s
end
end
end