Skip to content

Commit

Permalink
create a new renderer instance on calls to for
Browse files Browse the repository at this point in the history
This changes the renderer class to store the controller and defaults as
an instance variable rather than allocating a new class.  You can create
a new renderer with an new env by calling `Renderer#new` or use new
defaults by calling `Renderer#with_defaults` and saving the return value
somewhere.

Also I want to keep the `env` private since I would like to change the
keys in the future.  This commit only translates particular keys that
the user requested.
  • Loading branch information
tenderlove committed Sep 14, 2015
1 parent 8e489db commit 2db7304
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 55 deletions.
85 changes: 46 additions & 39 deletions actionpack/lib/action_controller/renderer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,67 +34,74 @@ module ActionController
# ApplicationController.renderer.new(method: 'post', https: true)
#
class Renderer
class_attribute :controller, :defaults
# Rack environment to render templates in.
attr_reader :env
attr_reader :defaults, :controller

class << self
delegate :render, to: :new
DEFAULTS = {
http_host: 'example.org',
https: false,
method: 'get',
script_name: '',
input: ''
}.freeze

# Create a new renderer class for a specific controller class.
def for(controller)
Class.new self do
self.controller = controller
self.defaults = {
http_host: 'example.org',
https: false,
method: 'get',
script_name: '',
'rack.input' => ''
}
end
end
# 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(env = {})
def initialize(controller, env, defaults)
@controller = controller
@defaults = defaults
@env = normalize_keys(defaults).merge normalize_keys(env)
@env['action_dispatch.routes'] = controller._routes
end

# Render templates with any options from ActionController::Base#render_to_string.
def render(*args)
raise 'missing controller' unless controller?
raise 'missing controller' unless controller

instance = controller.build_with_env(env)
instance = controller.build_with_env(@env)
instance.render_to_string(*args)
end

private
def normalize_keys(env)
http_header_format(env).tap do |new_env|
handle_method_key! new_env
handle_https_key! new_env
end
new_env = {}
env.each_pair { |k,v| new_env[rack_key_for(k)] = rack_value_for(k, v) }
new_env
end

def http_header_format(env)
env.transform_keys do |key|
key.is_a?(Symbol) ? key.to_s.upcase : key
end
end
RACK_KEY_TRANSLATION = {
http_host: 'HTTP_HOST',
https: 'HTTPS',
method: 'REQUEST_METHOD',
script_name: 'SCRIPT_NAME',
input: 'rack.input'
}

def handle_method_key!(env)
if method = env.delete('METHOD')
env['REQUEST_METHOD'] = method.upcase
end
end
IDENTITY = ->(_) { _ }

RACK_VALUE_TRANSLATION = {
https: ->(v) { v ? 'on' : 'off' },
method: ->(v) { v.upcase },
}

def rack_key_for(key); RACK_KEY_TRANSLATION[key]; end

def handle_https_key!(env)
if env.has_key? 'HTTPS'
env['HTTPS'] = env['HTTPS'] ? 'on' : 'off'
end
def rack_value_for(key, value)
RACK_VALUE_TRANSLATION.fetch(key, IDENTITY).call value
end
end
end
19 changes: 3 additions & 16 deletions actionpack/test/controller/renderer_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@ class RendererTest < ActiveSupport::TestCase
end

test 'rendering with defaults' do
renderer = ApplicationController.renderer
renderer.defaults[:https] = true
renderer = ApplicationController.renderer.new https: true
content = renderer.render inline: '<%= request.ssl? %>'

assert_equal 'true', content
Expand All @@ -71,8 +70,8 @@ class RendererTest < ActiveSupport::TestCase
test 'same defaults from the same controller' do
renderer_defaults = ->(controller) { controller.renderer.defaults }

assert renderer_defaults[AccountsController].equal? renderer_defaults[AccountsController]
assert_not renderer_defaults[AccountsController].equal? renderer_defaults[CommentsController]
assert_equal renderer_defaults[AccountsController], renderer_defaults[AccountsController]
assert_equal renderer_defaults[AccountsController], renderer_defaults[CommentsController]
end

test 'rendering with different formats' do
Expand All @@ -87,18 +86,6 @@ class RendererTest < ActiveSupport::TestCase
test 'rendering with helpers' do
assert_equal "<p>1\n<br />2</p>", render[inline: '<%= simple_format "1\n2" %>']
end

test 'rendering from inherited renderer' do
inherited = Class.new ApplicationController.renderer do
defaults[:script_name] = 'script'
def render(options)
super options.merge(locals: { param: :value })
end
end

template = '<%= url_for controller: :foo, action: :bar, param: param %>'
assert_equal 'script/foo/bar?param=value', inherited.render(inline: template)
end

private
def render
Expand Down

0 comments on commit 2db7304

Please sign in to comment.