2010-01-31 21:32:28 -05:00
|
|
|
require 'active_support/core_ext/class/attribute'
|
2010-05-30 09:53:14 -04:00
|
|
|
require 'active_support/core_ext/object/blank'
|
|
|
|
require 'action_dispatch/middleware/stack'
|
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
|
|
|
|
# use AuthenticationMiddleware, :except => [:index, :show]
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc:
|
|
|
|
class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
|
2010-09-11 17:30:21 -04:00
|
|
|
def initialize(klass, *args, &block)
|
2010-05-30 09:53:14 -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?
|
|
|
|
super
|
|
|
|
end
|
|
|
|
|
|
|
|
def valid?(action)
|
|
|
|
if @only.present?
|
|
|
|
@only.include?(action)
|
|
|
|
elsif @except.present?
|
|
|
|
!@except.include?(action)
|
|
|
|
else
|
|
|
|
true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def build(action, app=nil, &block)
|
|
|
|
app ||= block
|
|
|
|
action = action.to_s
|
|
|
|
raise "MiddlewareStack#build requires an app" unless app
|
|
|
|
|
2011-03-02 16:24:56 -05:00
|
|
|
middlewares.reverse.inject(app) do |a, middleware|
|
2010-05-30 09:53:14 -04:00
|
|
|
middleware.valid?(action) ?
|
|
|
|
middleware.build(a) : a
|
|
|
|
end
|
|
|
|
end
|
|
|
|
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>:
|
|
|
|
#
|
2011-02-02 17:03:17 -05:00
|
|
|
# match '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
|
|
|
|
# include ActionController::Rendering
|
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
|
|
|
|
2010-07-14 03:17:24 -04:00
|
|
|
attr_internal_writer :env
|
|
|
|
|
|
|
|
def env
|
|
|
|
@_env ||= {}
|
|
|
|
end
|
2009-05-01 20:27:44 -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
|
2010-06-22 14:47:31 -04:00
|
|
|
@controller_name ||= self.name.demodulize.sub(/Controller$/, '').underscore
|
2009-05-01 20:27:44 -04:00
|
|
|
end
|
|
|
|
|
2010-08-26 16:32:40 -04: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
|
|
|
|
2009-05-27 04:40:43 -04:00
|
|
|
# The details below can be overridden to support a specific
|
|
|
|
# Request and Response object. The default ActionController::Base
|
2009-12-20 21:05:26 -05:00
|
|
|
# implementation includes RackDelegation, which makes a request
|
2009-05-27 04:40:43 -04:00
|
|
|
# and response object available. You might wish to control the
|
|
|
|
# environment and response manually for performance reasons.
|
2009-05-20 19:38:48 -04:00
|
|
|
|
2010-03-17 02:24:00 -04:00
|
|
|
attr_internal :headers, :response, :request
|
2010-03-08 19:49:47 -05:00
|
|
|
delegate :session, :to => "@_request"
|
2009-05-27 04:40:43 -04:00
|
|
|
|
2011-04-15 17:34:14 -04:00
|
|
|
def initialize
|
2010-03-18 20:32:53 -04:00
|
|
|
@_headers = {"Content-Type" => "text/html"}
|
|
|
|
@_status = 200
|
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
|
|
|
|
|
2009-06-10 18:27:53 -04:00
|
|
|
# Basic implementations for content_type=, location=, and headers are
|
2009-12-20 21:05:26 -05:00
|
|
|
# provided to reduce the dependency on the RackDelegation module
|
|
|
|
# in Renderer and Redirector.
|
2010-09-24 15:52:47 -04:00
|
|
|
|
2009-05-27 04:40:43 -04:00
|
|
|
def content_type=(type)
|
|
|
|
headers["Content-Type"] = type.to_s
|
|
|
|
end
|
|
|
|
|
2010-02-16 13:45:59 -05:00
|
|
|
def content_type
|
|
|
|
headers["Content-Type"]
|
|
|
|
end
|
|
|
|
|
|
|
|
def location
|
|
|
|
headers["Location"]
|
|
|
|
end
|
|
|
|
|
2009-05-27 04:40:43 -04:00
|
|
|
def location=(url)
|
|
|
|
headers["Location"] = url
|
2009-05-12 19:21:34 -04:00
|
|
|
end
|
2009-05-28 10:49:02 -04:00
|
|
|
|
2010-10-10 03:51:52 -04:00
|
|
|
# basic url_for that can be overridden for more robust functionality
|
|
|
|
def url_for(string)
|
|
|
|
string
|
|
|
|
end
|
|
|
|
|
2010-03-17 02:24:00 -04:00
|
|
|
def status
|
|
|
|
@_status
|
|
|
|
end
|
|
|
|
|
2009-12-20 21:50:47 -05:00
|
|
|
def status=(status)
|
2009-12-22 17:08:03 -05:00
|
|
|
@_status = Rack::Utils.status_code(status)
|
2009-12-20 21:50:47 -05:00
|
|
|
end
|
|
|
|
|
2010-03-18 20:32:53 -04:00
|
|
|
def response_body=(val)
|
2011-11-07 02:24:54 -05:00
|
|
|
body = if val.is_a?(String)
|
|
|
|
[val]
|
|
|
|
elsif val.nil? || val.respond_to?(:each)
|
|
|
|
val
|
|
|
|
else
|
|
|
|
[val]
|
|
|
|
end
|
2010-03-18 20:32:53 -04:00
|
|
|
super body
|
|
|
|
end
|
|
|
|
|
2010-08-26 16:32:40 -04:00
|
|
|
def dispatch(name, request) #:nodoc:
|
2010-03-08 19:49:47 -05:00
|
|
|
@_request = request
|
|
|
|
@_env = request.env
|
2010-01-20 09:59:26 -05:00
|
|
|
@_env['action_controller.instance'] = self
|
2009-05-02 05:15:09 -04:00
|
|
|
process(name)
|
2009-08-06 19:05:14 -04:00
|
|
|
to_a
|
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:
|
2009-08-25 15:14:31 -04:00
|
|
|
response ? response.to_a : [status, headers, response_body]
|
|
|
|
end
|
|
|
|
|
2010-01-31 21:32:28 -05:00
|
|
|
class_attribute :middleware_stack
|
2010-05-30 09:53:14 -04:00
|
|
|
self.middleware_stack = ActionController::MiddlewareStack.new
|
2010-01-31 21:32:28 -05:00
|
|
|
|
2011-03-28 23:48:17 -04:00
|
|
|
def self.inherited(base) #nodoc:
|
2010-05-30 09:53:14 -04:00
|
|
|
base.middleware_stack = self.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
|
|
|
|
2011-03-28 23:48:17 -04:00
|
|
|
# Adds given middleware class and its args to bottom of middleware_stack
|
2010-09-11 17:30:21 -04:00
|
|
|
def self.use(*args, &block)
|
|
|
|
middleware_stack.use(*args, &block)
|
2009-09-12 14:51:15 -04:00
|
|
|
end
|
|
|
|
|
2011-03-28 23:48:17 -04:00
|
|
|
# Alias for middleware_stack
|
2009-09-12 14:51:15 -04:00
|
|
|
def self.middleware
|
|
|
|
middleware_stack
|
|
|
|
end
|
|
|
|
|
2011-03-28 23:48:17 -04:00
|
|
|
# Makes the controller a rack endpoint that points to the action in
|
|
|
|
# the given env's action_dispatch.request.path_parameters key.
|
2009-10-20 17:03:55 -04:00
|
|
|
def self.call(env)
|
|
|
|
action(env['action_dispatch.request.path_parameters'][:action]).call(env)
|
|
|
|
end
|
|
|
|
|
2009-06-10 18:27:53 -04:00
|
|
|
# Return a rack endpoint for the given action. Memoize the endpoint, so
|
|
|
|
# multiple calls into MyController.action will return the same object
|
|
|
|
# for the same action.
|
|
|
|
#
|
|
|
|
# ==== Parameters
|
2010-08-26 16:32:40 -04:00
|
|
|
# * <tt>action</tt> - An action name
|
2009-06-10 18:27:53 -04:00
|
|
|
#
|
|
|
|
# ==== Returns
|
2010-08-26 16:32:40 -04:00
|
|
|
# * <tt>proc</tt> - A rack application
|
2010-03-08 19:49:47 -05:00
|
|
|
def self.action(name, klass = ActionDispatch::Request)
|
2010-05-30 09:53:14 -04:00
|
|
|
middleware_stack.build(name.to_s) do |env|
|
2010-03-08 19:49:47 -05:00
|
|
|
new.dispatch(name, klass.new(env))
|
|
|
|
end
|
2009-08-25 15:14:31 -04:00
|
|
|
end
|
2009-05-01 20:27:44 -04:00
|
|
|
end
|
|
|
|
end
|