Skip to content

Commit

Permalink
ActionController::Metal can be a middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
Yehuda Katz + Carl Lerche committed Aug 25, 2009
1 parent 09fde64 commit c7ba911
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 13 deletions.
40 changes: 35 additions & 5 deletions actionpack/lib/action_controller/metal.rb
Expand Up @@ -47,7 +47,7 @@ def controller_path
# and response object available. You might wish to control the
# environment and response manually for performance reasons.

attr_internal :status, :headers, :content_type
attr_internal :status, :headers, :content_type, :app, :response

def initialize(*)
@_headers = {}
Expand Down Expand Up @@ -75,7 +75,34 @@ def call(name, env)

# :api: private
def to_a
[status, headers, response_body]
response ? response.to_a : [status, headers, response_body]
end

class ActionEndpoint
def initialize(controller, action)
@controller, @action = controller, action
end

def call(env)
controller = @controller.new.call(@action, env)

This comment has been minimized.

Copy link
@jonathan

jonathan Aug 25, 2009

Maybe I'm missing something but, I don't think you need the assignment here.

end
end

class ActionMiddleware
def initialize(controller, action)
@controller, @action = controller, action
end

def call(env)
controller = @controller.new
controller.app = @app
controller.call(@action, env)
end

def new(app)
@app = app
self
end
end

# Return a rack endpoint for the given action. Memoize the endpoint, so
Expand All @@ -89,9 +116,12 @@ def to_a
# Proc:: A rack application
def self.action(name)
@actions ||= {}
@actions[name.to_s] ||= proc do |env|
new.call(name, env)
end
@actions[name.to_s] ||= ActionEndpoint.new(self, name)
end

def self.middleware(name)
@middlewares ||= {}
@middlewares[name.to_s] ||= ActionMiddleware.new(self, name)

This comment has been minimized.

Copy link
@jonathan

jonathan Aug 25, 2009

Not sure this call to new will work because it looks like ActionMiddleware#new only takes one argument, app.

end
end
end
8 changes: 1 addition & 7 deletions actionpack/lib/action_controller/metal/rack_convenience.rb
Expand Up @@ -5,7 +5,7 @@ module RackConvenience
included do
delegate :headers, :status=, :location=, :content_type=,
:status, :location, :content_type, :to => "@_response"
attr_internal :request, :response
attr_internal :request
end

def call(name, env)
Expand All @@ -19,12 +19,6 @@ def params
@_params ||= @_request.parameters
end

# :api: private
def to_a
@_response.prepare!
@_response.to_a
end

def response_body=(body)
response.body = body if response
super
Expand Down
5 changes: 4 additions & 1 deletion actionpack/lib/action_dispatch/http/response.rb
Expand Up @@ -161,13 +161,16 @@ def assign_default_content_type_and_charset!
headers[CONTENT_TYPE] = type
end

def prepare!
def to_a
assign_default_content_type_and_charset!
handle_conditional_get!
self["Set-Cookie"] = @cookie.join("\n")
self["ETag"] = @etag if @etag
super
end

alias prepare! to_a

def each(&callback)
if @body.respond_to?(:call)
@writer = lambda { |x| callback.call(x) }
Expand Down
45 changes: 45 additions & 0 deletions actionpack/test/new_base/metal_test.rb
@@ -0,0 +1,45 @@
require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")

module MetalTest
class MetalMiddleware < ActionController::Metal
def index
if env["PATH_INFO"] =~ /authed/
self.response = app.call(env)
else
self.response_body = "Not authed!"
self.status = 401
end
end
end

class Endpoint
def call(env)
[200, {}, "Hello World"]
end
end

class TestMiddleware < ActiveSupport::TestCase
def setup
@app = Rack::Builder.new do
use MetalMiddleware.middleware(:index)
run Endpoint.new
end.to_app
end

test "it can call the next app by using @app" do
env = Rack::MockRequest.env_for("/authed")
response = @app.call(env)

assert_equal "Hello World", response[2]
end

test "it can return a response using the normal AC::Metal techniques" do
env = Rack::MockRequest.env_for("/")
response = @app.call(env)

assert_equal "Not authed!", response[2]
assert_equal 401, response[0]
end
end
end

3 comments on commit c7ba911

@Roman2K
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jonathan: +1

@carllerche
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jonathan, you are correct about the "controller = ". For the ActionMiddleware#new comment, #new is an instance method so ActionMiddleware.new does not call that method.

@jonathan
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, ok. Cool.

Please sign in to comment.