SlideShare uma empresa Scribd logo
1 de 73
Baixar para ler offline
用Sinatra建置高效能
              與高自訂性的系統
                Mu-Fan Teng@Rubyconf.TW 2011
                   TOMLAN Software Studio




                             1
11年8月26日星期五
About me
       •      鄧慕凡(Mu-Fan Teng)
              •a.k.a: Ryudo
              •http://twitter.com/ryudoawaru
              •Ruby developer since 2007
              •Founder of Tomlan Software Studio.




                                          2
11年8月26日星期五
3
11年8月26日星期五
Agenda
              • Sinatra Introduction
              • Tutorial
              • Rails Metal
              • Production tips


                                   4
11年8月26日星期五
Why Sinatra




                   5
11年8月26日星期五
Load lesser, run faster!
       Rails 3.1預設: 不含project本身約20個middleware




                         6
11年8月26日星期五
Load lesser, run faster!
                         Sinatra:預設不超過6個

               def build(*args, &bk)
                 builder = Rack::Builder.new
                 builder.use Rack::MethodOverride if method_override?
                 builder.use ShowExceptions       if show_exceptions?
                 builder.use Rack::CommonLogger   if logging?
                 builder.use Rack::Head
                 setup_sessions builder
                 middleware.each { |c,a,b| builder.use(c, *a, &b) }
                 builder.run new!(*args, &bk)
                 builder
               end




                                              7
11年8月26日星期五
Sinatra
              • very micro web framework(少於1600行)
              • pure Ruby/Rack
              • DSL化設計



                               8
11年8月26日星期五
完全自訂性
              • 自己選擇的ORM
              • 自己選擇template engine
              • 可簡單可複雜的路由設定



                             9
11年8月26日星期五
適用場合



               10
11年8月26日星期五
11
11年8月26日星期五
12
11年8月26日星期五
Sinatra適合
              • Tiny WEB App
               • 聊天室
               • Widget
              • WEB API


                               13
11年8月26日星期五
Tutorial開始



                  14
11年8月26日星期五
Hello Sinatra!
       require 'rubygems'
       require 'sinatra'
       get '/' do
         'Hello Sinatra!'
       end




                                  15
11年8月26日星期五
流程

       BEFORE              ROUTES            AFTER
      before            get ‘/’            after

      before ‘/users’   post ‘/users’      after ‘/users’

                        put ‘/users/:id’
                        get ‘/users/:id’




                                    16
11年8月26日星期五
Named Route Params
              require 'rubygems'
              require 'sinatra'

              get '/hello/:name' do
                # => get '/hello/Rubyconf'
                # params[:name] => Rubyconf
                "Hello #{params[:name]}!"
              end




        Sinatra中的Route就如同Rails的Action + Route, 可
        透過路由表示式的設定將URI字串中符合批配樣
        式的內容(冒號開頭)化為特定的params hash成員.

                                              17
11年8月26日星期五
Splat Param Route
          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




        在路由表示式中的*號會以陣列成員的形式集
        中到特定的params[:splat]中.

                                              18
11年8月26日星期五
Route matching with
                Regular Expressions
              get %r{/posts/name-([w]+)} do
                # => get /posts/name-abc, params[:captures][0] = 'abc'
                "Hello, #{params[:captures].first}!"
              end

              get %r{/posts/([w]+)} do |pid|
                # => put match content to block param(s)
                # => matches 「([w]+)」 to 「pid」
              end




        路由表示式也接受Regular Expression並可將match
        內容化為特定的params[:captures]陣列成員, 也可直
        接設定為Block區域變數

                                               19
11年8月26日星期五
Route with block
                     paramaters
     get '/thread/:tid' do |tid|
       # => tid == params[:tid]
     end

     get '/pictures/*.*' do |filename, ext|
       # => filename == first *
       # => ext == second *
       # => GET '/pictures/abc.gif' then filename = "abc" and ext = "gif"
       "filename = #{filename}, ext = #{ext}"
     end

     get %r{/posts/([w]+)} do |pid|
       # => put match content to block param(s)
       # => matches 「([w]+)」 to 「pid」
     end




       以上所介紹的路由表示式都可以將匹配的內容
       指派到區塊的區域變數中
                                             20
11年8月26日星期五
Conditional route
              get '/foo', :agent => /MSIEs(d.d+)/ do
                "You're using IE version #{params[:agent][0]}"
                # => IE特製版
              end

              get '/', :host_name => /^admin./ do
                "Admin Area, Access denied!"
              end




        也可以用user_agent或host_name來做路由啟發
        的條件; 注意的是agent可以是字串或正規表示
           式, hostname只能是正規表示式.
                                          21
11年8月26日星期五
Conditions(2)
               get '/', :provides => 'html' do
                 erb :index
               end

               get '/', :provides => ['rss', 'atom', 'xml']
               do
                  builder :feed
               end




        provides的條件是看accept header而非path


                                    22
11年8月26日星期五
RESTful Routes
              get '/posts' do
                # => Get some posts
              end

              get '/posts/:id' do
                # => Get 1 post
              end

              create '/posts' do
                # => create a post
              end

              put '/posts/:id'
                # => Update a post
              end

              delete '/posts/:id'
                # => Destroy a post
              end




                                      23
