├── 01__Introduction.md ├── 02__Getting_to_know_Sinatra.md ├── 03__Organizing_your_application.md ├── 04__Views.md ├── 05__Models.md ├── 06__Helpers.md ├── 07__Middleware.md ├── 08__Testing.md ├── 09__Development.md ├── 10__Deployment.md ├── 11__Contributing.md ├── META ├── LICENSE ├── book.yml └── contents.yml └── README.md /01__Introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | --- 4 | 5 | ## What is Sinatra? 6 | 7 | Sinatra is a Domain Specific Language (DSL) for quickly creating 8 | web-applications in Ruby. 9 | 10 | It keeps a minimal feature set, leaving the developer to use the 11 | tools that best suit them and their application. 12 | 13 | It doesn't assume much about your application, apart from that: 14 | 15 | * it will be written in Ruby programming language 16 | * it will have URLs 17 | 18 | In Sinatra, you can write short _ad hoc_ applications or mature, larger 19 | application with the same easiness. 20 | 21 | You can use the power of various Rubygems and other libraries available for 22 | Ruby. 23 | 24 | Sinatra really shines when used for experiments and application mock-ups or for 25 | creating a quick interface for your code. 26 | 27 | It isn't a _typical_ Model-View-Controller framework, but ties specific URL 28 | directly to relevant Ruby code and returns its output in response. It does 29 | enable you, however, to write clean, properly organized applications: 30 | separating _views_ from application code, for instance. 31 | 32 | ## Installation 33 | 34 | The simplest way to install Sinatra is through Rubygems: 35 | 36 | ``` 37 | $ gem install sinatra 38 | ``` 39 | 40 | ### Dependencies 41 | 42 | Sinatra depends on the _Rack_ gem (). 43 | 44 | Sinatra supports many different template engines (it uses the Tilt library 45 | internally to support practically every template engine in Ruby) 46 | For optimal experience, you should install the template engines you want to 47 | work with. The Sinatra dev team suggests using either ERB, which is included 48 | with Ruby, or installing HAML as your first template language. 49 | 50 | ``` 51 | $ gem install haml 52 | ``` 53 | 54 | ### Living on the Edge 55 | 56 | The _edge_ version of Sinatra lives in its Git repository, available at 57 | ****. 58 | 59 | You can use the _edge_ version to try new functionality or to contribute to the 60 | framework. You need to have [Git version control 61 | software](http://www.git-scm.com) and [bundler](http://gembundler.com/). 62 | 63 | ``` 64 | $ gem install bundler 65 | ``` 66 | 67 | To use Sinatra _edge_ with bundler, you'll have to create a `Gemfile` listing 68 | Sinatra's and any other dependencies you're going to need. 69 | 70 | ```ruby 71 | source :rubygems 72 | gem 'sinatra', :git => 'git://github.com/sinatra/sinatra.git' 73 | ``` 74 | 75 | Here we use the rubygems source to specify where to get Sinatra's 76 | dependencies; alternatively you can use the git version, but that is up to you. 77 | So now we can install our bundle: 78 | 79 | ``` 80 | $ bundle install 81 | ``` 82 | 83 | 84 | ## Hello World Application 85 | 86 | Sinatra is installed, how about making your first application? 87 | 88 | ```ruby 89 | require 'rubygems' 90 | 91 | # If you're using bundler, you will need to add this 92 | require 'bundler/setup' 93 | 94 | require 'sinatra' 95 | 96 | get '/' do 97 | "Hello world, it's #{Time.now} at the server!" 98 | end 99 | ``` 100 | 101 | Run this application by `$ ruby hello_world.rb` and load 102 | `http://localhost:4567` in your browser. 103 | 104 | As you can see, Sinatra doesn't force you to setup much infrastructure: a 105 | request to a URL evaluates some Ruby code and returns some text in response. 106 | Whatever the block returns is sent back to the browser. 107 | 108 | 109 | ## Real World Applications in Sinatra 110 | 111 | ### Github Services 112 | 113 | Git hosting provider Github uses Sinatra for post-receive hooks, calling user 114 | specified services/URLs, whenever someone pushes to their repository: 115 | 116 | * 117 | * 118 | * 119 | 120 | Check out a full list of Sinatra apps [in the wild][in-the-wild]. 121 | 122 | [in-the-wild]: http://www.sinatrarb.com/wild.html 123 | 124 | 125 | ## About this book 126 | 127 | This book will assume you have a basic knowledge of the Ruby scripting language 128 | and a working Ruby interpreter. 129 | 130 | For more information about the Ruby language visit the following links: 131 | 132 | * [ruby-lang](http://www.ruby-lang.org) 133 | * [ruby-lang / documentation](http://www.ruby-lang.org/en/documentation/) 134 | 135 | ## Need Help? 136 | 137 | The Sinatra club is small, but super-friendly. Join us on IRC at 138 | irc.freenode.org in #sinatra if you have any questions. It's a bit 139 | slow at times, so give us a bit to get back to your questions. 140 | -------------------------------------------------------------------------------- /02__Getting_to_know_Sinatra.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting to know Sinatra 3 | --- 4 | 5 | ## It's Witchcraft 6 | 7 | You saw in the introduction how to install Sinatra, its dependencies, and 8 | write a small "hello world" application. In this chapter you will get a 9 | whirlwind tour of the framework and familiarize yourself with its features. 10 | 11 | ## Routing 12 | 13 | Sinatra is super flexible when it comes to routing, which is essentially an 14 | HTTP method and a regular expression to match the requested URL. The four basic 15 | HTTP request methods will get you a long ways: 16 | 17 | * GET 18 | * POST 19 | * PATCH 20 | * PUT 21 | * DELETE 22 | 23 | Routes are the backbone of your application, they're like a guide-map to how 24 | users will navigate the actions you define for your application. 25 | 26 | They also enable to you create [RESTful web services][restful-web-services], in 27 | a very obvious manner. Here's an example of how one-such service might look: 28 | 29 | ```ruby 30 | get '/dogs' do 31 | # get a listing of all the dogs 32 | end 33 | 34 | get '/dog/:id' do 35 | # just get one dog, you might find him like this: 36 | @dog = Dog.find(params[:id]) 37 | # using the params convention, you specified in your route 38 | end 39 | 40 | post '/dog' do 41 | # create a new dog listing 42 | end 43 | 44 | put '/dog/:id' do 45 | # HTTP PUT request method to update an existing dog 46 | end 47 | 48 | patch '/dog/:id' do 49 | # HTTP PATCH request method to update an existing dog 50 | # See RFC 5789 for more information 51 | end 52 | 53 | delete '/dog/:id' do 54 | # HTTP DELETE request method to remove a dog who's been sold! 55 | end 56 | ``` 57 | 58 | As you can see from this contrived example, Sinatra's routing is very easy to get 59 | along with. Don't be fooled, though, Sinatra can do some pretty amazing things 60 | with Routes. 61 | 62 | Take a more in-depth look at [Sinatra's routes][routes], and see for yourself. 63 | 64 | [routes]: http://www.sinatrarb.com/intro#Routes 65 | [restful-web-services]: http://en.wikipedia.org/wiki/Representational_State_Transfer#RESTful_web_services 66 | [RFC 5789]: http://www.rfc-base.org/rfc-5789.html 67 | 68 | ## Filters 69 | 70 | Sinatra offers a way for you to hook into the request chain of your 71 | application via [Filters][filters]. 72 | 73 | Filters define two methods available, `before` and `after` which both accept a 74 | block to yield corresponding the request and optionally take a URL pattern to 75 | match to the request. 76 | 77 | ### before 78 | 79 | The `before` method will let you pass a block to be evaluated **before** _each_ 80 | and _every_ route gets processed. 81 | 82 | ```ruby 83 | before do 84 | MyStore.connect unless MyStore.connected? 85 | end 86 | 87 | get '/' do 88 | @list = MyStore.find(:all) 89 | erb :index 90 | end 91 | ``` 92 | 93 | In this example, we've set up a `before` filter to connect using a contrived 94 | `MyStore` module. 95 | 96 | ### after 97 | 98 | The `after` method lets you pass a block to be evaluated **after** _each_ and 99 | _every_ route gets processed. 100 | 101 | ```ruby 102 | after do 103 | MyStore.disconnect 104 | end 105 | ``` 106 | 107 | As you can see from this example, we're asking the `MyStore` module to 108 | disconnect after the request has been processed. 109 | 110 | ### Pattern Matching 111 | 112 | Filters optionally take a pattern to be matched against the requested URI 113 | during processing. Here's a quick example you could use to run a contrived 114 | `authenticate!` method before accessing any "admin" type requests. 115 | 116 | ```ruby 117 | before '/admin/*' do 118 | authenticate! 119 | end 120 | ``` 121 | 122 | [filters]: http://www.sinatrarb.com/intro#Filters 123 | 124 | ## Handlers 125 | 126 | Handlers are top-level methods available in Sinatra to take care of common HTTP 127 | routines. For instance there are handlers for [halting][halting] and 128 | [passing][passing]. 129 | 130 | There are also handlers for redirection: 131 | 132 | ```ruby 133 | get '/' do 134 | redirect '/someplace/else' 135 | end 136 | ``` 137 | 138 | This will return a 302 HTTP Response to `/someplace/else`. 139 | 140 | You can even use the Sinatra handler for sessions, just add this to your 141 | application or to a configure block: 142 | 143 | ```ruby 144 | enable :sessions 145 | ``` 146 | 147 | Then you will be able to use the default cookie based session handler in your 148 | application: 149 | 150 | ```ruby 151 | get '/' do 152 | session['counter'] ||= 0 153 | session['counter'] += 1 154 | "You've hit this page #{session['counter']} times!" 155 | end 156 | ``` 157 | 158 | Handlers can be extremely useful when used properly, probably the most common 159 | use is the `params` convention, which gives you access to any parameters passed 160 | in via the request object, or generated in your route pattern. 161 | 162 | [halting]: http://www.sinatrarb.com/intro#Halting 163 | [passing]: http://www.sinatrarb.com/intro#Passing 164 | 165 | 166 | ## Templates 167 | 168 | Sinatra is built upon an incredibly powerful templating engine, [Tilt][tilt]. 169 | Which, is designed to be a "thin interface" for frameworks that want to support 170 | multiple template engines. 171 | 172 | Some of Tilt's other all-star features include: 173 | 174 | * Custom template evaluation scopes / bindings 175 | * Ability to pass locals to template evaluation 176 | * Support for passing a block to template evaluation for "yield" 177 | * Backtraces with correct filenames and line numbers 178 | * Template file caching and reloading 179 | 180 | Best of all, Tilt includes support for some of the best templating engines 181 | available, including [HAML][haml], [Less CSS][less], and 182 | [CoffeeScript][coffeescript]. Also a ton of other lesser known, but equally 183 | awesome [templating languages][tilt] that would take too much space to list. 184 | 185 | All you need to get started is `erb`, which is included in Ruby. Views by 186 | default look in the `views` directory in your application root. 187 | 188 | So in your route you would have: 189 | 190 | ```ruby 191 | get '/' do 192 | erb :index # renders views/index.erb 193 | end 194 | ``` 195 | 196 | or to specify a template located in a subdirectory 197 | 198 | 199 | ```ruby 200 | get '/' do 201 | erb :"dogs/index" 202 | # would instead render views/dogs/index.erb 203 | end 204 | ``` 205 | 206 | Another default convention of Sinatra, is the layout, which automatically looks 207 | for a `views/layout` template file to render before loading any other views. In 208 | the case of using `erb`, your `views/layout.erb` would look something like 209 | this: 210 | 211 | ```ruby 212 | 213 | .. 214 | 215 | <%= yield %> 216 | 217 | 218 | ``` 219 | 220 | The possibilities are pretty much endless, here's a quick list of some of the 221 | most common use-cases covered in the README: 222 | 223 | * [Inline Templates][inline] 224 | * [Embedded Templates][embedded] 225 | * [Named Templates][named] 226 | 227 | For more specific details on how Sinatra handles templates, check the [README][templates]. 228 | 229 | [tilt]: http://github.com/rtomayko/tilt 230 | [haml]: http://haml-lang.com/ 231 | [less]: http://lesscss.org/ 232 | [coffeescript]: http://coffeescript.org/ 233 | [inline]: http://www.sinatrarb.com/intro#Inline%20Templates 234 | [embedded]: http://www.sinatrarb.com/intro#Embedded%20Templates 235 | [named]: http://www.sinatrarb.com/intro#Named%20Templates 236 | [templates]: http://www.sinatrarb.com/intro#Views%20/%20Templates 237 | 238 | ## Helpers 239 | 240 | Helpers are a great way to provide reusable code snippets in your application. 241 | 242 | ```ruby 243 | helpers do 244 | def bar(name) 245 | "#{name}bar" 246 | end 247 | end 248 | 249 | get '/:name' do 250 | bar(params[:name]) 251 | end 252 | ``` 253 | -------------------------------------------------------------------------------- /03__Organizing_your_application.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Organizing your application 3 | --- 4 | 5 | 6 | "You don't know how paralyzing that is, that stare of a blank canvas is" 7 | - Vincent van Gogh 8 | 9 | Sinatra is a blank canvas. It can transform into a single-file application, an 10 | embedded administration page, an API server, or a full-fledged hundred page 11 | website. Each use case is unique. This chapter will provide some advice on 12 | specific situations, but you will need to experiment to determine the best 13 | structure of your code. Don't be afraid to experiment. 14 | 15 | 16 | ## A Single File Application 17 | 18 | Obviously the file system structure is easy for this one. 19 | 20 | A single file can contain an entire multi-page application. Don't be afraid to 21 | use inline templates, multiple classes (this isn't Java, multiple classes will 22 | live happily next to each other in the same file) 23 | 24 | 25 | ## A Large Site 26 | 27 | This one is trickier. My advice is to look to Rails for advice. They have a 28 | well structured set of directories to hold many of the components that make up 29 | a larger application. Remember that this file structure is just a suggestion. 30 | 31 | ``` 32 | |- config.ru # A rackup file. Load server.rb, and 33 | |- server.rb # Loads all files, is the base class 34 | |- app/ 35 | \--- routes/ 36 | \------ users.rb 37 | \--- models/ 38 | \------ user.rb # Model. Database or not 39 | \--- views/ 40 | \------ users/ 41 | \--------- index.erb 42 | \--------- new.erb 43 | \--------- show.erb 44 | ``` 45 | 46 | In `server.rb` it should be a barebones application. 47 | 48 | ```ruby 49 | class Server < Sinatra::Base 50 | configure do 51 | # Load up database and such 52 | end 53 | end 54 | 55 | # Load all route files 56 | Dir[File.dirname(__FILE___) + "/app/routes/**"].each do |route| 57 | require route 58 | end 59 | ``` 60 | 61 | And the route files look something like: 62 | 63 | ```ruby 64 | # users.rb 65 | class Server < Sinatra::Base 66 | get '/users/:id' do 67 | erb :"users/show" 68 | end 69 | 70 | # more routes... 71 | end 72 | ``` 73 | -------------------------------------------------------------------------------- /04__Views.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Views 3 | --- 4 | 5 | ## RSS Feed 6 | 7 | The [builder][builder] gem/library for creating XML is required in this recipe. 8 | 9 | Assume that your site url is `http://liftoff.msfc.nasa.gov/`. 10 | 11 | ```ruby 12 | get '/rss.xml' do 13 | builder do |xml| 14 | xml.instruct! :xml, :version => '1.0' 15 | xml.rss :version => "2.0" do 16 | xml.channel do 17 | xml.title "Liftoff News" 18 | xml.description "Liftoff to Space Exploration." 19 | xml.link "http://liftoff.msfc.nasa.gov/" 20 | 21 | @posts.each do |post| 22 | xml.item do 23 | xml.title post.title 24 | xml.link "http://liftoff.msfc.nasa.gov/posts/#{post.id}" 25 | xml.description post.body 26 | xml.pubDate Time.parse(post.created_at.to_s).rfc822() 27 | xml.guid "http://liftoff.msfc.nasa.gov/posts/#{post.id}" 28 | end 29 | end 30 | end 31 | end 32 | end 33 | end 34 | ``` 35 | 36 | This will render the RSS inline, directly from the handler. 37 | 38 | 39 | ## CoffeeScript 40 | 41 | To render CoffeeScript templates you first need the `coffee-script` gem and 42 | `therubyracer`, or access to the `coffee` binary. 43 | 44 | Here's an example of using CoffeeScript with Sinatra's template rendering 45 | engine Tilt: 46 | 47 | ```ruby 48 | ## You'll need to require coffee-script in your app 49 | require 'coffee-script' 50 | 51 | get '/application.js' do 52 | coffee :application 53 | end 54 | ``` 55 | 56 | Renders `./views/application.coffee`. 57 | 58 | This works great if you have access to [nodejs][nodejs] or `therubyracer` gem 59 | on your platform of choice and hosting environment. If that's not the case, but 60 | you'd still like to use CoffeeScript, you can precompile your scripts using the 61 | `coffee` binary: 62 | 63 | ``` 64 | $ coffee -c -o public/javascripts/ src/ 65 | ``` 66 | 67 | Or you can use this example [rake][rake] task to compile them for you with the 68 | `coffee-script` gem, which can use either `therubyracer` gem or the `coffee` 69 | binary: 70 | 71 | ```ruby 72 | require 'coffee-script' 73 | 74 | namespace :js do 75 | desc "compile coffee-scripts from ./src to ./public/javascripts" 76 | task :compile do 77 | source = "#{File.dirname(__FILE__)}/src/" 78 | javascripts = "#{File.dirname(__FILE__)}/public/javascripts/" 79 | 80 | Dir.foreach(source) do |cf| 81 | unless cf == '.' || cf == '..' 82 | js = CoffeeScript.compile File.read("#{source}#{cf}") 83 | open "#{javascripts}#{cf.gsub('.coffee', '.js')}", 'w' do |f| 84 | f.puts js 85 | end 86 | end 87 | end 88 | end 89 | end 90 | ``` 91 | 92 | Now, with this rake task you can compile your coffee-scripts to 93 | `public/javascripts` by using the `rake js:compile` command. 94 | 95 | **Resources** 96 | 97 | If you get stuck or want to look into other ways of implementing CoffeeScript 98 | in your application, these are a great place to start: 99 | 100 | * [coffee-script][coffee-script-url] 101 | * [therubyracer][therubyracer] 102 | * [ruby-coffee-script][ruby-coffee-script] 103 | 104 | [therubyracer]: http://github.com/cowboyd/therubyracer 105 | [coffee-script-repo]: http://github.com/jashkenas/coffee-script 106 | [coffee-script-url]: http://coffeescript.org/ 107 | [builder]: http://builder.rubyforge.org/ 108 | [rake]: http://rake.rubyforge.org/ 109 | [nodejs]: http://nodejs.org/ 110 | [ruby-coffee-script]: http://github.com/josh/ruby-coffee-script 111 | -------------------------------------------------------------------------------- /05__Models.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Models 3 | --- 4 | 5 | ## DataMapper 6 | 7 | Start out by getting the DataMapper gem if you don't already have it, and then 8 | making sure it's in your application. A call to `setup` as usual will get the 9 | show started, and this example will include a 'Post' model. 10 | 11 | ```ruby 12 | require 'rubygems' 13 | require 'sinatra' 14 | require 'data_mapper' # metagem, requires common plugins too. 15 | 16 | # need install dm-sqlite-adapter 17 | DataMapper::setup(:default, "sqlite3://#{Dir.pwd}/blog.db") 18 | 19 | class Post 20 | include DataMapper::Resource 21 | property :id, Serial 22 | property :title, String 23 | property :body, Text 24 | property :created_at, DateTime 25 | end 26 | 27 | # Perform basic sanity checks and initialize all relationships 28 | # Call this when you've defined all your models 29 | DataMapper.finalize 30 | 31 | # automatically create the post table 32 | Post.auto_upgrade! 33 | ``` 34 | 35 | Once that is all well and good, you can actually start developing your 36 | application! 37 | 38 | ```ruby 39 | get '/' do 40 | # get the latest 20 posts 41 | @posts = Post.all(:order => [ :id.desc ], :limit => 20) 42 | erb :index 43 | end 44 | ``` 45 | 46 | Finally, the view at `./view/index.erb`: 47 | 48 | ```ruby 49 | <% @posts.each do |post| %> 50 |

