繁体   English   中英

在另一个模块中扩展Ruby模块,包括模块方法

[英]Extending a Ruby module in another module, including the module methods

每当我尝试扩展ruby模块时,我都会丢失模块方法。 既不包含也不延伸都不会这样做。 考虑一下片段:

module A 
  def self.say_hi
    puts "hi"
  end
end

module B 
  include A
end

B.say_hi  #undefined_method

无论B包括还是扩展A,都不会定义say_hi。

有没有办法完成这样的事情?

如果您是module A的作者并且经常需要这个,您可以像这样重新创作A:

module A 
  module ClassMethods
    def say_hi
      puts "hi"
    end
  end
  extend ClassMethods
  def self.included( other )
    other.extend( ClassMethods )
  end
end

module B 
  include A
end

A.say_hi #=> "hi"
B.say_hi #=> "hi" 

我认为没有任何简单的方法可以做到这一点。

所以这是一个复杂的方式:

module B
  class << self
    A.singleton_methods.each do |m|
      define_method m, A.method(m).to_proc
    end
  end
end

你可以把它放在这样的辅助方法中:

class Module
  def include_module_methods(mod)
    mod.singleton_methods.each do |m|
      (class << self; self; end).send :define_method, m, mod.method(m).to_proc
    end
  end
end

module B
  include_module_methods A
end

使用include_complete

gem install include_complete

module A 
  def self.say_hi
    puts "hi"
  end
end

module B 
  include_complete A
end

B.say_hi #=> "hi"

Johnathan,我不确定你是否还在想这个,但有两种不同的方法可以在ruby中使用模块。 A.)您在代码中直接使用自包含形式的Base :: Tree.entity(params)模块,或B.)您将模块用作mixins或helper方法。

A.允许您将模块用作命名空间模式。 这对于可能存在方法名称冲突的大型项目非常有用

module Base
  module Tree
    def self.entity(params={},&block)
      # some great code goes here
    end
  end
end

现在,您可以使用它在代码中创建某种树结构,而无需为每次调用Base :: Tree.entity实例化一个新类。

另一种进行命名空间的方法是逐类进行。

module Session
  module Live
    class Actor
      attr_accessor :type, :uuid, :name, :status
      def initialize(params={},&block)
        # check params, insert init values for vars..etc
        # save your callback as a class variable, and use it sometime later
        @block = block
      end

      def hit_rock_bottom
      end

      def has_hit_rock_bottom?
      end

      ...
    end
 end
 class Actor
   attr_accessor :id,:scope,:callback
   def initialize(params={},&block)
     self.callback = block if block_given?
   end

   def respond
     if self.callback.is_a? Proc
       # do some real crazy things...
     end
   end
 end
end

现在我们有可能在课堂上重叠。 我们想要知道,当我们创建一个Actor类,它是正确的类时,所以这就是命名空间派上用场的地方。

Session::Live::Actor.new(params) do |res|...
Session::Actor.new(params) 

B.混合这些是你的朋友。 只要您认为必须在代码中执行多次操作,请使用它们。

module Friendly
  module Formatter
    def to_hash(xmlstring)
      #parsing methods
      return hash
    end

    def remove_trailing_whitespace(string,&block)
      # remove trailing white space from that idiot who pasted from textmate
    end
  end
end

现在,无论何时需要将xmlstring格式化为哈希,或者在将来的任何代码中删除尾随空格,只需将其混合即可。

module Fun
  class Ruby
    include Friendly::Formatter

    attr_accessor :string

    def initialize(params={})
    end

  end
end

现在您可以在类中格式化字符串。

fun_ruby = Fun::Ruby.new(params)
fun_ruby.string = "<xml><why><do>most</do><people></people><use>this</use><it>sucks</it></why></xml>"
fun_ruby_hash = fun_ruby.to_hash(fun_ruby.string)

希望这是一个很好的解释。 上面提到的几点是扩展类的方法的很好的例子,但是对于模块,困难的部分是何时使用self关键字。 它指的是ruby对象层次结构中对象的范围。 因此,如果您想将模块用作混合,并且不想声明任何单例,请不要使用self关键字,但是如果您想在对象中保持状态,只需使用类并混合 - 在你想要的模块中。

我不喜欢每个人都使用self.included 我有更简单的解决方案:

module A
  module ClassMethods
    def a
      'a1'
    end
  end
  def a
    'a2'
  end
end

module B
  include A

  module ClassMethods
    include A::ClassMethods
    def b
      'b1'
    end
  end
  def b
    'b2'
  end
end

class C
  include B
  extend B::ClassMethods
end

class D < C; end

puts D.a
puts D.b
puts D.new.a
puts D.new.b

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM