やる夫が Rack についても学ぶそうです(Rack ミドルウェア編)

            / ̄ ̄\ 
          /   _ノ  \ 
          |    ( ●)(●)  
          |     (__人__)   
             |     ` ⌒´ノ 次は Rack ミドルウェアだ。 
              |         }   
              ヽ        }   
            ヽ、.,__ __ノ   
   _, 、 -― ''"::l:::::::\ー-..,ノ,、.゙,i 、 
  /;;;;;;::゙:':、::::::::::::|_:::;、>、_ l|||||゙!:゙、-、_ 
 丿;;;;;;;;;;;:::::i::::::::::::::/:::::::\゙'' ゙||i l\>::::゙'ー、 
. i;;;;;;;;;;;;;;;;;;;;;;|::::::::::::::\::::::::::\ .||||i|::::ヽ::::::|:::! 
/;;;;;;;;;;;;;;;;;;;;;;;;!:::::::::::::::::::\:::::::::ヽ|||||:::::/::::::::i:::| 
;;;;;;;;;;;;;;;;;;;;;;;;;;|;;;;:::::::::::::::::::::::\:::::゙、|||:::/::::::::::|::: 
     ____   
   /      \ 
  /  ─    ─\  
/    (●)  (●) \ Rack ミドルウェア? 
|       (__人__)    |   
/     ∩ノ ⊃  / 
(  \ / _ノ |  | 
.\ “  /__|  |   
  \ /___ /   
            / ̄ ̄\ 
          /   _ノ  \ 
          |    ( ●)(●)  
          |     (__人__)   
             |     ` ⌒´ノ Rack アプリケーションの一種だ。 
              |         }  他の Rackアプリケーションをラップして、
              ヽ        }   リクエストやレスポンスを加工したり、
            ヽ、.,__ __ノ    処理を差し替えたりできる。
   _, 、 -― ''"::l:::::::\ー-..,ノ,、.゙,i 、 
  /;;;;;;::゙:':、::::::::::::|_:::;、>、_ l|||||゙!:゙、-、_ 
 丿;;;;;;;;;;;:::::i::::::::::::::/:::::::\゙'' ゙||i l\>::::゙'ー、 
. i;;;;;;;;;;;;;;;;;;;;;;|::::::::::::::\::::::::::\ .||||i|::::ヽ::::::|:::! 
/;;;;;;;;;;;;;;;;;;;;;;;;!:::::::::::::::::::\:::::::::ヽ|||||:::::/::::::::i:::| 
;;;;;;;;;;;;;;;;;;;;;;;;;;|;;;;:::::::::::::::::::::::\:::::゙、|||:::/::::::::::|::: 
            / ̄ ̄\ 
          /   _ノ  \ 
          |    ( ●)(●)  
          |     (__人__)   
             |     ` ⌒´ノ Rack ミドルウェアを簡単に説明するなら、
              |         }  (1)Rack アプリケーションの仕様を満たしている
              ヽ        }   (2)initialize で Rack アプリケーションを受け取る
            ヽ、.,__ __ノ    という仕様を満たしている Rack アプリケーションだ。
   _, 、 -― ''"::l:::::::\ー-..,ノ,、.゙,i 、    コードにするならこんな感じだな。
  /;;;;;;::゙:':、::::::::::::|_:::;、>、_ l|||||゙!:゙、-、_ 
 丿;;;;;;;;;;;:::::i::::::::::::::/:::::::\゙'' ゙||i l\>::::゙'ー、 
. i;;;;;;;;;;;;;;;;;;;;;;|::::::::::::::\::::::::::\ .||||i|::::ヽ::::::|:::! 
/;;;;;;;;;;;;;;;;;;;;;;;;!:::::::::::::::::::\:::::::::ヽ|||||:::::/::::::::i:::| 
;;;;;;;;;;;;;;;;;;;;;;;;;;|;;;;:::::::::::::::::::::::\:::::゙、|||:::/::::::::::|::: 
class SampleMiddleware
  # initialize で他の Rack アプリケーションを受け取る
  def initialize(app)
    @app = app
  end

  # Rack アプリケーションなので、call メソッドをもっている
  def call(env)
    # env に対して何かする

    # ラップしている Rack アプリケーションを呼び出す
    resp = @app.call(env)

    # resp に対して何かする

    # resp を返す
    resp
  end
end
            / ̄ ̄\ 
          /   _ノ  \ 
          |    ( ●)(●)  
          |     (__人__)   
             |     ` ⌒´ノ 試しに レスポンスボディを全て大文字にする 
              |         }  ミドルウェアを書いてみる。 
              ヽ        }   
            ヽ、.,__ __ノ   
   _, 、 -― ''"::l:::::::\ー-..,ノ,、.゙,i 、 
  /;;;;;;::゙:':、::::::::::::|_:::;、>、_ l|||||゙!:゙、-、_ 
 丿;;;;;;;;;;;:::::i::::::::::::::/:::::::\゙'' ゙||i l\>::::゙'ー、 
. i;;;;;;;;;;;;;;;;;;;;;;|::::::::::::::\::::::::::\ .||||i|::::ヽ::::::|:::! 
/;;;;;;;;;;;;;;;;;;;;;;;;!:::::::::::::::::::\:::::::::ヽ|||||:::::/::::::::i:::| 
;;;;;;;;;;;;;;;;;;;;;;;;;;|;;;;:::::::::::::::::::::::\:::::゙、|||:::/::::::::::|::: 
# レスポンスボディを大文字に変えるミドルウェア
class UpcaseMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    resp = @app.call(env)

    # レスポンスボディを取り出す
    resp[2].each do |body|
      # 大文字に変換
      body.upcase!
    end
    resp
  end
end
        / ̄ ̄\
      /       \      ____
      |::::::        |   /     \
     . |:::::::::::     |  / ⌒   ⌒  \  
       |::::::::::::::    |/  (●) (●)   \ この Rack ミドルウェアはどうやって使うんだお?
     .  |::::::::::::::    } |    (__人__)     | 
     .  ヽ::::::::::::::    } \   ` ⌒´     _/ 
        ヽ::::::::::  ノ   |           \
        /:::::::::::: く    | |         |  |
-―――――|:::::::::::::::: \-―┴┴―――――┴┴――
            / ̄ ̄\ 
          /   _ノ  \ 
          |    ( ●)(●)  
          |     (__人__)   
             |     ` ⌒´ノ 作成したミドルウェアを使うには、 
              |         }  config.ru 内で use で指定する。
              ヽ        }   
            ヽ、.,__ __ノ   
   _, 、 -― ''"::l:::::::\ー-..,ノ,、.゙,i 、 
  /;;;;;;::゙:':、::::::::::::|_:::;、>、_ l|||||゙!:゙、-、_ 
 丿;;;;;;;;;;;:::::i::::::::::::::/:::::::\゙'' ゙||i l\>::::゙'ー、 
. i;;;;;;;;;;;;;;;;;;;;;;|::::::::::::::\::::::::::\ .||||i|::::ヽ::::::|:::! 
/;;;;;;;;;;;;;;;;;;;;;;;;!:::::::::::::::::::\:::::::::ヽ|||||:::::/::::::::i:::| 
;;;;;;;;;;;;;;;;;;;;;;;;;;|;;;;:::::::::::::::::::::::\:::::゙、|||:::/::::::::::|::: 
require 'sample_app'
require 'upcase_middleware'

# useはRack::Builderで用意されたDSLの記法
# 後続のミドルウェアやアプリケーションを引数に取ってnewしてHandlerの引数にする
# (例)
# use MiddlewareA
# use MiddlewareB
# run App.new
#    ↓
# Rack::Handler::CGI.run MiddlewareA.new( MiddlewareB.new( App.new ) )

