# Sinatra *注ï¼æ¬ææ¡£æ¯è±æççç¿»è¯ï¼å 容æ´æ°æå¯è½ä¸åæ¶ã å¦æä¸ä¸è´çå°æ¹ï¼è¯·ä»¥è±æç为åã* Sinatraæ¯ä¸ä¸ªåºäºRubyè¯è¨ç[DSL](http://en.wikipedia.org/wiki/Domain-specific_language)ï¼ é¢åä¸å±è¯è¨ï¼ï¼å¯ä»¥è½»æ¾ãå¿«éçå建webåºç¨ã ~~~~ ruby # myapp.rb require 'sinatra' get '/' do 'Hello world!' end ~~~~ å®è£ gemï¼ç¶åè¿è¡ï¼ ~~~~ shell gem install sinatra ruby myapp.rb ~~~~ å¨è¯¥å°åæ¥çï¼ http://localhost:4567 è¿ä¸ªæ¶å访é®å°åå°ç»å®å° 127.0.0.1 å localhost ï¼å¦æä½¿ç¨ vagrant è¿è¡å¼åï¼è®¿é®ä¼å¤±è´¥ï¼æ¤æ¶å°±éè¦è¿è¡ ip ç»å®äºï¼ ~~~~ shell ruby myapp.rb -o 0.0.0.0 ~~~~ ```-o``` è¿ä¸ªåæ°å°±æ¯è¿è¡ Listening æ¶åçå¬çç»å®ï¼è½ä»éè¿ IPã127.0.0.1ãlocalhost + 端å£å·è¿è¡è®¿é®ã å®è£ Sintraåï¼æ好åè¿è¡`gem install thin`å®è£ Thinãè¿æ ·ï¼Sinatraä¼ä¼å éæ©Thinä½ä¸ºæå¡å¨ã ## è·¯ç±(route) å¨Sinatraä¸ï¼ä¸ä¸ªè·¯ç±å为两é¨åï¼HTTPæ¹æ³(GET, POSTç)åURLå¹é èå¼ã æ¯ä¸ªè·¯ç±é½æä¸ä¸ªè¦æ§è¡ç代ç åï¼ ~~~~ ruby get '/' do .. æ¾ç¤ºå 容 .. end post '/' do .. å建å 容 .. end put '/' do .. æ´æ°å 容 .. end delete '/' do .. å é¤å 容 .. end options '/' do .. æ¾ç¤ºå½ä»¤å表 .. end link '/' do .. 建ç«æç§èç³» .. end unlink '/' do .. 解é¤æç§èç³» .. end ~~~~ è·¯ç±æç §å®ä»¬è¢«å®ä¹ç顺åºè¿è¡å¹é ã 第ä¸ä¸ªä¸è¯·æ±å¹é çè·¯ç±ä¼è¢«è°ç¨ã è·¯ç±èå¼å¯ä»¥å æ¬å ·ååæ°ï¼å¯éè¿`params`åå¸è¡¨è·å¾ï¼ ~~~~ ruby get '/hello/:name' do # å¹é "GET /hello/foo" å "GET /hello/bar" # params['name'] çå¼æ¯ 'foo' æè 'bar' "Hello #{params['name']}!" end ~~~~ ä½ åæ ·å¯ä»¥éè¿ä»£ç ååæ°è·å¾å ·ååæ°ï¼ ~~~~ ruby get '/hello/:name' do |n| "Hello #{n}!" end ~~~~ è·¯ç±èå¼ä¹å¯ä»¥å å«éé 符åæ°ï¼ å¯ä»¥éè¿`params['splat']`æ°ç»è·å¾ã ~~~~ ruby get '/say/*/to/*' do # å¹é /say/hello/to/world params['splat'] # => ["hello", "world"] end get '/download/*.*' do # å¹é /download/path/to/file.xml params['splat'] # => ["path/to/file", "xml"] end ~~~~ éè¿æ£å表达å¼å¹é çè·¯ç±ï¼ ~~~~ ruby get /\A\/hello\/([\w]+)\z/ do "Hello, #{params['captures'].first}!" end ~~~~ æè 使ç¨ä»£ç ååæ°ï¼ ~~~~ ruby get %r{/hello/([\w]+)} do |c| "Hello, #{c}!" end ~~~~ ### æ¡ä»¶ è·¯ç±ä¹å¯ä»¥å å«å¤æ ·çå¹é æ¡ä»¶ï¼æ¯å¦user agentï¼ ~~~~ ruby get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do "ä½ æ£å¨ä½¿ç¨Songbirdï¼çæ¬æ¯ #{params['agent'][0]}" end get '/foo' do # å¹é é¤Songbird以å¤çæµè§å¨ end ~~~~ å ¶ä»å¯éçæ¡ä»¶æ¯ `host_name` å `provides`ï¼ ~~~~ ruby get '/', :host_name => /^admin\./ do "管çååºåï¼æ æè¿å ¥ï¼" end get '/', :provides => 'html' do haml :index end get '/', :provides => ['rss', 'atom', 'xml'] do builder :feed end ~~~~ ä½ ä¹å¯ä»¥èªå®ä¹æ¡ä»¶ï¼ ~~~~ ruby set(:probability) { |value| condition { rand <= value } } get '/win_a_car', :probability => 0.1 do "You won!" end get '/win_a_car' do "Sorry, you lost." end ~~~~ ### è¿åå¼ è·¯ç±ä»£ç åçè¿åå¼è³å°å³å®äºè¿åç»HTTP客æ·ç«¯çååºä½ï¼ æè è³å°å³å®äºå¨Rackå æ ä¸çä¸ä¸ä¸ªä¸é´ä»¶ã 大å¤æ°æ åµä¸ï¼å°æ¯ä¸ä¸ªå符串ï¼å°±åä¸é¢çä¾åä¸çä¸æ ·ã ä½æ¯å ¶ä»å¼ä¹æ¯å¯ä»¥æ¥åçã ä½ å¯ä»¥è¿åä»»ä½å¯¹è±¡ï¼æè æ¯ä¸ä¸ªåççRackååºï¼ Rack body对象æè HTTPç¶æç ï¼ - ä¸ä¸ªå å«ä¸ä¸ªå ç´ çæ°ç»: `[ç¶æ (Fixnum), 头 (Hash), ååºä½ (ååº #each)]` - ä¸ä¸ªå å«ä¸¤ä¸ªå ç´ çæ°ç»: `[ç¶æ (Fixnum), ååºä½ (ååº #each)]` - ä¸ä¸ªè½å¤ååº `#each` ï¼åªä¼ åå符串ç对象 - ä¸ä¸ªä»£è¡¨ç¶æç çæ°å é£æ ·ï¼æ们å¯ä»¥è½»æ¾çå®ç°ä¾å¦æµå¼ä¼ è¾çä¾åï¼ ~~~~ ruby class Stream def each 100.times { |i| yield "#{i}\n" } end end get('/') { Stream.new } ~~~~ ### èªå®ä¹è·¯ç±å¹é å¨ å¦ä¸æ¾ç¤ºï¼Sinatraå ç½®äºå¯¹äºä½¿ç¨å符串åæ£å表达å¼ä½ä¸ºè·¯ç±å¹é çæ¯æã ä½æ¯ï¼å®å¹¶æ²¡æåªéäºæ¤ã ä½ å¯ä»¥é常容æå°å®ä¹ä½ èªå·±çå¹é å¨: ~~~~ ruby class AllButPattern Match = Struct.new(:captures) def initialize(except) @except = except @captures = Match.new([]) end def match(str) @captures unless @except === str end end def all_but(pattern) AllButPattern.new(pattern) end get all_but("/index") do # ... end ~~~~ ä¸é¢çä¾åå¯è½å¤ªç¹çäºï¼ å 为å®ä¹å¯ä»¥ç¨æ´ç®åçæ¹å¼è¡¨è¿°: ~~~~ ruby get // do pass if request.path_info == "/index" # ... end ~~~~ æè ï¼ä½¿ç¨æ¶æååæ¥æ¾: ~~~~ ruby get %r{^(?!/index$)} do # ... end ~~~~ ## éææ件 éææ件æ¯ä» `./public_folder` ç®å½æä¾æå¡ãä½ å¯ä»¥éè¿è®¾ç½®`:public` é项设å®ä¸ä¸ªä¸åçä½ç½®ï¼ ~~~~ ruby set :public_folder, File.dirname(__FILE__) + '/static' ~~~~ 请注æpublicç®å½å并没æ被å å«å¨URLä¹ä¸ãæ件 `./public/css/style.css`æ¯éè¿ `http://example.com/css/style.css`å°å访é®çã ## è§å¾ / æ¨¡æ¿ æ¨¡æ¿è¢«åå®ç´æ¥ä½äº`./views`ç®å½ã è¦ä½¿ç¨ä¸åçè§å¾ç®å½ï¼ ~~~~ ruby set :views, File.dirname(__FILE__) + '/templates' ~~~~ éè¦æ示ï¼ä½ åªå¯ä»¥éè¿ç¬¦å·å¼ç¨æ¨¡æ¿ï¼ å³ä½¿å®ä»¬å¨åç®å½ä¸ ï¼å¨è¿ç§æ åµä¸ï¼ä½¿ç¨ `:'subdir/template'`ï¼ã å¦æä½ ä¸ç¨ç¬¦å·ãèç¨å符串çè¯ï¼ å¡«å æ¹æ³ä¼åªæä½ ä¼ å ¥çå符串å½æå 容æ¾ç¤ºåºæ¥ï¼èä¸è°ç¨æ¨¡æ¿ã ### Hamlæ¨¡æ¿ éè¦å¼å ¥ `haml` gem/library以填å HAML 模æ¿ï¼ ~~~~ ruby # ä½ éè¦å¨ä½ çåºç¨ä¸å¼å ¥ haml require 'haml' get '/' do haml :index end ~~~~ å¡«å `./views/index.haml`ã [Hamlçé项](http://haml.info/docs/yardoc/file.HAML_REFERENCE.html#options) å¯ä»¥éè¿Sinatraçé ç½®å ¨å±è®¾å®ï¼ åè§ [é项åé ç½®](http://www.sinatrarb.com/configuration.html)ï¼ ä¹å¯ä»¥ä¸ªå«ç被è¦çã ~~~~ ruby set :haml, {:format => :html5 } # é»è®¤çHamlè¾åºæ ¼å¼æ¯ :xhtml get '/' do haml :index, :haml_options => {:format => :html4 } # 被è¦çï¼åæ:html4 end ~~~~ ### Erbæ¨¡æ¿ ~~~~ ruby # ä½ éè¦å¨ä½ çåºç¨ä¸å¼å ¥ erb require 'erb' get '/' do erb :index end ~~~~ è¿éè°ç¨çæ¯ `./views/index.erb` ### Erubis éè¦å¼å ¥ `erubis` gem/library以填å erubis 模æ¿ï¼ ~~~~ ruby # ä½ éè¦å¨ä½ çåºç¨ä¸å¼å ¥ erubis require 'erubis' get '/' do erubis :index end ~~~~ è¿éè°ç¨çæ¯ `./views/index.erubis` 使ç¨Erubis代æ¿Erbä¹æ¯å¯è½ç: ~~~~ ruby require 'erubis' Tilt.register :erb, Tilt[:erubis] get '/' do erb :index end ~~~~ 使ç¨Erubisæ¥å¡«å `./views/index.erb`ã ### Builder æ¨¡æ¿ éè¦å¼å ¥ `builder` gem/library 以填å builder templatesï¼ ~~~~ ruby # éè¦å¨ä½ çåºç¨ä¸å¼å ¥builder require 'builder' get '/' do builder :index end ~~~~ è¿éè°ç¨çæ¯ `./views/index.builder`ã ### Nokogiri æ¨¡æ¿ éè¦å¼å ¥ `nokogiri` gem/library 以填å nokogiri 模æ¿ï¼ ~~~~ ruby # éè¦å¨ä½ çåºç¨ä¸å¼å ¥ nokogiri require 'nokogiri' get '/' do nokogiri :index end ~~~~ è¿éè°ç¨çæ¯ `./views/index.nokogiri`ã ### Sass æ¨¡æ¿ éè¦å¼å ¥ `haml` æè `sass` gem/library 以填å Sass 模æ¿ï¼ ~~~~ ruby # éè¦å¨ä½ çåºç¨ä¸å¼å ¥ haml æè sass require 'sass' get '/stylesheet.css' do sass :stylesheet end ~~~~ è¿éè°ç¨çæ¯ `./views/stylesheet.sass`ã [Sass çé项](http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options) å¯ä»¥éè¿Sinatraéé¡¹å ¨å±è®¾å®ï¼ åè [é项åé ç½®ï¼è±æï¼](http://www.sinatrarb.com/configuration.html), ä¹å¯ä»¥å¨ä¸ªä½çåºç¡ä¸è¦çã ~~~~ ruby set :sass, {:style => :compact } # é»è®¤ç Sass æ ·å¼æ¯ :nested get '/stylesheet.css' do sass :stylesheet, :style => :expanded # è¦ç end ~~~~ ### Scss æ¨¡æ¿ éè¦å¼å ¥ `haml` æè `sass` gem/library æ¥å¡«å Scss templatesï¼ ~~~~ ruby # éè¦å¨ä½ çåºç¨ä¸å¼å ¥ haml æè sass require 'sass' get '/stylesheet.css' do scss :stylesheet end ~~~~ è¿éè°ç¨çæ¯ `./views/stylesheet.scss`ã [Scssçé项](http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options) å¯ä»¥éè¿Sinatraéé¡¹å ¨å±è®¾å®ï¼ åè [é项åé ç½®ï¼è±æï¼](http://www.sinatrarb.com/configuration.html), ä¹å¯ä»¥å¨ä¸ªä½çåºç¡ä¸è¦çã ~~~~ ruby set :scss, :style => :compact # default Scss style is :nested get '/stylesheet.css' do scss :stylesheet, :style => :expanded # overridden end ~~~~ ### Less æ¨¡æ¿ éè¦å¼å ¥ `less` gem/library 以填å Less 模æ¿ï¼ ~~~~ ruby # éè¦å¨ä½ çåºç¨ä¸å¼å ¥ less require 'less' get '/stylesheet.css' do less :stylesheet end ~~~~ è¿éè°ç¨çæ¯ `./views/stylesheet.less`ã ### Liquid æ¨¡æ¿ éè¦å¼å ¥ `liquid` gem/library æ¥å¡«å Liquid 模æ¿ï¼ ~~~~ ruby # éè¦å¨ä½ çåºç¨ä¸å¼å ¥ liquid require 'liquid' get '/' do liquid :index end ~~~~ è¿éè°ç¨çæ¯ `./views/index.liquid`ã å ä¸ºä½ ä¸è½å¨Liquid 模æ¿ä¸è°ç¨ Ruby æ¹æ³ (é¤äº `yield`) ï¼ ä½ å ä¹æ»æ¯éè¦ä¼ élocalsç»å®ï¼ ~~~~ ruby liquid :index, :locals => { :key => 'value' } ~~~~ ### Markdown æ¨¡æ¿ éè¦å¼å ¥ `rdiscount` gem/library 以填å Markdown 模æ¿ï¼ ~~~~ ruby # éè¦å¨ä½ çåºç¨ä¸å¼å ¥rdiscount require "rdiscount" get '/' do markdown :index end ~~~~ è¿éè°ç¨çæ¯ `./views/index.markdown` (`md` å `mkd` ä¹æ¯åççæ件æ©å±å)ã å¨markdownä¸æ¯ä¸å¯ä»¥è°ç¨æ¹æ³çï¼ä¹ä¸å¯ä»¥ä¼ é localsç»å®ã ä½ å æ¤ä¸è¬ä¼ç»åå ¶ä»çå¡«å å¼ææ¥ä½¿ç¨å®ï¼ ~~~~ ruby erb :overview, :locals => { :text => markdown(:introduction) } ~~~~ 请注æä½ ä¹å¯ä»¥ä»å ¶ä»æ¨¡æ¿ä¸è°ç¨ markdown æ¹æ³ï¼ ~~~~ ruby %h1 Hello From Haml! %p= markdown(:greetings) ~~~~ æ¢ç¶ä½ ä¸è½å¨Markdownä¸è°ç¨Rubyï¼ä½ ä¸è½ä½¿ç¨Markdownç¼åçå¸å±ã ä¸è¿ï¼ä½¿ç¨å ¶ä»å¡«å å¼æä½ä¸ºæ¨¡ççå¸å±æ¯å¯è½çï¼ éè¿ä¼ é`:layout_engine`é项: ~~~~ ruby get '/' do markdown :index, :layout_engine => :erb end ~~~~ è¿å°ä¼è°ç¨ `./views/index.md` å¹¶ä½¿ç¨ `./views/layout.erb` ä½ä¸ºå¸å±ã 请记ä½ä½ å¯ä»¥å ¨å±è®¾å®è¿ä¸ªé项: ~~~~ ruby set :markdown, :layout_engine => :haml, :layout => :post get '/' do markdown :index end ~~~~ è¿å°ä¼è°ç¨ `./views/index.markdown` (åä»»ä½å ¶ä»ç Markdown 模ç) å¹¶ä½¿ç¨ `./views/post.haml` ä½ä¸ºå¸å±. ä¹å¯è½ä½¿ç¨BlueClothèä¸æ¯RDiscountæ¥è§£æMarkdownæ件: ~~~~ ruby require 'bluecloth' Tilt.register 'markdown', BlueClothTemplate Tilt.register 'mkd', BlueClothTemplate Tilt.register 'md', BlueClothTemplate get '/' do markdown :index end ~~~~ 使ç¨BlueClothæ¥å¡«å `./views/index.md` ã ### Textile æ¨¡æ¿ éè¦å¼å ¥ `RedCloth` gem/library 以填å Textile 模æ¿ï¼ ~~~~ ruby # å¨ä½ çåºç¨ä¸å¼å ¥redcloth require "redcloth" get '/' do textile :index end ~~~~ è¿éè°ç¨çæ¯ `./views/index.textile`ã å¨textileä¸æ¯ä¸å¯ä»¥è°ç¨æ¹æ³çï¼ä¹ä¸å¯ä»¥ä¼ é localsç»å®ã ä½ å æ¤ä¸è¬ä¼ç»åå ¶ä»çå¡«å å¼ææ¥ä½¿ç¨å®ï¼ ~~~~ ruby erb :overview, :locals => { :text => textile(:introduction) } ~~~~ 请注æä½ ä¹å¯ä»¥ä»å ¶ä»æ¨¡æ¿ä¸è°ç¨`textile`æ¹æ³ï¼ ~~~~ ruby %h1 Hello From Haml! %p= textile(:greetings) ~~~~ æ¢ç¶ä½ ä¸è½å¨Textileä¸è°ç¨Rubyï¼ä½ ä¸è½ä½¿ç¨Textileç¼åçå¸å±ã ä¸è¿ï¼ä½¿ç¨å ¶ä»å¡«å å¼æä½ä¸ºæ¨¡ççå¸å±æ¯å¯è½çï¼ éè¿ä¼ é`:layout_engine`é项: ~~~~ ruby get '/' do textile :index, :layout_engine => :erb end ~~~~ è¿å°ä¼å¡«å `./views/index.textile` å¹¶ä½¿ç¨ `./views/layout.erb` ä½ä¸ºå¸å±ã 请记ä½ä½ å¯ä»¥å ¨å±è®¾å®è¿ä¸ªé项: ~~~~ ruby set :textile, :layout_engine => :haml, :layout => :post get '/' do textile :index end ~~~~ è¿å°ä¼è°ç¨ `./views/index.textile` (åä»»ä½å ¶ä»ç Textile 模ç) å¹¶ä½¿ç¨ `./views/post.haml` ä½ä¸ºå¸å±. ### RDoc æ¨¡æ¿ éè¦å¼å ¥ `RDoc` gem/library 以填å RDoc模æ¿ï¼ ~~~~ ruby # éè¦å¨ä½ çåºç¨ä¸å¼å ¥rdoc/markup/to_html require "rdoc" require "rdoc/markup/to_html" get '/' do rdoc :index end ~~~~ è¿éè°ç¨çæ¯ `./views/index.rdoc`ã å¨rdocä¸æ¯ä¸å¯ä»¥è°ç¨æ¹æ³çï¼ä¹ä¸å¯ä»¥ä¼ élocalsç»å®ã ä½ å æ¤ä¸è¬ä¼ç»åå ¶ä»çå¡«å å¼ææ¥ä½¿ç¨å®ï¼ ~~~~ ruby erb :overview, :locals => { :text => rdoc(:introduction) } ~~~~ 请注æä½ ä¹å¯ä»¥ä»å ¶ä»æ¨¡æ¿ä¸è°ç¨`rdoc`æ¹æ³ï¼ ~~~~ ruby %h1 Hello From Haml! %p= rdoc(:greetings) ~~~~ æ¢ç¶ä½ ä¸è½å¨RDocä¸è°ç¨Rubyï¼ä½ ä¸è½ä½¿ç¨RDocç¼åçå¸å±ã ä¸è¿ï¼ä½¿ç¨å ¶ä»å¡«å å¼æä½ä¸ºæ¨¡ççå¸å±æ¯å¯è½çï¼ éè¿ä¼ é`:layout_engine`é项: ~~~~ ruby get '/' do rdoc :index, :layout_engine => :erb end ~~~~ è¿å°ä¼è°ç¨ `./views/index.rdoc` å¹¶ä½¿ç¨ `./views/layout.erb` ä½ä¸ºå¸å±ã 请记ä½ä½ å¯ä»¥å ¨å±è®¾å®è¿ä¸ªé项: ~~~~ ruby set :rdoc, :layout_engine => :haml, :layout => :post get '/' do rdoc :index end ~~~~ è¿å°ä¼è°ç¨ `./views/index.rdoc` (åä»»ä½å ¶ä»ç RDoc 模ç) å¹¶ä½¿ç¨ `./views/post.haml` ä½ä¸ºå¸å±. ### Radius æ¨¡æ¿ éè¦å¼å ¥ `radius` gem/library 以填å Radius 模æ¿ï¼ ~~~~ ruby # éè¦å¨ä½ çåºç¨ä¸å¼å ¥radius require 'radius' get '/' do radius :index end ~~~~ è¿éè°ç¨çæ¯ `./views/index.radius`ã å ä¸ºä½ ä¸è½å¨Radius 模æ¿ä¸è°ç¨ Ruby æ¹æ³ (é¤äº `yield`) ï¼ ä½ å ä¹æ»æ¯éè¦ä¼ élocalsç»å®ï¼ ~~~~ ruby radius :index, :locals => { :key => 'value' } ~~~~ ### Markaby æ¨¡æ¿ éè¦å¼å ¥`markaby` gem/library以填å Markaby模æ¿ï¼ ~~~~ ruby #éè¦å¨ä½ çåºç¨ä¸å¼å ¥ markaby require 'markaby' get '/' do markaby :index end ~~~~ è¿éè°ç¨çæ¯ `./views/index.mab`ã ä½ ä¹å¯ä»¥ä½¿ç¨åµå ¥ç Markaby: ~~~~ ruby get '/' do markaby { h1 "Welcome!" } end ~~~~ ### Slim æ¨¡æ¿ éè¦å¼å ¥ `slim` gem/library æ¥å¡«å Slim 模æ¿ï¼ ~~~~ ruby # éè¦å¨ä½ çåºç¨ä¸å¼å ¥ slim require 'slim' get '/' do slim :index end ~~~~ è¿éè°ç¨çæ¯ `./views/index.slim`ã ### Creole æ¨¡æ¿ éè¦å¼å ¥ `creole` gem/library æ¥å¡«å Creole 模æ¿ï¼ ~~~~ ruby # éè¦å¨ä½ çåºç¨ä¸å¼å ¥ creole require 'creole' get '/' do creole :index end ~~~~ è¿éè°ç¨çæ¯ `./views/index.creole`ã ### CoffeeScript æ¨¡æ¿ éè¦å¼å ¥ `coffee-script` gem/library 并è³å°æ»¡è¶³ä¸é¢æ¡ä»¶ä¸é¡¹ 以æ§è¡Javascript: - `node` (æ¥èª Node.js) å¨ä½ çè·¯å¾ä¸ - ä½ æ£å¨è¿è¡ OSX - `therubyracer` gem/library 请å¯ç [github.com/josh/ruby-coffee-script](http://github.com/josh/ruby-coffee-script) è·åæ´æ°çé项ã ç°å¨ä½ å¯ä»¥è°ç¨ CoffeeScript 模çäº: ~~~~ ruby # éè¦å¨ä½ çåºç¨ä¸å¼å ¥coffee-script require 'coffee-script' get '/application.js' do coffee :application end ~~~~ è¿éè°ç¨çæ¯ `./views/application.coffee`ã ### åµå ¥æ¨¡æ¿å符串 ~~~~ ruby get '/' do haml '%div.title Hello World' end ~~~~ è°ç¨åµå ¥æ¨¡æ¿å符串ã ### å¨æ¨¡æ¿ä¸è®¿é®åé 模æ¿åè·¯ç±æ§è¡å¨å¨åæ ·çä¸ä¸ææ±å¼ã å¨è·¯ç±æ§è¡å¨ä¸èµå¼çå®ä¾åéå¯ä»¥ç´æ¥è¢«æ¨¡æ¿è®¿é®ã ~~~~ ruby get '/:id' do @foo = Foo.find(params['id']) haml '%h1= @foo.name' end ~~~~ æè ï¼æ¾å¼å°æå®ä¸ä¸ªæ¬å°åéçåå¸ï¼ ~~~~ ruby get '/:id' do foo = Foo.find(params['id']) haml '%h1= foo.name', :locals => { :foo => foo } end ~~~~ å ¸åç使ç¨æ åµæ¯å¨å«ç模æ¿ä¸æç §å±é¨æ¨¡æ¿çæ¹å¼æ¥å¡«å ã ### å èæ¨¡æ¿ æ¨¡æ¿å¯ä»¥å¨æºæ件çæ«å°¾å®ä¹ï¼ ~~~~ ruby require 'sinatra' get '/' do haml :index end __END__ @@ layout %html = yield @@ index %div.title Hello world!!!!! ~~~~ 注æï¼å¼å ¥sinatraçæºæ件ä¸å®ä¹çå è模æ¿æè½è¢«èªå¨è½½å ¥ã å¦æä½ å¨å ¶ä»æºæ件ä¸æå è模æ¿ï¼ éè¦æ¾å¼æ§è¡è°ç¨`enable :inline_templates`ã ### å ·åæ¨¡æ¿ æ¨¡æ¿å¯ä»¥éè¿ä½¿ç¨é¡¶å± `template` æ¹æ³å®ä¹ï¼ ~~~~ ruby template :layout do "%html\n =yield\n" end template :index do '%div.title Hello World!' end get '/' do haml :index end ~~~~ å¦æåå¨å为âlayoutâç模æ¿ï¼è¯¥æ¨¡æ¿ä¼å¨æ¯ä¸ªæ¨¡æ¿å¡«å çæ¶å被使ç¨ã ä½ å¯ä»¥åç¬å°éè¿ä¼ é `:layout => false`æ¥ç¦ç¨ï¼ æè éè¿`set :haml, :layout => false`æ¥ç¦ç¨ä»ä»¬ã ~~~~ ruby get '/' do haml :index, :layout => !request.xhr? end ~~~~ ### å ³èæ件æ©å±å 为äºå ³èä¸ä¸ªæ件æ©å±åå°ä¸ä¸ªæ¨¡çå¼æï¼ä½¿ç¨ `Tilt.register`ãæ¯å¦ï¼å¦æä½ åæ¬¢ä½¿ç¨ `tt` ä½ä¸ºTextile模ççæ©å±åï¼ä½ å¯ä»¥è¿æ ·å: ~~~~ ruby Tilt.register :tt, Tilt[:textile] ~~~~ ### æ·»å ä½ èªå·±ç模çå¼æ é¦å ï¼éè¿Tilt注åä½ èªå·±çå¼æï¼ç¶åå建ä¸ä¸ªå¡«å æ¹æ³: ~~~~ ruby Tilt.register :myat, MyAwesomeTemplateEngine helpers do def myat(*args) render(:myat, *args) end end get '/' do myat :index end ~~~~ è¿éè°ç¨çæ¯ `./views/index.myat`ãå¯ç [github.com/rtomayko/tilt](https://github.com/rtomayko/tilt) æ¥æ´å¤äºè§£Tilt. ## è¿æ»¤å¨ åç½®è¿æ»¤å¨å¨æ¯ä¸ªè¯·æ±åï¼å¨è¯·æ±çä¸ä¸æç¯å¢ä¸è¢«æ§è¡ï¼ èä¸å¯ä»¥ä¿®æ¹è¯·æ±åååºã å¨è¿æ»¤å¨ä¸è®¾å®çå®ä¾åéå¯ä»¥è¢«è·¯ç±å模æ¿è®¿é®ï¼ ~~~~ ruby before do @note = 'Hi!' request.path_info = '/foo/bar/baz' end get '/foo/*' do @note #=> 'Hi!' params['splat'] #=> 'bar/baz' end ~~~~ åç½®è¿æ»¤å¨å¨æ¯ä¸ªè¯·æ±ä¹åï¼å¨è¯·æ±çä¸ä¸æç¯å¢ä¸æ§è¡ï¼ èä¸å¯ä»¥ä¿®æ¹è¯·æ±åååºã å¨åç½®è¿æ»¤å¨åè·¯ç±ä¸è®¾å®çå®ä¾åéå¯ä»¥è¢«åç½®è¿æ»¤å¨è®¿é®ï¼ ~~~~ ruby after do puts response.status end ~~~~ 请注æï¼é¤éä½ æ¾å¼ä½¿ç¨ `body` æ¹æ³ï¼èä¸æ¯å¨è·¯ç±ä¸ç´æ¥è¿ååç¬¦ä¸²ï¼ æ¶æ¯ä½å¨åç½®è¿æ»¤å¨æ¯ä¸å¯ç¨çï¼ å 为å®å¨ä¹åæä¼çæã è¿æ»¤å¨å¯ä»¥å¯éå°å¸¦æèå¼ï¼ åªæ请æ±è·¯å¾æ»¡è¶³è¯¥èå¼æ¶æä¼æ§è¡ï¼ ~~~~ ruby before '/protected/*' do authenticate! end after '/create/:slug' do |slug| session['last_slug'] = slug end ~~~~ åè·¯ç±ä¸æ ·ï¼è¿æ»¤å¨ä¹å¯ä»¥å¸¦ææ¡ä»¶: ~~~~ ruby before :agent => /Songbird/ do # ... end after '/blog/*', :host_name => 'example.com' do # ... end ~~~~ ## è¾ å©æ¹æ³ 使ç¨é¡¶å±ç `helpers` æ¹æ³æ¥å®ä¹è¾ å©æ¹æ³ï¼ 以便å¨è·¯ç±å¤çå¨å模æ¿ä¸ä½¿ç¨ï¼ ~~~~ ruby helpers do def bar(name) "#{name}bar" end end get '/:name' do bar(params['name']) end ~~~~ ### ä½¿ç¨ Sessions Session被ç¨æ¥å¨è¯·æ±ä¹é´ä¿æç¶æãå¦æ被æ¿æ´»ï¼æ¯ä¸ä¸ªç¨æ·ä¼è¯ 对åºæä¸ä¸ªsessionåå¸: ~~~~ ruby enable :sessions get '/' do "value = " << session['value'].inspect end get '/:value' do session['value'] = params['value'] end ~~~~ 请注æ `enable :sessions` å®é ä¸ä¿åææçæ°æ®å¨ä¸ä¸ªcookieä¹ä¸ã è¿å¯è½ä¸ä¼æ»æ¯åä½ æ³è¦çï¼æ¯å¦ï¼ä¿å大éçæ°æ®ä¼å¢å ä½ çæµéï¼ã ä½ å¯ä»¥ä½¿ç¨ä»»ä½çRack sessionä¸é´ä»¶ï¼ä¸ºäºè¿ä¹åï¼ \*ä¸è¦\*è°ç¨ `enable :sessions`ï¼èæ¯ æç §èªå·±çéè¦å¼å ¥ä½ çä¸é´ä»¶ï¼ ~~~~ ruby use Rack::Session::Pool, :expire_after => 2592000 get '/' do "value = " << session['value'].inspect end get '/:value' do session['value'] = params['value'] end ~~~~ ### æèµ· è¦æ³ç´æ¥å°åæ¢è¯·æ±ï¼å¨è¿æ»¤å¨æè è·¯ç±ä¸ä½¿ç¨ï¼ ~~~~ ruby halt ~~~~ ä½ ä¹å¯ä»¥æå®æèµ·æ¶çç¶æç ï¼ ~~~~ ruby halt 410 ~~~~ æè æ¶æ¯ä½ï¼ ~~~~ ruby halt 'this will be the body' ~~~~ æè 两è ; ~~~~ ruby halt 401, 'go away!' ~~~~ ä¹å¯ä»¥å¸¦æ¶æ¯å¤´ï¼ ~~~~ ruby halt 402, {'Content-Type' => 'text/plain'}, 'revenge' ~~~~ ### 让路 ä¸ä¸ªè·¯ç±å¯ä»¥æ¾å¼å¤çï¼å°å¤ç让ç»ä¸ä¸ä¸ªå¹é çè·¯ç±ï¼ä½¿ç¨ `pass`ï¼ ~~~~ ruby get '/guess/:who' do pass unless params['who'] == 'Frank' 'You got me!' end get '/guess/*' do 'You missed!' end ~~~~ è·¯ç±ä»£ç å被ç´æ¥éåºï¼æ§å¶æµç»§ç»åè¿å°ä¸ä¸ä¸ªå¹é çè·¯ç±ã å¦æ没æå¹é çè·¯ç±ï¼å°è¿å404ã ### 触åå¦ä¸ä¸ªè·¯ç± æäºæ¶åï¼`pass` 并ä¸æ¯ä½ æ³è¦çï¼ä½ å¸æå¾å°çæ¯å¦ä¸ä¸ªè·¯ç±çç»æ ãç®åçä½¿ç¨ `call` å¯ä»¥åå°è¿ä¸ç¹: ~~~~ ruby get '/foo' do status, headers, body = call env.merge("PATH_INFO" => '/bar') [status, headers, body.map(&:upcase)] end get '/bar' do "bar" end ~~~~ 请注æå¨ä»¥ä¸ä¾åä¸ï¼ä½ å¯ä»¥æ´å ç®åæµè¯å¹¶å¢å æ§è½ï¼åªè¦ç®åçç§»å¨ <tt>"bar"</tt>å°ä¸ä¸ªè¢«<tt>/foo</tt> å `/bar`åæ¶ä½¿ç¨çhelperã å¦æä½ å¸æ请æ±è¢«åéå°åä¸ä¸ªåºç¨ï¼èä¸æ¯å¯æ¬ï¼ ä½¿ç¨ `call!` èä¸æ¯ `call`. å¦ææ³æ´å¤äºè§£ `call`ï¼è¯·å¯ç Rack specificationã ### è®¾å® æ¶æ¯ä½ï¼ç¶æç åæ¶æ¯å¤´ éè¿è·¯ç±ä»£ç åçè¿åå¼æ¥è®¾å®ç¶æç åæ¶æ¯ä½ä¸ä» æ¯å¯è½çï¼èä¸æ¯æ¨èçã ä½æ¯ï¼å¨æäºåºæ¯ä¸ä½ å¯è½æ³å¨ä½ä¸æµç¨ä¸çç¹å®ç¹ä¸è®¾ç½®æ¶æ¯ä½ã ä½ å¯ä»¥éè¿ `body` è¾ å©æ¹æ³è¿ä¹åã å¦æä½ è¿æ ·åäºï¼ ä½ å¯ä»¥å¨é£ä»¥å使ç¨è¯¥æ¹æ³è·å¾æ¶æ¯ä½: ~~~~ ruby get '/foo' do body "bar" end after do puts body end ~~~~ ä¹å¯ä»¥ä¼ ä¸ä¸ªä»£ç åç» `body`ï¼å®å°ä¼è¢«Rackå¤çå¨æ§è¡ï¼ è¿å°å¯ä»¥è¢«ç¨æ¥å®ç°streamingï¼åè§âè¿åå¼âï¼ã åæ¶æ¯ä½ç±»ä¼¼ï¼ä½ ä¹å¯ä»¥è®¾å®ç¶æç åæ¶æ¯å¤´: ~~~~ ruby get '/foo' do status 418 headers \ "Allow" => "BREW, POST, GET, PROPFIND, WHEN", "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt" body "I'm a tea pot!" end ~~~~ å¦å `body`, ä¸å¸¦åæ°ç `headers` å `status` å¯ä»¥ç¨æ¥è®¿é® ä»ä»¬ä½ çå½åå¼. ### åªä½(MIME)ç±»å ä½¿ç¨ `send_file` æè éææ件çæ¶åï¼Sinatraå¯è½ä¸è½è¯å«ä½ çåªä½ç±»åã ä½¿ç¨ `mime_type` éè¿æ件æ©å±åæ¥æ³¨åå®ä»¬ï¼ ~~~~ ruby mime_type :foo, 'text/foo' ~~~~ ä½ ä¹å¯ä»¥ä½¿ç¨ `content_type` è¾ å©æ¹æ³ï¼ ~~~~ ruby get '/' do content_type :foo "foo foo foo" end ~~~~ ### çæ URL 为äºçæURLï¼ä½ éè¦ä½¿ç¨ `url` è¾ å©æ¹æ³ï¼ ä¾å¦ï¼å¨Hamlä¸: ~~~~ ruby %a{:href => url('/foo')} foo ~~~~ å¦æ使ç¨åå代çåRackè·¯ç±ï¼çæURLçæ¶åä¼èèè¿äºå ç´ ã è¿ä¸ªæ¹æ³è¿æä¸ä¸ªå«å `to` (è§ä¸é¢çä¾å). ### æµè§å¨éå®å ä½ å¯ä»¥éè¿ `redirect` è¾ å©æ¹æ³è§¦åæµè§å¨éå®å: ~~~~ ruby get '/foo' do redirect to('/bar') end ~~~~ å ¶ä»åæ°çç¨æ³ï¼ä¸ `halt`ç¸å: ~~~~ ruby redirect to('/bar'), 303 redirect 'http://google.com', 'wrong place, buddy' ~~~~ ç¨ `redirect back`å¯ä»¥æç¨æ·éå®åå°åå§é¡µé¢: ~~~~ ruby get '/foo' do "<a href='/bar'>do something</a>" end get '/bar' do do_something redirect back end ~~~~ å¦ææ³ä¼ éåæ°ç»redirectï¼å¯ä»¥ç¨query string: ~~~~ ruby redirect to('/bar?sum=42') ~~~~ æè ç¨session: ~~~~ ruby enable :sessions get '/foo' do session['secret'] = 'foo' redirect to('/bar') end get '/bar' do session['secret'] end ~~~~ ### ç¼åæ§å¶ è¦ä½¿ç¨HTTPç¼åï¼å¿ é¡»æ£ç¡®å°è®¾å®æ¶æ¯å¤´ã ä½ å¯ä»¥è¿æ ·è®¾å® Cache-Control æ¶æ¯å¤´: ~~~~ ruby get '/' do cache_control :public "cache it!" end ~~~~ æ ¸å¿æ示: å¨åç½®è¿æ»¤å¨ä¸è®¾å®ç¼å. ~~~~ ruby before do cache_control :public, :must_revalidate, :max_age => 60 end ~~~~ å¦æä½ æ£å¨ç¨ `expires` è¾ å©æ¹æ³è®¾å®å¯¹åºçæ¶æ¯å¤´ `Cache-Control` ä¼èªå¨è®¾å®ï¼ ~~~~ ruby before do expires 500, :public, :must_revalidate end ~~~~ 为äºåéå°ä½¿ç¨ç¼åï¼ä½ åºè¯¥èèä½¿ç¨ `etag` å `last_modified`æ¹æ³ã æ¨èå¨æ§è¡ç¹éä»»å¡\*ä¹å\*使ç¨è¿äºhelpersï¼è¿æ ·ä¸æ¥ï¼ å¦æ客æ·ç«¯å¨ç¼åä¸å·²ç»æç¸å ³å 容ï¼å°±ä¼ç«å³å¾å°æ¾ç¤ºã ~~~~ ruby get '/article/:id' do @article = Article.find params['id'] last_modified @article.updated_at etag @article.sha1 erb :article end ~~~~ ä½¿ç¨ [weak ETag](http://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation) ä¹æ¯æå¯è½ç: ~~~~ ruby etag @article.sha1, :weak ~~~~ è¿äºè¾ å©æ¹æ³å¹¶ä¸ä¼ä¸ºä½ åä»»ä½ç¼åï¼èæ¯å°å¿ è¦çä¿¡æ¯ä¼ éç»ä½ çç¼å å¦æä½ å¨å¯»æ¾ç¼åçå¿«é解å³æ¹æ¡ï¼è¯è¯ [rack-cache](https://github.com/rtomayko/rack-cache): ~~~~ ruby require "rack/cache" require "sinatra" use Rack::Cache get '/' do cache_control :public, :max_age => 36000 sleep 5 "hello" end ~~~~ ### åéæ件 为äºåéæ件ï¼ä½ å¯ä»¥ä½¿ç¨ `send_file` è¾ å©æ¹æ³: ~~~~ ruby get '/' do send_file 'foo.png' end ~~~~ ä¹å¯ä»¥å¸¦ä¸äºé项: ~~~~ ruby send_file 'foo.png', :type => :jpg ~~~~ å¯ç¨çé项æ: <dl> <dt>filename</dt> <dd>ååºä¸çæ件åï¼é»è®¤æ¯çå®æ件çååã</dd> <dt>last_modified</dt> <dd>Last-Modified æ¶æ¯å¤´çå¼ï¼é»è®¤æ¯æ件çmtimeï¼ä¿®æ¹æ¶é´ï¼ã</dd> <dt>type</dt> <dd>使ç¨çå 容类åï¼å¦æ没æä¼ä»æ件æ©å±åçæµã</dd> <dt>disposition</dt> <dd> ç¨äº Content-Dispositionï¼å¯è½çå æ¬ï¼ <tt>nil</tt> (é»è®¤), <tt>:attachment</tt> å <tt>:inline</tt> </dd> <dt>length</dt> <dd>Content-Length çå¼ï¼é»è®¤æ¯æ件ç大å°ã</dd> </dl> å¦æRackå¤çå¨æ¯æçè¯ï¼Rubyè¿ç¨ä¹è½ä½¿ç¨é¤streaming以å¤çæ¹æ³ã å¦æä½ ä½¿ç¨è¿ä¸ªè¾ å©æ¹æ³ï¼ Sinatraä¼èªå¨å¤çrange请æ±ã ### 访é®è¯·æ±å¯¹è±¡ ä¼ å ¥ç请æ±å¯¹è±¡å¯ä»¥å¨è¯·æ±å±ï¼è¿æ»¤å¨ï¼è·¯ç±ï¼é误å¤çï¼ éè¿ `request` æ¹æ³è¢«è®¿é®ï¼ ~~~~ ruby # å¨ http://example.com/example ä¸è¿è¡çåºç¨ get '/foo' do request.body # 被客æ·ç«¯è®¾å®ç请æ±ä½ï¼è§ä¸ï¼ request.scheme # "http" request.script_name # "/example" request.path_info # "/foo" request.port # 80 request.request_method # "GET" request.query_string # "" request.content_length # request.bodyçé¿åº¦ request.media_type # request.bodyçåªä½ç±»å request.host # "example.com" request.get? # true (å ¶ä»å¨è¯ä¹å ·æ类似æ¹æ³) request.form_data? # false request["SOME_HEADER"] # SOME_HEADER headerçå¼ request.referrer # 客æ·ç«¯çreferrer æè '/' request.user_agent # user agent (被 :agent æ¡ä»¶ä½¿ç¨) request.cookies # æµè§å¨ cookies åå¸ request.xhr? # è¿æ¯å¦æ¯ajax请æ±ï¼ request.url # "http://example.com/example/foo" request.path # "/example/foo" request.ip # 客æ·ç«¯IPå°å request.secure? # falseï¼å¦ææ¯sslå为trueï¼ request.forwarded? # true ï¼å¦ææ¯è¿è¡å¨åå代çä¹åï¼ request.env # Rackä¸ä½¿ç¨çæªå¤ççenvåå¸ end ~~~~ ä¸äºé项ï¼ä¾å¦ `script_name` æè `path_info` ä¹æ¯å¯åçï¼ ~~~~ ruby before { request.path_info = "/" } get "/" do "all requests end up here" end ~~~~ `request.body` æ¯ä¸ä¸ªIOæè StringIOå¯¹è±¡ï¼ ~~~~ ruby post "/api" do request.body.rewind # å¦æå·²ç»æ人读äºå® data = JSON.parse request.body.read "Hello #{data['name']}!" end ~~~~ ### é件 ä½ å¯ä»¥ä½¿ç¨ `attachment` è¾ å©æ¹æ³æ¥åè¯æµè§å¨ååº åºå½è¢«åå ¥ç£çèä¸æ¯å¨æµè§å¨ä¸æ¾ç¤ºã ~~~~ ruby get '/' do attachment "store it!" end ~~~~ ä½ ä¹å¯ä»¥ä¼ éä¸ä¸ªæ件å: ~~~~ ruby get '/' do attachment "info.txt" "store it!" end ~~~~ ### æ¥æ¾æ¨¡æ¿æ件 `find_template` è¾ å©æ¹æ³è¢«ç¨äºå¨å¡«å æ¶æ¥æ¾æ¨¡æ¿æ件: ~~~~ ruby find_template settings.views, 'foo', Tilt[:haml] do |file| puts "could be #{file}" end ~~~~ è¿å¹¶ä¸æ¯å¾æç¨ãä½æ¯å¨ä½ éè¦éè½½è¿ä¸ªæ¹æ³ æ¥å®ç°ä½ èªå·±çæ¥æ¾æºå¶çæ¶åæç¨ã æ¯å¦ï¼å¦æä½ æ³æ¯æå¤äºä¸ä¸ªè§å¾ç®å½: ~~~~ ruby set :views, ['views', 'templates'] helpers do def find_template(views, name, engine, &block) Array(views).each { |v| super(v, name, engine, &block) } end end ~~~~ å¦ä¸ä¸ªä¾åæ¯ä¸ºä¸åçå¼æ使ç¨ä¸åçç®å½: ~~~~ ruby set :views, :sass => 'views/sass', :haml => 'templates', :default => 'views' helpers do def find_template(views, name, engine, &block) _, folder = views.detect { |k,v| engine == Tilt[k] } folder ||= views[:default] super(folder, name, engine, &block) end end ~~~~ ä½ å¯ä»¥å¾å®¹æå°å è£ æä¸ä¸ªæ©å±ç¶åä¸ä»äººåäº«ï¼ è¯·æ³¨æ `find_template` 并ä¸ä¼æ£æ¥æ件ççåå¨ï¼ èæ¯å¯¹ä»»ä½å¯è½çè·¯å¾è°ç¨ç»å ¥ç代ç åãè¿å¹¶ä¸ä¼å¸¦æ¥æ§è½é®é¢ï¼ å 为 `render` ä¼å¨æ¾å°æ件çæ¶å马ä¸ä½¿ç¨ `break` ã åæ ·çï¼æ¨¡æ¿çè·¯å¾ï¼åå 容ï¼ä¼å¨é¤development mode以å¤çåºå 被ç¼åãä½ åºè¯¥æ¶å»æéèªå·±è¿ä¸ç¹ï¼ å¦æä½ ççæ³åä¸ä¸ªé常ç¯ççæ¹æ³ã ## é ç½® è¿è¡ä¸æ¬¡ï¼å¨å¯å¨çæ¶åï¼å¨ä»»ä½ç¯å¢ä¸ï¼ ~~~~ ruby configure do # setting one option set :option, 'value' # setting multiple options set :a => 1, :b => 2 # same as `set :option, true` enable :option # same as `set :option, false` disable :option # you can also have dynamic settings with blocks set(:css_dir) { File.join(views, 'css') } end ~~~~ åªå½ç¯å¢ (RACK\_ENV environment åé) 被设å®ä¸º `:production`çæ¶åè¿è¡ï¼ ~~~~ ruby configure :production do ... end ~~~~ å½ç¯å¢è¢«è®¾å®ä¸º `:production` æè `:test`çæ¶åè¿è¡ï¼ ~~~~ ruby configure :production, :test do ... end ~~~~ ä½ å¯ä»¥ä½¿ç¨ `settings` è·å¾è¿äºé ç½®: ~~~~ ruby configure do set :foo, 'bar' end get '/' do settings.foo? # => true settings.foo # => 'bar' ... end ~~~~ ### å¯éç设置 <dl> <dt>absolute_redirects</dt> <dd> <p> å¦æ被ç¦ç¨ï¼Sinatraä¼å 许使ç¨ç¸å¯¹è·¯å¾éå®åï¼ ä½æ¯ï¼Sinatraå°±ä¸åéµå® RFC 2616æ å (HTTP 1.1), 该æ ååªå 许ç»å¯¹è·¯å¾éå®åã </p> <p> å¦æä½ çåºç¨è¿è¡å¨ä¸ä¸ªæªæ°å½è®¾ç½®çåå代çä¹åï¼ ä½ éè¦å¯ç¨è¿ä¸ªé项ã注æ <tt>url</tt> è¾ å©æ¹æ³ ä»ç¶ä¼çæç»å¯¹ URLï¼é¤éä½ ä¼ å ¥ <tt>false</tt> ä½ä¸ºç¬¬äºåæ°ã </p> <p> é»è®¤ç¦ç¨ã </p> </dd> <dt>add_charset</dt> <dd> <p> è®¾å® <tt>content_type</tt> è¾ å©æ¹æ³ä¼ èªå¨å ä¸å符éä¿¡æ¯çå¤åªä½ç±»åã </p> <p> ä½ åºè¯¥æ·»å èä¸æ¯è¦çè¿ä¸ªé项: <tt>settings.add_charset << "application/foobar"</tt> </p> </dd> <dt>app_file</dt> <dd> 主åºç¨æ件ï¼ç¨æ¥æ£æµé¡¹ç®çæ ¹è·¯å¾ï¼ viewsåpublicæ件夹åå è模æ¿ã </dd> <dt>bind</dt> <dd> ç»å®çIP å°å (é»è®¤: 0.0.0.0)ã ä» å¯¹äºå ç½®çæå¡å¨æç¨ã </dd> <dt>default_encoding</dt> <dd> é»è®¤ç¼ç (é»è®¤ä¸º <tt>"utf-8"</tt>)ã </dd> <dt>dump_errors</dt> <dd> å¨logä¸æ¾ç¤ºé误ã </dd> <dt>environment</dt> <dd> å½åç¯å¢ï¼é»è®¤æ¯ <tt>ENV['RACK_ENV']</tt>ï¼ æè <tt>"development"</tt> å¦æä¸å¯ç¨ã </dd> <dt>logging</dt> <dd> 使ç¨logger </dd> <dt>lock</dt> <dd> <p> 对æ¯ä¸ä¸ªè¯·æ±æ¾ç½®ä¸ä¸ªéï¼ åªä½¿ç¨è¿ç¨å¹¶åå¤ç请æ±ã </p> <p> å¦æä½ çåºç¨ä¸æ¯çº¿ç¨å®å ¨åéå¯å¨ã é»è®¤ç¦ç¨ã </p> </dd> <dt>method_override</dt> <dd> ä½¿ç¨ <tt>_method</tt> éæ³ä»¥å 许å¨æ§çæµè§å¨ä¸å¨ 表åä¸ä½¿ç¨ put/delete æ¹æ³ </dd> <dt>port</dt> <dd> çå¬ç端å£å·ãåªå¯¹å ç½®æå¡å¨æç¨ã </dd> <dt>prefixed_redirects</dt> <dd> æ¯å¦æ·»å <tt>request.script_name</tt> å° éå®å请æ±ï¼å¦æ没æ设å®ç»å¯¹è·¯å¾ãé£æ ·çè¯ <tt>redirect '/foo'</tt> ä¼å <tt>redirect to('/foo')</tt>èµ·ç¸åä½ç¨ãé»è®¤ç¦ç¨ã </dd> <dt>public_folder</dt> <dd> publicæ件夹çä½ç½®ã </dd> <dt>reload_templates</dt> <dd> æ¯å¦æ¯ä¸ªè¯·æ±é½éæ°è½½å ¥æ¨¡æ¿ã å¨development modeå Ruby 1.8.6 ä¸è¢«ä¼ä¸ï¼ç¨æ¥ æ¶é¤ä¸ä¸ªRubyå åæ³æ¼çbugï¼ã </dd> <dt>root</dt> <dd> 项ç®çæ ¹ç®å½ã </dd> <dt>raise_errors</dt> <dd> æåºå¼å¸¸ï¼åºç¨ä¼åä¸ï¼ã </dd> <dt>run</dt> <dd> å¦æå¯ç¨ï¼Sinatraä¼å¼å¯webæå¡å¨ã å¦æ使ç¨rackupæå ¶ä»æ¹å¼åä¸è¦å¯ç¨ã </dd> <dt>running</dt> <dd> å ç½®çæå¡å¨å¨è¿è¡åï¼ ä¸è¦ä¿®æ¹è¿ä¸ªè®¾ç½®ï¼ </dd> <dt>server</dt> <dd> æå¡å¨ï¼æç¨äºå ç½®æå¡å¨çå表ã é»è®¤æ¯ [âthinâ, âmongrelâ, âwebrickâ], 顺åºè¡¨æäº ä¼å 级ã </dd> <dt>sessions</dt> <dd> å¼å¯åºäºcookieçsessonã </dd> <dt>show_exceptions</dt> <dd> å¨æµè§å¨ä¸æ¾ç¤ºä¸ä¸ªstack traceã </dd> <dt>static</dt> <dd> Sinatraæ¯å¦å¤çéææ件ã å½æå¡å¨è½å¤å¤çåç¦ç¨ã ç¦ç¨ä¼å¢å¼ºæ§è½ã é»è®¤å¼å¯ã </dd> <dt>views</dt> <dd> views æ件夹ã </dd> </dl> ## é误å¤ç é误å¤çå¨ä¸è·¯ç±ååç½®è¿æ»¤å¨ç¸åçä¸ä¸æä¸è¿è¡ï¼ è¿æå³çä½ å¯ä»¥ä½¿ç¨è®¸å¤å¥½ä¸è¥¿ï¼æ¯å¦ `haml`, `erb`, `halt`ï¼ççã ### æªæ¾å° å½ä¸ä¸ª `Sinatra::NotFound` é误被æåºçæ¶åï¼ æè ååºç¶æç æ¯404ï¼`not_found` å¤çå¨ä¼è¢«è°ç¨ï¼ ~~~~ ruby not_found do 'This is nowhere to be found' end ~~~~ ### é误 `error` å¤çå¨ï¼å¨ä»»ä½è·¯ç±ä»£ç åæè è¿æ»¤å¨æåºå¼å¸¸çæ¶åä¼è¢«è°ç¨ã å¼å¸¸å¯¹è±¡å¯ä»¥éè¿`sinatra.error` Rack åéè·å¾ï¼ ~~~~ ruby error do 'Sorry there was a nasty error - ' + env['sinatra.error'].message end ~~~~ èªå®ä¹éè¯¯ï¼ ~~~~ ruby error MyCustomError do 'So what happened was...' + env['sinatra.error'].message end ~~~~ é£ä¹ï¼å½è¿ä¸ªåççæ¶åï¼ ~~~~ ruby get '/' do raise MyCustomError, 'something bad' end ~~~~ ä½ ä¼å¾å°ï¼ So what happened was... something bad å¦ä¸ç§æ¿ä»£æ¹æ³æ¯ï¼ä¸ºä¸ä¸ªç¶æç å®è£ é误å¤çå¨ï¼ ~~~~ ruby error 403 do 'Access forbidden' end get '/secret' do 403 end ~~~~ æè ä¸ä¸ªèå´ï¼ ~~~~ ruby error 400..510 do 'Boom' end ~~~~ å¨è¿è¡å¨developmentç¯å¢ä¸æ¶ï¼Sinatraä¼å®è£ ç¹æ®ç `not_found` å `error` å¤çå¨ã ## Rack ä¸é´ä»¶ Sinatra ä¾é [Rack](http://rack.github.io/), ä¸ä¸ªé¢åRuby webæ¡æ¶çæå°æ åæ¥å£ã Rackçä¸ä¸ªææ趣çé¢ååºç¨å¼åè çè½åæ¯æ¯æâä¸é´ä»¶âââåè½å¨æå¡å¨åä½ çåºç¨ä¹é´ï¼ çè§ å¹¶/æ æä½HTTP请æ±/ååºä»¥ æä¾å¤æ ·ç±»åç常ç¨åè½ã Sinatra 让建ç«Rackä¸é´ä»¶ç®¡éå¼å¸¸ç®åï¼ éè¿é¡¶å±ç `use` æ¹æ³ï¼ ~~~~ ruby require 'sinatra' require 'my_custom_middleware' use Rack::Lint use MyCustomMiddleware get '/hello' do 'Hello World' end ~~~~ `use` çè¯ä¹åå¨ [Rack::Builder](http://rubydoc.info/github/rack/rack/master/Rack/Builder) DSL(å¨rackæ件ä¸æé¢ç¹ä½¿ç¨)ä¸å®ä¹çå®å ¨ä¸æ ·ãä¾å¦ï¼`use` æ¹æ³æ¥å å¤ä¸ª/å¯å åæ°ï¼å æ¬ä»£ç åï¼ ~~~~ ruby use Rack::Auth::Basic do |username, password| username == 'admin' && password == 'secret' end ~~~~ Rackä¸åå¸æå¤æ ·çæ åä¸é´ä»¶ï¼é对æ¥å¿ï¼ è°è¯ï¼URLè·¯ç±ï¼è®¤è¯åsessionå¤çã Sinatraä¼èªå¨ä½¿ç¨è¿éé¢ç大é¨åç»ä»¶ï¼ æä»¥ä½ ä¸è¬ä¸éè¦æ¾ç¤ºå° `use` ä»ä»¬ã ## æµè¯ Sinatraçæµè¯å¯ä»¥ä½¿ç¨ä»»ä½åºäºRackçæµè¯ç¨åºåºæè æ¡æ¶æ¥ç¼åã [Rack::Test](http://gitrdoc.com/brynary/rack-test) æ¯æ¨èåéï¼ ~~~~ ruby require 'my_sinatra_app' require 'minitest/autorun' require 'rack/test' class MyAppTest < Minitest::Test include Rack::Test::Methods def app Sinatra::Application end def test_my_default get '/' assert_equal 'Hello World!', last_response.body end def test_with_params get '/meet', :name => 'Frank' assert_equal 'Hello Frank!', last_response.body end def test_with_rack_env get '/', {}, 'HTTP_USER_AGENT' => 'Songbird' assert_equal "You're using Songbird!", last_response.body end end ~~~~ 请注æ: å ç½®ç Sinatra::Test 模åå Sinatra::TestHarness ç±» å¨ 0.9.2 çæ¬å·²åºå¼ã ## Sinatra::Base - ä¸é´ä»¶ï¼ç¨åºåºå模åååºç¨ æä½ çåºç¨å®ä¹å¨é¡¶å±ï¼å¯¹äºå¾®ååºç¨è¿ä¼å·¥ä½å¾å¾å¥½ï¼ ä½æ¯å¨æ建å¯å¤ç¨çç»ä»¶æ¶åä¼å¸¦æ¥å®¢è§çä¸å©ï¼ æ¯å¦æ建Rackä¸é´ä»¶ï¼Rails metalï¼å¸¦ææå¡å¨ç»ä»¶çç®åç¨åºåºï¼ æè çè³æ¯Sinatraæ©å±ã顶å±çDSL污æäºObjectå½å空é´ï¼ 并åå®äºä¸ä¸ªå¾®ååºç¨é£æ ¼çé ç½® (ä¾å¦, åä¸çåºç¨æä»¶ï¼ ./public å ./views ç®å½ï¼æ¥å¿ï¼å¼å¸¸ç»è页é¢ï¼ççï¼ã è¿æ¶åºè¯¥è®© Sinatra::Base èµ°å°å°åäºï¼ ~~~~ ruby require 'sinatra/base' class MyApp < Sinatra::Base set :sessions, true set :foo, 'bar' get '/' do 'Hello world!' end end ~~~~ Sinatra::Baseåç±»å¯ç¨çæ¹æ³å®é ä¸å°±æ¯éè¿é¡¶å± DSL å¯ç¨çæ¹æ³ã 大é¨å顶å±åºç¨å¯ä»¥éè¿ä¸¤ä¸ªæ¹å转æ¢æSinatra::Baseç»ä»¶ï¼ - ä½ çæ件åºå½å¼å ¥ `sinatra/base` èä¸æ¯ `sinatra`; å¦åï¼ææçSinatraç DSL æ¹æ³å°ä¼è¢«å¼è¿å° 主å½å空é´ã - æä½ çåºç¨çè·¯ç±ï¼é误å¤çï¼è¿æ»¤å¨åé项æ¾å¨ ä¸ä¸ªSinatra::Baseçåç±»ä¸ã `+Sinatra::Base+` æ¯ä¸å¼ ç½çº¸ã大é¨åçé项é»è®¤æ¯ç¦ç¨çï¼ å å«å ç½®çæå¡å¨ãåè§ [é项åé ç½®](http://sinatra.github.com/configuration.html) æ¥çå¯ç¨é项çå ·ä½ç»èåä»ä»¬çè¡ä¸ºã ### 模åå vs. ä¼ ç»çæ¹å¼ ä¸é常ç认è¯ç¸åï¼ä¼ ç»çæ¹å¼æ²¡æä»»ä½é误ã å¦æå®éåä½ çåºç¨ï¼ä½ ä¸éè¦è½¬æ¢å°æ¨¡ååçåºç¨ã å模ååæ¹å¼ç¸æ¯åªæ两个缺ç¹: - ä½ å¯¹æ¯ä¸ªRubyè¿ç¨åªè½å®ä¹ä¸ä¸ªSinatraåºç¨ï¼å¦æä½ éè¦æ´å¤ï¼ åæ¢å°æ¨¡ååæ¹å¼ã - ä¼ ç»æ¹å¼ä½¿ç¨ä»£çæ¹æ³æ±¡æäº Object ãå¦æä½ æç® æä½ çåºç¨å°è£ è¿ä¸ä¸ª library/gemï¼è½¬æ¢å°æ¨¡ååæ¹å¼ã 没æä»»ä½åå é»æ¢ä½ æ··å模åååä¼ ç»æ¹å¼ã å¦æä»ä¸ç§è½¬æ¢å°å¦ä¸ç§ï¼ä½ éè¦æ³¨æsettingsä¸ç ä¸äºå¾®å°çä¸å: Setting Classic Modular app_file file loading sinatra nil run $0 == app_file false logging true false method_override true false inline_templates true false ### è¿è¡ä¸ä¸ªæ¨¡åååºç¨ æ两ç§æ¹å¼è¿è¡ä¸ä¸ªæ¨¡åååºç¨ï¼ä½¿ç¨ `run!`æ¥è¿è¡: ~~~~ ruby # my_app.rb require 'sinatra/base' class MyApp < Sinatra::Base # ... app code here ... # start the server if ruby file executed directly run! if app_file == $0 end ~~~~ è¿è¡: ruby my_app.rb æè 使ç¨ä¸ä¸ª `config.ru`ï¼å è®¸ä½ ä½¿ç¨ä»»ä½Rackå¤çå¨: ~~~~ ruby # config.ru require './my_app' run MyApp ~~~~ è¿è¡: rackup -p 4567 ### 使ç¨config.ruè¿è¡ä¼ ç»æ¹å¼çåºç¨ ç¼åä½ çåºç¨: ~~~~ ruby # app.rb require 'sinatra' get '/' do 'Hello world!' end ~~~~ å å ¥ç¸åºç `config.ru`: ~~~~ ruby require './app' run Sinatra::Application ~~~~ ### ä»ä¹æ¶åç¨ config.ru? 以ä¸æ åµä½ å¯è½éè¦ä½¿ç¨ `config.ru`: - ä½ è¦ä½¿ç¨ä¸åç Rack å¤çå¨é¨ç½² (Passenger, Unicorn, Heroku, â¦). - ä½ æ³ä½¿ç¨ä¸ä¸ªæè å¤ä¸ª `Sinatra::Base`çåç±». - ä½ åªæ³æSinatraå½ä½ä¸é´ä»¶ä½¿ç¨ï¼èä¸æ¯ç«¯ç¹ã **ä½ å¹¶ä¸éè¦åæ¢å°`config.ru`ä» ä» å ä¸ºä½ åæ¢å°æ¨¡ååæ¹å¼ï¼ ä½ åæ ·ä¸éè¦åæ¢å°æ¨¡ååæ¹å¼ï¼ ä» ä» å 为è¦è¿è¡ `config.ru`.** ### æSinatraå½æä¸é´ä»¶æ¥ä½¿ç¨ ä¸ä» Sinatraæè½å使ç¨å ¶ä»çRackä¸é´ä»¶ï¼ä»»ä½Sinatra åºç¨ç¨åºé½å¯ä»¥åè¿æ¥èªèº«è¢«å½ä½ä¸é´ä»¶ï¼è¢«å å¨ä»»ä½Rack端ç¹åé¢ã è¿ä¸ªç«¯ç¹å¯ä»¥æ¯ä»»ä½Sinatraåºç¨ï¼æè ä»»ä½åºäºRackçåºç¨ç¨åº (Rails/Ramaze/Camping/â¦)ã ~~~~ ruby require 'sinatra/base' class LoginScreen < Sinatra::Base enable :sessions get('/login') { haml :login } post('/login') do if params['name'] = 'admin' and params['password'] = 'admin' session['user_name'] = params['name'] else redirect '/login' end end end class MyApp < Sinatra::Base # å¨åç½®è¿æ»¤å¨åè¿è¡ä¸é´ä»¶ use LoginScreen before do unless session['user_name'] halt "Access denied, please <a href='/login'>login</a>." end end get('/') { "Hello #{session['user_name']}." } end ~~~~ ## åéååç»å® å½åæå¨çåéåå³å®äºåªäºæ¹æ³ååéæ¯å¯ç¨çã ### åºç¨/ç±» åéå æ¯ä¸ªSinatraåºç¨ç¸å½ä¸Sinatra::Baseçä¸ä¸ªåç±»ã å¦æä½ å¨ä½¿ç¨é¡¶å±DSL(`require 'sinatra'`)ï¼é£ä¹è¿ä¸ªç±»å°±æ¯ Sinatra::Applicationï¼æè è¿ä¸ªç±»å°±æ¯ä½ æ¾å¼å建çåç±»ã å¨ç±»å±é¢ï¼ä½ å ·æçæ¹æ³ç±»ä¼¼äº \`get\` æè \`before\`ï¼ä½æ¯ä½ ä¸è½è®¿é® \`request\` 对象æè \`session\`, å 为对äºææç请æ±ï¼ åªæåä¸çåºç¨ç±»ã éè¿ \`set\` å建çé项æ¯ç±»å±é¢çæ¹æ³ï¼ ~~~~ ruby class MyApp < Sinatra::Base # å¿ï¼æå¨åºç¨åéåï¼ set :foo, 42 foo # => 42 get '/foo' do # å¿ï¼æä¸åå¤äºåºç¨åéåäºï¼ end end ~~~~ å¨ä¸åæ åµä¸ä½ å°æ¥æåºç¨åéåçç»å®ï¼ - å¨åºç¨ç±»ä¸ - å¨æ©å±ä¸å®ä¹çæ¹æ³ - ä¼ éç» \`helpers\` ç代ç å - ç¨ä½\`set\`å¼çè¿ç¨/代ç å ä½ å¯ä»¥è®¿é®åéå对象ï¼å°±æ¯åºç¨ç±»ï¼å°±åè¿æ ·ï¼ - éè¿ä¼ éç»ä»£ç åç对象 (`configure { |c| ... }`) - å¨è¯·æ±åéåä¸ä½¿ç¨\`settings\` ### 请æ±/å®ä¾ åéå 对äºæ¯ä¸ªè¿å ¥ç请æ±ï¼ä¸ä¸ªæ°çåºç¨ç±»çå®ä¾ä¼è¢«å建 ææçå¤çå¨ä»£ç åå¨è¯¥åéå被è¿è¡ãå¨è¿ä¸ªåéåä¸ï¼ ä½ å¯ä»¥è®¿é® \`request\` å \`session\` 对象ï¼æè è°ç¨å¡«å æ¹æ³æ¯å¦ \`erb\` æè \`haml\`ãä½ å¯ä»¥å¨è¯·æ±åéåå½ä¸éè¿\`settings\`è¾ å©æ¹æ³ 访é®åºç¨åéåï¼ ~~~~ ruby class MyApp < Sinatra::Base # å¿ï¼æå¨åºç¨åéå! get '/define_route/:name' do # é对 '/define_route/:name' ç请æ±åéå @value = 42 settings.get("/#{params['name']}") do # é对 "/#{params['name']}" ç请æ±åéå @value # => nil (并ä¸æ¯ç¸åç请æ±) end "Route defined!" end end ~~~~ å¨ä»¥ä¸æ åµå°è·å¾è¯·æ±åéåï¼ - get/head/post/put/delete 代ç å - åç½®/åç½® è¿æ»¤å¨ - è¾ å©æ¹æ³ - 模æ¿/è§å¾ ### 代çåéå 代çåéååªæ¯ææ¹æ³è½¬éå°ç±»åéåãå¯æ¯ï¼ ä»å¹¶é表ç°å¾100%类似äºç±»åéå, å ä¸ºä½ å¹¶ä¸è½è·å¾ç±»çç»å®: åªææ¾å¼å°æ 记为ä¾ä»£ç使ç¨çæ¹æ³ææ¯å¯ç¨çï¼ èä¸ä½ ä¸è½åç±»åéåå ±äº«åé/ç¶æã(解éï¼ä½ æäºä¸ä¸ªä¸åç \`self\`)ã ä½ å¯ä»¥æ¾å¼å°å¢å æ¹æ³ä»£çï¼éè¿è°ç¨ `Sinatra::Delegator.delegate :method_name`ã å¨ä»¥ä¸æ åµå°è·å¾ä»£çåéåï¼ - 顶å±çç»å®ï¼å¦æä½ åè¿ `require "sinatra"` - å¨æ©å±äº \`Sinatra::Delegator\` mixinç对象 èªå·±å¨è¿éçä¸ä¸ä»£ç : [Sinatra::Delegator mixin](http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/base.rb#L1128) å·²ç» [被å å«è¿äºä¸»å½å空é´](http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/main.rb#L28)ã ## å½ä»¤è¡ Sinatra åºç¨å¯ä»¥è¢«ç´æ¥è¿è¡ï¼ ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-o HOST] [-s HANDLER] é项æ¯ï¼ -h # help -p # 设å®ç«¯å£ (é»è®¤æ¯ 4567) -o # 设å®ä¸»æºå (é»è®¤æ¯ 0.0.0.0) -e # 设å®ç¯å¢ (é»è®¤æ¯ development) -s # éå® rack æå¡å¨/å¤çå¨ (é»è®¤æ¯ thin) -x # æå¼äºæ¥é (é»è®¤æ¯ off) ## å¿ è¦æ¡ä»¶ æ¨èå¨ Ruby 1.8.7, 1.9.2, JRuby æè Rubinius ä¸å®è£ Sinatraã ä¸é¢çRubyçæ¬æ¯å®æ¹æ¯æç: <dl> <dt>Ruby 1.8.6</dt> <dd> ä¸æ¨èå¨1.8.6ä¸å®è£ Sinatraï¼ ä½æ¯ç´å°Sinatra 1.3.0åå¸æä¼æ¾å¼å¯¹å®çæ¯æã RDoc å CoffeScript模æ¿ä¸è¢«è¿ä¸ªRubyçæ¬æ¯æã 1.8.6å¨å®çHashå®ç°ä¸å å«ä¸ä¸ªå åæ³æ¼é®é¢ï¼ 该é®é¢ä¼è¢«1.1.1çæ¬ä¹åçSinatraå¼åã å½åçæ¬ä½¿ç¨æ§è½ä¸éç代价æé¤äºè¿ä¸ªé®é¢ãä½ éè¦æRacké级å°1.1.xï¼ å 为Rack \>= 1.2ä¸åæ¯æ1.8.6ã </dd> <dt>Ruby 1.8.7</dt> <dd> 1.8.7 被å®å ¨æ¯æï¼ä½æ¯ï¼å¦æ没æç¹å«åå ï¼ æ们æ¨èä½ åçº§å° 1.9.2 æè åæ¢å° JRuby æè Rubinius. </dd> <dt>Ruby 1.9.2</dt> <dd> 1.9.2 被æ¯æèä¸æ¨èã注æ Radius å Markaby 模æ¿å¹¶ä¸å1.9å ¼å®¹ãä¸è¦ä½¿ç¨ 1.9.2p0, å®è¢«å·²ç¥ä¼äº§ç segmentation faults. </dd> <dt>Rubinius</dt> <dd> Rubinius 被å®æ¹æ¯æ (Rubinius \>= 1.2.2)ï¼ é¤äºTextile模æ¿ã </dd> <dt>JRuby</dt> <dd> JRuby 被å®æ¹æ¯æ (JRuby \>= 1.5.6)ã ç®åæªç¥å第ä¸æ¹æ¨¡æ¿åºæå ³çé®é¢ï¼ ä½æ¯ï¼å¦æä½ éæ©äºJRubyï¼è¯·æ¥çä¸ä¸JRuby rack å¤çå¨ï¼ å 为 Thin web æå¡å¨è¿æ²¡æå¨JRubyä¸è·å¾æ¯æã </dd> </dl> æ们ä¹ä¼æ¶å»å ³æ³¨æ°çRubyçæ¬ã ä¸é¢ç Ruby å®ç°æ²¡æ被å®æ¹æ¯æï¼ ä½æ¯å·²ç¥å¯ä»¥è¿è¡ Sinatra: - JRuby å Rubinius èçæ¬ - MacRuby - Maglev - IronRuby - Ruby 1.9.0 and 1.9.1 ä¸è¢«å®æ¹æ¯æçæææ¯ï¼å¦æå¨ä¸è¢«æ¯æçå¹³å°ä¸æè¿è¡éè¯¯ï¼ æ们åå®ä¸æ¯æ们çé®é¢ï¼èæ¯å¹³å°çé®é¢ã Sinatraåºè¯¥ä¼è¿è¡å¨ä»»ä½æ¯æä¸è¿°Rubyå®ç°çæä½ç³»ç»ã ## 紧追å沿 å¦æä½ åæ¬¢ä½¿ç¨ Sinatra çææ°é²ç代ç ï¼è¯·æ¾å¿çä½¿ç¨ master åæ¯æ¥è¿è¡ä½ çç¨åºï¼å®ä¼é常ç稳å®ã cd myapp git clone git://github.com/sinatra/sinatra.git ruby -Isinatra/lib myapp.rb æ们ä¹ä¼ä¸å®æçåå¸é¢åå¸gemsï¼æä»¥ä½ ä¹å¯ä»¥è¿è¡ gem install sinatra --pre æ¥è·å¾ææ°çç¹æ§ã ### éè¿Bundler å¦æä½ æ³ä½¿ç¨ææ°çSinatraè¿è¡ä½ çåºç¨ï¼éè¿ [Bundler](http://gembundler.com/) æ¯æ¨èçæ¹å¼ã é¦å ï¼å®è£ bundlerï¼å¦æä½ è¿æ²¡æå®è£ : gem install bundler ç¶åï¼å¨ä½ ç项ç®ç®å½ä¸ï¼å建ä¸ä¸ª `Gemfile`: ~~~~ ruby source :rubygems gem 'sinatra', :git => "git://github.com/sinatra/sinatra.git" # å ¶ä»çä¾èµå ³ç³» gem 'haml' # 举ä¾ï¼å¦æä½ æ³ç¨haml gem 'activerecord', '~> 3.0' # ä¹è®¸ä½ è¿éè¦ ActiveRecord 3.x ~~~~ 请注æå¨è¿éä½ éè¦ååºä½ çåºç¨çææä¾èµå ³ç³»ã Sinatraçç´æ¥ä¾èµå ³ç³» (Rack and Tilt) å°ä¼ï¼ èªå¨è¢«Bundlerè·ååæ·»å ã ç°å¨ä½ å¯ä»¥åè¿æ ·è¿è¡ä½ çåºç¨: bundle exec ruby myapp.rb ### 使ç¨èªå·±ç å建ä¸ä¸ªæ¬å°å é并éè¿ `sinatra/lib` ç®å½è¿è¡ä½ çåºç¨ï¼ éè¿ `$LOAD_PATH`: cd myapp git clone git://github.com/sinatra/sinatra.git ruby -Isinatra/lib myapp.rb 为äºå¨æªæ¥æ´æ° Sinatra æºä»£ç : cd myapp/sinatra git pull ### å ¨å±å®è£ ä½ å¯ä»¥èªè¡ç¼è¯ gem : git clone git://github.com/sinatra/sinatra.git cd sinatra rake sinatra.gemspec rake install å¦æä½ ä»¥root身份å®è£ gemsï¼æåä¸æ¥åºè¯¥æ¯ sudo rake install ## æ´å¤ - [项ç®ä¸»é¡µï¼è±æï¼](http://www.sinatrarb.com/) - æ´å¤çææ¡£ï¼ æ°é»ï¼åå ¶ä»èµæºçé¾æ¥ã - [è´¡ç®](http://www.sinatrarb.com/contributing) - æ¾å°äºä¸ä¸ªbugï¼ éè¦å¸®å©ï¼æäºä¸ä¸ª patch? - [é®é¢è¿½è¸ª](http://github.com/sinatra/sinatra/issues) - [Twitter](http://twitter.com/sinatra) - [é®ä»¶å表](http://groups.google.com/group/sinatrarb/topics) - [IRC: \#sinatra](irc://chat.freenode.net/#sinatra) on [freenode.net](http://freenode.net) - [Sinatraå®å ¸](https://github.com/sinatra/sinatra-book/) Cookbookæç¨ - [Sinatra使ç¨æå·§](http://recipes.sinatrarb.com/) ç½åè´¡ç®çå®ç¨æå·§ - [ææ°çæ¬](http://rubydoc.info/gems/sinatra)APIææ¡£ï¼[http://rubydoc.info](http://rubydoc.info)ç[å½åHEAD](http://rubydoc.info/github/sinatra/sinatra) - [CIæå¡å¨](http://travis-ci.org/sinatra/sinatra)