如何动态使用细化

时间:2016-12-07 21:41:26

标签: ruby

试图理解这种“改进”业务。

我正在制作一个改进核心类的模块:

module StringPatch
  refine String do
    def foo
      true
    end
  end
end

然后是一个使用细化的类

class PatchedClass
end

PatchedClass.send :using, StringPatch

我收到此错误:

RuntimeError: Module#using is not permitted in methods

我该如何使这项工作? 我试图仅在某个范围内动态修补核心类。我想在类和实例范围中使补丁可用。

3 个答案:

答案 0 :(得分:5)

据我所知,当system/Form_validation.php在main中时,细化处于活动状态,直到当前类/模块定义结束时using在类中或模块。

using

这意味着如果您设法动态调用类或模块上的module StringPatch refine String do def foo true end end end class PatchedClass using StringPatch puts "test".foo end class PatchedClass puts "test".foo #=> undefined method `foo' for "test":String (NoMethodError) end ,其效果将被直接删除。

您不能在方法中使用using,但您可以在已经改进的类中定义方法:

refine

对于您的问题,这discussion可能很有趣。看起来精简是有目的的限制,但我不知道为什么:

  

因为细化激活应尽可能保持静态。

答案 1 :(得分:2)

优化范围严格,不能动态使用。我通过构造模块来解决这个问题,这样我可以在需要时使用它们进行细化或作为猴子补丁。这种方法仍有局限性,并且总体上有一些改进,但仍然很方便。

module StringPatch      
    def foo
      true
    end

    refine String do
      include StringPatch
    end
 end

用作细化

class PatchedClass
   using StringPatch
end

用作单实例补丁

obj = PatchedClass.new
obj.extend(StringPatch)

你当然也可以将整个班级扩展为猴子补丁,但是你会永远污染整个班级。“/ p>

PatchedClass.prepend(StringPatch)

答案 2 :(得分:2)

我很欣赏其他答案,但我想我必须自己添加。

似乎using的余地很小 - 它无法在方法中使用的错误是严重的。我找到的最好的摆动空间是迭代地调用它,将变量作为参数传递。

因此,如果我有两个补丁类,我可以创建一个常量Patches,一个包含两者的列表。然后在类/模块中我想加载补丁,我可以运行迭代using

module StringPatch
  refine String do
    def patch; :string_patch; end
  end
end

module HashPatch
  refine Hash do
    def patch; :hash_patch; end
  end
end

Patches = [StringPatch, HashPatch]
class C
  Patches.each { |x| using x }
  def self.test
    [''.patch, {}.patch]
    new.test
  end
  def test
    [''.patch, {}.patch]
  end
end

C.test

using确实使补丁在实例和类范围内都可用。

限制是没有办法抽象出using电话。

我不能说Patches.each &method(:using)

或将Patches.each { |x| using x }移至方法

或使用eval "Patches.each { |x| using x}"