# Hobbit [![Build Status](http://img.shields.io/travis/patriciomacadden/hobbit.svg)](https://travis-ci.org/patriciomacadden/hobbit) [![Code Climate](http://img.shields.io/codeclimate/github/patriciomacadden/hobbit.svg)](https://codeclimate.com/github/patriciomacadden/hobbit) [![Code Climate Coverage](http://img.shields.io/codeclimate/coverage/github/patriciomacadden/hobbit.svg)](https://codeclimate.com/github/patriciomacadden/hobbit) [![Dependency Status](http://img.shields.io/gemnasium/patriciomacadden/hobbit.svg)](https://gemnasium.com/patriciomacadden/hobbit) [![Gem Version](http://img.shields.io/gem/v/hobbit.svg)](http://badge.fury.io/rb/hobbit) A minimalistic microframework built on top of [Rack](http://rack.github.io/). ## Installation Add this line to your application's Gemfile: ```ruby gem 'hobbit' # or this if you want to use hobbit master # gem 'hobbit', github: 'patriciomacadden/hobbit' ``` And then execute: ```bash $ bundle ``` Or install it yourself as: ```bash $ gem install hobbit ``` ## Features * DSL inspired by [Sinatra](http://www.sinatrarb.com/). * [Speed](https://github.com/luislavena/bench-micro). * Extensible with standard ruby classes and modules, with no extra logic. See [hobbit-contrib](https://github.com/patriciomacadden/hobbit-contrib). * Zero configuration. ## Philosophy * [Don't repeat yourself](http://en.wikipedia.org/wiki/Don't_repeat_yourself) * Encourages the understanding and use of [Rack](http://rack.github.io/) and its extensions instead of providing such functionality. ## Usage Hobbit applications are just instances of classes that inherits from `Hobbit::Base`, which complies the [Rack SPEC](http://rubydoc.info/github/rack/rack/master/file/SPEC). ### Hello World example Create a file called `app.rb`: ```ruby require 'hobbit' class App < Hobbit::Base get '/' do 'Hello World!' end end ``` Create a `config.ru` file: ```ruby require './app' run App.new # or just `run App` ``` Run it with `rackup`: ```bash $ rackup ``` View your app at [http://localhost:9292](http://localhost:9292). ### Routes Every route is composed of a verb, a path (optional) and a block. When an incoming request matches a route, the block is executed and a response is sent back to the client. The return value of the block will be the `body` of the response. The `headers` and `status code` of the response will be calculated by `Hobbit::Response`, but you could modify it anyway you want it. See an example: ```ruby class App < Hobbit::Base get '/' do # ... end post '/' do # ... end put '/' do # ... end patch '/' do # ... end delete '/' do # ... end options '/' do # ... end end ``` When a route gets called you have this methods available: * `env`: The Rack environment. * `request`: a `Hobbit::Request` instance. * `response`: a `Hobbit::Response` instance. And any other method defined in your application. #### Available methods * `delete` * `get` * `head` * `options` * `patch` * `post` * `put` **Note**: Since most browsers don't support methods other than **GET** and **POST** you must use the `Rack::MethodOverride` middleware. (See [Rack::MethodOverride](https://github.com/rack/rack/blob/master/lib/rack/methodoverride.rb)). #### Routes with parameters Besides the standard `GET` and `POST` parameters, you can have routes with parameters: ```ruby require 'hobbit' class App < Hobbit::Base # matches both /hi/hobbit and /hi/patricio get '/hi/:name' do # request.params is filled with the route paramters, like this: "Hello #{request.params[:name]}" end end ``` #### Redirecting If you look at Hobbit implementation, you may notice that there is no `redirect` method (or similar). This is because such functionality is provided by [Rack::Response](https://github.com/rack/rack/blob/master/lib/rack/response.rb) and for now we [don't wan't to repeat ourselves](http://en.wikipedia.org/wiki/Don't_repeat_yourself) (obviously you can create an extension!). So, if you want to redirect to another route, do it like this: ```ruby require 'hobbit' class App < Hobbit::Base get '/' do response.redirect '/hi' end get '/hi' do 'Hello World!' end end ``` #### Halting To immediately stop a request within route you can use `halt`. ```ruby require 'hobbit' class App < Hobbit::Base use Rack::Session::Cookie, secret: SecureRandom.hex(64) def session env['rack.session'] end get '/' do response.status = 401 halt response.finish end end ``` ### Built on top of rack Each Hobbit application is a Rack stack (See this [blog post](http://m.onkey.org/ruby-on-rack-2-the-builder) for more information). #### Mapping applications You can mount any Rack application to the stack by using the `map` class method: ```ruby require 'hobbit' class InnerApp < Hobbit::Base # gets called when path_info = '/inner' get do 'Hello InnerApp!' end end class App < Hobbit::Base map('/inner') { run InnerApp.new } get '/' do 'Hello App!' end end ``` #### Using middleware You can add any Rack middleware to the stack by using the `use` class method: ```ruby require 'hobbit' class App < Hobbit::Base use Rack::Session::Cookie, secret: SecureRandom.hex(64) use Rack::ShowExceptions def session env['rack.session'] end get '/' do session[:name] = 'hobbit' end # more routes... end run App.new ``` ### Security By default, Hobbit (nor Rack) comes without any protection against web attacks. The use of [rack-protection](https://github.com/rkh/rack-protection) is highly recommended: ```ruby require 'hobbit' require 'rack/protection' require 'securerandom' class App < Hobbit::Base use Rack::Session::Cookie, secret: SecureRandom.hex(64) use Rack::Protection get '/' do 'Hello World!' end end ``` See the [rack-protection](https://github.com/rkh/rack-protection) documentation for futher information. ### Testing [rack-test](https://github.com/brynary/rack-test) is highly recommended. See an example: In `app.rb`: ```ruby require 'hobbit' class App < Hobbit::Base get '/' do 'Hello World!' end end ``` In `app_spec.rb`: ```ruby require 'minitest/autorun' # imagine that app.rb and app_spec.rb are stored in the same directory require 'app' describe App do include Rack::Test::Methods def app App.new end describe 'GET /' do it 'must be ok' do get '/' last_response.must_be :ok? last_response.body.must_match /Hello World!/ end end end ``` See the [rack-test](https://github.com/brynary/rack-test) documentation for futher information. ### Extensions You can extend Hobbit by creating standard ruby modules. See an example: ```ruby module MyExtension def do_something # do something end end class App < Hobbit::Base include MyExtension get '/' do do_something 'Hello World!' end end ``` #### Hobbit::Contrib [hobbit-contrib](https://github.com/patriciomacadden/hobbit-contrib) is a ruby gem that comes with a lot of hobbit extensions, such as: * `Hobbit::Render`: provides basic template rendering. * `Hobbit::Session`: provides helper methods for handling user sessions. * `Hobbit::Environment`: provides helper methods for handling application environments. * `Hobbit::Filter`: provides helper class methods for handling Sinatra-like filters. * `Hobbit::ErrorHandling`: provides helper class methods for handling Sinatra-like error handling. ... And many more! ## Community * [Wiki](https://github.com/patriciomacadden/hobbit/wiki): Guides, how-tos and recipes * IRC: [#hobbitrb](irc://chat.freenode.net/#hobbitrb) on [http://freenode.net](http://freenode.net) ## Presentations * Building web applications in Ruby, by [Krzysztof Wawer](https://github.com/wafcio) ([english](https://speakerdeck.com/wafcio/hobbit-english), [polish](https://speakerdeck.com/wafcio/hobbit)) ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request ## License See the [LICENSE](https://github.com/patriciomacadden/hobbit/blob/master/LICENSE).