From ee6e13f3da11b21f3966e009b681d05c65f79c9e Mon Sep 17 00:00:00 2001 From: brainopia Date: Thu, 15 Jan 2015 16:26:50 +0300 Subject: Add `ActionController::Metal#set_request!` Add `ActionController::Metal#set_request!` to set a request on controller instance without calling dispatch. --- actionpack/lib/action_controller/metal.rb | 8 ++++++-- actionpack/lib/action_controller/metal/rack_delegation.rb | 4 ++-- actionpack/test/controller/new_base/bare_metal_test.rb | 9 +++++++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index 993f8e150d..ae111e4951 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -190,11 +190,15 @@ module ActionController end def dispatch(name, request) #:nodoc: + set_request!(request) + process(name) + to_a + end + + def set_request!(request) #:nodoc: @_request = request @_env = request.env @_env['action_controller.instance'] = self - process(name) - to_a end def to_a #:nodoc: diff --git a/actionpack/lib/action_controller/metal/rack_delegation.rb b/actionpack/lib/action_controller/metal/rack_delegation.rb index 545d4a7e6e..82054880e9 100644 --- a/actionpack/lib/action_controller/metal/rack_delegation.rb +++ b/actionpack/lib/action_controller/metal/rack_delegation.rb @@ -8,9 +8,9 @@ module ActionController delegate :headers, :status=, :location=, :content_type=, :status, :location, :content_type, :response_code, :to => "@_response" - def dispatch(action, request) + def set_request!(request) #:nodoc: + super set_response!(request) - super(action, request) end def response_body=(body) diff --git a/actionpack/test/controller/new_base/bare_metal_test.rb b/actionpack/test/controller/new_base/bare_metal_test.rb index 246ba099af..710c428dcc 100644 --- a/actionpack/test/controller/new_base/bare_metal_test.rb +++ b/actionpack/test/controller/new_base/bare_metal_test.rb @@ -31,6 +31,15 @@ module BareMetalTest controller.index assert_equal ["Hello world"], controller.response_body end + + test "connect a request to controller instance without dispatch" do + env = {} + controller = BareController.new + controller.set_request! ActionDispatch::Request.new(env) + assert controller.request + assert controller.response + assert env['action_controller.instance'] + end end class HeadController < ActionController::Metal -- cgit v1.2.3 From 685142e4f46292190cedb1d9c69953a948ea92fc Mon Sep 17 00:00:00 2001 From: brainopia Date: Thu, 15 Jan 2015 16:52:46 +0300 Subject: Support `:assigns` option when rendering with controllers/mailers. --- actionpack/CHANGELOG.md | 4 ++++ actionview/lib/action_view/rendering.rb | 7 +++++-- actionview/test/actionpack/controller/render_test.rb | 9 +++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 6cd0b2d15d..a9e8b82107 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,7 @@ +* Support `:assigns` option when rendering with controllers/mailers. + + *Ravil Bayramgalin* + * Default headers, removed in controller actions, are no longer reapplied on the test response. diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb index abd3b77c67..1e8e7415d1 100644 --- a/actionview/lib/action_view/rendering.rb +++ b/actionview/lib/action_view/rendering.rb @@ -92,12 +92,15 @@ module ActionView # Find and render a template based on the options given. # :api: private def _render_template(options) #:nodoc: - variant = options[:variant] + variant = options.delete(:variant) + assigns = options.delete(:assigns) + context = view_context + context.assign assigns if assigns lookup_context.rendered_format = nil if options[:formats] lookup_context.variants = variant if variant - view_renderer.render(view_context, options) + view_renderer.render(context, options) end # Assign the rendered format to lookup context. diff --git a/actionview/test/actionpack/controller/render_test.rb b/actionview/test/actionpack/controller/render_test.rb index 563caee8a2..94f519e330 100644 --- a/actionview/test/actionpack/controller/render_test.rb +++ b/actionview/test/actionpack/controller/render_test.rb @@ -453,6 +453,10 @@ class TestController < ApplicationController render :text => "foo" end + def render_with_assigns_option + render inline: '<%= @hello %>', assigns: { hello: "world" } + end + def yield_content_for render :action => "content_for", :layout => "yield" end @@ -1102,6 +1106,11 @@ class RenderTest < ActionController::TestCase assert_equal "world", assigns["hello"] end + def test_render_text_with_assigns_option + get :render_with_assigns_option + assert_equal 'world', response.body + end + # :ported: def test_template_with_locals get :render_with_explicit_template_with_locals -- cgit v1.2.3 From 47b6fe06824893f9b0c4628e766f387956cabf22 Mon Sep 17 00:00:00 2001 From: brainopia Date: Thu, 15 Jan 2015 22:35:40 +0300 Subject: Add ActionController#build_with_env To have an easier way to setup a controller instance with custom environment --- actionpack/lib/action_controller/metal/rack_delegation.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/actionpack/lib/action_controller/metal/rack_delegation.rb b/actionpack/lib/action_controller/metal/rack_delegation.rb index 82054880e9..ae9d89cc8c 100644 --- a/actionpack/lib/action_controller/metal/rack_delegation.rb +++ b/actionpack/lib/action_controller/metal/rack_delegation.rb @@ -8,6 +8,12 @@ module ActionController delegate :headers, :status=, :location=, :content_type=, :status, :location, :content_type, :response_code, :to => "@_response" + module ClassMethods + def build_with_env(env = {}) #:nodoc: + new.tap { |c| c.set_request! ActionDispatch::Request.new(env) } + end + end + def set_request!(request) #:nodoc: super set_response!(request) -- cgit v1.2.3 From 801e399e42cab610860e307f2dd77c1edb6e1fac Mon Sep 17 00:00:00 2001 From: brainopia Date: Sun, 18 Jan 2015 03:06:10 +0300 Subject: Add ActionController::Renderer Render arbitrary templates outside of controller actions --- actionpack/CHANGELOG.md | 5 + actionpack/lib/action_controller.rb | 1 + .../lib/action_controller/metal/rendering.rb | 8 ++ actionpack/lib/action_controller/renderer.rb | 103 +++++++++++++++++++++ actionpack/test/controller/renderer_test.rb | 99 ++++++++++++++++++++ 5 files changed, 216 insertions(+) create mode 100644 actionpack/lib/action_controller/renderer.rb create mode 100644 actionpack/test/controller/renderer_test.rb diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index a9e8b82107..d8c26fa281 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,8 @@ +* Add `ActionController::Renderer` to render arbitrary templates + outside controller actions. + + *Ravil Bayramgalin* + * Support `:assigns` option when rendering with controllers/mailers. *Ravil Bayramgalin* diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index e977bbce99..7667e469d3 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -11,6 +11,7 @@ module ActionController autoload :Caching autoload :Metal autoload :Middleware + autoload :Renderer autoload_under "metal" do autoload :Compatibility diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index 7bbff0450a..2370c607ed 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -4,6 +4,14 @@ module ActionController RENDER_FORMATS_IN_PRIORITY = [:body, :text, :plain, :html] + module ClassMethods + # Returns a renderer class (inherited from ActionController::Renderer) + # for the controller. + def renderer + @renderer ||= Renderer.for(self) + end + end + # Before processing, set the request formats in current controller formats. def process_action(*) #:nodoc: self.formats = request.formats.map(&:ref).compact diff --git a/actionpack/lib/action_controller/renderer.rb b/actionpack/lib/action_controller/renderer.rb new file mode 100644 index 0000000000..a122954968 --- /dev/null +++ b/actionpack/lib/action_controller/renderer.rb @@ -0,0 +1,103 @@ +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 + class_attribute :controller, :defaults + # Rack environment to render templates in. + attr_reader :env + + class << self + delegate :render, to: :new + + # 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 + end + + # Accepts a custom Rack environment to render templates in. + # It will be merged with ActionController::Renderer.defaults + def initialize(env = {}) + @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? + + instance = controller.build_with_env(env) + instance.render_to_string(*args) + end + + private + def normalize_keys(env) + env.dup.tap do |new_env| + convert_symbols! new_env + handle_method_key! new_env + handle_https_key! new_env + end + end + + def convert_symbols!(env) + env.keys.each do |key| + if key.is_a? Symbol + value = env.delete key + key = key.to_s.upcase + env[key] = value + end + end + end + + def handle_method_key!(env) + if method = env.delete('METHOD') + env['REQUEST_METHOD'] = method.upcase + end + end + + def handle_https_key!(env) + if env.has_key? 'HTTPS' + env['HTTPS'] = env['HTTPS'] ? 'on' : 'off' + end + end + end +end diff --git a/actionpack/test/controller/renderer_test.rb b/actionpack/test/controller/renderer_test.rb new file mode 100644 index 0000000000..6efc9c8dfd --- /dev/null +++ b/actionpack/test/controller/renderer_test.rb @@ -0,0 +1,99 @@ +require 'abstract_unit' + +class RendererTest < ActiveSupport::TestCase + test 'creating with a controller' do + controller = CommentsController + renderer = ActionController::Renderer.for controller + + assert_equal controller, renderer.controller + end + + test 'creating from a controller' do + controller = AccountsController + renderer = controller.renderer + + assert_equal controller, renderer.controller + end + + test 'rendering with a class renderer' do + renderer = ApplicationController.renderer + content = renderer.render template: 'ruby_template' + + assert_equal 'Hello from Ruby code', content + end + + test 'rendering with an instance renderer' do + renderer = ApplicationController.renderer.new + content = renderer.render file: 'test/hello_world' + + assert_equal 'Hello world!', content + end + + test 'rendering with locals' do + renderer = ApplicationController.renderer + content = renderer.render template: 'test/render_file_with_locals', + locals: { secret: 'bar' } + + assert_equal "The secret is bar\n", content + end + + test 'rendering with assigns' do + renderer = ApplicationController.renderer + content = renderer.render template: 'test/render_file_with_ivar', + assigns: { secret: 'foo' } + + assert_equal "The secret is foo\n", content + end + + test 'rendering with custom env' do + renderer = ApplicationController.renderer.new method: 'post' + content = renderer.render inline: '<%= request.post? %>' + + assert_equal 'true', content + end + + test 'rendering with defaults' do + renderer = ApplicationController.renderer + renderer.defaults[:https] = true + content = renderer.render inline: '<%= request.ssl? %>' + + assert_equal 'true', content + end + + test 'same defaults from the same controller' do + defaults = ->(controller) { controller.renderer.defaults } + + assert defaults[AccountsController].equal? defaults[AccountsController] + assert_not defaults[AccountsController].equal? defaults[CommentsController] + end + + test 'rendering with different formats' do + html = 'Hello world!' + xml = "

