Sinatra เป็น DSL สำหรับการสร้างเว็บแอปพลิเคชันอย่างรวดเร็วใน Ruby โดยใช้ความพยายามเพียงเล็กน้อย:
# myapp.rb
require 'sinatra'
get '/' do
'Hello world!'
end
ติดตั้งอัญมณีที่จำเป็น:
gem install sinatra rackup puma
และดำเนินการด้วย:
ruby myapp.rb
ดูได้ที่: http://localhost:4567
รหัสที่คุณเปลี่ยนจะไม่มีผลจนกว่าคุณจะรีสตาร์ทเซิร์ฟเวอร์ โปรดรีสตาร์ทเซิร์ฟเวอร์ทุกครั้งที่คุณเปลี่ยนหรือใช้ตัวโหลดโค้ดเช่นการรันซ้ำหรือตัวโหลดแบบแร็ค
ขอแนะนำให้รัน gem install puma
ด้วย ซึ่ง Sinatra จะรับหากมี
yield
และเลย์เอาต์ที่ซ้อนกันใน Sinatra เส้นทางคือวิธี HTTP ที่จับคู่กับรูปแบบการจับคู่ URL แต่ละเส้นทางเชื่อมโยงกับบล็อก:
get '/' do
.. show something ..
end
post '/' do
.. create something ..
end
put '/' do
.. replace something ..
end
patch '/' do
.. modify something ..
end
delete '/' do
.. annihilate something ..
end
options '/' do
.. appease something ..
end
link '/' do
.. affiliate something ..
end
unlink '/' do
.. separate something ..
end
เส้นทางจะถูกจับคู่ตามลำดับที่กำหนดไว้ เส้นทางแรกที่ตรงกับคำขอจะถูกเรียกใช้
เส้นทางที่มีเครื่องหมายทับต่อท้ายจะแตกต่างจากเส้นทางที่ไม่มี:
get '/foo' do
# Does not match "GET /foo/"
end
รูปแบบเส้นทางอาจรวมถึงพารามิเตอร์ที่มีชื่อ ซึ่งเข้าถึงได้ผ่านทางแฮช params
:
get '/hello/:name' do
# matches "GET /hello/foo" and "GET /hello/bar"
# params['name'] is 'foo' or 'bar'
"Hello #{ params [ 'name' ] } !"
end
คุณยังสามารถเข้าถึงพารามิเตอร์ที่มีชื่อผ่านพารามิเตอร์บล็อก:
get '/hello/:name' do | n |
# matches "GET /hello/foo" and "GET /hello/bar"
# params['name'] is 'foo' or 'bar'
# n stores params['name']
"Hello #{ n } !"
end
รูปแบบเส้นทางอาจรวมถึงพารามิเตอร์ splat (หรือ wildcard) ซึ่งสามารถเข้าถึงได้ผ่านอาร์เรย์ params['splat']
:
get '/say/*/to/*' do
# matches /say/hello/to/world
params [ 'splat' ] # => ["hello", "world"]
end
get '/download/*.*' do
# matches /download/path/to/file.xml
params [ 'splat' ] # => ["path/to/file", "xml"]
end
หรือด้วยพารามิเตอร์บล็อก:
get '/download/*.*' do | path , ext |
[ path , ext ] # => ["path/to/file", "xml"]
end
การจับคู่เส้นทางด้วยนิพจน์ทั่วไป:
get / / hello / ([ w ]+)/ do
"Hello, #{ params [ 'captures' ] . first } !"
end
หรือด้วยพารามิเตอร์บล็อก:
get %r{/hello/([ w ]+)} do | c |
# Matches "GET /meta/hello/world", "GET /hello/world/1234" etc.
"Hello, #{ c } !"
end
รูปแบบเส้นทางอาจมีพารามิเตอร์ทางเลือก:
get '/posts/:format?' do
# matches "GET /posts/" and any extension "GET /posts/json", "GET /posts/xml" etc
end
เส้นทางอาจใช้พารามิเตอร์การสืบค้น:
get '/posts' do
# matches "GET /posts?title=foo&author=bar"
title = params [ 'title' ]
author = params [ 'author' ]
# uses title and author variables; query is optional to the /posts route
end
อย่างไรก็ตาม เว้นแต่คุณจะปิดใช้งานการป้องกันการโจมตีเส้นทางข้ามเส้นทาง (ดูด้านล่าง) เส้นทางคำขออาจถูกแก้ไขก่อนที่จะจับคู่กับเส้นทางของคุณ
คุณสามารถปรับแต่งตัวเลือก Mustermann ที่ใช้สำหรับเส้นทางที่กำหนดได้โดยส่งผ่านแฮช :mustermann_opts
:
get 'A/postsz' , :mustermann_opts => { :type => :regexp , :check_anchors => false } do
# matches /posts exactly, with explicit anchoring
"If you match an anchored pattern clap your hands!"
end
ดูเหมือนมีเงื่อนไข แต่ไม่ใช่เลย! ตัวเลือกเหล่านี้จะถูกรวมเข้ากับแฮช :mustermann_opts
ส่วนกลางที่อธิบายไว้ด้านล่าง
เส้นทางอาจมีเงื่อนไขการจับคู่ที่หลากหลาย เช่น ตัวแทนผู้ใช้:
get '/foo' , :agent => /Songbird ( d . d )[ d / ]*?/ do
"You're using Songbird version #{ params [ 'agent' ] [ 0 ] } "
end
get '/foo' do
# Matches non-songbird browsers
end
เงื่อนไขอื่นๆ ที่มีคือ host_name
และ provides
:
get '/' , :host_name => /^admin . / do
"Admin Area, Access denied!"
end
get '/' , :provides => 'html' do
haml :index
end
get '/' , :provides => [ 'rss' , 'atom' , 'xml' ] do
builder :feed
end
provides
การค้นหาส่วนหัวยอมรับคำขอ
คุณสามารถกำหนดเงื่อนไขของคุณเองได้อย่างง่ายดาย:
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
สำหรับเงื่อนไขที่รับหลายค่า ให้ใช้เครื่องหมาย:
set ( :auth ) do |* roles | # <- notice the splat here
condition do
unless logged_in? && roles . any? { | role | current_user . in_role? role }
redirect "/login/" , 303
end
end
end
get "/my/account/" , :auth => [ :user , :admin ] do
"Your Account Details"
end
get "/only/admin/" , :auth => :admin do
"Only admins are allowed here!"
end
ค่าที่ส่งคืนของบล็อกเส้นทางจะกำหนดอย่างน้อยเนื้อหาการตอบสนองที่ส่งไปยังไคลเอนต์ HTTP หรืออย่างน้อยมิดเดิลแวร์ถัดไปในสแต็กแร็ค โดยทั่วไป นี่คือสตริง ดังตัวอย่างข้างต้น แต่ค่าอื่นๆ ก็ได้รับการยอมรับเช่นกัน
คุณสามารถส่งคืนอ็อบเจ็กต์ที่อาจเป็นการตอบสนองของ Rack, อ็อบเจ็กต์ Body Rack หรือรหัสสถานะ HTTP ที่ถูกต้อง:
[status (Integer), headers (Hash), response body (responds to #each)]
[status (Integer), response body (responds to #each)]
#each
และไม่ส่งผ่านอะไรเลยนอกจากสตริงไปยังบล็อกที่กำหนดด้วยวิธีนี้เราสามารถนำตัวอย่างการสตรีมไปใช้ได้อย่างง่ายดาย:
class Stream
def each
100 . times { | i | yield " #{ i } n " }
end
end
get ( '/' ) { Stream . new }
คุณยังสามารถใช้วิธีตัวช่วย stream
(อธิบายไว้ด้านล่าง) เพื่อลดขนาดสำเร็จรูปและฝังตรรกะการสตรีมในเส้นทาง
ดังที่แสดงไว้ข้างต้น Sinatra มาพร้อมกับการรองรับในตัวสำหรับการใช้รูปแบบสตริงและนิพจน์ทั่วไปเป็นการจับคู่เส้นทาง อย่างไรก็ตามมันไม่ได้หยุดเพียงแค่นั้น คุณสามารถกำหนดตัวจับคู่ของคุณเองได้อย่างง่ายดาย:
class AllButPattern
def initialize ( except )
@except = except
end
def to_pattern ( options )
return self
end
def params ( route )
return { } unless @except === route
end
end
def all_but ( pattern )
AllButPattern . new ( pattern )
end
get all_but ( "/index" ) do
# ...
end
โปรดทราบว่าตัวอย่างข้างต้นอาจมีการออกแบบมากเกินไป เนื่องจากสามารถแสดงเป็น:
get /.*/ do
pass if request . path_info == "/index"
# ...
end
ไฟล์สแตติกจะให้บริการจากไดเร็กทอรี ./public
public คุณสามารถระบุตำแหน่งอื่นได้โดยตั้งค่าตัวเลือก :public_folder
:
set :public_folder , __dir__ + '/static'
โปรดทราบว่าชื่อไดเรกทอรีสาธารณะไม่รวมอยู่ใน URL ไฟล์ ./public/css/style.css
มีให้เป็น http://example.com/css/style.css
ใช้การตั้งค่า :static_cache_control
(ดูด้านล่าง) เพื่อเพิ่มข้อมูลส่วนหัว Cache-Control
ภาษาเทมเพลตแต่ละภาษาจะถูกเปิดเผยผ่านวิธีการเรนเดอร์ของตัวเอง วิธีการเหล่านี้เพียงแค่ส่งคืนสตริง:
get '/' do
erb :index
end
สิ่งนี้จะแสดงผล views/index.erb
แทนที่จะใช้ชื่อเทมเพลต คุณสามารถส่งเนื้อหาเทมเพลตได้โดยตรง:
get '/' do
code = "<%= Time.now %>"
erb code
end
เทมเพลตใช้อาร์กิวเมนต์ที่สอง ตัวเลือกแฮช:
get '/' do
erb :index , :layout => :post
end
สิ่งนี้จะแสดงผล views/index.erb
ที่ฝังอยู่ใน views/post.erb
(ค่าเริ่มต้นคือ views/layout.erb
หากมีอยู่)
ตัวเลือกใดๆ ที่ Sinatra ไม่เข้าใจจะถูกส่งไปยังเครื่องมือเทมเพลต:
get '/' do
haml :index , :format => :html5
end
คุณยังสามารถตั้งค่าตัวเลือกตามภาษาเทมเพลตโดยทั่วไปได้:
set :haml , :format => :html5
get '/' do
haml :index
end
ตัวเลือกที่ส่งผ่านไปยังตัวเลือกการแทนที่วิธีการเรนเดอร์ที่ตั้งค่าผ่าน set
ตัวเลือกที่ใช้ได้:
เทมเพลตจะถือว่าอยู่ใต้ไดเร็กทอรี ./views
views โดยตรง หากต้องการใช้ไดเร็กทอรีมุมมองอื่น:
set :views , settings . root + '/templates'
สิ่งสำคัญอย่างหนึ่งที่ต้องจำไว้ก็คือ คุณจะต้องอ้างอิงเทมเพลตที่มีสัญลักษณ์เสมอ แม้ว่าจะอยู่ในไดเรกทอรีย่อยก็ตาม (ในกรณีนี้ ให้ใช้: :'subdir/template'
หรือ 'subdir/template'.to_sym
) คุณต้องใช้สัญลักษณ์เพราะมิฉะนั้นวิธีการเรนเดอร์จะเรนเดอร์สตริงใด ๆ ที่ส่งไปยังพวกมันโดยตรง
get '/' do
haml '%div.title Hello World'
end
แสดงผลสตริงเทมเพลต คุณสามารถเลือกระบุ :path
และ :line
เพื่อให้ backtrace ชัดเจนยิ่งขึ้น หากมีพาธหรือบรรทัดของระบบไฟล์ที่เกี่ยวข้องกับสตริงนั้น:
get '/' do
haml '%div.title Hello World' , :path => 'examples/file.haml' , :line => 3
end
บางภาษามีการใช้งานหลายอย่าง หากต้องการระบุการใช้งานที่จะใช้ (และเพื่อให้เธรดปลอดภัย) คุณควรกำหนดให้มีก่อน:
require 'rdiscount'
get ( '/' ) { markdown :index }
การพึ่งพาอาศัยกัน | แฮมิล |
นามสกุลไฟล์ | .haml |
ตัวอย่าง | haml :index, :format => :html5 |
การพึ่งพาอาศัยกัน | erubi หรือ erb (รวมอยู่ใน Ruby) |
นามสกุลไฟล์ | .erb , .rhtml หรือ .erubi (Erubi เท่านั้น) |
ตัวอย่าง | เอ้อ :index |
การพึ่งพาอาศัยกัน | ผู้สร้าง |
นามสกุลไฟล์ | .ผู้สร้าง |
ตัวอย่าง | ตัวสร้าง { |xml| xml.em "สวัสดี" } |
นอกจากนี้ยังใช้บล็อกสำหรับเทมเพลตอินไลน์ด้วย (ดูตัวอย่าง)
การพึ่งพาอาศัยกัน | โนโคกิริ |
นามสกุลไฟล์ | .โนโคกิริ |
ตัวอย่าง | โนโคกิริ { |xml| xml.em "สวัสดี" } |
นอกจากนี้ยังใช้บล็อกสำหรับเทมเพลตอินไลน์ด้วย (ดูตัวอย่าง)
การพึ่งพาอาศัยกัน | ฝังหน้าด้าน |
นามสกุลไฟล์ | .sass |
ตัวอย่าง | sass :stylesheet, :style => :expanded |
การพึ่งพาอาศัยกัน | ฝังหน้าด้าน |
นามสกุลไฟล์ | .scss |
ตัวอย่าง | scss :stylesheet, :style => :expanded |
การพึ่งพาอาศัยกัน | ของเหลว |
นามสกุลไฟล์ | .ของเหลว |
ตัวอย่าง | ของเหลว :index, :locals => { :key => 'value' } |
เนื่องจากคุณไม่สามารถเรียกใช้เมธอด Ruby ได้ (ยกเว้น yield
) จากเทมเพลต Liquid คุณมักจะต้องการส่ง Locals ไปให้
การพึ่งพาอาศัยกัน | ใครก็ตามของ: RDiscount, RedCarpet, kramdown, commonmarker pandoc |
นามสกุลไฟล์ | .markdown , .mkd และ .md |
ตัวอย่าง | มาร์กดาวน์ :index, :layout_engine => :erb |
ไม่สามารถเรียกเมธอดจาก Markdown หรือส่งคนในพื้นที่ไปได้ ดังนั้นคุณมักจะใช้ร่วมกับกลไกการเรนเดอร์อื่น:
erb :overview , :locals => { :text => markdown ( :introduction ) }
โปรดทราบว่าคุณอาจเรียกใช้เมธอด markdown
จากภายในเทมเพลตอื่นได้:
% h1 Hello From Haml!
% p = markdown ( :greetings )
เนื่องจากคุณไม่สามารถเรียก Ruby จาก Markdown ได้ คุณจึงไม่สามารถใช้เลย์เอาต์ที่เขียนด้วย Markdown ได้ อย่างไรก็ตาม คุณสามารถใช้กลไกการเรนเดอร์อื่นสำหรับเทมเพลตมากกว่าโครงร่างได้โดยส่งตัวเลือก :layout_engine
การพึ่งพาอาศัยกัน | รพ |
นามสกุลไฟล์ | .rdoc |
ตัวอย่าง | rdoc :README, :layout_engine => :erb |
ไม่สามารถเรียกวิธีการจาก RDoc หรือส่งผ่านคนท้องถิ่นไปได้ ดังนั้นคุณมักจะใช้ร่วมกับกลไกการเรนเดอร์อื่น:
erb :overview , :locals => { :text => rdoc ( :introduction ) }
โปรดทราบว่าคุณอาจเรียกใช้เมธอด rdoc
จากภายในเทมเพลตอื่นได้:
% h1 Hello From Haml!
% p = rdoc ( :greetings )
เนื่องจากคุณไม่สามารถเรียก Ruby จาก RDoc ได้ คุณจึงไม่สามารถใช้เลย์เอาต์ที่เขียนใน RDoc ได้ อย่างไรก็ตาม คุณสามารถใช้กลไกการเรนเดอร์อื่นสำหรับเทมเพลตมากกว่าโครงร่างได้โดยส่งตัวเลือก :layout_engine
การพึ่งพาอาศัยกัน | แอสซิอิด็อกเตอร์ |
นามสกุลไฟล์ | .asciidoc , .adoc และ .ad |
ตัวอย่าง | asciidoc :README, :layout_engine => :erb |
เนื่องจากคุณไม่สามารถเรียกใช้เมธอด Ruby ได้โดยตรงจากเทมเพลต AsciiDoc คุณจึงมักต้องการส่ง Locals ไปให้
การพึ่งพาอาศัยกัน | มาร์คาบี |
นามสกุลไฟล์ | .mab |
ตัวอย่าง | มาร์กาบี { h1 "ยินดีต้อนรับ!" - |
นอกจากนี้ยังใช้บล็อกสำหรับเทมเพลตอินไลน์ด้วย (ดูตัวอย่าง)
การพึ่งพาอาศัยกัน | ราเบล |
นามสกุลไฟล์ | .rabl |
ตัวอย่าง | rabl :index |
การพึ่งพาอาศัยกัน | สลิม แลง |
นามสกุลไฟล์ | .บาง |
ตัวอย่าง | บาง :index |
การพึ่งพาอาศัยกัน | yajl-ทับทิม |
นามสกุลไฟล์ | .yajl |
ตัวอย่าง | yajl :index, :locals => { :key => 'qux' }, :callback => 'present', :variable => 'resource' |
แหล่งที่มาของเทมเพลตได้รับการประเมินเป็นสตริง Ruby และตัวแปร json ผลลัพธ์จะถูกแปลงโดยใช้ #to_json
:
json = { :foo => 'bar' }
json [ :baz ] = key
ตัวเลือก :callback
และ :variable
สามารถใช้เพื่อตกแต่งวัตถุที่แสดงผลได้:
var resource = { "foo" : "bar" , "baz" : "qux" } ;
present ( resource ) ;
เทมเพลตได้รับการประเมินภายในบริบทเดียวกับตัวจัดการเส้นทาง ตัวแปรอินสแตนซ์ที่ตั้งค่าในตัวจัดการเส้นทางสามารถเข้าถึงได้โดยตรงโดยเทมเพลต:
get '/:id' do
@foo = Foo . find ( params [ 'id' ] )
haml '%h1= @foo.name'
end
หรือระบุแฮชที่ชัดเจนของตัวแปรท้องถิ่น:
get '/:id' do
foo = Foo . find ( params [ 'id' ] )
haml '%h1= bar.name' , :locals => { :bar => foo }
end
โดยทั่วไปจะใช้เมื่อแสดงเทมเพลตบางส่วนจากภายในเทมเพลตอื่น
yield
และเลย์เอาต์ที่ซ้อนกัน โดยทั่วไปเลย์เอาต์จะเป็นเพียงเทมเพลตที่เรียก yield
เทมเพลตดังกล่าวสามารถใช้ได้ผ่านตัวเลือก :template
ตามที่อธิบายไว้ข้างต้น หรือสามารถแสดงผลด้วยบล็อกดังต่อไปนี้:
erb :post , :layout => false do
erb :index
end
รหัสนี้ส่วนใหญ่เทียบเท่ากับ erb :index, :layout => :post
การส่งผ่านบล็อกไปยังวิธีการเรนเดอร์มีประโยชน์มากที่สุดสำหรับการสร้างเลย์เอาต์ที่ซ้อนกัน:
erb :main_layout , :layout => false do
erb :admin_layout do
erb :user
end
end
ซึ่งสามารถทำได้โดยใช้โค้ดน้อยลงด้วย:
erb :admin_layout , :layout => :main_layout do
erb :user
end
ปัจจุบัน วิธีการเรนเดอร์ต่อไปนี้ยอมรับบล็อก: erb
, haml
, liquid
, slim
นอกจากนี้วิธี render
ทั่วไปยังยอมรับบล็อกอีกด้วย
เทมเพลตอาจถูกกำหนดไว้ที่ส่วนท้ายของไฟล์ต้นฉบับ:
require 'sinatra'
get '/' do
haml :index
end
__END__
@@ layout
%html
!= yield
@@ index
%div.title Hello world.
หมายเหตุ: เทมเพลตอินไลน์ที่กำหนดไว้ในไฟล์ต้นฉบับที่ต้องใช้ Sinatra จะถูกโหลดโดยอัตโนมัติ โทร enable :inline_templates
อย่างชัดเจนหากคุณมีเทมเพลตอินไลน์ในไฟล์ต้นฉบับอื่น
เทมเพลตอาจถูกกำหนดโดยใช้วิธี template
ระดับบนสุด:
template :layout do
"%html n =yield n "
end
template :index do
'%div.title Hello World!'
end
get '/' do
haml :index
end
หากมีเทมเพลตชื่อ "เลย์เอาต์" อยู่ ระบบจะใช้เทมเพลตนั้นทุกครั้งที่มีการแสดงผลเทมเพลต คุณสามารถปิดการใช้งานเลย์เอาต์แต่ละรายการได้โดยส่ง :layout => false
หรือปิดการใช้งานโดยค่าเริ่มต้นผ่าน set :haml, :layout => false
:
get '/' do
haml :index , :layout => ! request . xhr?
end
หากต้องการเชื่อมโยงนามสกุลไฟล์กับกลไกเทมเพลต ให้ใช้ Tilt.register
ตัวอย่างเช่น หากคุณต้องการใช้นามสกุลไฟล์ tt
สำหรับเทมเพลต Haml คุณสามารถทำสิ่งต่อไปนี้:
Tilt . register Tilt [ :haml ] , :tt
ขั้นแรก ให้ลงทะเบียนเครื่องยนต์ของคุณด้วย Tilt จากนั้นสร้างวิธีการเรนเดอร์:
Tilt . register MyAwesomeTemplateEngine , :myat
helpers do
def myat ( * args ) render ( :myat , * args ) end
end
get '/' do
myat :index
end
แสดงผล ./views/index.myat
เรียนรู้เพิ่มเติมเกี่ยวกับเอียง
หากต้องการใช้กลไกการค้นหาเทมเพลตของคุณเอง คุณสามารถเขียนวิธี #find_template
ของคุณเองได้:
configure do
set :views , [ './views/a' , './views/b' ]
end
def find_template ( views , name , engine , & block )
Array ( views ) . each do | v |
super ( v , name , engine , & block )
end
end
ก่อนที่ตัวกรองจะได้รับการประเมินก่อนแต่ละคำขอภายในบริบทเดียวกับเส้นทางที่จะเป็นและสามารถแก้ไขคำขอและการตอบสนองได้ ตัวแปรอินสแตนซ์ที่ตั้งค่าในตัวกรองสามารถเข้าถึงได้โดยเส้นทางและเทมเพลต:
before do
@note = 'Hi!'
request . path_info = '/foo/bar/baz'
end
get '/foo/*' do
@note #=> 'Hi!'
params [ 'splat' ] #=> 'bar/baz'
end
หลังจากที่ตัวกรองได้รับการประเมินหลังจากแต่ละคำขอภายในบริบทเดียวกันกับเส้นทางจะเป็นและยังสามารถแก้ไขคำขอและการตอบสนองได้ ตัวแปรอินสแตนซ์ที่ตั้งค่าไว้ก่อนตัวกรองและเส้นทางสามารถเข้าถึงได้โดยตัวกรองหลัง:
after do
puts response . status
end
หมายเหตุ: ยกเว้นกรณีที่คุณใช้เมธอด body
แทนที่จะเพียงส่งคืนสตริงจากเส้นทาง เนื้อความจะยังไม่พร้อมใช้งานในตัวกรอง after เนื่องจากจะถูกสร้างขึ้นในภายหลัง
ตัวกรองจะใช้รูปแบบหรือไม่ก็ได้ ซึ่งทำให้ได้รับการประเมินหากเส้นทางคำขอตรงกับรูปแบบนั้นเท่านั้น:
before '/protected/*' do
authenticate!
end
after '/create/:slug' do | slug |
session [ :last_slug ] = slug
end
เช่นเดียวกับเส้นทาง ตัวกรองก็มีเงื่อนไขเช่นกัน:
before :agent => /Songbird/ do
# ...
end
after '/blog/*' , :host_name => 'example.com' do
# ...
end
ใช้วิธี helpers
ระดับบนสุดเพื่อกำหนดวิธีตัวช่วยสำหรับใช้ในตัวจัดการเส้นทางและเทมเพลต: