Ruby赋值方法不会收到块?

时间:2017-01-30 20:54:33

标签: ruby syntax

我正在构建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

1 个答案:

答案 0 :(得分:2)

的SyntaxError

  

在我看来,赋值[方法]的定义是句法的   糖不会考虑块。

看来你是对的。看起来没有=的方法可以接受一个块,即使使用普通的方法调用也没有语法糖:

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

看起来很奇怪,但它与你想要达到的目标非常接近。