11年8月26日星期五
Route Return Values

         1. HTTP Status Code
         2. Headers
         3. Response Body(Responds to #each)




                              24
11年8月26日星期五
Http Streaming
              class FlvStream    #http://goo.gl/B8BdU
                  ....
                  def each ... end
              end
              class Application < Sinatra::Base
                # Catch everyting and serve as stream
                get %r((.*)) do |path|
                   path = File.expand_path(STORAGE_PATH + path)
                   status(401) && return unless path =~ Regexp.new(STORAGE_PATH)
                   flv = FlvStream.new(path, params[:start].to_i)
                   throw :response, [200, {'Content-Type' => 'application/x-flv',
              "Content-Length" => flv.length.to_s}, flv]
                end
              end



        由於只要有each這個method就可以當做Response
        Body, 因此在App Server支援的前提下就可以做出Http
        Streaming
                                                25
11年8月26日星期五
Template Engines
              • 支援數不清的樣板引擎(erb/haml/sass/
               coffeescript/erubis/builder/markdown...)
              • 支援inline template
              • 透過Tilt可自訂樣板引擎


                                  26
11年8月26日星期五
Template Eengines
        get '/' do
          haml :index, :format => :html4 # overridden
        #render '/views/index.haml'
        end

        get '/posts/:id.html' do
          @post = Post.find(params[:id])
          erb "posts/show.html".to_sym, :layout => 'layouts/app.html'.to_sym
        #render '/views/posts/show.html.erb' with
        #layout '/views/layouts/app.html.erb'
        end
        get '/application.css' do
          sass :application
        #render '/views/application.sass'
        end




        所有的view名稱都必需是symbol,
        不可以是String
                                              27
11年8月26日星期五
Compass Integration
        set   :app_file, __FILE__
        set   :root, File.dirname(__FILE__)
        set   :views, "views"
        set   :public, 'static'

        configure do
          Compass.add_project_configuration(File.join(Sinatra::Application.root,
        'config', 'compass.config'))
        end
        get '/stylesheets/:name.css' do
          content_type 'text/css', :charset => 'utf-8'
          sass(:"stylesheets/#{params[:name]}", Compass.sass_engine_options )
        end




                                              28
11年8月26日星期五
Inline Templates
              get '/' do
                erb :"root"
              end

              template :root do
              <<"EOB"
              <p>Hello Sinatra!</p>

              EOB
              end




                                      29
11年8月26日星期五
Before filters
              before '/rooms/:room_id' do
                puts "Before route「/rooms/*」 only"
                @room = Room.find(params[:room_id])
              end
              before do
                puts "Before all routes"
              end
              get '/' do
                ...
              end
              get '/rooms/:room_id' do
                puts "object variable 「@room」 is accessiable now!"
                "You are in room #{@room.name}"
              end




                                                 30
11年8月26日星期五
Before filters
              • 和路由使用相同表示式
              • 沒有Rails的「prepend_before_filter」
              • 先載入的先執行



                              31
11年8月26日星期五
Before filter order
               before '/rooms/:room_id' do
                 # 先執行
                 # Before route「/rooms/*」 only
                 @room = Room.find(params[:room_id])
               end
               before do
                 # 後執行
                 # Before all routes
               end
               get '/' do
                 ...
               end
               get '/rooms/:room_id' do
                 # object variable 「@room」 is accessiable
               now!
                 "You are in room #{@room.name}"
               end




                                     32
11年8月26日星期五
Session
              enable :sessions # equal to 「use Rack::Session::Cookie」

              post '/sessions' do
                @current_user = User.auth(params[:account], params[:passwd])
                session[:uid] = @current_user.id if @current_user
              end
              delete '/sessions' do
                session[:uid] = nil
                redirect '/'
              end




                                                 33
11年8月26日星期五
Cookies
              get '/' do
                response.set_cookie('foo', :value => 'BAR')
                response.set_cookie("thing", { :value => "thing2",
                                      :domain => 'localhost',
                                      :path => '/',
                                      :expires => Time.today,
                                      :secure => true,
                                      :httponly => true })
              end

              get '/readcookies' do
                cookies['thing'] # => thing2
              end




                                                 34
11年8月26日星期五
Helpers
              helpers do
                def member2json(id)
                  Member.find(id).attributes.to_json
                end
              end
              get '/members/:id.json' do
                member2json(params[:id])
              end




                                                35
11年8月26日星期五
Helpers
              Helpers can use in:
         1. filters
         2. routes
         3. templates




                                    36
11年8月26日星期五
Halt & Pass
              • Halt
               • 相當於控制結構中的break, 阻斷後續執
                行強制回應.

              • Pass
               • 相當於控制結構中的next.

                         37
11年8月26日星期五
Halt Example
              helpers do
                def auth
                  unless session[:uid]
                    halt 404, 'You have not logged in yet.'
                  end
                end
              end

              before '/myprofile' do
                auth
              end

              get '/myprofile' do
                #如果session[:uid]為nil,則此route不會執行
              end




                                                   38
11年8月26日星期五
Pass Example
              get '/checkout' do
                pass if @current_member.vip?
                #一般客結帳處理
                erb "normal_checkout".to_sym
              end
              get '/checkout' do
                #VIP專用結帳處理
                erb 'vip_checkout'.to_sym
              end




                                               39
11年8月26日星期五
body,status,headers
              get '/' do
                status 200
                headers "Allow"   => "BREW, POST, GET, PROPFIND, WHEN"
                body 'Hello' # => 設定body
                body "#{body} Sinatra" # => 加料body
              end




                                           40
11年8月26日星期五
url and redirect

                get '/foo' do
                  redirect to('/bar')
                end




        url(別名to)可以生成包含了baseuri的url; redirect則
        同其名可進行http重定向並可附加訊息或狀態碼.

                                        41
11年8月26日星期五
send_file
        get '/attachments/:file' do
          send_file File.join('/var/www/attachments/', params[:file])
          #可用選項有
          # filename:檔名
          # last_modified:顧名思義, 預設值為該檔案的mtime
          # type:內容類型,如果沒有會從文件擴展名猜測。
          # disposition:Content-Disposition, 可能的包括: nil (默認), :attachment (下載附件)
        和 :inline(瀏覽器內顯示)
          # length:Content-Length,預設為檔案size
        end




        另⼀一個helper: attachment等於send_file的disposition
        為:attachement的狀況.

                                              42
11年8月26日星期五
request物件

         # 在 http://example.com/example 上運行的應用
         get '/foo' do
           request.body              # 被客戶端設定的請求體(見下)
           request.scheme            # "http"
           request.script_name       # "/example", 即為SUB-URI
           request.path_info         # "/foo"
           request.port              # 80
           request.request_method    # "GET"
           request.query_string      # "" 查詢參數
           request.content_length    # request.body的長度
           request.media_type        # request.body的媒體類型
         end




                                                     43
11年8月26日星期五
settings
        #以下三行都是同一件事
        set :abc, 123
        set :abc => 123
        settings.abc = 123
        ###可用區塊######
        set(:foo){|val| puts(val) }
        get '/' do
          settings.foo('Sinatra') #will puts "Sinatra"
          "setting abc = #{settings.abc}"
        end




        Sinatra提供了多種方式讓你在Class Scope和Request
        Scope都能取用與設定資料或區塊, 其中有⼀一些預設
        的settings是有關系統運作與設定的.
                                              44
11年8月26日星期五
重要的setting
              •   public(public)
                  •   指定public目錄的位置

              •   views(views)
                  •   指定template/views目錄位置

              •   static(true)
                  •   是否由Sinatra處理靜態檔案, 設為false交給WEB伺服器會增強效能

              •   lock(false)
                  •   設定false開啟thread模式(單⼀一行程⼀一次處理多個requests)

              •   methid_override(視情況而定)

                  •   開始「_method」參數以使用get/post以外的http method

              •   show_exceptions(預設值與environment有關)

                  •   是否像rails⼀一樣顯示error stack



                                                 45
11年8月26日星期五
settings的特別用途
          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




        condition是⼀一個Sinatra內建的method,可以視傳
        入區塊的執行結果為true或false決定視否執行
        該route或pass掉.

                                              46
11年8月26日星期五
configure do
                     Configure Block
          # 直接設定值
          set :option, 'value'
          # 一次設定多個值
          set :a => 1, :b => 2
          # 等於設定該值為true
          enable :option
          # 同上
          disable :option
          # 可用區塊
          set(:css_dir) { File.join(views, 'css') }
        end

        configure :production do
          # 可針對環境(RACK_ENV)做設定
          LOGGER.level = Logger::WARN
        end

        get '/' do
          settings.a? # => true
          settings.a # => 1
        end


        類似Rails的environment.rb, 在行程啟動時執行⼀一次.
                                              47
11年8月26日星期五
錯誤處理
              1. not_found
              2. 特定error class
              3. http status code
              4. 對應所有錯誤




                                    48
11年8月26日星期五
error block

        error do
          #透過env['sinatra.error'] 可取得錯誤物件
          'Sorry there was a nasty error - ' + env['sinatra.error'].name
        end




        error 處理區塊在任何route或filter拋出錯誤的時候
        會被調用。 錯誤物件可以通過sinatra.error的env
        hash項目取得, 可以使用任何在錯誤發生前的filter或
        route中定義的instance variable及環境變數等
                                              49
11年8月26日星期五
自定義error block
        error MyCustomError do
          'So what happened was...' + env['sinatra.error'].message
          #輸出為:「 So what happened was... something bad」
        end

        get '/' do
          raise MyCustomError, 'something bad'
        end




        如同Ruby的rescue區塊, error處理⼀一樣可以針對
        error class做定義;也可以在執行期故意啟發特定
        error class的錯誤並附加訊息.
                                                 50
11年8月26日星期五
自定義error block
        error 403 do
          'Access forbidden'
        end

        get '/secret' do
          403
        end
        error 400..510 do
          'Boom'
        end




        針對特定的HTTP CODE設定錯誤處理區塊, 可以是
        代碼範圍

                               51
11年8月26日星期五
not_found block

        not_found do
          'This is nowhere to be found'
        end




        not_found區塊等於 error 404區塊

                                          52
11年8月26日星期五
Rack Middleware
        require 'rubygems'
        require 'sinatra'

        use Rack::Auth::Basic, "Restricted Area" do |username, password|
          [username, password] == ['admin', '12345']
        end

        get '/' do
          "You are authorized!"
        end




        和Rails⼀一樣, Sinatra也是基於Rack的middleware, 所以
        可以使用其它的Rack middleware.

                                              53
11年8月26日星期五
模組化
        require 'rubygems'
        require 'sinatra/base'
        #不可以require "sinatra" 以避免頂層Object Class被汙染
        class MyApp < Sinatra::Base
          set :sessions, true
          set :foo, 'bar'
          get '/' do
            'Hello world!'
          end
        end




        為了建構可重用的組件,需要將你的Sinatra應用程式
        模組化以將程式化為⼀一個獨立的Rack Middleware.



                                              54
11年8月26日星期五
Multiple App in 1 process
        #config.ru
        require 'rubygems'
        require 'sinatra/base'
        class App1 < Sinatra::Base
          get '/' do
            'I am App1'
          end
        end
        class App2 < Sinatra::Base
          get '/' do
            'I am App2'
          end
        end
        map '/' do
          run App1
        end
        map '/app2' do
          run App2
        end



        利用Rack::Builder可將不同的Rack App掛在不同的uri
        下面.
                                     55
11年8月26日星期五
何時需要模組化
        • 特定的Rack App Server(Passenger/Heroku等)
        • 將你的Sinatra App當做⼀一個Middleware而非終
              點(endpoint), 例如:
              1. ⼀一次掛載多個Sinatra App在同⼀一個rackup
              2. 在Rails內掛載Sinatra App




                                 56
11年8月26日星期五
Rails Metal

        #routes.rb
        TestMixin::Application.routes.draw do
          devise_for :users
          mount ApiApp, :at => '/api'# => 掛載 ApiApp在/api 下
          root :to => "welcome#index"
        end
        #lib/api_app.rb
        class ApiApp < Sinatra::Base
          get '/users/:id.json' do
            User.find(params[:id]).attributes.to_json
          end
        end




                                              57
11年8月26日星期五
限制
              • 「可以」使用Rails的model
              • 「不可以」使用Rails的helper/controller
              • 「可能可以」使用Rails的views, 但極不建
               議

              • not_found的錯誤是「由Rails端」處理
              • 「不可以」直接使用綁定Rails(Railties)的
               組件

                             58
11年8月26日星期五
Devise Mix-in

        #config/routes.rb
        TestMixin::Application.routes.draw do
          devise_for :users
          mount ApiApp, :at => '/api'# => 掛載 ApiApp在/api 下
        end
        #lib/api_app.rb
        class ApiApp < Sinatra::Base
          get '/users/:id.json' do
            #等效 devise的 authenticate_user!
            request.env['warden'].authenticate!(:scope => 'user')
            User.find(params[:id]).attributes.to_json
          end
        end




        由於Devise是建構在Warden(Rack Middleware)之上,
        雖然不能直接使用Devise的認證helper, 但可以用
        warden的方式來處理認證的問題.
                                              59
11年8月26日星期五
Scopes and Binding
              • Sinatra的變數定義域分成兩種
               1. Application/Class Scope
               2. Request/Instance Scope




                                  60
11年8月26日星期五
Scopes-範例
        class MyApp < Sinatra::Base
          # => Application/Class Scope
          configure do
            # => Application/Class Scope
            set :foo, 100
          end
          self.foo # => 100
          helpers do
            # => Application/Class Scope
            def foo
              # => Request scope for all routes
            end
          end
          get '/users/:id' do
            # => Request scope for "/users/:id" only
            settings.foo # => 100
          end
          get '/' do
            # => Request scope for '/' only
          end
        end



        Request scope可透過settings helper取得在
        Application scope定義的設定值
                                              61
11年8月26日星期五
Application/Class scope
              1. 應用程式的Class Body
              2. helpers/configure的區塊內
              3. 傳遞給set的區塊




                              62
11年8月26日星期五
Request/Instance scope
              1. session/request物件只在這裡有效
              2. 作用於:
               2.1.routes區塊
               2.2.helper methods
               2.3.view/templates
               2.4.before/after filters
               2.5.error block
               2.6. 傳給settings的區塊中的condition區塊

                                         63
11年8月26日星期五
實體變數的定義範圍
        class MyApp < Sinatra::Base
          before do
            # => Request scope for all routes
            @varall = 100
          end
          before '/posts/:id' do
            @post = Post.find(params[:id])
          end
          get '/posts/:id' do
            # => Request scope for 「/posts/:id」
            @post.nil? # => false
            @varall # => 100
            settings.get('/foo'){
              # => Request scope for 「/foo」 only
              @varall # => 100
              @post.nil? # => true
            }
          end
          get '/' do
            @varall # => 100
            @post # => nil
          end
        end



        就算是在route block中定義的另⼀一個route block, ⼀一樣
        不能共用實體變數, 在before filter中定義的實體變數
        會傳到下⼀一個符合條件的before filter與route block.
                                                   64
11年8月26日星期五
存在Class Body中的Request Scope
        #####sinatra/base.rb##
        module Sinatra
          class Base
            class << self
              def host_name(pattern)
                condition { pattern === request.host }
              end
            end
          end
        end
        ##等同於自行定義#############
        set(:host_name){|pattern| condition { pattern === request.host }}
        ######################

        get '/', :host_name => /^admin./ do
          "Admin Area, Access denied!"
        end




        傳遞給condition method的區塊內的scope是Request
        Scope, 所以可以使用request物件
                                               65
11年8月26日星期五
Production Tips
              1. ORM
              2. Useful Extensions
              3. Paginators
              4. boot.rb




                                     66
11年8月26日星期五
ActiveRecord
        require 'rubygems'
        require 'sinatra'
        require 'active_record'

        #先建立連線
        ActiveRecord::Base.establish_connection(
          :adapter => 'sqlite3',
          :database => 'sinatra_application.sqlite3.db'
        )
        #require或宣告class
        class Post < ActiveRecord::Base
        end

        get '/' do
          @posts = Post.all()
          erb :index
        end




                                             67
11年8月26日星期五
Mongoid

        require 'rubygems'
        require 'sinatra'
        require 'mongoid'
        Mongoid.configure do |config|
          config.master = Mongo::Connection.new.db("godfather")
        end
        class User
          include Mongoid::Document
          include Mongoid::Timestamps
        end
        get '/users/:id.json' do
          User.find(params[:id]).to_json
        end




                                              68
11年8月26日星期五
Sinatra More
              •   MarkupPlugin
                  •   設定form以及html tag的helper等

              •   RenderPlugin
                  •   content_for/yield等

              •   WardenPlugin
                  •   綁定單⼀一Class做登入處理

              •   MailerPlugin
                  •   顧名思義, 但不是使用ActionMailer

              •   RoutingPlugin
                  •   做出有namespace以及restful的路由

              •   code generator

                  •   建立project框架



                                             69
11年8月26日星期五
WillPaginate
         • 3.0後版本已直接以extension型式支援Sinatra
         • 2.3版本需要手動改寫renderer
         • 由於Sinatra不像Rails有固定的url形式約束,
              必要時還是要自己改寫renderer




                         70
11年8月26日星期五
Kaminari
         • Model Paginate功能可正常使用
         • View Helpers的部份無法使用
         • 需依照ORM require正確的組件



                        71
11年8月26日星期五
boot.rb
        require 'bundler'
        Bundler.setup
        Bundler.require
        class FreeChat3 < Sinatra::Base

          configure do
            #settings
            set :sessions, true
            #Middlewares
            use Rack::Flash
            #Sinatra Extensions
            register SinatraMore::MarkupPlugin
            #DB Connections
            ActiveRecord::Base.establish_connection(DB_CONFIG[RACK_ENV])
            Mongoid.load! File.join(ROOT_DIR, '/config/mongoid.yml')
            #load Model/Controller/helper/Libs
            Dir.glob(File.join(ROOT_DIR, '/lib/*.rb')).each{|f| require f }
            Dir.glob(File.join(ROOT_DIR, '/app/models/*.rb')).each{|f| require f }
            Dir.glob(File.join(ROOT_DIR, '/app/helpers/*.rb')).each{|f| require f }
            Dir.glob(File.join(ROOT_DIR, '/app/controllers/*.rb')).each{|f| load f }
          end

          helpers do
            include ApplicationHelper
            include ActionView::Helpers::TextHelper
          end

        end

                                                72
11年8月26日星期五
Q&A



               73
11年8月26日星期五

Mais conteúdo relacionado

Mais procurados

開發環境建置
開發環境建置開發環境建置
開發環境建置Shengyou Fan
 
啟動 Laravel 與環境設定
啟動 Laravel 與環境設定啟動 Laravel 與環境設定
啟動 Laravel 與環境設定Shengyou Fan
 
開發環境建置
開發環境建置開發環境建置
開發環境建置Shengyou Fan
 
View 與 Blade 樣板引擎
View 與 Blade 樣板引擎View 與 Blade 樣板引擎
View 與 Blade 樣板引擎Shengyou Fan
 
COSCUP 2016 Laravel 部署工作坊 - 部署指南
COSCUP 2016 Laravel 部署工作坊 - 部署指南COSCUP 2016 Laravel 部署工作坊 - 部署指南
COSCUP 2016 Laravel 部署工作坊 - 部署指南Shengyou Fan
 
Migrations 與 Schema操作
Migrations 與 Schema操作Migrations 與 Schema操作
Migrations 與 Schema操作Shengyou Fan
 
Model 設定與 Seeding
Model 設定與 SeedingModel 設定與 Seeding
Model 設定與 SeedingShengyou Fan
 
Laravel - 系統全攻略(續)
Laravel - 系統全攻略(續)Laravel - 系統全攻略(續)
Laravel - 系統全攻略(續)Vincent Chi
 
開發流程與工具介紹
開發流程與工具介紹開發流程與工具介紹
開發流程與工具介紹Shengyou Fan
 
Route 路由控制
Route 路由控制Route 路由控制
Route 路由控制Shengyou Fan
 
Model 設定與 Seeding
Model 設定與 SeedingModel 設定與 Seeding
Model 設定與 SeedingShengyou Fan
 
Laravel - 系統全攻略
Laravel - 系統全攻略Laravel - 系統全攻略
Laravel - 系統全攻略Vincent Chi
 
Schema & Migration操作
Schema & Migration操作Schema & Migration操作
Schema & Migration操作Shengyou Fan
 
Package 安裝與使用
Package 安裝與使用Package 安裝與使用
Package 安裝與使用Shengyou Fan
 

Mais procurados (20)

開發環境建置
開發環境建置開發環境建置
開發環境建置
 
啟動 Laravel 與環境設定
啟動 Laravel 與環境設定啟動 Laravel 與環境設定
啟動 Laravel 與環境設定
 
Eloquent ORM
Eloquent ORMEloquent ORM
Eloquent ORM
 
開發環境建置
開發環境建置開發環境建置
開發環境建置
 
View 與 Blade 樣板引擎
View 與 Blade 樣板引擎View 與 Blade 樣板引擎
View 與 Blade 樣板引擎
 
COSCUP 2016 Laravel 部署工作坊 - 部署指南
COSCUP 2016 Laravel 部署工作坊 - 部署指南COSCUP 2016 Laravel 部署工作坊 - 部署指南
COSCUP 2016 Laravel 部署工作坊 - 部署指南
 
Migrations 與 Schema操作
Migrations 與 Schema操作Migrations 與 Schema操作
Migrations 與 Schema操作
 
Model 設定與 Seeding
Model 設定與 SeedingModel 設定與 Seeding
Model 設定與 Seeding
 
Laravel - 系統全攻略(續)
Laravel - 系統全攻略(續)Laravel - 系統全攻略(續)
Laravel - 系統全攻略(續)
 
開發流程與工具介紹
開發流程與工具介紹開發流程與工具介紹
開發流程與工具介紹
 
Route 路由控制
Route 路由控制Route 路由控制
Route 路由控制
 
Model 設定與 Seeding
Model 設定與 SeedingModel 設定與 Seeding
Model 設定與 Seeding
 
CRUD 綜合運用
CRUD 綜合運用CRUD 綜合運用
CRUD 綜合運用
 
Laravel - 系統全攻略
Laravel - 系統全攻略Laravel - 系統全攻略
Laravel - 系統全攻略
 
使用者認證
使用者認證使用者認證
使用者認證
 
Schema & Migration操作
Schema & Migration操作Schema & Migration操作
Schema & Migration操作
 
Package 安裝與使用
Package 安裝與使用Package 安裝與使用
Package 安裝與使用
 
HTML 語法教學
HTML 語法教學HTML 語法教學
HTML 語法教學
 
Eloquent ORM
Eloquent ORMEloquent ORM
Eloquent ORM
 
CRUD 綜合運用
CRUD 綜合運用CRUD 綜合運用
CRUD 綜合運用
 

Mais de Mu-Fan Teng

My experience of Ruby Education in Taiwan
My experience of Ruby Education in TaiwanMy experience of Ruby Education in Taiwan
My experience of Ruby Education in TaiwanMu-Fan Teng
 
WebSocket For Web Rubyists
WebSocket For Web RubyistsWebSocket For Web Rubyists
WebSocket For Web RubyistsMu-Fan Teng
 
20150118 學個 Sinatra 好過年
20150118 學個 Sinatra 好過年20150118 學個 Sinatra 好過年
20150118 學個 Sinatra 好過年Mu-Fan Teng
 
實踐大學教案20140329
實踐大學教案20140329實踐大學教案20140329
實踐大學教案20140329Mu-Fan Teng
 
Rails Girls Taiwan 2014 Intro
Rails Girls Taiwan 2014 IntroRails Girls Taiwan 2014 Intro
Rails Girls Taiwan 2014 IntroMu-Fan Teng
 
Introduce Ruby Taiwan@Rubykaigi2013
Introduce Ruby Taiwan@Rubykaigi2013Introduce Ruby Taiwan@Rubykaigi2013
Introduce Ruby Taiwan@Rubykaigi2013Mu-Fan Teng
 
Webconf2013-非典型貧窮網站維運經驗分享
Webconf2013-非典型貧窮網站維運經驗分享Webconf2013-非典型貧窮網站維運經驗分享
Webconf2013-非典型貧窮網站維運經驗分享Mu-Fan Teng
 
Concurrency model for mysql data processing@rubyconf.tw 2012
Concurrency model for mysql data processing@rubyconf.tw 2012Concurrency model for mysql data processing@rubyconf.tw 2012
Concurrency model for mysql data processing@rubyconf.tw 2012Mu-Fan Teng
 

Mais de Mu-Fan Teng (9)

My experience of Ruby Education in Taiwan
My experience of Ruby Education in TaiwanMy experience of Ruby Education in Taiwan
My experience of Ruby Education in Taiwan
 
WebSocket For Web Rubyists
WebSocket For Web RubyistsWebSocket For Web Rubyists
WebSocket For Web Rubyists
 
20150118 學個 Sinatra 好過年
20150118 學個 Sinatra 好過年20150118 學個 Sinatra 好過年
20150118 學個 Sinatra 好過年
 
實踐大學教案20140329
實踐大學教案20140329實踐大學教案20140329
實踐大學教案20140329
 
Rails Girls Taiwan 2014 Intro
Rails Girls Taiwan 2014 IntroRails Girls Taiwan 2014 Intro
Rails Girls Taiwan 2014 Intro
 
Introduce Ruby Taiwan@Rubykaigi2013
Introduce Ruby Taiwan@Rubykaigi2013Introduce Ruby Taiwan@Rubykaigi2013
Introduce Ruby Taiwan@Rubykaigi2013
 
Webconf2013-非典型貧窮網站維運經驗分享
Webconf2013-非典型貧窮網站維運經驗分享Webconf2013-非典型貧窮網站維運經驗分享
Webconf2013-非典型貧窮網站維運經驗分享
 
Concurrency model for mysql data processing@rubyconf.tw 2012
Concurrency model for mysql data processing@rubyconf.tw 2012Concurrency model for mysql data processing@rubyconf.tw 2012
Concurrency model for mysql data processing@rubyconf.tw 2012
 
Ruby on discuz
Ruby on discuzRuby on discuz
Ruby on discuz
 

Sinatra Tutorial@Rubyconf.TW2011

  • 1. 用Sinatra建置高效能 與高自訂性的系統 Mu-Fan Teng@Rubyconf.TW 2011 TOMLAN Software Studio 1 11年8月26日星期五
  • 2. About me • 鄧慕凡(Mu-Fan Teng) •a.k.a: Ryudo •http://twitter.com/ryudoawaru •Ruby developer since 2007 •Founder of Tomlan Software Studio. 2 11年8月26日星期五
  • 4. Agenda • Sinatra Introduction • Tutorial • Rails Metal • Production tips 4 11年8月26日星期五
  • 5. Why Sinatra 5 11年8月26日星期五
  • 6. Load lesser, run faster! Rails 3.1預設: 不含project本身約20個middleware 6 11年8月26日星期五
  • 7. Load lesser, run faster! Sinatra:預設不超過6個 def build(*args, &bk) builder = Rack::Builder.new builder.use Rack::MethodOverride if method_override? builder.use ShowExceptions if show_exceptions? builder.use Rack::CommonLogger if logging? builder.use Rack::Head setup_sessions builder middleware.each { |c,a,b| builder.use(c, *a, &b) } builder.run new!(*args, &bk) builder end 7 11年8月26日星期五
  • 8. Sinatra • very micro web framework(少於1600行) • pure Ruby/Rack • DSL化設計 8 11年8月26日星期五
  • 9. 完全自訂性 • 自己選擇的ORM • 自己選擇template engine • 可簡單可複雜的路由設定 9 11年8月26日星期五
  • 10. 適用場合 10 11年8月26日星期五
  • 13. Sinatra適合 • Tiny WEB App • 聊天室 • Widget • WEB API 13 11年8月26日星期五
  • 14. Tutorial開始 14 11年8月26日星期五
  • 15. Hello Sinatra! require 'rubygems' require 'sinatra' get '/' do 'Hello Sinatra!' end 15 11年8月26日星期五
  • 16. 流程 BEFORE ROUTES AFTER before get ‘/’ after before ‘/users’ post ‘/users’ after ‘/users’ put ‘/users/:id’ get ‘/users/:id’ 16 11年8月26日星期五
  • 17. Named Route Params require 'rubygems' require 'sinatra' get '/hello/:name' do # => get '/hello/Rubyconf' # params[:name] => Rubyconf "Hello #{params[:name]}!" end Sinatra中的Route就如同Rails的Action + Route, 可 透過路由表示式的設定將URI字串中符合批配樣 式的內容(冒號開頭)化為特定的params hash成員. 17 11年8月26日星期五
  • 18. Splat Param Route 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 在路由表示式中的*號會以陣列成員的形式集 中到特定的params[:splat]中. 18 11年8月26日星期五
  • 19. Route matching with Regular Expressions get %r{/posts/name-([w]+)} do # => get /posts/name-abc, params[:captures][0] = 'abc' "Hello, #{params[:captures].first}!" end get %r{/posts/([w]+)} do |pid| # => put match content to block param(s) # => matches 「([w]+)」 to 「pid」 end 路由表示式也接受Regular Expression並可將match 內容化為特定的params[:captures]陣列成員, 也可直 接設定為Block區域變數 19 11年8月26日星期五
  • 20. Route with block paramaters get '/thread/:tid' do |tid| # => tid == params[:tid] end get '/pictures/*.*' do |filename, ext| # => filename == first * # => ext == second * # => GET '/pictures/abc.gif' then filename = "abc" and ext = "gif" "filename = #{filename}, ext = #{ext}" end get %r{/posts/([w]+)} do |pid| # => put match content to block param(s) # => matches 「([w]+)」 to 「pid」 end 以上所介紹的路由表示式都可以將匹配的內容 指派到區塊的區域變數中 20 11年8月26日星期五
  • 21. Conditional route get '/foo', :agent => /MSIEs(d.d+)/ do "You're using IE version #{params[:agent][0]}" # => IE特製版 end get '/', :host_name => /^admin./ do "Admin Area, Access denied!" end 也可以用user_agent或host_name來做路由啟發 的條件; 注意的是agent可以是字串或正規表示 式, hostname只能是正規表示式. 21 11年8月26日星期五
  • 22. Conditions(2) get '/', :provides => 'html' do erb :index end get '/', :provides => ['rss', 'atom', 'xml'] do builder :feed end provides的條件是看accept header而非path 22 11年8月26日星期五
  • 23. RESTful Routes get '/posts' do # => Get some posts end get '/posts/:id' do # => Get 1 post end create '/posts' do # => create a post end put '/posts/:id' # => Update a post end delete '/posts/:id' # => Destroy a post end 23 11年8月26日星期五
  • 24. Route Return Values 1. HTTP Status Code 2. Headers 3. Response Body(Responds to #each) 24 11年8月26日星期五
  • 25. Http Streaming class FlvStream #http://goo.gl/B8BdU .... def each ... end end class Application < Sinatra::Base # Catch everyting and serve as stream get %r((.*)) do |path| path = File.expand_path(STORAGE_PATH + path) status(401) && return unless path =~ Regexp.new(STORAGE_PATH) flv = FlvStream.new(path, params[:start].to_i) throw :response, [200, {'Content-Type' => 'application/x-flv', "Content-Length" => flv.length.to_s}, flv] end end 由於只要有each這個method就可以當做Response Body, 因此在App Server支援的前提下就可以做出Http Streaming 25 11年8月26日星期五
  • 26. Template Engines • 支援數不清的樣板引擎(erb/haml/sass/ coffeescript/erubis/builder/markdown...) • 支援inline template • 透過Tilt可自訂樣板引擎 26 11年8月26日星期五
  • 27. Template Eengines get '/' do haml :index, :format => :html4 # overridden #render '/views/index.haml' end get '/posts/:id.html' do @post = Post.find(params[:id]) erb "posts/show.html".to_sym, :layout => 'layouts/app.html'.to_sym #render '/views/posts/show.html.erb' with #layout '/views/layouts/app.html.erb' end get '/application.css' do sass :application #render '/views/application.sass' end 所有的view名稱都必需是symbol, 不可以是String 27 11年8月26日星期五
  • 28. Compass Integration set :app_file, __FILE__ set :root, File.dirname(__FILE__) set :views, "views" set :public, 'static' configure do Compass.add_project_configuration(File.join(Sinatra::Application.root, 'config', 'compass.config')) end get '/stylesheets/:name.css' do content_type 'text/css', :charset => 'utf-8' sass(:"stylesheets/#{params[:name]}", Compass.sass_engine_options ) end 28 11年8月26日星期五
  • 29. Inline Templates get '/' do erb :"root" end template :root do <<"EOB" <p>Hello Sinatra!</p> EOB end 29 11年8月26日星期五
  • 30. Before filters before '/rooms/:room_id' do puts "Before route「/rooms/*」 only" @room = Room.find(params[:room_id]) end before do puts "Before all routes" end get '/' do ... end get '/rooms/:room_id' do puts "object variable 「@room」 is accessiable now!" "You are in room #{@room.name}" end 30 11年8月26日星期五
  • 31. Before filters • 和路由使用相同表示式 • 沒有Rails的「prepend_before_filter」 • 先載入的先執行 31 11年8月26日星期五
  • 32. Before filter order before '/rooms/:room_id' do # 先執行 # Before route「/rooms/*」 only @room = Room.find(params[:room_id]) end before do # 後執行 # Before all routes end get '/' do ... end get '/rooms/:room_id' do # object variable 「@room」 is accessiable now! "You are in room #{@room.name}" end 32 11年8月26日星期五
  • 33. Session enable :sessions # equal to 「use Rack::Session::Cookie」 post '/sessions' do @current_user = User.auth(params[:account], params[:passwd]) session[:uid] = @current_user.id if @current_user end delete '/sessions' do session[:uid] = nil redirect '/' end 33 11年8月26日星期五
  • 34. Cookies get '/' do response.set_cookie('foo', :value => 'BAR') response.set_cookie("thing", { :value => "thing2", :domain => 'localhost', :path => '/', :expires => Time.today, :secure => true, :httponly => true }) end get '/readcookies' do cookies['thing'] # => thing2 end 34 11年8月26日星期五
  • 35. Helpers helpers do def member2json(id) Member.find(id).attributes.to_json end end get '/members/:id.json' do member2json(params[:id]) end 35 11年8月26日星期五
  • 36. Helpers Helpers can use in: 1. filters 2. routes 3. templates 36 11年8月26日星期五
  • 37. Halt & Pass • Halt • 相當於控制結構中的break, 阻斷後續執 行強制回應. • Pass • 相當於控制結構中的next. 37 11年8月26日星期五
  • 38. Halt Example helpers do def auth unless session[:uid] halt 404, 'You have not logged in yet.' end end end before '/myprofile' do auth end get '/myprofile' do #如果session[:uid]為nil,則此route不會執行 end 38 11年8月26日星期五
  • 39. Pass Example get '/checkout' do pass if @current_member.vip? #一般客結帳處理 erb "normal_checkout".to_sym end get '/checkout' do #VIP專用結帳處理 erb 'vip_checkout'.to_sym end 39 11年8月26日星期五
  • 40. body,status,headers get '/' do status 200 headers "Allow" => "BREW, POST, GET, PROPFIND, WHEN" body 'Hello' # => 設定body body "#{body} Sinatra" # => 加料body end 40 11年8月26日星期五
  • 41. url and redirect get '/foo' do redirect to('/bar') end url(別名to)可以生成包含了baseuri的url; redirect則 同其名可進行http重定向並可附加訊息或狀態碼. 41 11年8月26日星期五
  • 42. send_file get '/attachments/:file' do send_file File.join('/var/www/attachments/', params[:file]) #可用選項有 # filename:檔名 # last_modified:顧名思義, 預設值為該檔案的mtime # type:內容類型,如果沒有會從文件擴展名猜測。 # disposition:Content-Disposition, 可能的包括: nil (默認), :attachment (下載附件) 和 :inline(瀏覽器內顯示) # length:Content-Length,預設為檔案size end 另⼀一個helper: attachment等於send_file的disposition 為:attachement的狀況. 42 11年8月26日星期五
  • 43. request物件 # 在 http://example.com/example 上運行的應用 get '/foo' do request.body # 被客戶端設定的請求體(見下) request.scheme # "http" request.script_name # "/example", 即為SUB-URI request.path_info # "/foo" request.port # 80 request.request_method # "GET" request.query_string # "" 查詢參數 request.content_length # request.body的長度 request.media_type # request.body的媒體類型 end 43 11年8月26日星期五
  • 44. settings #以下三行都是同一件事 set :abc, 123 set :abc => 123 settings.abc = 123 ###可用區塊###### set(:foo){|val| puts(val) } get '/' do settings.foo('Sinatra') #will puts "Sinatra" "setting abc = #{settings.abc}" end Sinatra提供了多種方式讓你在Class Scope和Request Scope都能取用與設定資料或區塊, 其中有⼀一些預設 的settings是有關系統運作與設定的. 44 11年8月26日星期五
  • 45. 重要的setting • public(public) • 指定public目錄的位置 • views(views) • 指定template/views目錄位置 • static(true) • 是否由Sinatra處理靜態檔案, 設為false交給WEB伺服器會增強效能 • lock(false) • 設定false開啟thread模式(單⼀一行程⼀一次處理多個requests) • methid_override(視情況而定) • 開始「_method」參數以使用get/post以外的http method • show_exceptions(預設值與environment有關) • 是否像rails⼀一樣顯示error stack 45 11年8月26日星期五
  • 46. settings的特別用途 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 condition是⼀一個Sinatra內建的method,可以視傳 入區塊的執行結果為true或false決定視否執行 該route或pass掉. 46 11年8月26日星期五
  • 47. configure do Configure Block # 直接設定值 set :option, 'value' # 一次設定多個值 set :a => 1, :b => 2 # 等於設定該值為true enable :option # 同上 disable :option # 可用區塊 set(:css_dir) { File.join(views, 'css') } end configure :production do # 可針對環境(RACK_ENV)做設定 LOGGER.level = Logger::WARN end get '/' do settings.a? # => true settings.a # => 1 end 類似Rails的environment.rb, 在行程啟動時執行⼀一次. 47 11年8月26日星期五
  • 48. 錯誤處理 1. not_found 2. 特定error class 3. http status code 4. 對應所有錯誤 48 11年8月26日星期五
  • 49. error block error do #透過env['sinatra.error'] 可取得錯誤物件 'Sorry there was a nasty error - ' + env['sinatra.error'].name end error 處理區塊在任何route或filter拋出錯誤的時候 會被調用。 錯誤物件可以通過sinatra.error的env hash項目取得, 可以使用任何在錯誤發生前的filter或 route中定義的instance variable及環境變數等 49 11年8月26日星期五
  • 50. 自定義error block error MyCustomError do 'So what happened was...' + env['sinatra.error'].message #輸出為:「 So what happened was... something bad」 end get '/' do raise MyCustomError, 'something bad' end 如同Ruby的rescue區塊, error處理⼀一樣可以針對 error class做定義;也可以在執行期故意啟發特定 error class的錯誤並附加訊息. 50 11年8月26日星期五
  • 51. 自定義error block error 403 do 'Access forbidden' end get '/secret' do 403 end error 400..510 do 'Boom' end 針對特定的HTTP CODE設定錯誤處理區塊, 可以是 代碼範圍 51 11年8月26日星期五
  • 52. not_found block not_found do 'This is nowhere to be found' end not_found區塊等於 error 404區塊 52 11年8月26日星期五
  • 53. Rack Middleware require 'rubygems' require 'sinatra' use Rack::Auth::Basic, "Restricted Area" do |username, password| [username, password] == ['admin', '12345'] end get '/' do "You are authorized!" end 和Rails⼀一樣, Sinatra也是基於Rack的middleware, 所以 可以使用其它的Rack middleware. 53 11年8月26日星期五
  • 54. 模組化 require 'rubygems' require 'sinatra/base' #不可以require "sinatra" 以避免頂層Object Class被汙染 class MyApp < Sinatra::Base set :sessions, true set :foo, 'bar' get '/' do 'Hello world!' end end 為了建構可重用的組件,需要將你的Sinatra應用程式 模組化以將程式化為⼀一個獨立的Rack Middleware. 54 11年8月26日星期五
  • 55. Multiple App in 1 process #config.ru require 'rubygems' require 'sinatra/base' class App1 < Sinatra::Base get '/' do 'I am App1' end end class App2 < Sinatra::Base get '/' do 'I am App2' end end map '/' do run App1 end map '/app2' do run App2 end 利用Rack::Builder可將不同的Rack App掛在不同的uri 下面. 55 11年8月26日星期五
  • 56. 何時需要模組化 • 特定的Rack App Server(Passenger/Heroku等) • 將你的Sinatra App當做⼀一個Middleware而非終 點(endpoint), 例如: 1. ⼀一次掛載多個Sinatra App在同⼀一個rackup 2. 在Rails內掛載Sinatra App 56 11年8月26日星期五
  • 57. Rails Metal #routes.rb TestMixin::Application.routes.draw do devise_for :users mount ApiApp, :at => '/api'# => 掛載 ApiApp在/api 下 root :to => "welcome#index" end #lib/api_app.rb class ApiApp < Sinatra::Base get '/users/:id.json' do User.find(params[:id]).attributes.to_json end end 57 11年8月26日星期五
  • 58. 限制 • 「可以」使用Rails的model • 「不可以」使用Rails的helper/controller • 「可能可以」使用Rails的views, 但極不建 議 • not_found的錯誤是「由Rails端」處理 • 「不可以」直接使用綁定Rails(Railties)的 組件 58 11年8月26日星期五
  • 59. Devise Mix-in #config/routes.rb TestMixin::Application.routes.draw do devise_for :users mount ApiApp, :at => '/api'# => 掛載 ApiApp在/api 下 end #lib/api_app.rb class ApiApp < Sinatra::Base get '/users/:id.json' do #等效 devise的 authenticate_user! request.env['warden'].authenticate!(:scope => 'user') User.find(params[:id]).attributes.to_json end end 由於Devise是建構在Warden(Rack Middleware)之上, 雖然不能直接使用Devise的認證helper, 但可以用 warden的方式來處理認證的問題. 59 11年8月26日星期五
  • 60. Scopes and Binding • Sinatra的變數定義域分成兩種 1. Application/Class Scope 2. Request/Instance Scope 60 11年8月26日星期五
  • 61. Scopes-範例 class MyApp < Sinatra::Base # => Application/Class Scope configure do # => Application/Class Scope set :foo, 100 end self.foo # => 100 helpers do # => Application/Class Scope def foo # => Request scope for all routes end end get '/users/:id' do # => Request scope for "/users/:id" only settings.foo # => 100 end get '/' do # => Request scope for '/' only end end Request scope可透過settings helper取得在 Application scope定義的設定值 61 11年8月26日星期五
  • 62. Application/Class scope 1. 應用程式的Class Body 2. helpers/configure的區塊內 3. 傳遞給set的區塊 62 11年8月26日星期五
  • 63. Request/Instance scope 1. session/request物件只在這裡有效 2. 作用於: 2.1.routes區塊 2.2.helper methods 2.3.view/templates 2.4.before/after filters 2.5.error block 2.6. 傳給settings的區塊中的condition區塊 63 11年8月26日星期五
  • 64. 實體變數的定義範圍 class MyApp < Sinatra::Base before do # => Request scope for all routes @varall = 100 end before '/posts/:id' do @post = Post.find(params[:id]) end get '/posts/:id' do # => Request scope for 「/posts/:id」 @post.nil? # => false @varall # => 100 settings.get('/foo'){ # => Request scope for 「/foo」 only @varall # => 100 @post.nil? # => true } end get '/' do @varall # => 100 @post # => nil end end 就算是在route block中定義的另⼀一個route block, ⼀一樣 不能共用實體變數, 在before filter中定義的實體變數 會傳到下⼀一個符合條件的before filter與route block. 64 11年8月26日星期五
  • 65. 存在Class Body中的Request Scope #####sinatra/base.rb## module Sinatra class Base class << self def host_name(pattern) condition { pattern === request.host } end end end end ##等同於自行定義############# set(:host_name){|pattern| condition { pattern === request.host }} ###################### get '/', :host_name => /^admin./ do "Admin Area, Access denied!" end 傳遞給condition method的區塊內的scope是Request Scope, 所以可以使用request物件 65 11年8月26日星期五
  • 66. Production Tips 1. ORM 2. Useful Extensions 3. Paginators 4. boot.rb 66 11年8月26日星期五
  • 67. ActiveRecord require 'rubygems' require 'sinatra' require 'active_record' #先建立連線 ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :database => 'sinatra_application.sqlite3.db' ) #require或宣告class class Post < ActiveRecord::Base end get '/' do @posts = Post.all() erb :index end 67 11年8月26日星期五
  • 68. Mongoid require 'rubygems' require 'sinatra' require 'mongoid' Mongoid.configure do |config| config.master = Mongo::Connection.new.db("godfather") end class User include Mongoid::Document include Mongoid::Timestamps end get '/users/:id.json' do User.find(params[:id]).to_json end 68 11年8月26日星期五
  • 69. Sinatra More • MarkupPlugin • 設定form以及html tag的helper等 • RenderPlugin • content_for/yield等 • WardenPlugin • 綁定單⼀一Class做登入處理 • MailerPlugin • 顧名思義, 但不是使用ActionMailer • RoutingPlugin • 做出有namespace以及restful的路由 • code generator • 建立project框架 69 11年8月26日星期五
  • 70. WillPaginate • 3.0後版本已直接以extension型式支援Sinatra • 2.3版本需要手動改寫renderer • 由於Sinatra不像Rails有固定的url形式約束, 必要時還是要自己改寫renderer 70 11年8月26日星期五
  • 71. Kaminari • Model Paginate功能可正常使用 • View Helpers的部份無法使用 • 需依照ORM require正確的組件 71 11年8月26日星期五
  • 72. boot.rb require 'bundler' Bundler.setup Bundler.require class FreeChat3 < Sinatra::Base configure do #settings set :sessions, true #Middlewares use Rack::Flash #Sinatra Extensions register SinatraMore::MarkupPlugin #DB Connections ActiveRecord::Base.establish_connection(DB_CONFIG[RACK_ENV]) Mongoid.load! File.join(ROOT_DIR, '/config/mongoid.yml') #load Model/Controller/helper/Libs Dir.glob(File.join(ROOT_DIR, '/lib/*.rb')).each{|f| require f } Dir.glob(File.join(ROOT_DIR, '/app/models/*.rb')).each{|f| require f } Dir.glob(File.join(ROOT_DIR, '/app/helpers/*.rb')).each{|f| require f } Dir.glob(File.join(ROOT_DIR, '/app/controllers/*.rb')).each{|f| load f } end helpers do include ApplicationHelper include ActionView::Helpers::TextHelper end end 72 11年8月26日星期五
  • 73. Q&A 73 11年8月26日星期五