Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

ActionController::Metal can be a middleware

  • Loading branch information...
commit c7ba911a43e513bd1adbee93f16d2b8efea7cc88 1 parent 09fde64
Yehuda Katz + Carl Lerche authored
View
40 actionpack/lib/action_controller/metal.rb
@@ -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 = {}
@@ -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)

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ 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
@@ -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)

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
end
end
end
View
8 actionpack/lib/action_controller/metal/rack_convenience.rb
@@ -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)
@@ -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
View
5 actionpack/lib/action_dispatch/http/response.rb
@@ -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) }
View
45 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

@jonathan

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

@Roman2K

jonathan: +1

@jonathan

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

@carllerche
Collaborator

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

Ah, ok. Cool.

Please sign in to comment.
Something went wrong with that request. Please try again.