use UpcaseMiddleware
run SampleApp.new
            / ̄ ̄\ 
          /   _ノ  \ 
          |    ( ●)(●)  
          |     (__人__)   
             |     ` ⌒´ノ ミドルウェア自身も Rack アプリケーションなので、 
              |         }  次のように書く事もできる。
              ヽ        }   
            ヽ、.,__ __ノ   
   _, 、 -― ''"::l:::::::\ー-..,ノ,、.゙,i 、 
  /;;;;;;::゙:':、::::::::::::|_:::;、>、_ l|||||゙!:゙、-、_ 
 丿;;;;;;;;;;;:::::i::::::::::::::/:::::::\゙'' ゙||i l\>::::゙'ー、 
. i;;;;;;;;;;;;;;;;;;;;;;|::::::::::::::\::::::::::\ .||||i|::::ヽ::::::|:::! 
/;;;;;;;;;;;;;;;;;;;;;;;;!:::::::::::::::::::\:::::::::ヽ|||||:::::/::::::::i:::| 
;;;;;;;;;;;;;;;;;;;;;;;;;;|;;;;:::::::::::::::::::::::\:::::゙、|||:::/::::::::::|::: 
require 'sample_app'
require 'upcase_middleware'

run UpcaseMiddleware.new(SampleApp.new)
      / ̄ ̄ ̄\  
    / ─    ─ \  
   /  (●)  (●)  \.   いったい、これの何がうれしいんだお?
   |    (__人__)    |  
   \    ` ⌒´    / 
   /              \ 
            / ̄ ̄\ 
          /   _ノ  \ 
          |    ( ●)(●)  
          |     (__人__)   
             |     ` ⌒´ノ 今回のサンプルは大したことないから、恩恵は実感できないな。
              |         }  例えば、セッション管理やユーザー認証なんかは、
              ヽ        }   いろんな Rack アプリケーションで使うよな。
            ヽ、.,__ __ノ    
   _, 、 -― ''"::l:::::::\ー-..,ノ,、.゙,i 、 
  /;;;;;;::゙:':、::::::::::::|_:::;、>、_ l|||||゙!:゙、-、_ 
 丿;;;;;;;;;;;:::::i::::::::::::::/:::::::\゙'' ゙||i l\>::::゙'ー、 
. i;;;;;;;;;;;;;;;;;;;;;;|::::::::::::::\::::::::::\ .||||i|::::ヽ::::::|:::! 
/;;;;;;;;;;;;;;;;;;;;;;;;!:::::::::::::::::::\:::::::::ヽ|||||:::::/::::::::i:::| 
;;;;;;;;;;;;;;;;;;;;;;;;;;|;;;;:::::::::::::::::::::::\:::::゙、|||:::/::::::::::|::: 
         ___
       /     \ キリッ
      /   \ , , /\
    /    (●)  (●) \  その通りだお。毎回ユーザー認証を実装するの面倒だお!
     |       (__人__)   | だからライブラリ作ったお!
      \      ` ⌒ ´  ,/ 
.      /⌒〜" ̄, ̄ ̄〆⌒,ニつ
      |  ,___゙___、rヾイソ⊃
     |            `l ̄
.      |          |
            / ̄ ̄\ 
          /   _ノ  \ 
          |    ( ●)(●)  
          |     (__人__)   
             |     ` ⌒´ノ いろんな Rack アプリで使うものはライブラリにすると思うが、
              |         }  Rack ミドルウェアとして実装しておけば、config.ru を編集するだけで、
              ヽ        }   機能の着脱ができる。
            ヽ、.,__ __ノ    アプリケーション本体のコードには手を加えなくていい。
   _, 、 -― ''"::l:::::::\ー-..,ノ,、.゙,i 、 
  /;;;;;;::゙:':、::::::::::::|_:::;、>、_ l|||||゙!:゙、-、_ 
 丿;;;;;;;;;;;:::::i::::::::::::::/:::::::\゙'' ゙||i l\>::::゙'ー、 
. i;;;;;;;;;;;;;;;;;;;;;;|::::::::::::::\::::::::::\ .||||i|::::ヽ::::::|:::! 
/;;;;;;;;;;;;;;;;;;;;;;;;!:::::::::::::::::::\:::::::::ヽ|||||:::::/::::::::i:::| 
;;;;;;;;;;;;;;;;;;;;;;;;;;|;;;;:::::::::::::::::::::::\:::::゙、|||:::/::::::::::|::: 
         / ̄ ̄ ̄ \
      /   :::::\:::/\  
     /    。<一>:::::<ー>。 
     |    .:::。゚~(__人__)~゚j   確かに、やらない夫の言うとおりだお…。
     \、   ゜ ` ⌒´,;/゜  やる夫の作ったライブラリも Rack ミドルウェアにするお。
    /  ⌒ヽ゚  '"'"´(;゚ 。  
   / ,_ \ \/\ \
    と___)_ヽ_つ_;_ヾ_つ.;._
            / ̄ ̄\ 
          /   _ノ  \ 
          |    ( ●)(●)  
          |     (__人__)   
             |     ` ⌒´ノ よく使う機能は既に Rack に用意してあるけどな。
              |         }  とりあえず Rack::Session::Cookie や Rack::Auth::Basic
              ヽ        }   あたりからチェックしておけ。
            ヽ、.,__ __ノ   
   _, 、 -― ''"::l:::::::\ー-..,ノ,、.゙,i 、 
  /;;;;;;::゙:':、::::::::::::|_:::;、>、_ l|||||゙!:゙、-、_ 
 丿;;;;;;;;;;;:::::i::::::::::::::/:::::::\゙'' ゙||i l\>::::゙'ー、 
. i;;;;;;;;;;;;;;;;;;;;;;|::::::::::::::\::::::::::\ .||||i|::::ヽ::::::|:::! 
/;;;;;;;;;;;;;;;;;;;;;;;;!:::::::::::::::::::\:::::::::ヽ|||||:::::/::::::::i:::| 
;;;;;;;;;;;;;;;;;;;;;;;;;;|;;;;:::::::::::::::::::::::\:::::゙、|||:::/::::::::::|:::