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

时间:2022-06-24 23:20:54

Whenever I try to extend a ruby module, I lose the module methods. Neither include nor extend will do this. Consider the snippet:

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

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

module B 
  include A
end

B.say_hi  #undefined_method

Whether B Includes or Extends A, say_hi will not be defined.

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

Is there any way to accomplish something like this?

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

5 个解决方案

#1


25  

If you are the author of module A and will frequently need this, you can re-author A like so:

如果您是模块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" 

#2


9  

I don't think there's any simple way to do it.

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

So here is a complex way:

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

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

You can put it in a helper method like this:

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

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

#3


3  

Use include_complete

使用include_complete

gem install 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"

#4


2  

Johnathan, I am not sure if you're still wondering about this, but there are two different ways to use modules in ruby. A.) you use modules in their self contained form Base::Tree.entity(params) directly in your code, or B.) you use modules as mixins or helper methods.

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

A. Will allow you to use modules as a Namespace pattern. This is good for larger projects where there is the chance for method name conflicts

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

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

Now you can use this to create some sort of Tree structure in your code, without having to instantiate a new class for every call to Base::Tree.entity.

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

Another way to do Namespace-ing is on a class by class basis.

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

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

Now we have the potential for overlap in our classes. We want to know that when we make a create an Actor class that it is the correct class, so this is where namespaces come in handy.

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

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

B. Mix-Ins These are your friends. Use them whenever you think you will have to do something more than once in your code.

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

Now whenever you need to format an xmlstring as a hash, or remove trailing whitespace in any of your future code, just mix it in.

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

module Fun
  class Ruby
    include Friendly::Formatter

    attr_accessor :string

    def initialize(params={})
    end

  end
end

Now you can format the string in your class.

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

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)

Hope this is a good enough explanation. The points raised above are good examples of ways to extend classes, but with modules, the hard part is when to use the self keyword. It refers to the scope of the object within ruby's object hierarchy. So if you want to use a module as a mix-in, and don't want to declare anything singleton, don't use the self keyword, however if you want to keep state within the object, just use a class and mix-in the modules you want.

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

#5


0  

I don't like everyone using self.included. I have simpler solution:

我不喜欢每个人都使用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

#1


25  

If you are the author of module A and will frequently need this, you can re-author A like so:

如果您是模块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" 

#2


9  

I don't think there's any simple way to do it.

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

So here is a complex way:

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

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

You can put it in a helper method like this:

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

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

#3


3  

Use include_complete

使用include_complete

gem install 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"

#4


2  

Johnathan, I am not sure if you're still wondering about this, but there are two different ways to use modules in ruby. A.) you use modules in their self contained form Base::Tree.entity(params) directly in your code, or B.) you use modules as mixins or helper methods.

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

A. Will allow you to use modules as a Namespace pattern. This is good for larger projects where there is the chance for method name conflicts

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

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

Now you can use this to create some sort of Tree structure in your code, without having to instantiate a new class for every call to Base::Tree.entity.

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

Another way to do Namespace-ing is on a class by class basis.

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

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

Now we have the potential for overlap in our classes. We want to know that when we make a create an Actor class that it is the correct class, so this is where namespaces come in handy.

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

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

B. Mix-Ins These are your friends. Use them whenever you think you will have to do something more than once in your code.

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

Now whenever you need to format an xmlstring as a hash, or remove trailing whitespace in any of your future code, just mix it in.

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

module Fun
  class Ruby
    include Friendly::Formatter

    attr_accessor :string

    def initialize(params={})
    end

  end
end

Now you can format the string in your class.

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

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)

Hope this is a good enough explanation. The points raised above are good examples of ways to extend classes, but with modules, the hard part is when to use the self keyword. It refers to the scope of the object within ruby's object hierarchy. So if you want to use a module as a mix-in, and don't want to declare anything singleton, don't use the self keyword, however if you want to keep state within the object, just use a class and mix-in the modules you want.

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

#5


0  

I don't like everyone using self.included. I have simpler solution:

我不喜欢每个人都使用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