Otto - 1.0 (2024-04-05)

Auto-define your rack-apps in plain-text.

Overview

Apps built with Otto have three, basic parts: a rackup file, a ruby file, and a routes file. If you've built a Rack app before, then you've seen a rackup file before. The ruby file is your actual app and the routes file is what Otto uses to map URI paths to a Ruby class and method.

A barebones app directory looks something like this:

  $ cd myapp
  $ ls
  config.ru app.rb routes

See the examples/ directory for a working app.

Routes

The routes file is just a plain-text file which defines the end points of your application. Each route has three parts:

  • HTTP verb (GET, POST, PUT, DELETE or HEAD)
  • URI path
  • Ruby class and method to call

Here is an example:

  GET   /                         App#index
  POST  /                         App#receive_feedback
  GET   /redirect                 App#redirect
  GET   /robots.txt               App#robots_text
  GET   /product/:prodid          App#display_product

  # You can also define these handlers when no
  # route can be found or there's a server error. (optional)
  GET   /404                      App#not_found
  GET   /500                      App#server_error

App

There is nothing special about the Ruby class. The only requirement is that the first two arguments to initialize be a Rack::Request object and a Rack::Response object. Otherwise, you can do anything you want. You're free to use any templating engine, any database mapper, etc. There is no magic.

  class App
    attr_reader :req, :res

    # Otto creates an instance of this class for every request
    # and passess the Rack::Request and Rack::Response objects.
    def initialize req, res
      @req, @res = req, res
    end

    def index
      res.header['Content-Type'] = "text/html; charset=utf-8"
      lines = [
        '<img src="/img/otto.jpg" /><br/><br/>',
        'Send feedback:<br/>',
        '<form method="post"><input name="msg" /><input type="submit" /></form>',
        '<a href="/product/100">A product example</a>'
      ]
      res.body = lines.join($/)
    end

    def receive_feedback
      res.body = req.params.inspect
    end

    def redirect
      res.redirect '/robots.txt'
    end

    def robots_text
      res.header['Content-Type'] = "text/plain"
      rules = 'User-agent: *', 'Disallow: /private'
      res.body = rules.join($/)
    end

    def display_product
      res.header['Content-Type'] = "application/json; charset=utf-8"
      prodid = req.params[:prodid]
      res.body = '{"product":%s,"msg":"Hint: try another value"}' % [prodid]
    end

    def not_found
      res.status = 404
      res.body = "Item not found!"
    end

    def server_error
      res.status = 500
      res.body = "There was a server error!"
    end
  end

Rackup

There is also nothing special about the rackup file. It just builds a Rack app using your routes file.

  require 'otto'
  require 'app'

  app = Otto.new("./routes")

  map('/') {
    run app
  }

See the examples/ directory for a working app.

Installation

Get it in one of the following ways:

  $ gem install otto

  [ Add it to yer Gemfile]
  $ bundle install

  $ git clone git://github.com/delano/otto.git

You can also download via tarball or zip.

More Information

In the wild

Services that use Otto:

License

See LICENSE.txt