2014-02-25 37 views
1

我试图为模型创建一个关注点,但这是一个基本的Ruby方法查找问题,而不是Rails问题本身。从模块中的调度表中查找红宝石方法

我希望能够通过选择基于值的某些特征的格式化方法来提供格式化值的功能。在下面的抽象示例中,如果要格式化的值的值为:attribute且值为:a_characteristic,我想调用foo格式化程序。特别是,我想要一个调度表,我可以从特征映射到格式化函数。

这里是我的尝试:

module FormatConcern 
    FORMATTERS = { 
    a_characteristic: ->(v){foo v} 
    } 

    def format(v) 
    FORMATTERS[v[:attribute]].call(v) 
    end 

    def foo(v) 
    "A value with #{v[:foo]}" 
    end 
end 

class ModelWithConcern 
    include FormatConcern 
end 

test_value = {attribute: :a_characteristic, 
       foo: "bar"} 
m = ModelWithConcern.new 
puts "formatted value: #{m.format(test_value)}" 

当我执行这段代码中,foo方法查找失败:

$ ruby concern_test.rb 
concern_test.rb:3:in `block in <module:FormatConcern>': undefined method `foo' for FormatConcern:Module (NoMethodError) 
from concern_test.rb:7:in `call' 
from concern_test.rb:7:in `format' 
from concern_test.rb:22:in `<main>' 

我想我明白红宝石如何查找方法名在一类,但显然我不太了解它!我如何从我的调度表中的lambda函数引用foo方法?

回答

1

你的问题是块是词汇绑定。由于块是在模块的上下文中定义的,块中的self指的是模块。最直接的方法是在格式化程序块中调用一个被调用者参数。

module FormatConcern 
    FORMATTERS = { 
    a_characteristic: ->(obj, v){obj.foo v} 
    } 

    def format(v) 
    FORMATTERS[v[:attribute]].call(self, v) 
    end 

    def foo(v) 
    "A value with #{v[:foo]}" 
    end 
end 

另一个解决办法是instance_exec块,而不是调用它:

module FormatConcern 
    FORMATTERS = { 
    a_characteristic: ->(v){foo v} 
    } 

    def format(v) 
    instance_exec(v, &FORMATTERS[v[:attribute]]) 
    end 

    def foo(v) 
    "A value with #{v[:foo]}" 
    end 
end 
0

制作FOO一个类的方法与.self

def self.foo(v) 
    "A value with #{v[:foo]}" 
    end