1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/actionpack/lib/action_controller/metal.rb
José Valim 9a93844aba Add :only and :except to controllers MiddlewareStack. This allows
you to do the following:

  class PostsController < ApplicationController
    use AutheMiddleware, :except => [:index, :show]
  end
2010-05-30 15:53:14 +02:00

170 lines
4.7 KiB
Ruby

require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/object/blank'
require 'action_dispatch/middleware/stack'
module ActionController
# 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:
def initialize(klass, *args)
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
reverse.inject(app) do |a, middleware|
middleware.valid?(action) ?
middleware.build(a) : a
end
end
end
# ActionController::Metal provides a way to get a valid Rack application from a controller.
#
# In AbstractController, dispatching is triggered directly by calling #process on a new controller.
# ActionController::Metal provides an #action method that returns a valid Rack application for a
# given action. Other rack builders, such as Rack::Builder, Rack::URLMap, and the Rails router,
# can dispatch directly to the action returned by FooController.action(:index).
class Metal < AbstractController::Base
abstract!
# :api: public
attr_internal :params, :env
# Returns the last part of the controller's name, underscored, without the ending
# "Controller". For instance, MyApp::MyPostsController would return "my_posts" for
# controller_name
#
# ==== Returns
# String
def self.controller_name
@controller_name ||= controller_path.split("/").last
end
# Delegates to the class' #controller_name
def controller_name
self.class.controller_name
end
# The details below can be overridden to support a specific
# Request and Response object. The default ActionController::Base
# implementation includes RackDelegation, which makes a request
# and response object available. You might wish to control the
# environment and response manually for performance reasons.
attr_internal :headers, :response, :request
delegate :session, :to => "@_request"
def initialize(*)
@_headers = {"Content-Type" => "text/html"}
@_status = 200
super
end
# Basic implementations for content_type=, location=, and headers are
# provided to reduce the dependency on the RackDelegation module
# in Renderer and Redirector.
def content_type=(type)
headers["Content-Type"] = type.to_s
end
def content_type
headers["Content-Type"]
end
def location
headers["Location"]
end
def location=(url)
headers["Location"] = url
end
def status
@_status
end
def status=(status)
@_status = Rack::Utils.status_code(status)
end
def response_body=(val)
body = val.respond_to?(:each) ? val : [val]
super body
end
# :api: private
def dispatch(name, request)
@_request = request
@_env = request.env
@_env['action_controller.instance'] = self
process(name)
to_a
end
# :api: private
def to_a
response ? response.to_a : [status, headers, response_body]
end
class_attribute :middleware_stack
self.middleware_stack = ActionController::MiddlewareStack.new
def self.inherited(base)
base.middleware_stack = self.middleware_stack.dup
super
end
def self.use(*args)
middleware_stack.use(*args)
end
def self.middleware
middleware_stack
end
def self.call(env)
action(env['action_dispatch.request.path_parameters'][:action]).call(env)
end
# 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
# action<#to_s>:: An action name
#
# ==== Returns
# Proc:: A rack application
def self.action(name, klass = ActionDispatch::Request)
middleware_stack.build(name.to_s) do |env|
new.dispatch(name, klass.new(env))
end
end
end
end