2017-07-24 16:20:53 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2016-08-06 12:51:43 -04:00
|
|
|
require "active_support/core_ext/array/extract_options"
|
|
|
|
require "action_dispatch/middleware/stack"
|
|
|
|
require "action_dispatch/http/request"
|
|
|
|
require "action_dispatch/http/response"
|
2009-09-14 15:47:31 -04:00
|
|
|
|
2009-05-01 20:27:44 -04:00
|
|
|
module ActionController
|
2010-05-30 09:53:14 -04:00
|
|
|
# Extend ActionDispatch middleware stack to make it aware of options
|
|
|
|
# allowing the following syntax in controllers:
|
|
|
|
#
|
|
|
|
# class PostsController < ApplicationController
|
2012-10-27 16:05:27 -04:00
|
|
|
# use AuthenticationMiddleware, except: [:index, :show]
|
2010-05-30 09:53:14 -04:00
|
|
|
# end
|
|
|
|
#
|
|
|
|
class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc:
|
|
|
|
class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
|
2019-12-28 04:09:41 -05:00
|
|
|
def initialize(klass, args, actions, strategy, block)
|
2015-08-07 16:35:31 -04:00
|
|
|
@actions = actions
|
|
|
|
@strategy = strategy
|
2019-12-28 04:09:41 -05:00
|
|
|
super(klass, args, block)
|
2010-05-30 09:53:14 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def valid?(action)
|
2015-08-07 16:35:31 -04:00
|
|
|
@strategy.call @actions, action
|
2010-05-30 09:53:14 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-02-02 21:35:05 -05:00
|
|
|
def build(action, app = nil, &block)
|
2010-05-30 09:53:14 -04:00
|
|
|
action = action.to_s
|
|
|
|
|
2019-02-02 21:35:05 -05:00
|
|
|
middlewares.reverse.inject(app || block) do |a, middleware|
|
2013-04-20 07:11:44 -04:00
|
|
|
middleware.valid?(action) ? middleware.build(a) : a
|
2010-05-30 09:53:14 -04:00
|
|
|
end
|
|
|
|
end
|
2015-08-07 15:03:13 -04:00
|
|
|
|
|
|
|
private
|
2016-08-06 13:55:02 -04:00
|
|
|
INCLUDE = ->(list, action) { list.include? action }
|
|
|
|
EXCLUDE = ->(list, action) { !list.include? action }
|
|
|
|
NULL = ->(list, action) { true }
|
2015-08-07 16:35:31 -04:00
|
|
|
|
2019-12-28 04:09:41 -05:00
|
|
|
def build_middleware(klass, args, block)
|
2016-08-06 13:55:02 -04:00
|
|
|
options = args.extract_options!
|
|
|
|
only = Array(options.delete(:only)).map(&:to_s)
|
|
|
|
except = Array(options.delete(:except)).map(&:to_s)
|
|
|
|
args << options unless options.empty?
|
2015-08-07 15:03:13 -04:00
|
|
|
|
2016-08-06 13:55:02 -04:00
|
|
|
strategy = NULL
|
|
|
|
list = nil
|
2015-08-07 16:35:31 -04:00
|
|
|
|
2016-08-06 13:55:02 -04:00
|
|
|
if only.any?
|
|
|
|
strategy = INCLUDE
|
|
|
|
list = only
|
|
|
|
elsif except.any?
|
|
|
|
strategy = EXCLUDE
|
|
|
|
list = except
|
|
|
|
end
|
2015-08-07 16:35:31 -04:00
|
|
|
|
2019-12-28 04:09:41 -05:00
|
|
|
Middleware.new(klass, args, list, strategy, block)
|
2016-08-06 13:55:02 -04:00
|
|
|
end
|
2010-05-30 09:53:14 -04:00
|
|
|
end
|
|
|
|
|
2011-02-02 17:03:17 -05:00
|
|
|
# <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
|
2011-02-02 10:02:28 -05:00
|
|
|
# valid Rack interface without the additional niceties provided by
|
2011-02-02 17:03:17 -05:00
|
|
|
# <tt>ActionController::Base</tt>.
|
2011-02-02 10:02:28 -05:00
|
|
|
#
|
2011-02-02 17:03:17 -05:00
|
|
|
# A sample metal controller might look like this:
|
2011-02-02 10:02:28 -05:00
|
|
|
#
|
|
|
|
# class HelloController < ActionController::Metal
|
|
|
|
# def index
|
|
|
|
# self.response_body = "Hello World!"
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
2011-02-02 17:03:17 -05:00
|
|
|
# And then to route requests to your metal controller, you would add
|
2011-02-02 10:02:28 -05:00
|
|
|
# something like this to <tt>config/routes.rb</tt>:
|
|
|
|
#
|
2013-04-16 00:15:45 -04:00
|
|
|
# get 'hello', to: HelloController.action(:index)
|
2011-02-02 10:02:28 -05:00
|
|
|
#
|
2011-02-02 17:03:17 -05:00
|
|
|
# The +action+ method returns a valid Rack application for the \Rails
|
2011-02-02 10:02:28 -05:00
|
|
|
# router to dispatch to.
|
|
|
|
#
|
|
|
|
# == Rendering Helpers
|
|
|
|
#
|
2011-02-02 17:03:17 -05:00
|
|
|
# <tt>ActionController::Metal</tt> by default provides no utilities for rendering
|
2011-02-02 10:02:28 -05:00
|
|
|
# views, partials, or other responses aside from explicitly calling of
|
2011-02-02 17:03:17 -05:00
|
|
|
# <tt>response_body=</tt>, <tt>content_type=</tt>, and <tt>status=</tt>. To
|
2011-02-02 10:02:28 -05:00
|
|
|
# add the render helpers you're used to having in a normal controller, you
|
|
|
|
# can do the following:
|
|
|
|
#
|
|
|
|
# class HelloController < ActionController::Metal
|
2014-04-15 16:57:42 -04:00
|
|
|
# include AbstractController::Rendering
|
|
|
|
# include ActionView::Layouts
|
2011-02-02 17:03:17 -05:00
|
|
|
# append_view_path "#{Rails.root}/app/views"
|
2011-02-02 10:02:28 -05:00
|
|
|
#
|
|
|
|
# def index
|
|
|
|
# render "hello/index"
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# == Redirection Helpers
|
|
|
|
#
|
2011-02-02 17:03:17 -05:00
|
|
|
# To add redirection helpers to your metal controller, do the following:
|
2011-02-02 10:02:28 -05:00
|
|
|
#
|
|
|
|
# class HelloController < ActionController::Metal
|
|
|
|
# include ActionController::Redirecting
|
2011-02-02 17:27:27 -05:00
|
|
|
# include Rails.application.routes.url_helpers
|
2011-02-02 10:02:28 -05:00
|
|
|
#
|
|
|
|
# def index
|
2011-02-02 17:27:27 -05:00
|
|
|
# redirect_to root_url
|
2011-02-02 10:02:28 -05:00
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# == Other Helpers
|
|
|
|
#
|
2011-02-02 17:27:27 -05:00
|
|
|
# You can refer to the modules included in <tt>ActionController::Base</tt> to see
|
|
|
|
# other features you can bring into your metal controller.
|
2009-06-10 18:27:53 -04:00
|
|
|
#
|
2009-08-06 18:50:22 -04:00
|
|
|
class Metal < AbstractController::Base
|
2009-05-01 20:27:44 -04:00
|
|
|
abstract!
|
2009-05-28 10:49:02 -04:00
|
|
|
|
2009-06-10 18:27:53 -04:00
|
|
|
# Returns the last part of the controller's name, underscored, without the ending
|
2010-08-26 16:32:40 -04:00
|
|
|
# <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>.
|
|
|
|
# Namespaces are left out, so Admin::PostsController returns <tt>posts</tt> as well.
|
2009-06-10 18:27:53 -04:00
|
|
|
#
|
|
|
|
# ==== Returns
|
2010-08-26 16:32:40 -04:00
|
|
|
# * <tt>string</tt>
|
2009-05-01 20:27:44 -04:00
|
|
|
def self.controller_name
|
2020-08-27 16:45:15 -04:00
|
|
|
@controller_name ||= (name.demodulize.delete_suffix("Controller").underscore unless anonymous?)
|
2009-05-01 20:27:44 -04:00
|
|
|
end
|
|
|
|
|
2015-08-25 21:35:44 -04:00
|
|
|
def self.make_response!(request)
|
2017-03-29 17:36:54 -04:00
|
|
|
ActionDispatch::Response.new.tap do |res|
|
2015-08-25 21:35:44 -04:00
|
|
|
res.request = request
|
|
|
|
end
|
|
|
|
end
|
2015-08-26 14:52:55 -04:00
|
|
|
|
2020-10-27 12:48:55 -04:00
|
|
|
def self.action_encoding_template(action) # :nodoc:
|
2016-12-21 14:21:01 -05:00
|
|
|
false
|
2016-08-09 13:35:59 -04:00
|
|
|
end
|
|
|
|
|
2017-03-11 08:17:44 -05:00
|
|
|
# Delegates to the class' <tt>controller_name</tt>.
|
2009-05-28 10:49:02 -04:00
|
|
|
def controller_name
|
|
|
|
self.class.controller_name
|
|
|
|
end
|
2009-05-01 20:27:44 -04:00
|
|
|
|
2015-08-25 21:35:44 -04:00
|
|
|
attr_internal :response, :request
|
2016-08-06 13:35:13 -04:00
|
|
|
delegate :session, to: "@_request"
|
2015-08-26 14:52:55 -04:00
|
|
|
delegate :headers, :status=, :location=, :content_type=,
|
2019-04-17 02:37:16 -04:00
|
|
|
:status, :location, :content_type, :media_type, to: "@_response"
|
2009-05-27 04:40:43 -04:00
|
|
|
|
2011-04-15 17:34:14 -04:00
|
|
|
def initialize
|
2010-09-24 15:52:47 -04:00
|
|
|
@_request = nil
|
|
|
|
@_response = nil
|
2010-09-27 18:39:10 -04:00
|
|
|
@_routes = nil
|
2009-05-27 04:40:43 -04:00
|
|
|
super
|
|
|
|
end
|
|
|
|
|
2010-06-19 11:19:00 -04:00
|
|
|
def params
|
|
|
|
@_params ||= request.parameters
|
|
|
|
end
|
|
|
|
|
|
|
|
def params=(val)
|
|
|
|
@_params = val
|
|
|
|
end
|
|
|
|
|
2015-08-26 14:52:55 -04:00
|
|
|
alias :response_code :status # :nodoc:
|
2009-05-28 10:49:02 -04:00
|
|
|
|
2015-11-17 21:52:16 -05:00
|
|
|
# Basic url_for that can be overridden for more robust functionality.
|
2010-10-10 03:51:52 -04:00
|
|
|
def url_for(string)
|
|
|
|
string
|
|
|
|
end
|
|
|
|
|
2012-01-19 15:25:52 -05:00
|
|
|
def response_body=(body)
|
|
|
|
body = [body] unless body.nil? || body.respond_to?(:each)
|
2015-10-05 19:50:37 -04:00
|
|
|
response.reset_body!
|
2016-01-26 19:15:09 -05:00
|
|
|
return unless body
|
2016-02-17 17:00:10 -05:00
|
|
|
response.body = body
|
2012-01-19 15:25:52 -05:00
|
|
|
super
|
2010-03-18 20:32:53 -04:00
|
|
|
end
|
2014-10-19 12:36:53 -04:00
|
|
|
|
2014-08-02 08:05:26 -04:00
|
|
|
# Tests if render or redirect has already happened.
|
2012-01-19 13:52:10 -05:00
|
|
|
def performed?
|
2015-08-26 13:29:24 -04:00
|
|
|
response_body || response.committed?
|
2012-01-19 13:52:10 -05:00
|
|
|
end
|
|
|
|
|
2015-08-25 21:35:44 -04:00
|
|
|
def dispatch(name, request, response) #:nodoc:
|
2015-01-15 08:26:50 -05:00
|
|
|
set_request!(request)
|
2015-08-25 21:35:44 -04:00
|
|
|
set_response!(response)
|
2015-01-15 08:26:50 -05:00
|
|
|
process(name)
|
2015-09-25 18:35:34 -04:00
|
|
|
request.commit_flash
|
2015-01-15 08:26:50 -05:00
|
|
|
to_a
|
|
|
|
end
|
|
|
|
|
2015-08-25 21:35:44 -04:00
|
|
|
def set_response!(response) # :nodoc:
|
|
|
|
@_response = response
|
|
|
|
end
|
|
|
|
|
2015-01-15 08:26:50 -05:00
|
|
|
def set_request!(request) #:nodoc:
|
2010-03-08 19:49:47 -05:00
|
|
|
@_request = request
|
2015-08-07 19:00:57 -04:00
|
|
|
@_request.controller_instance = self
|
2009-05-02 05:15:09 -04:00
|
|
|
end
|
2009-05-28 10:49:02 -04:00
|
|
|
|
2010-08-26 16:32:40 -04:00
|
|
|
def to_a #:nodoc:
|
2015-08-26 13:26:21 -04:00
|
|
|
response.to_a
|
2009-08-25 15:14:31 -04:00
|
|
|
end
|
|
|
|
|
2015-08-26 14:52:55 -04:00
|
|
|
def reset_session
|
|
|
|
@_request.reset_session
|
|
|
|
end
|
|
|
|
|
2017-05-29 12:01:50 -04:00
|
|
|
class_attribute :middleware_stack, default: ActionController::MiddlewareStack.new
|
2010-01-31 21:32:28 -05:00
|
|
|
|
2012-09-17 02:15:03 -04:00
|
|
|
def self.inherited(base) # :nodoc:
|
2012-09-17 04:42:22 -04:00
|
|
|
base.middleware_stack = middleware_stack.dup
|
2010-03-03 03:25:41 -05:00
|
|
|
super
|
2010-01-31 21:32:28 -05:00
|
|
|
end
|
2009-09-12 14:51:15 -04:00
|
|
|
|
2019-12-26 04:30:11 -05:00
|
|
|
class << self
|
|
|
|
# Pushes the given Rack middleware and its arguments to the bottom of the
|
|
|
|
# middleware stack.
|
|
|
|
def use(*args, &block)
|
2019-12-26 03:59:13 -05:00
|
|
|
middleware_stack.use(*args, &block)
|
|
|
|
end
|
2019-12-26 04:30:11 -05:00
|
|
|
ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
|
2009-09-12 14:51:15 -04:00
|
|
|
end
|
|
|
|
|
2012-09-17 02:15:03 -04:00
|
|
|
# Alias for +middleware_stack+.
|
2009-09-12 14:51:15 -04:00
|
|
|
def self.middleware
|
|
|
|
middleware_stack
|
|
|
|
end
|
|
|
|
|
2012-09-17 02:15:03 -04:00
|
|
|
# Returns a Rack endpoint for the given action name.
|
2015-08-07 13:45:55 -04:00
|
|
|
def self.action(name)
|
2018-01-05 11:24:39 -05:00
|
|
|
app = lambda { |env|
|
|
|
|
req = ActionDispatch::Request.new(env)
|
|
|
|
res = make_response! req
|
|
|
|
new.dispatch(name, req, res)
|
|
|
|
}
|
|
|
|
|
2014-05-26 20:30:17 -04:00
|
|
|
if middleware_stack.any?
|
2018-01-05 11:24:39 -05:00
|
|
|
middleware_stack.build(name, app)
|
2014-05-26 20:30:17 -04:00
|
|
|
else
|
2018-01-05 11:24:39 -05:00
|
|
|
app
|
2010-03-08 19:49:47 -05:00
|
|
|
end
|
2009-08-25 15:14:31 -04:00
|
|
|
end
|
2015-08-25 19:25:52 -04:00
|
|
|
|
2017-03-11 08:17:44 -05:00
|
|
|
# Direct dispatch to the controller. Instantiates the controller, then
|
2015-08-25 19:25:52 -04:00
|
|
|
# executes the action named +name+.
|
2015-08-25 21:35:44 -04:00
|
|
|
def self.dispatch(name, req, res)
|
2015-08-25 19:25:52 -04:00
|
|
|
if middleware_stack.any?
|
2015-08-25 21:35:44 -04:00
|
|
|
middleware_stack.build(name) { |env| new.dispatch(name, req, res) }.call req.env
|
2015-08-25 19:25:52 -04:00
|
|
|
else
|
2015-08-25 21:35:44 -04:00
|
|
|
new.dispatch(name, req, res)
|
2015-08-25 19:25:52 -04:00
|
|
|
end
|
|
|
|
end
|
2009-05-01 20:27:44 -04:00
|
|
|
end
|
|
|
|
end
|