Rack-webconsole, a Ruby/Rails console inside your browser

"Wouldn't it be nice to have an in-browser console for a Rails application?"

The funniest afternoons tend to start with questions like this.

And why stop at Rails? Let's build a Rack middleware and make it real for Sinatra, Padrino and other Rack web frameworks!

Introducing rack-webconsole

Rack-webconsole is a Rack middleware that enhances your development experienc providing a JavaScript-powered bridge to your Ruby application backend.

With it you can interact with your database and explore the runtim environment from within the browser. Although it should be used mainly in development environments, I personally think it could be a useful tool for staging as well. Avoids the pain in the ass of interacting with some console over an SSH session :)

Here you have a little video showing how it works (watch in HD, otherwise you won't see
the tiny letters
!):

Built from the ground up to be Rack-compliant, it has pretty much no configuration (other than including it in your Gemfile). So, let's give it a try:

gem 'rack-webconsole'  

Rack-webconsole needs JQuery. If you are using Rails 3, JQuery is loaded b default. In case you don't want to use JQuery in your application rack-webconsole can inject it for you only when it needs it. To do that yo should put this line somewhere in your application (a Rails initializer, or some configuration file):

Rack::Webconsole.inject_jquery = true  

If you are using Rails 3, you are done. Rack-webconsole comes with a Railtie that automatically injects the gem into your middleware chain, and that's it!

To fire up the console, just press the ` (backtick) key.

And in case you are a Sinatra or Padrino user, you only have to specify that you want to use this middleware:

rack-webconsole

Manually requiring rack-webconsole is only needed if you are not using Bundler.setup to initialize your dependencies in the app.

You can check out the github repo here.

Behind the scenes

TL;DR: The idea behind rack-webconsole is simple: client-side JavaScript communicating with an IRB-esque server-side API which maintains state (i.e. local variables) across requests. The funny part it's all implemented behind a single Rack middleware, in that 90's Plug & Play™ sense.

For those who are curious, let's do a bit more of an in-depth explanation. Let's start with what Rack applications and middlewares are.

Rack applications and middlewares

As the Rack specification says:

A Rack application is a Ruby object that respond to `call`. It takes
exactly one argument, the environment, and returns an Array of exactly
three values: the status, the headers, and the body.

As an example, the following Ruby object is a fully-compliant Rack application:

lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['microsoft is so cool']] }  

A Rack middleware is just a normal Rack application that is initialized from another Rack application and may manipulate its output (by output I mean status, headers or body). For example, let's build a middleware that filters gross words from the body:

class GrossFilter  
  # This is the Rack application from which we take the response.
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env) # Get the original response

    # Filter out gross words from the body
    modified_body = body.map do |response_line|
      response_line.gsub('microsoft', '')
    end

    # Return the modified response
    [status, headers, modified_body]
  end
end  

On every request, our middleware chain will look like this:

Rack-webconsole internals

Rack-webconsole is a middleware that hides two different middlewares inside: the asset injecter and the server-side console service.

When the browser is requesting an HTML view, the asset injecter injects the console HTML, CSS and JavaScript code in the view.

Then, whenever the client-side JavaScript performs an AJAX call to the server-side console (in path '/webconsole'), the server-side console returns evaluated Ruby as JSON.

Conclusion

Rack middlewares are simple yet powerful tools worth adding to your stack. But we just saw they are not only valuable in production! Can you think of other development-oriented tools that could fit as a nice, pluggable Rack middleware?

Now you can get an idea of how simple it is to write them :)