代码之家  ›  专栏  ›  技术社区  ›  RedPointyJackson

使用define_方法时块中的实例变量

  •  0
  • RedPointyJackson  · 技术社区  · 5 年前

    我试图做一个DSL,用户可以在其中传递一个块并期望一个实例变量 @arg 待定义。这是一个单元测试失败的完整示例:

    # Implementation
    class Filter
      def initialize
        @arg = 'foo'
      end
    
      def self.filters &block
        define_method :filter do |els|
          els.select &block
        end
      end
    end
    
    # Usage
    class Foo < Filter
      filters {|el| el == @arg}
    end
    
    # Expected behavior
    describe 'filters created with the DSL' do
      subject { Foo.new }
      it 'can use @arg in the filters block' do
        els = %w[notthearg  either  foo  other]
        expect(subject.filter els).to be_eql(['foo'])
      end
    end
    

    使用 pry 或放置 puts 在块内的语句,我可以看到 @阿格 是零。但是 Foo.new.instance_variable_get :@arg 正确输出 foo ,因此它必须与一些作用域规则相关。

    为了使测试通过和DSL工作,我需要在实现中做些什么改变?

    0 回复  |  直到 5 年前
        1
  •  2
  •   Amadan    5 年前

    instance_exec 去营救!

    class Filter
      def initialize
        @arg = 'foo'
      end
    
      def self.filters &block
        define_method :filter do |els|
          els.select { |e| self.instance_exec(e, &block) }
        end
      end
    end
    
    class Foo < Filter
      filters {|el| el == @arg }
    end
    
    Foo.new.filter(%w[notthearg  either  foo  other])
    # => ["foo"]
    

    注意:确保这是 非常 有很好的记录,因为任何涉及 实例执行 或者它的同辈们在设计上左右都打破了程序员的期望,你在破坏“范围”的概念。我很确定OP知道这一点,但值得一提。

    另外,考虑使用访问器而不是普通的实例变量-访问器是选中的,而变量不是。即 { |el| el == urg } 会导致错误,但是 { |el| el == @urg } 将自动失败(并筛选 nil ).