<%= post.title %>

51 |

<%= post.body %>

52 | <% end %> 53 | ``` 54 | 55 | For more information on DataMapper, check out the [project 56 | documentation](http://datamapper.org/docs/ "DataMapper"). 57 | -------------------------------------------------------------------------------- /06__Helpers.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Helpers 3 | --- 4 | 5 | ## Implementation of rails style partials 6 | 7 | Using partials in your views is a great way to keep them clean. Since Sinatra takes the hands off approach to framework 8 | design, you'll have to implement a partial handler yourself. 9 | 10 | Here is a really basic version: 11 | 12 | ```ruby 13 | # Usage: partial :foo 14 | helpers do 15 | def partial(page, options={}) 16 | haml page, options.merge!(:layout => false) 17 | end 18 | end 19 | ``` 20 | 21 | A more advanced version that would handle passing local options, and looping over a hash would look like: 22 | 23 | ```ruby 24 | # Render the page once: 25 | # Usage: partial :foo 26 | # 27 | # foo will be rendered once for each element in the array, passing in a local variable named "foo" 28 | # Usage: partial :foo, :collection => @my_foos 29 | 30 | helpers do 31 | def partial(template, *args) 32 | options = args.extract_options! 33 | options.merge!(:layout => false) 34 | if collection = options.delete(:collection) then 35 | collection.inject([]) do |buffer, member| 36 | buffer << haml(template, options.merge( 37 | :layout => false, 38 | :locals => {template.to_sym => member} 39 | ) 40 | ) 41 | end.join("\n") 42 | else 43 | haml(template, options) 44 | end 45 | end 46 | end 47 | ``` 48 | -------------------------------------------------------------------------------- /07__Middleware.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Middleware 3 | --- 4 | 5 | Sinatra rides on [Rack][rack], a minimal standard interface for Ruby web 6 | frameworks. One of Rack’s most interesting capabilities for application 7 | developers is support for "middleware" -- components that sit between the 8 | server and your application monitoring and/or manipulating the HTTP 9 | request/response to provide various types of common functionality. 10 | 11 | Sinatra makes building Rack middleware pipelines a cinch via a top-level `use` method: 12 | 13 | ```ruby 14 | require 'sinatra' 15 | require 'my_custom_middleware' 16 | 17 | use Rack::Lint 18 | use MyCustomMiddleware 19 | 20 | get '/hello' do 21 | 'Hello World' 22 | end 23 | ``` 24 | 25 | ## Rack HTTP Basic Authentication 26 | 27 | The semantics of "use" are identical to those defined for the 28 | [Rack::Builder][rack_builder] DSL (most frequently used from rackup files). For 29 | example, the use method accepts multiple/variable args as well as blocks: 30 | 31 | ```ruby 32 | use Rack::Auth::Basic do |username, password| 33 | username == 'admin' && password == 'secret' 34 | end 35 | ``` 36 | 37 | Rack is distributed with a variety of standard middleware for logging, 38 | debugging, URL routing, authentication, and session handling. Sinatra uses many 39 | of of these components automatically based on configuration so you typically 40 | don’t have to use them explicitly. 41 | 42 | [rack]: http://rack.rubyforge.org/ 43 | [rack_builder]: http://rack.rubyforge.org/doc/classes/Rack/Builder.html 44 | -------------------------------------------------------------------------------- /08__Testing.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Testing 3 | --- 4 | 5 | 6 | ## Using Rack::Test 7 | 8 | Testing is an integral part of software development. In this section we will 9 | look into testing the Sinatra application itself. For unit testing your models 10 | or other classes, please consult the documentation of frameworks used 11 | (including your test framework itself). Sinatra itself uses Contest for 12 | testing, but feel free to use any framework you like. 13 | 14 | Bryan Helmkamp's [Rack::Test](https://github.com/brynary/rack-test) 15 | offers tools for mocking Rack request, sending those to your application and 16 | inspecting the response all wrapped in a small DSL. 17 | 18 | ### Firing Requests 19 | 20 | You import the DSL by including `Rack::Test::Methods` into your test 21 | framework. It is even usable without a framework and for other tasks besides 22 | testing. 23 | 24 | Imagine you have an application like this: 25 | 26 | ```ruby 27 | # myapp.rb 28 | require 'sinatra' 29 | 30 | get '/' do 31 | "Welcome to my page!" 32 | end 33 | 34 | post '/' do 35 | "Hello #{params[:name]}!" 36 | end 37 | ``` 38 | 39 | You have to define an `app` method pointing to your application class (which is 40 | `Sinatra::Application` per default): 41 | 42 | ```ruby 43 | begin 44 | # try to use require_relative first 45 | # this only works for 1.9 46 | require_relative 'my-app.rb' 47 | rescue NameError 48 | # oops, must be using 1.8 49 | # no problem, this will load it then 50 | require File.expand_path('my-app.rb', __FILE__) 51 | end 52 | 53 | require 'test/unit' 54 | require 'rack/test' 55 | 56 | class MyAppTest < Test::Unit::TestCase 57 | include Rack::Test::Methods 58 | 59 | def app 60 | Sinatra::Application 61 | end 62 | 63 | def test_my_default 64 | get '/' 65 | assert last_response.ok? 66 | assert_equal 'Welcome to my page!', last_response.body 67 | end 68 | 69 | def test_with_params 70 | post '/', :name => 'Frank' 71 | assert_equal 'Hello Frank!', last_response.body 72 | end 73 | end 74 | ``` 75 | 76 | ### Modifying `env` 77 | 78 | While parameters can be send via the second argument of a get/post/put/delete 79 | call (see the post example above), the env hash (and thereby the HTTP headers) 80 | can be modified with a third argument: 81 | 82 | ```ruby 83 | get '/foo', {}, 'HTTP_USER_AGENT' => 'Songbird 1.0' 84 | ``` 85 | 86 | This also allows passing internal `env` settings: 87 | 88 | ```ruby 89 | get '/foo', {}, 'rack.session' => { 'user_id' => 20 } 90 | ``` 91 | 92 | ### Cookies 93 | 94 | For example, add the following to your app to test against: 95 | 96 | ```ruby 97 | "Hello #{request.cookies['foo']}!" 98 | ``` 99 | 100 | Use `set_cookie` for setting and removing cookies, and the access them in your response: 101 | 102 | ```ruby 103 | response.set_cookie 'foo=bar' 104 | get '/' 105 | assert_equal 'Hello bar!', last_response.body 106 | ``` 107 | 108 | ### Asserting Expectations About The Response 109 | 110 | Once a request method has been invoked, the following attributes are 111 | available for making assertions: 112 | 113 | * `app` - The Sinatra application class that handled the mock request. 114 | 115 | * `last_request` - The 116 | [`Rack::MockRequest`](http://rdoc.info/gems/rack/1.2.1/frames/Rack/MockRequest) 117 | used to generate the request. 118 | 119 | * `last_response` - A 120 | [`Rack::MockResponse`](http://rdoc.info/gems/rack/1.2.1/frames/Rack/MockResponse) 121 | instance with information on the response generated by the application. 122 | 123 | Assertions are typically made against the `last_response` object. 124 | Consider the following examples: 125 | 126 | ```ruby 127 | def test_it_says_hello_world 128 | get '/' 129 | assert last_response.ok? 130 | assert_equal 'Hello World'.length.to_s, last_response.headers['Content-Length'] 131 | assert_equal 'Hello World', last_response.body 132 | end 133 | ``` 134 | 135 | ### Optional Test Setup 136 | 137 | The `Rack::Test` mock request methods send requests to the return value of 138 | a method named `app`. 139 | 140 | If you're testing a modular application that has multiple `Sinatra::Base` 141 | subclasses, simply set the `app` method to return your particular class. 142 | 143 | ```ruby 144 | def app 145 | MySinatraApp 146 | end 147 | ``` 148 | 149 | If you're using a classic style Sinatra application, then you need to return an 150 | instance of `Sinatra::Application`. 151 | 152 | ```ruby 153 | def app 154 | Sinatra::Application 155 | end 156 | ``` 157 | 158 | ### Making `Rack::Test` available to all test cases 159 | 160 | If you'd like the `Rack::Test` methods to be available to all test cases 161 | without having to include it each time, you can include the `Rack::Test` 162 | module in the `Test::Unit::TestCase` class: 163 | 164 | ```ruby 165 | require 'test/unit' 166 | require 'rack/test' 167 | 168 | class Test::Unit::TestCase 169 | include Rack::Test::Methods 170 | end 171 | ``` 172 | 173 | Now all `TestCase` subclasses will automatically have `Rack::Test` 174 | available to them. 175 | -------------------------------------------------------------------------------- /09__Development.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Development Techniques 3 | --- 4 | 5 | ## Automatic Code Reloading 6 | 7 | Restarting an application manually after every code change is both slow and 8 | painful. It can easily be avoided by using a tool for automatic code reloading. 9 | 10 | ### Shotgun 11 | 12 | Shotgun will actually restart your application on every request. This has the 13 | advantage over other reloading techniques of always producing correct results. 14 | However, since it actually restarts your application, it is rather slow 15 | compared to the alternatives. Moreover, since it relies on `fork`, it is not 16 | available on Windows and JRuby. 17 | 18 | Usage is rather simple: 19 | 20 | ```ruby 21 | gem install shotgun # run only once, to install shotgun 22 | shotgun my_app.rb 23 | ``` 24 | 25 | If you want to run a modular application, create a file named `config.ru` with 26 | similar content: 27 | 28 | ```ruby 29 | require 'my_app' 30 | run MyApp 31 | ``` 32 | 33 | And run it by calling `shotgun` without arguments. 34 | 35 | The `shotgun` executable takes arguments similar to those of the `rackup` 36 | command, run `shotgun --help` for more information. 37 | -------------------------------------------------------------------------------- /10__Deployment.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Deployment 3 | --- 4 | 5 | ## Heroku 6 | 7 | This is the easiest configuration + deployment option. [Heroku] has full 8 | support for Sinatra applications. Deploying to Heroku is simply a matter of 9 | pushing to a remote git repository. 10 | 11 | Steps to deploy to Heroku: 12 | 13 | * Create an [account](http://heroku.com/signup) if you don't have one 14 | * Download and install [Heroku toolbelt](https://toolbelt.heroku.com/) 15 | * Make sure you have a `Gemfile`. Otherwise, you can create one and install the `sinatra` gem using `bundler`. 16 | * Make a config.ru in the root-directory 17 | * Create the app on heroku 18 | * Push to it 19 | 20 | 1. Install `bundler` if you haven't yet (`gem install bundler`). Create a `Gemfile` using `bundle init`. Modify your `Gemfile` to look like: 21 | 22 | ```ruby 23 | source 'http://rubygems.org' 24 | 25 | gem 'sinatra' 26 | ``` 27 | 28 | Run `bundle` to install the gem. 29 | 30 | It is possible to specify a specific Ruby version on your Gemfile. 31 | For more information, check [this](https://devcenter.heroku.com/articles/ruby-versions). 32 | 33 | 2. Here is an example config.ru file that does two things. First, it requires 34 | your main app file, whatever it's called. In the example, it will look for 35 | `myapp.rb`. Second, run your application. If you're subclassing, use the 36 | subclass's name, otherwise use Sinatra::Application. 37 | 38 | ```ruby 39 | require "myapp" 40 | 41 | run Sinatra::Application 42 | ``` 43 | 44 | 3. Create the app and push to it 45 | 46 | From the root directory of the application, run these commands: 47 | 48 | ```bash 49 | $ heroku create # This will add heroku as a remote 50 | $ git push heroku master 51 | ``` 52 | 53 | For more details see [this](http://github.com/sinatra/heroku-sinatra-app). 54 | 55 | [Heroku]: http://www.heroku.com 56 | -------------------------------------------------------------------------------- /11__Contributing.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Contributing 3 | --- 4 | 5 | ## There are plenty of ways to [contribute to Sinatra][contributing]. 6 | 7 | **Got a recipe or tutorial?** 8 | Check out the [Sinatra Recipes][sinatra-recipes] project for all of the recent 9 | additions from the community. 10 | 11 | If you're looking for something to work on be sure to check the [issue 12 | tracker][issues]. Once you have [forked the project][forking], feel free to 13 | send us a [pull request][pull-requests]. 14 | 15 | Check the [wiki][wiki] for more information. 16 | 17 | Join us on IRC (#sinatra at irc.freenode.org) if you need help with anything. 18 | 19 | [contributing]: http://www.sinatrarb.com/contributing 20 | [sinatra-book]: http://github.com/sinatra/sinatra-book 21 | [sinatra-recipes]: http://github.com/sinatra/sinatra-recipes 22 | [issues]: http://github.com/sinatra/sinatra-book/issues 23 | [wiki]: http://github.com/sinatra/sinatra-book/wiki/How-to-contribute 24 | [forking]: http://help.github.com/forking/ 25 | [pull-requests]: http://help.github.com/pull-requests/ 26 | -------------------------------------------------------------------------------- /META/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Sinatra 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /META/book.yml: -------------------------------------------------------------------------------- 1 | ############################ 2 | # Book (Meta) Info 3 | 4 | title: "Sinatra (Book Edition)" 5 | author: 6 | name: "Chris Schneider, Zachary Scott et al" 7 | -------------------------------------------------------------------------------- /META/contents.yml: -------------------------------------------------------------------------------- 1 | ###################### 2 | # Table of Contents 3 | 4 | - title: Introduction 5 | path: 01__Introduction.md 6 | sections: 7 | - title: What is Sinatra? 8 | - title: Installation 9 | sections: 10 | - title: Dependencies 11 | - title: Living on the Edge 12 | - title: Hello World Application 13 | - title: Real World Applications in Sinatra 14 | sections: 15 | - title: Github Services 16 | - title: About this book 17 | - title: Need Help? 18 | 19 | - title: Getting to know Sinatra 20 | path: 02__Getting_to_know_Sinatra.md 21 | sections: 22 | - title: It's Witchcraft 23 | - title: Routing 24 | - title: Filters 25 | sections: 26 | - title: before 27 | - title: after 28 | - title: Pattern Matching 29 | - title: Handlers 30 | - title: Templates 31 | - title: Helpers 32 | 33 | - title: Organizing your application 34 | path: 03__Organizing_your_application.md 35 | 36 | - title: Views 37 | path: 04__Views.md 38 | sections: 39 | - title: RSS Feed 40 | - title: CoffeeScript 41 | 42 | - title: Models 43 | path: 05__Models.md 44 | sections: 45 | - title: DataMapper 46 | 47 | - title: Helpers 48 | path: 06__Helpers.md 49 | sections: 50 | - title: Implementation of rails style partials 51 | 52 | - title: Middleware 53 | path: 07__Middleware.md 54 | sections: 55 | - title: Rack HTTP Basic Authentication 56 | 57 | - title: Testing 58 | path: 08__Testing.md 59 | sections: 60 | - title: Using Rack::Test 61 | sections: 62 | - title: Firing Requests 63 | - title: "Modifying `env`" 64 | - title: Cookies 65 | - title: Asserting Expectations About The Response 66 | - title: Optional Test Setup 67 | - title: "Making `Rack::Test` available to all test cases" 68 | 69 | - title: Development Techniques 70 | path: 09__Development.md 71 | sections: 72 | - title: Automatic Code Reloading 73 | sections: 74 | - title: Shotgun 75 | 76 | - title: Deployment 77 | path: 10__Deployment.md 78 | sections: 79 | - title: Heroku 80 | 81 | - title: Contributing 82 | path: 11__Contributing.md 83 | sections: 84 | - title: There are plenty of ways to contribute to Sinatra. 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | See the live version @ [`yukimotopress.github.io/sinatra-intro`](http://yukimotopress.github.io/sinatra-intro) 3 | 4 | --- 5 | 6 | # Sinatra (Manuscripts Book Edition) 7 | 8 | by [Chris Schneider](https://github.com/cschneid), [Zachary Scott](https://github.com/zzak) et al 9 | 10 | This is the original source reformatted in a single-page book edition (using the [Manuscripts format](http://manuscripts.github.io)). 11 | 12 | - [Introduction](01__Introduction.md) 13 | - [Getting to know Sinatra](02__Getting_to_know_Sinatra.md) 14 | - [Organizing your application](03__Organizing_your_application.md) 15 | - [Views](04__Views.md) 16 | - [Models](05__Models.md) 17 | - [Helpers](06__Helpers.md) 18 | - [Middleware](07__Middleware.md) 19 | - [Testing](08__Testing.md) 20 | - [Development](09__Development.md) 21 | - [Deployment](10__Deployment.md) 22 | - [Contributing](11__Contributing.md) 23 | 24 | 25 | ## Meta 26 | 27 | ### Sources 28 | 29 | See the [original source](https://github.com/sinatra/sinatra-book) repo. 30 | --------------------------------------------------------------------------------