aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller/metal.rb
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_controller/metal.rb')
-rw-r--r--actionpack/lib/action_controller/metal.rb101
1 files changed, 80 insertions, 21 deletions
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 2281c500c5..e5db31061b 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -12,7 +12,7 @@ module ActionController
#
class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc:
class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
- def initialize(klass, *args)
+ def initialize(klass, *args, &block)
options = args.extract_options!
@only = Array(options.delete(:only)).map(&:to_s)
@except = Array(options.delete(:except)).map(&:to_s)
@@ -36,35 +36,88 @@ module ActionController
action = action.to_s
raise "MiddlewareStack#build requires an app" unless app
- reverse.inject(app) do |a, middleware|
+ middlewares.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.
+ # <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
+ # valid Rack interface without the additional niceties provided by
+ # <tt>ActionController::Base</tt>.
+ #
+ # A sample metal controller might look like this:
+ #
+ # class HelloController < ActionController::Metal
+ # def index
+ # self.response_body = "Hello World!"
+ # end
+ # end
+ #
+ # And then to route requests to your metal controller, you would add
+ # something like this to <tt>config/routes.rb</tt>:
+ #
+ # match 'hello', :to => HelloController.action(:index)
+ #
+ # The +action+ method returns a valid Rack application for the \Rails
+ # router to dispatch to.
+ #
+ # == Rendering Helpers
+ #
+ # <tt>ActionController::Metal</tt> by default provides no utilities for rendering
+ # views, partials, or other responses aside from explicitly calling of
+ # <tt>response_body=</tt>, <tt>content_type=</tt>, and <tt>status=</tt>. To
+ # 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
+ # append_view_path "#{Rails.root}/app/views"
+ #
+ # def index
+ # render "hello/index"
+ # end
+ # end
+ #
+ # == Redirection Helpers
+ #
+ # To add redirection helpers to your metal controller, do the following:
+ #
+ # class HelloController < ActionController::Metal
+ # include ActionController::Redirecting
+ # include Rails.application.routes.url_helpers
+ #
+ # def index
+ # redirect_to root_url
+ # end
+ # end
+ #
+ # == Other Helpers
+ #
+ # You can refer to the modules included in <tt>ActionController::Base</tt> to see
+ # other features you can bring into your metal 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!
- attr_internal :env
+ attr_internal_writer :env
+
+ def env
+ @_env ||= {}
+ end
# 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
+ # <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>.
+ # Namespaces are left out, so Admin::PostsController returns <tt>posts</tt> as well.
#
# ==== Returns
- # String
+ # * <tt>string</tt>
def self.controller_name
@controller_name ||= self.name.demodulize.sub(/Controller$/, '').underscore
end
- # Delegates to the class' #controller_name
+ # Delegates to the class' <tt>controller_name</tt>
def controller_name
self.class.controller_name
end
@@ -81,6 +134,9 @@ module ActionController
def initialize(*)
@_headers = {"Content-Type" => "text/html"}
@_status = 200
+ @_request = nil
+ @_response = nil
+ @_routes = nil
super
end
@@ -112,6 +168,11 @@ module ActionController
headers["Location"] = url
end
+ # basic url_for that can be overridden for more robust functionality
+ def url_for(string)
+ string
+ end
+
def status
@_status
end
@@ -121,12 +182,11 @@ module ActionController
end
def response_body=(val)
- body = val.respond_to?(:each) ? val : [val]
+ body = val.nil? ? nil : (val.respond_to?(:each) ? val : [val])
super body
end
- # :api: private
- def dispatch(name, request)
+ def dispatch(name, request) #:nodoc:
@_request = request
@_env = request.env
@_env['action_controller.instance'] = self
@@ -134,8 +194,7 @@ module ActionController
to_a
end
- # :api: private
- def to_a
+ def to_a #:nodoc:
response ? response.to_a : [status, headers, response_body]
end
@@ -147,8 +206,8 @@ module ActionController
super
end
- def self.use(*args)
- middleware_stack.use(*args)
+ def self.use(*args, &block)
+ middleware_stack.use(*args, &block)
end
def self.middleware
@@ -164,10 +223,10 @@ module ActionController
# for the same action.
#
# ==== Parameters
- # action<#to_s>:: An action name
+ # * <tt>action</tt> - An action name
#
# ==== Returns
- # Proc:: A rack application
+ # * <tt>proc</tt> - A rack application
def self.action(name, klass = ActionDispatch::Request)
middleware_stack.build(name.to_s) do |env|
new.dispatch(name, klass.new(env))