26

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复制细化模块内的类的实现吗?

  • 我知道多重继承是通过模块完成的,但是社区会如何看待上述内容Class#include_refined?从改进中提取这方面是否合理?“本地”修补类内部而不是使用“使用”开关来激活细化?
4

2 回答 2

4

我确实对 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

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

于 2015-03-29T12:45:27.250 回答
1

安德里亚,感谢您在评论中提供的信息。请原谅我缺乏了解这一点的知识,尽管根据您的研究听起来是可行的。

我认为在 Rails 中做某事不需要太低级。

如果我要在 Engine 上做类似的事情,我会尝试以下想法,从易到难。

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

    恐怕这种最常见的用法无法满足您的需要

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

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

  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 repo 提供定制服务。

总之,以上应该能够解决大多数情况,我自己在可能和需要时更喜欢#5。

于 2013-09-01T23:55:13.967 回答