Skip to content

Instantly share code, notes, and snippets.

@godfat
Last active December 17, 2015 21:39
Show Gist options
  • Save godfat/5676447 to your computer and use it in GitHub Desktop.
Save godfat/5676447 to your computer and use it in GitHub Desktop.
require 'rest-core'
module RestCore
middleware = Class.new
middleware.module_eval do
def initialize app
@app = app
end
def call env, &k
puts "Calling"
@app.call(env, &k)
end
end
Circular = Builder.client do
use middleware
end
Circular::Middleware = middleware
end
puts RC::Circular
puts RC::Circular::Middleware
puts RC::Circular.new.head('http://example.com/')
require 'rest-core'
module RestCore
middleware = Class.new
middleware.module_eval do
def initialize app
@app = app
end
def call env, &k
puts env['secret']
@app.call(env, &k)
end
end
Secret = Builder.client(:secret) do
use middleware
end
Secret::Middleware = middleware
end
puts RC::Secret.new(:secret => 'mission').head('http://example.com/')
require 'rest-core/middleware'
require 'rest-core/util/hmac'
class RestCore::Signature
def self.members; [:secret]; end
include RestCore::Middleware
def call env, &k
uri = request_uri(env)
sig = Hmac.sha1(secret(env), uri)
app.call(env.merge(REQUEST_QUERY => {},
REQUEST_PATH => "#{uri}&Signature=#{sig}", &k))
end
end
@godfat
Copy link
Author

godfat commented Jun 4, 2013

對,一方面給一些方便的 methods 可用,另一方面則是讓 client 也可以記得一份可改變的設定(變數)

對,Builder.client 給的參數是額外需要記得的變數,確實如 RC::Facebookapp_id, 不過這裡並沒有例外,這個 app_id 也同樣會傳到 env 裡面,只是 middleware 裡面可能不會用到而已。

意思就是,在之前的版本裡(現已修好),如果 Builder.client 和 middleware 都給 secret, 則 client 實際上會有兩個 secret (!) 修正好的版本在所有的 attributes 中取 uniq, 因此 secret 會只剩一個。

這樣並不會造成任何問題(在新的版本裡),只是沒有意義而已。也可以寫成:

class Signature
  def initialize app; @app = app; end
  def call env, &k
    puts env['secret']
    app.call(&k)
  end
end
Client = RC::Builder.client(:secret){ use Signature }

也就是說,middleware 裡面就不自己記得一份 secret 了,一律靠 client 傳過去。這樣做的缺點是,使用 middleware 的人要自己記得這個 middleware 可能需要一份 secret 傳進去。

至於 api_key 如果要放在每個 request 中,那取決於要放在 query 或是 payload 甚或 headers 裡,
我會用對應的 middleware 並定義 default_query. 範例:

Client = RC::Builder.client(:api_key){ use RC::DefaultQuery }
class Client
  def default_query
    {:api_key => api_key}
  end
end

這樣 client 可記得 api_key, 同時會自動夾在 query 裡面。payload 或 headers 亦然。

@lulalala
Copy link

lulalala commented Jun 5, 2013

請問一下,你說的 dry run是原本就會丟出 timeout 囉?

2.0.0-p0 :002 > client = RC::Facebook.new
 => #<struct RestCore::Facebook timeout=10, site="https://gra..., headers={"Accept"=>"..., access_token=nil, log_method=nil, cache=nil, expires_in=600, error_handler=#<Proc:0x007..., error_detector=#<Proc:0x007..., json_response=true, defaults={:old_site=>..., data={}, app_id=nil, secret=nil, old_site="https://api...>
2.0.0-p0 :003 > client.request_full({RC::REQUEST_PATH => '/'}, client.dry)
 => {"REQUEST_METHOD"=>:get, "REQUEST_PATH"=>"https://graph.facebook.com//", "REQUEST_QUERY"=>{}, "REQUEST_PAYLOAD"=>{}, "REQUEST_HEADERS"=>{"Accept"=>"application/json", "Accept-Language"=>"en-us"}, "core.fail"=>[], "core.log"=>[#<struct RestCore::Event::Requested duration=0.010871, message="GET https://graph.facebook.com//">], "timeout"=>10, "site"=>"https://graph.facebook.com/", "headers"=>{"Accept"=>"application/json", "Accept-Language"=>"en-us"}, "access_token"=>nil, "log_method"=>nil, "cache"=>nil, "expires_in"=>600, "error_handler"=>#<Proc:0x007fd739686420@Code/rest-more/lib/rest-core/client/facebook.rb:18 (lambda)>, "error_detector"=>#<Proc:0x007fd7396864c0@Code/rest-more/lib/rest-core/client/facebook.rb:19 (lambda)>, "json_response"=>true, "defaults"=>{:old_site=>"https://api.facebook.com/"}, "data"=>{}, "app_id"=>nil, "secret"=>nil, "old_site"=>"https://api.facebook.com/", "async.callback"=>false, "async.timer"=>#<RestCore::Timeout::TimerThread:0x007fd7396a6dd8 @timeout=10, @error=#<Timeout::Error: execution expired>, @block=#<Proc:0x007fd7396a6d88@Code/rest-more/rest-core/lib/rest-core/middleware/timeout/timer_thread.rb:9 (lambda)>, @canceled=false, @thread=#<Thread:0x007fd7396a6d60 sleep>>, "RESPONSE_BODY"=>nil}
2.0.0-p0 :004 > .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/input-method.rb:152:in `readline': execution expired (Timeout::Error)
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/input-method.rb:152:in `gets'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:472:in `block (2 levels) in eval_input'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:624:in `signal_status'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:471:in `block in eval_input'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:190:in `call'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:190:in `buf_input'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:105:in `getc'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/slex.rb:206:in `match_io'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/slex.rb:76:in `match'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:290:in `token'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:266:in `lex'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:237:in `block (2 levels) in each_top_level_statement'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:233:in `loop'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:233:in `block in each_top_level_statement'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:232:in `catch'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:232:in `each_top_level_statement'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:488:in `eval_input'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:397:in `block in start'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:396:in `catch'
    from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:396:in `start'
    from .rvm/rubies/ruby-2.0.0-p0/bin/irb:16:in `<main>'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment