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

On Ruby on Rails with PostgreSQL, and Acts as Paranoid

Back a few years ago I was researching differences between PostgreSQL and MySQL databases, and chose PostgreSQL because at the time it supported foreign key constraints and many other fantastic SQL extensions that make developer's life a lot easier. Today I am sure MySQL is just as functional as PostgreSQL, and it does appear to be a more popular choice as the Rails DB than MySQL. I still prefer PostgreSQL, it just feels more natural to me coming out of Oracle background, so I am probably biased (who isn't?) Anyway, in the last year and a half or so, I've been writing Rails apps that use PG-based databases and found a few random tricks I'd love to share here. Opinions differ here, but just like accountants like the ancient double entry accounting system, I personally prefer a double validation system - where Rails validates my objects before/after updates, but the database double checks this using proper constraints. Rail's validation system is very robust and exte

Getting RMagic and friends to work on OS-X Mountain Lion

Upgraded my ruby environment today to Mountain Lion. Here is a quick checklist that I went through to get everything working.  The largest change was having to reinstall XCode and command line tools, and also download XQuarts in order to reinstall ImageMagick successfully. Without it, I was getting errors building RMagick of the following shape: ld: file not found: /usr/lib/libltdl.7.dylib for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [RMagick2.bundle] Error 1 Quick checklist: Install Mountain Lion Install XCode 4.4 Install command line tools from XCode 4.4 Preferences dialog Install XQuartz In terminal run brew update brew uninstall imagemagick brew install --fresh imagemagick wipe out your ~/.rvm folder reinstall RVM and install the latest ruby 1.9.3-p-194 run "bundle" in the project folder run "rake" and rejoice References: https://github.com/mroth/lolcommits/issu