Skip to main content

Rails3, Rack and "Where did my Metal go?"

Our Rails3 (beta4) application had one route mapped in config/routes to a Sinatra app, by means of the following route:

match '/foo', :to => EndPointApp, :as => :endpoint

The route was being defined to run as a Sinatra Application

require 'sinatra'
class EndPointApp < Sinatra::Application
  post '/foo' do
    ...
  end
end

This was working mostly fine, but it was returning Set-Cookie header with the standard Rails sessions cookie, which in this case was preventing the client of this endpoint from successfully interpreting the result. As I could do nothing about the client side, I had to remove Set-Cookie from the headers, but only for this end-point and obviously not from the entire app. This proved to be somewhat more complicated than I had hoped, so let me share the solution here in hopes it might save someone else an hour or two.

First, I ran "rake middleware" and observed the following Rack stack:

use ActionDispatch::Static
use Rack::Lock
use ActiveSupport::Cache::Strategy::LocalCache
use Rack::Runtime
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::RemoteIp
use Rack::Sendfile
use ActionDispatch::Callbacks
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ParamsParser
use Rack::MethodOverride
use ActionDispatch::Head
run Kigster::Application.routes

As can be immediately seen from here, the routes execute very last after Session::CookieStore already wrapped the request. OK, so looks like I need to bypass the routes somehow, and so I started to look at Rails::Metal, which is supposed to run before all other processing.

Once I started to look for Rails::Metal, I realized pretty quickly that I am missing metal generator:

> rails g metal
Could not find generator metal.

After a few more rounds of digging around, it turns out that in Rails3 Beta4 Rails::Metal has been completely removed, because it is no longer needed in a Rack environment.

So I had convert my Sinatra module to a Rack module, and insert it into the Rack middleware stack before the Cookie/Sessions:

require File.expand_path('../../../config/environment',  __FILE__) unless defined?(Rails)

module Kigster
  class EndPoint
    def initialize(app)
      @app = app
    end
    def call(env)
      if env["PATH_INFO"] =~ /^foo/
        process_request(env)
      else
        @app.call(env)
      end
    end

    private
    def process_request(env) 
       req = Rack::Request.new(env)
       params = req.params
       # do stuff
       [ 200, 
         { "Content-Type"   => "text/html", 
           "Content-Length" => "0" }, 
         [""]   
       ]
    end
  end
end

I had add the following to my config/application.rb to enable this Rack module, and have it run before the ActionDispatch::Session::CookieStore:

# config/application.rb
# require the file directly
require File.join(File.dirname(__FILE__), '../app/metal/kigster_endpoint')
....
module Kigster
  class Application < Rails::Application
    config.middleware.insert_after Rails::Rack::Logger, 
                                   Kigster::EndPoint
  end
end

Now my handler executes before the session, and the result does not include Set-Cookie header.

Any other suggestions on how to make this any simpler, or more correct are as always welcome!

Comments

Anonymous said…
I have a class that extends from ActionController::Metal
This has one method hit. How can get access to the cookies method? Which module should I include?

Popular posts from this blog

Car or Auto Make-Model-Year Database : For Breakfast

Make Model What?If you like me were tasked with loading a database of recent car makes/models/years, you would start by looking on the web and seeing if someone else just has it out there, readily available, hopefully for free, but perhaps for a tiny nominal fee.?If only it was that simple... I looked and looked, and couldn't find anything that would fit the above requirements. So I thought, who would know about US car models better than Kelly Blue Book? So I went on their site, and sure enough they have a javascript file that lists all known to them makes and models of used cars. Since the file is public, I figured it's not really "evil" if I scrape and parse it for my own benefit. Disagree? Have a better source? Then leave a comment.Anyway, to cut the long story short, I'm hoping to save a day or so to someone else who may, like me, be looking for this information. The ruby module shown below retrieves and parses the javascript from KBB site into a Ruby da…

Mac OS-X tips: How to run SSHD on an alternative port

This tip falls into one of those "I had to spend more than 10 minutes looking for an answer" category, so it's a worthwhile subject for a quick blog post. Why Run SSH? Running SSH on Mac OS-X allows you to login to your machine remotely, and also copy file securely via SCP command to and from your Mac OS-X host. But I am behind my own $50 router/firewall. Can I still connect to my computer from outiside?Yes. Most off the shelf routers and firewalls will allow you to do two things that are needed for this: Assign a permanent IP address to your Mac on a local network (see your router documentation for more details) Create a port forwarding rule on your router. Eg - any request to port 22 on your external IP (provided by your router) can be routed to the specific IP address of your Mac. Exact specifics on this configuration are once again available in your router documentation. Most off the shelf routers support this, including basic Netgear and D-Link. So let'…

Why I Like PostgreSQL

Today I gave a short presentation at work about PostgreSQL, and why I much prefer it to MySQL.

PostgreSQL vs MySQL: Eternal Battle
I may be misreading this, but it seems that there is a recent trend within startups to move away from MySQL, probably thanks to folks like Heroku on one side (who use PostgreSQL to the extreme, and help and contribute to it's development), vs folks like Oracle on the other side, tainting the "open source pureness" of MySQL :)

At my work we currently use a mid-sized MySQL 5.1 Percona instance, which is holding up quite well I must admit. Both PostgreSQL and MySQL have definitely converged to cover most features that people want, but my leaning is still towards PostgreSQL. I just agree with it's focus on data integrity, recovery, constraints, extensibility, while some of the early decisions in MySQL's design do not agree with me at all (like truncating long strings, 1/0 instead of booleans, ambiguous group by, etc). I think that data …