如何在包含模块的类中设置实例变量

时间:2013-11-03 01:33:41

标签: ruby metaprogramming

我正在尝试为包含我的模块的任何类创建DSL。它正在处理股票。

这是我的测试:

    context 'when price is provided' do 
        let(:stock_with_price) {
            class Stock
                include StopLimit
                stock_attributes price: 3.33
            end

            Stock.new
        }
        it 'sets the price to given value' do 
            stock_with_price.price.should eq(3.33)
        end
    end

到目前为止,这是我的模块:

    module StopLimit
      DEFAULT_STOCK_PRICE = 0
      def self.included(base)
        attr_accessor :price
        def base.stock_attributes(options = {})
            define_method('price') do 
                instance_variable_get("@price") ? instance_variable_get("@price") : DEFAULT_STOCK_PRICE
            end
            options.each_pair do |attribute, value|
                if self.method_defined?(attribute)
                        instance_variable_set("@#{attribute.to_s}", value)
                        # raise instance_variable_get("@price").inspect <-- This prints out 3.33!
                    end
            end
        end
      end
    end

我的测试似乎被打破了。 stock.price正在返回0。为什么实例变量打印正确的东西,但我的测试失败了?

更新:

这有效:

    options.each_pair do |attribute, value|
        if self.method_defined?(attribute)
            @price = value
            end
    end

然而,它是硬编码的。如何动态创建和设置实例变量值,以便循环遍历所有属性和值对并为每个创建@ [[attribute]] =值?

1 个答案:

答案 0 :(得分:0)

因为在类方法中调用了instance_variable_set,所以self被设置为类,@price被设置为Stock上的类实例变量。

但是,您的price方法是实例方法,因此它会尝试在实例上找到@price,找不到它,并返回默认值。< / p>

编辑: 怎么样:

define_method('price') do 
  @price ||= self.class.instance_variable_get(:@price) || DEFAULT_STOCK_PRICE
end