在下面的示例中,块参数如何变得可调用?

时间:2014-02-12 20:38:12

标签: ruby rspec

我最近一直在玩ruby并且了解一些关于传递块的基础知识 一个方法以及如何将值返回到块等,但我遇到了以下内容 Rspec中的代码:

RSpec.configure do |config|
    config.use_transactional_fixtures = true
end

现在这对我来说看起来非常疲惫和有趣,但我并不完全理解它。 怎么可能通过' config'配置,同时它成为一个可调用的对象 在块内作为' config.use_transactional_fixtures ....'

有人可以帮助我一个可以在上面的例子中调用的基本实现,并且这个技术有名称吗?我将继续探索这个问题!

3 个答案:

答案 0 :(得分:2)

以下是具有配置对象和配置方法的类的示例:

class ConfigObject
  attr_accessor :some_configuration_variable, :some_option

  def initialize
    # set default values in initialize
    @some_configuration_variable = false
    @some_option = :default
  end
end

class MyClass

  # open the metaclass (access to the MyClass object instance)
  class << self
    def configure
      yield configuration_object
    end

    # return the configuration object, or initialize it if it doesn't yet exist.
    def configuration_object
      @configuration_object ||= ConfigObject.new
    end
  end
end

由于类实例MyClass是单例对象,因此只存在一个配置对象。 MyClass的实例可以MyClass.new.class.configuration_object访问该对象。或者,对于更灵活的方法,您可以定义配置对象的访问者并将消息转发给它:

require 'forwardable'

class MyClass
  extend Forwardable

  def_delegators :configuration_object, :some_configuration_variable, :some_option

  def configuration_object
    MyClass.configuration_object
  end
end

MyClass.configure do |config|
  config.some_configuration_variable = true
  config.some_option = :option_a
end

instance = MyClass.new
instance.some_configuration_variable #=> true
instance.some_option #=> :option_a

答案 1 :(得分:1)

幕后发生的事情如下:

  • 方法Rspec.configure是一种需要阻止的方法。
  • 当您调用该方法时,它会调用您的块(通过yield)传入参数。
  • 传入的参数基本上是configuration object,是RSpec::Core::Configuration
  • 的一个实例
  • 配置对象,基本上具有所有可用配置选项的访问器方法。

答案 2 :(得分:0)

好吧,块(对我来说)是Ruby中最棘手的事情之一,但是当你完全理解它们是如何工作的时候它们也非常强大。

让我们采用最常用的使用块的函数each

['a', 'b', 'c'].each do |value|
  # do something with value
end

这相当于['a', 'b', 'c'].each(&Proc.new{ |v| print v }),其中&是将对象传递给函数的语法,将其标记为可通过yield 调用的块(更多在yield以后)。所以一个块只是一个Proc实例(一个匿名函数),因此就像一个对象,就像Ruby中的其他东西一样。

这是数组的each的基本实现(它不包括你没有传递一个块的情况,因为它对我们来说不是很有趣):< / p>

def each(array)
  i = 0
  while i < array.size
    yield array[i]
    i += 1
  end
  array
end

each( ['a', 'b', 'c'] ) { |v| print v } #=> prints abc , returns array

这里的关键是yield:正如我们所说,块({ |v| print v })是一个Proc实例,您通常会将它传递给其他函数,通常< strong>执行它,使用yield [args] 调用它。你不是被迫执行它,如果声明为参数,你甚至可以有一个块引用,并按你的意愿使用它:

def puts_block(&block)
  puts block
end

puts_block { |v| } #=> #<Proc:0x007f16092b7660@(irb):15>

所以,RSpec.configure可能是这样实现的:

class RSpec
  def self.configure
    @config ||= Config.new
    block_given? ? yield @config : @config
  end
end

block_given?允许知道函数是否被调用将块传递给它;以这种方式调用Rspec.configure只返回@config对象,而RSpec.configure { |c| ... }@config对象传递给您传递给RSpec.configure的块。

关于Google上的Ruby阻止的更多信息:P