Tag Archives: Rack

Creating a Ruby Rack Middleware for Application Version Id

If you are developing an application that uses the Ruby rack framework, and most Ruby application frameworks and servers do, it is pretty simple to create your own rack middleware.

In this post I outline how simple it is go create a new middleware for your rack application. In fact, I did something very similar in one of the web services I worked on so that we could get the exact version of the code that our app was running against for troubleshooting purposes. I suggested we add a version identifier to all responses so we could log the requests, the responses and their response codes, and have the version of the running code in every response as well. This would help us troubleshoot failures that are not failing anymore, or failed “intermittently” because they might be hitting one of two load balanced servers in case someone updated one server, but not the other, such as for A-B testing.

This was actually a very easy thing to do by taking advantage of rack’s middleware capability. The code below was all that it took to create a new piece of middleware to give us version identifiers on each response.

module Middleware
  class VersionIdentifier
    def initialize(app, version_id)
      @app = app
      @version_id = version_id
    end

    def call(env)
      @app.call(env).tap do |_status, headers, _body|
        headers["version-id"] = @version_id
      end
    end

    private
      attr_reader :version_id
  end
end

In the above source, we create a initialize method which takes app, which is the rack application that this middleware component is wrapping, and version_id, which is how we will identify the version of the application is running.

The next step to define the standard rack protocol of call(env). In this, since we are only concerned about the response, we call the next rack component by invoking call on @app which we got when the middleware was constructed. This could be another piece of middleware, or it could be the actual application, but we don’t care about specifics, which is part of the beauty of the way middleware for rack works. What we do care about is the return of the call to @app.call(env), since we are interested in modifying the response headers. We do this with using the tap method on all Ruby objects, and adding a new entry to the header hash with the key version-id and the value that we got on creation for @version_id.

That is all there is to creating a rack middleware component to add a version identifier to the headers for every response that is served by the rack web server.

To use this middleware, we first need to determine what the will represent the version of our app. For this example, we will use the git SHA that represents the code we are running against, since we are assuming we are behaving and there is no code running in production that is not checked into source control. We add this to our startup script/configuration file, e.g. config.ru.

git_sha = `git rev-parse HEAD`

Once we have that settled, we just declare we are going to use our middleware, and we pass in the git_sha that we found above.

use Middleware::VersionIdentifier, git_sha

We start up our application and voilĂ , we have a version identifier getting returned on every response with the header of version-id.

$ curl -i "localhost:9292"
HTTP/1.1 200 OK
version-id: 7df6e26a3dd1d3d05130e054fbcb1b878d965767
Content-Length: 7

Howdy!
$

The full source code can be found at https://github.com/stevenproctor/versionid-middleware-example.

Ruby and Puma – Read error: #

Was standing up a Puma web service late into the work day yesterday, and could not figure out why I was getting the below error when I should be seeing my results as a CSV result.

2013-05-28 19:00:59 -0500: Read error: #<NoMethodError: undefined method `each' for #<String:0x00000001611790>>
/home/reporting/reporting_mux/shared/bundle/ruby/1.9.1/gems/puma-2.0.1/lib/puma/server.rb:482:in `handle_request'
/home/reporting/reporting_mux/shared/bundle/ruby/1.9.1/gems/puma-2.0.1/lib/puma/server.rb:243:in `process_client'
/home/reporting/reporting_mux/shared/bundle/ruby/1.9.1/gems/puma-2.0.1/lib/puma/server.rb:142:in `block in run'
/home/reporting/reporting_mux/shared/bundle/ruby/1.9.1/gems/puma-2.0.1/lib/puma/thread_pool.rb:92:in `call'
/home/reporting/reporting_mux/shared/bundle/ruby/1.9.1/gems/puma-2.0.1/lib/puma/thread_pool.rb:92:in `block in spawn_thread'

The original return value of the call method was setup as:

[200, {"Content-Type" => "text/html"}, str]

For those with some more experience in using Puma, or Rack, you may even see what the problem was right away. Being my first attempt at standing up a Puma instance, I chased down a few red herrings, but at least they were needed updates. I first tried to update the MIME type to be text/comma-separated-values instead of text/html, and had the results setup to return as an attachment. These changes were kept, as they were the behavior of an existing implementation we are mirroring, but the call was still erroring out.

I finally stumbled across Puma’s Example config.rb file on Github.com and buried in there I saw the issue. The last element of the array returned by a Rack call method, has to be an Array, or at least Enumerable. I was returning a String, and that is what was causing the #<NoMethodError: undefined method `each' for #<String:0x00000001611790>>. I changed the last element to be the string wrapped in an array, and voilĂ , everything worked. So below is what I wound up with after the too long bug hunt.

[200, {"Content-Type" => "text/comma-separated-values",
       "Content-Disposition" => "attachment; filename=#{filename}" }, [str + "n"]]

Hope this helps someone else with a similar problem, and can save you the long evening of debugging that I went through.
–Proctor