Running Sinatra inside a Ruby Gem

series / ruby / picky / gems

This is a post in the Picky series on its workings. If you haven’t tried it yet, do so in the Getting Started section. It’s quick and painless :)

In this post I’ll show how to have Sinatra run directly from inside a gem. And at the end, how Picky uses this for its advantage.

Let’s go singing in the gem!

The thing is…

What I wanted, was to add a nice statistics web interface to Picky.

First I though about adding it to the server, but soon after (~1.2µs) decided that this was a silly idea.

Picky is heavily designed around loosely connected elements in the server. I think this is even a better idea outside of a large component such as a server. So what I found myself thinking about – while showering – next was, to have a gem which generates a Sinatra application…

Suddenly the room lit up and I spotted, scrawled on the wall in burning letters of blood:

The wrong question.

I gave it not much thought, as it can get crazy in this part of Zürich. Then, while gorging myself on my beloved alphabet soup, and thinking about how to structure files in this web application exactly, the letters suddenly formed a sentence:

Dude the wrong, fucking question.

(Soups can only spell so well)

I only got it a few hours later, while three swedish massage therapists kneaded my shoulders.

In computer science, the answers aren’t nearly as important as asking:

…the right fucking question.

The right fucking question

The right question is:

How do I fit a web application wholly in a gem, such that I can do a

$ picky stats log/search.log

on any Picky logfile and it will parse it and show me a nice statistical representation of it in a browser without soiling the directory and everything else?

The right fucking tool for the job

That’s Sinatra I’m talking about. The great and extremely easy to use Ruby DSL for web applications.

Give it a whirl if you haven’t seen it!

How to do it

First, set up a gem structure – let’s call the gem “rain_sining”. Then, inside it, set up the following structure:

rain_singing
  /bin
  /lib
    /rain_singing
      /application   # <- the app is in here
        app.rb       # <- the webapp itself
        /images
        /javascripts
        /stylesheets
        /views
    rain_singing.rb
  rain_singing.gemspec
  /spec

The “hardest” thing is getting the directories correctly set up.

So what you do inside app.rb is:

require 'sinatra'
require 'haml' # if you use haml views

class SingingRain < Sinatra::Base

  set :static, true                             # set up static file routing
  set :public, File.expand_path('..', __FILE__) # set up the static dir (with images/js/css inside)
  
  set :views,  File.expand_path('../views', __FILE__) # set up the views dir
  set :haml, { :format => :html5 }                    # if you use haml
  
  # Your "actions" go here…
  #
  get '/' do
    haml :'/index'
  end
  
end

# Run the app!
#
puts "Hello, you're running your web app from a gem!"
SingingRain.run!

And that’s already it for the app.

Now, if you want to define a binary for the gem, put an executable rain_singing file into /bin. Into this file you’d write:

#!/usr/bin/env ruby
#
begin
  require 'rain_singing/application/app.rb'
rescue LoadError => e
  require 'rubygems'
  path = File.expand_path '../../lib', __FILE__
  $:.unshift(path) if File.directory?(path) && !$:.include?(path)
  require 'rain_singing/application/app.rb'
end

Then, we need to tell rubygems that this gem has an executable:

Gem::Specification.new do |s|
  
  ...
  
  s.executables = ['rain_singing']
  s.default_executable = 'rain_singing'
  
  ...
  
end

After generating your gem with

$ gem build rain_singing.gemspec

and installing it with

$ gem install rain_singing-1.0.0.gem

you are ready to run

$ rain_singing
Hello, you're running your web app from a gem!

Good stuff. Good stuff. Makes me want to sing in the rain.

In Picky

Picky uses this for two things.

A statistics interface ($ gem install picky-statistics), run

$ picky stats path/to/your/search.log 1234

or the live interface to the running server ($ gem install picky-live), run

$ picky live localhost:8080/admin 1234

You need to add route %r{/admin} => LiveParameters.new in the server to have it work. But then you get the interface described in this blog post.

Nice, eh?

Conclusions

So we’ve seen

  1. that Sinatra rocks my noodles
  2. that a Gem can contain a whole webapp without footprint
  3. that Picky uses both for maximal profit!

Hope you learnt something new :)

Next A better Rubygems search

Share


Previous

Comments?