将ruby类转换为模块而不是使用细化的更好方法?

时间:2013-08-31 17:44:33

标签: ruby ruby-2.0 refinements

Module#refine方法接受一个类和一个块并返回一个细化模块,所以我想我可以定义:

class Class
  def include_refined(klass)
    _refinement = Module.new do
      include refine(klass) {
        yield if block_given?
      }
    end
    self.send :include, _refinement
  end
end

以及以下测试通过

class Base
  def foo
    "foo"
  end
end

class Receiver
  include_refined(Base) {
    def foo
      "refined " + super
    end
  }
end

describe Receiver do
  it { should respond_to(:foo) }
  its(:foo) { should eq("refined foo") }
end

因此,使用细化,我可以将类转换为模块,动态优化其行为,并将其包含在其他类中。

  • 是否有更简单的方法将类转换为Ruby中的模块(比如ruby< 2)?
  • rb_mod_refine的C实现中 我们看到

    refinement = rb_module_new();
    RCLASS_SET_SUPER(refinement, klass);
    

    这只是将精炼的超类设置为klass,在复制模块中复制类的实现吗?

  • 我知道多重继承IS 通过模块完成,但社区对上述Class#include_refined的看法是什么? 从改进中提取这个方面是否合理? 在本地“修补”而不是使用“使用”开关激活优化?

2 个答案:

答案 0 :(得分:3)

我很高兴使用Ruby 2.1(以及后来的)类级“私有”范围的改进。我上面的例子可以改为:

# spec/modulify_spec.rb
module Modulify
  refine(Class) do
    def include_refined(klass)
      _refined = Module.new do
        include refine(klass) { yield if block_given? }
      end
      include _refined
    end
  end
end

class A
  def a
    "I am an 'a'"
  end
end

class B
  using Modulify

  include_refined(A) do
    def a
      super + " and not a 'b'"
    end
  end

  def b
    "I cannot say: " + a
  end
end

RSpec.describe B do
  it "can use refined methods from A" do
    expect(subject.b).to eq "I cannot say: I am an 'a' and not a 'b'"
  end
end

并且适合作为原始问题的解决方案。

答案 1 :(得分:1)

Andrea,谢谢你在评论中的信息。借口我缺乏知识,了解这是非常必要的,尽管根据你的研究听起来可行。

我认为我们不需要在Rails中做那么低级别的事情。

如果我要在引擎上做类似的话,我会尝试以下想法,从易到难。

  1. 在routes.rb中,将整个引擎安装在正确的路径中。

    我担心这种最常见的用法不能满足您的需求

  2. 在routes.rb中,为应用程序路由中的特定控制器自定义引擎的路由。

    作为引擎,Devise可以轻松完成。但我知道不是每个引擎都能做到这一点

  3. 在routes.rb中,将特定或整套路线重定向到引擎的路线

  4. 在您的应用程序操作中,重定向到应用程序操作中的特定引擎操作。

    这应该足够自定义以执行特定操作

    class FoosController < ApplicationController
      def foo
        redirect_to some_engine_path if params[:foo] == 'bar'
      end
    
  5. 继承引擎的控制器 - 进行一系列操作,如果上述所有操作都不合适

    *引擎的类在所有应用程序中都可用,您可以从它们继承控制器,而不是普通的ApplicationController。

    # class FoosController < ApplicationController
    class FoosController < BarEngine::BarsController
    

    *由于大多数引擎的控制器都继承自ApplicationController,因此这种继承仍允许您使用ApplicationController中自己的东西,完全没有任何不良影响。

  6. 如果上述所有情况都无法做到,我可以尝试在本地或从我的github回购中进行定制。

  7. 总之,上述应该能够解决大多数情况,我自己更喜欢#5,如果可能和需要。