mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
ec6638a237
This commit is to abstract the code away from the env hash. It no longer needs to have the routes key hard coded.
111 lines
3.3 KiB
Ruby
111 lines
3.3 KiB
Ruby
require 'active_support/core_ext/hash/keys'
|
|
|
|
module ActionController
|
|
# ActionController::Renderer allows to render arbitrary templates
|
|
# without requirement of being in controller actions.
|
|
#
|
|
# You get a concrete renderer class by invoking ActionController::Base#renderer.
|
|
# For example,
|
|
#
|
|
# ApplicationController.renderer
|
|
#
|
|
# It allows you to call method #render directly.
|
|
#
|
|
# ApplicationController.renderer.render template: '...'
|
|
#
|
|
# You can use a shortcut on controller to replace previous example with:
|
|
#
|
|
# ApplicationController.render template: '...'
|
|
#
|
|
# #render method allows you to use any options as when rendering in controller.
|
|
# For example,
|
|
#
|
|
# FooController.render :action, locals: { ... }, assigns: { ... }
|
|
#
|
|
# The template will be rendered in a Rack environment which is accessible through
|
|
# ActionController::Renderer#env. You can set it up in two ways:
|
|
#
|
|
# * by changing renderer defaults, like
|
|
#
|
|
# ApplicationController.renderer.defaults # => hash with default Rack environment
|
|
#
|
|
# * by initializing an instance of renderer by passing it a custom environment.
|
|
#
|
|
# ApplicationController.renderer.new(method: 'post', https: true)
|
|
#
|
|
class Renderer
|
|
attr_reader :defaults, :controller
|
|
|
|
DEFAULTS = {
|
|
http_host: 'example.org',
|
|
https: false,
|
|
method: 'get',
|
|
script_name: '',
|
|
input: ''
|
|
}.freeze
|
|
|
|
# Create a new renderer instance for a specific controller class.
|
|
def self.for(controller, env = {}, defaults = DEFAULTS)
|
|
new(controller, env, defaults)
|
|
end
|
|
|
|
# Create a new renderer for the same controller but with a new env.
|
|
def new(env = {})
|
|
self.class.new controller, env, defaults
|
|
end
|
|
|
|
# Create a new renderer for the same controller but with new defaults.
|
|
def with_defaults(defaults)
|
|
self.class.new controller, env, self.defaults.merge(defaults)
|
|
end
|
|
|
|
# Accepts a custom Rack environment to render templates in.
|
|
# It will be merged with ActionController::Renderer.defaults
|
|
def initialize(controller, env, defaults)
|
|
@controller = controller
|
|
@defaults = defaults
|
|
@env = normalize_keys defaults.merge(env)
|
|
end
|
|
|
|
# Render templates with any options from ActionController::Base#render_to_string.
|
|
def render(*args)
|
|
raise 'missing controller' unless controller
|
|
|
|
request = ActionDispatch::Request.new @env
|
|
request.routes = controller._routes
|
|
|
|
instance = controller.new
|
|
instance.set_request! request
|
|
instance.set_response! controller.make_response!(request)
|
|
instance.render_to_string(*args)
|
|
end
|
|
|
|
private
|
|
def normalize_keys(env)
|
|
new_env = {}
|
|
env.each_pair { |k,v| new_env[rack_key_for(k)] = rack_value_for(k, v) }
|
|
new_env
|
|
end
|
|
|
|
RACK_KEY_TRANSLATION = {
|
|
http_host: 'HTTP_HOST',
|
|
https: 'HTTPS',
|
|
method: 'REQUEST_METHOD',
|
|
script_name: 'SCRIPT_NAME',
|
|
input: 'rack.input'
|
|
}
|
|
|
|
IDENTITY = ->(_) { _ }
|
|
|
|
RACK_VALUE_TRANSLATION = {
|
|
https: ->(v) { v ? 'on' : 'off' },
|
|
method: ->(v) { v.upcase },
|
|
}
|
|
|
|
def rack_key_for(key); RACK_KEY_TRANSLATION[key]; end
|
|
|
|
def rack_value_for(key, value)
|
|
RACK_VALUE_TRANSLATION.fetch(key, IDENTITY).call value
|
|
end
|
|
end
|
|
end
|