Hello world!

\n" + + assert_equal html, render['respond_to/using_defaults'] + assert_equal xml, render['respond_to/using_defaults.xml.builder'] + assert_equal xml, render['respond_to/using_defaults', formats: :xml] + end + + test 'rendering with helpers' do + assert_equal "

1\n
2

", 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 + @render ||= ApplicationController.renderer.method(:render) + end +end -- cgit v1.2.3 From 656628961c009524bd97ec5682b3dab3b850cb8a Mon Sep 17 00:00:00 2001 From: brainopia Date: Thu, 22 Jan 2015 00:23:22 +0300 Subject: Add ActionController::Base.render --- actionpack/CHANGELOG.md | 3 +++ actionpack/lib/action_controller/metal/rendering.rb | 3 +++ actionpack/test/controller/renderer_test.rb | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index d8c26fa281..d545c59afc 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,6 +1,9 @@ * Add `ActionController::Renderer` to render arbitrary templates outside controller actions. + Its functionality is accessible through class methods `render` and + `renderer` of `ActionController::Base`. + *Ravil Bayramgalin* * Support `:assigns` option when rendering with controllers/mailers. diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index 2370c607ed..2d15c39d88 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -5,6 +5,9 @@ module ActionController RENDER_FORMATS_IN_PRIORITY = [:body, :text, :plain, :html] module ClassMethods + # Documentation at ActionController::Renderer#render + delegate :render, to: :renderer + # Returns a renderer class (inherited from ActionController::Renderer) # for the controller. def renderer diff --git a/actionpack/test/controller/renderer_test.rb b/actionpack/test/controller/renderer_test.rb index 6efc9c8dfd..6d5508323b 100644 --- a/actionpack/test/controller/renderer_test.rb +++ b/actionpack/test/controller/renderer_test.rb @@ -29,6 +29,10 @@ class RendererTest < ActiveSupport::TestCase assert_equal 'Hello world!', content end + test 'rendering with a controller class' do + assert_equal 'Hello world!', ApplicationController.render('test/hello_world') + end + test 'rendering with locals' do renderer = ApplicationController.renderer content = renderer.render template: 'test/render_file_with_locals', -- cgit v1.2.3 From 3011b64184747f4d26e5c5ca32170be370bc0a1c Mon Sep 17 00:00:00 2001 From: brainopia Date: Wed, 21 Jan 2015 22:59:30 +0300 Subject: Add ApplicationController.renderer initializer [ci skip] --- .../config/initializers/application_controller_renderer.rb | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb new file mode 100644 index 0000000000..ea930f54da --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb @@ -0,0 +1,6 @@ +## Change renderer defaults here. +# +# ApplicationController.renderer.defaults.merge!( +# http_host: 'example.org', +# https: false +# ) -- cgit v1.2.3