Ruby/Rails:为http请求创建自定义block/yield以避免重复
Ruby/Rails: create a custom block/yield for http requests to avoid repetition
我想在我的代码中避免过多 copy/paste 的 http 请求,因此我尝试创建以下内容:
post "#{URL}/api/v1/notify" do
header 'Content-Type', 'application/json'
header 'Authorization', "Bearer 123456"
success do
# what to do in case of success
end
error do |e|
# what to do in case of error
end
end
但到目前为止,我在创建所需的 post 方法时遇到问题
def post(url, &block)
puts "post"
url = URI("#{URL}/api/v1/notify")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = url.instance_of?(URI::HTTPS)
request = Net::HTTP::Post.new(url) # or GET
request['Content-Type'] = #header content type
request['Authorization'] = #header bearer
response = http.request(request)
response.success? ? success : failure
end
def success(&block)
puts "success"
yield
end
def error(&block)
puts "error"
yield
end
该方法应该替换以下多次使用的代码:
url = URI("#{URL}/api/v1/notify")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = url.instance_of?(URI::HTTPS)
request = Net::HTTP::Post.new(url) # or GET
request['Content-Type'] = 'application/json'
request['Authorization'] = "Bearer 123456"
response = http.request(request)
if response.code == '200'
#success
else
#failure
end
您可以围绕此构建自己的 DSL:
class Client
def self.post(&block)
dsl = Dsl.new
dsl.instance_eval(&block)
# build request with data from dsl calls
# and execute request
puts "Making a request to #{dsl.url} with headers #{dsl.headers}"
response = "response" # this would be your http response
if rand > 0.5 # response.success?
dsl.on_success.call(response) if dsl.on_success
else
dsl.on_error.call(response) if dsl.on_error
end
end
end
class Dsl
def initialize
@headers = {}
end
def url(*values)
return @url if values.length == 0
@url = values[0]
end
def headers
@headers.freeze
end
def header(*values)
return @headers[values.first] if values.length == 1
@headers[values[0]] = values[1]
end
def on_success(&block)
return @on_success if !block
@on_success = block
end
def on_error(&block)
return @on_error if !block
@on_error = block
end
end
Client.post do |client|
url "http://somewhere"
header "Kwazy", "Cupcakes"
on_success do |response|
puts "got #{response} which is a success"
end
on_error do |response|
puts "gor #{response} which is an error"
end
end
显然,如果您通过可变参数长度调用 getter/setter,区别就有点特殊,也可以通过使用真实的 setter 名称来完成(url=
,.. .) 或使用不同的 getter 名称 (get_url
)
比较特别的部分是instance_eval(block)
。它允许更改 self
以便 post
块内方法的接收者不是 Client
而是 Dsl
实例。
这可能会引起麻烦并使调试变得有点困难。所以你也可以
yield(dsl)
然后将 dsl 作为参数传递给块。
Client.post do |dsl|
dsl.url "http://somewhere"
#...
end
更新:
Stefans 的提议听起来也不错。我认为他的意思是这样的(或在评论中查看他的 link):
class DslData
attr_accessor :url
end
class Dsl
attr_reader :dsl_data
def initialize
@dsl_data = DslData.new
end
def url(value)
dsl_data.url = value
end
end
class Client
def self.post
dsl = Dsl.new
dsl.instance_eval(&block)
puts "Url: #{dsl.dsl_data.url}"
end
end
我想在我的代码中避免过多 copy/paste 的 http 请求,因此我尝试创建以下内容:
post "#{URL}/api/v1/notify" do
header 'Content-Type', 'application/json'
header 'Authorization', "Bearer 123456"
success do
# what to do in case of success
end
error do |e|
# what to do in case of error
end
end
但到目前为止,我在创建所需的 post 方法时遇到问题
def post(url, &block)
puts "post"
url = URI("#{URL}/api/v1/notify")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = url.instance_of?(URI::HTTPS)
request = Net::HTTP::Post.new(url) # or GET
request['Content-Type'] = #header content type
request['Authorization'] = #header bearer
response = http.request(request)
response.success? ? success : failure
end
def success(&block)
puts "success"
yield
end
def error(&block)
puts "error"
yield
end
该方法应该替换以下多次使用的代码:
url = URI("#{URL}/api/v1/notify")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = url.instance_of?(URI::HTTPS)
request = Net::HTTP::Post.new(url) # or GET
request['Content-Type'] = 'application/json'
request['Authorization'] = "Bearer 123456"
response = http.request(request)
if response.code == '200'
#success
else
#failure
end
您可以围绕此构建自己的 DSL:
class Client
def self.post(&block)
dsl = Dsl.new
dsl.instance_eval(&block)
# build request with data from dsl calls
# and execute request
puts "Making a request to #{dsl.url} with headers #{dsl.headers}"
response = "response" # this would be your http response
if rand > 0.5 # response.success?
dsl.on_success.call(response) if dsl.on_success
else
dsl.on_error.call(response) if dsl.on_error
end
end
end
class Dsl
def initialize
@headers = {}
end
def url(*values)
return @url if values.length == 0
@url = values[0]
end
def headers
@headers.freeze
end
def header(*values)
return @headers[values.first] if values.length == 1
@headers[values[0]] = values[1]
end
def on_success(&block)
return @on_success if !block
@on_success = block
end
def on_error(&block)
return @on_error if !block
@on_error = block
end
end
Client.post do |client|
url "http://somewhere"
header "Kwazy", "Cupcakes"
on_success do |response|
puts "got #{response} which is a success"
end
on_error do |response|
puts "gor #{response} which is an error"
end
end
显然,如果您通过可变参数长度调用 getter/setter,区别就有点特殊,也可以通过使用真实的 setter 名称来完成(url=
,.. .) 或使用不同的 getter 名称 (get_url
)
比较特别的部分是instance_eval(block)
。它允许更改 self
以便 post
块内方法的接收者不是 Client
而是 Dsl
实例。
这可能会引起麻烦并使调试变得有点困难。所以你也可以
yield(dsl)
然后将 dsl 作为参数传递给块。
Client.post do |dsl|
dsl.url "http://somewhere"
#...
end
更新:
Stefans 的提议听起来也不错。我认为他的意思是这样的(或在评论中查看他的 link):
class DslData
attr_accessor :url
end
class Dsl
attr_reader :dsl_data
def initialize
@dsl_data = DslData.new
end
def url(value)
dsl_data.url = value
end
end
class Client
def self.post
dsl = Dsl.new
dsl.instance_eval(&block)
puts "Url: #{dsl.dsl_data.url}"
end
end