我正在构建DSL并拥有此模块
module EDAApiBuilder
module Client
attr_accessor :api_client, :endpoint, :url
def api_client(api_name)
@apis ||= {}
raise ArgumentError.new('API name already exists.') if @apis.has_key?(api_name)
@api_client = api_name
@apis[@api_client] = {}
yield(self) if block_given?
end
def fetch_client(api_name)
@apis[api_name]
end
def endpoint(endpoint_name)
raise ArgumentError.new("Endpoint #{endpoint_name} already exists for #{@api_client} API client.") if fetch_client(@api_client).has_key?(endpoint_name)
@endpoint = endpoint_name
@apis[@api_client][@endpoint] = {}
yield(self) if block_given?
end
def url=(endpoint_url)
fetch_client(@api_client)[@endpoint]['url'] = endpoint_url
end
end
end
所以我有像
这样的测试 context 'errors' do
it 'raises an ArgumentError when trying to create an already existent API client' do
expect {
obj = MixinTester.new
obj.api_client('google')
obj.api_client('google')
}.to raise_error(ArgumentError,'API name already exists.')
end
it 'raises an ArgumentError when trying to create a repeated endpoint for the same API client' do
expect {
obj = MixinTester.new
obj.api_client('google') do |apic|
apic.endpoint('test1')
apic.endpoint('test1')
end
}.to raise_error(ArgumentError,"Endpoint test1 already exists for google API client.")
end
end
我宁愿将#api_client
写为作业块
def api_client=(api_name)
这样我就可以写
了obj = MixinTester.new
obj.api_client = 'google' do |apic| # <=== Notice the difference here
apic.endpoint('test1')
apic.endpoint('test1')
end
因为我认为这种符号(带有赋值)更有意义。但是,当我以这种方式运行我的测试时,我只是得到一个错误,说keyworkd_do
在这种情况下是意外的。
在我看来,赋值块的定义是语法糖,它不会考虑块。
这是对的吗?有没有人有关于此的一些信息?
顺便说一下:MixinTester
只是一个测试类,在我的spec/spec_helper.rb
中定义为
class MixinTester
include EDAApiBuilder::Client
end
答案 0 :(得分:2)
在我看来,赋值[方法]的定义是句法的 糖不会考虑块。
看来你是对的。看起来没有=
的方法可以接受一个块,即使使用普通的方法调用也没有语法糖:
class MixinTester
def name=(name,&block)
end
def set_name(name, &block)
end
end
obj = MixinTester.new
obj.set_name('test') do |x|
puts x
end
obj.name=('test') do |x| # <- syntax error, unexpected keyword_do, expecting end-of-input
puts x
end
替代方案可以使用Hash编写:
class MixinTester
def api(params, &block)
block.call(params)
end
end
obj = MixinTester.new
obj.api client: 'google' do |apic|
puts apic
end
#=> {:client=>"google"}
您可以调整方法名称和哈希参数。
如果块属于method参数,而不是setter方法,则接受语法:
def google(&block)
puts "Instantiate Google API"
block.call("custom apic object")
end
class MixinTester
attr_writer :api_client
end
obj = MixinTester.new
obj.api_client = google do |apic|
puts apic
end
# =>
# Instantiate Google API
# custom apic object
看起来很奇怪,但它与你想要达到的目标非常接近。