如何声明mixin方法,使其在实例方法和类方法中都可用?

时间:2022-11-25 08:28:35

I want to put a method in a Ruby module such that it can be called from a class method or an instance method, using simple syntax:

我想把一个方法放在Ruby模块中,这样就可以使用简单的语法从类方法或实例方法调用它:

module MyMod
  def fmt *args
    args.map { | a | "You said #{a}" }
  end
end

class MyClass
  include MyMod
  def inst
    puts fmt 1,2,3
  end
  def self.cls
    puts fmt 4,5,6
  end
end

The above doesn't work because the class method (cls) can't see the instance method fmt. If I change the definition to self.fmt, then the instance method has to invoke it as MyMod.fmt.

上面的方法不起作用,因为类方法(cls)不能看到实例方法fmt。如果我把定义改成self。然后实例方法必须调用它作为MyMod.fmt。

I'd like to be able to just invoke fmt (some stuff) from both types of method. Is there a "ruby-ish" way of doing this? I can define the module as

我希望能够从这两种方法中调用fmt(一些东西)。有没有一种“像红宝石一样”的方式来做这件事?我可以将模块定义为

module MyMod
  def self.fmt *args
    args.map { | a | "You said #{a}" }
  end
  def fmt *args
    MyMod.fmt args
  end
end

but that's not very DRY, is it? Is there a simpler way?

但那不是很干燥,是吗?有更简单的方法吗?

2 个解决方案

#1


5  

You can use advantage of Module#included method to do it like that:

您可以利用模块#include方法来实现:

module MyMod
  # here base is a class the module is included into
  def self.included(base)
    # extend includes all methods of the module as class methods
    # into the target class
    base.extend self
  end

  def fmt(*args)
    args.map { |a| "You said #{a}" }
  end
end

class MyClass
  # regular include provides us with instance methods
  # and defined above MyMod#included hook - with class methods
  include MyMod

  def inst
    puts fmt(1, 2, 3)
  end

  def self.cls
    puts fmt(4, 5, 6)
  end
end

puts MyClass.cls
puts MyClass.new.inst

And here's the output:

这是输出:

You said 4
You said 5
You said 6

You said 1
You said 2
You said 3

For more detailed explanation have a look at this article.

有关更详细的解释,请参阅本文。

#2


1  

Both include and extendthe MyMod module in MyClass, so that the fmt method gets added both as an instance and a class method to MyClass.

在MyClass中包含和扩展MyMod模块,以便将fmt方法作为实例和类方法添加到MyClass中。

What Object#extend does is add the methods of a module to a single instance. In this case, that instance is the class itself, which is why the methods will be available as class methods.

对象#extend做的是将模块的方法添加到单个实例。在本例中,该实例是类本身,这就是为什么这些方法可以作为类方法使用。

#1


5  

You can use advantage of Module#included method to do it like that:

您可以利用模块#include方法来实现:

module MyMod
  # here base is a class the module is included into
  def self.included(base)
    # extend includes all methods of the module as class methods
    # into the target class
    base.extend self
  end

  def fmt(*args)
    args.map { |a| "You said #{a}" }
  end
end

class MyClass
  # regular include provides us with instance methods
  # and defined above MyMod#included hook - with class methods
  include MyMod

  def inst
    puts fmt(1, 2, 3)
  end

  def self.cls
    puts fmt(4, 5, 6)
  end
end

puts MyClass.cls
puts MyClass.new.inst

And here's the output:

这是输出:

You said 4
You said 5
You said 6

You said 1
You said 2
You said 3

For more detailed explanation have a look at this article.

有关更详细的解释,请参阅本文。

#2


1  

Both include and extendthe MyMod module in MyClass, so that the fmt method gets added both as an instance and a class method to MyClass.

在MyClass中包含和扩展MyMod模块,以便将fmt方法作为实例和类方法添加到MyClass中。

What Object#extend does is add the methods of a module to a single instance. In this case, that instance is the class itself, which is why the methods will be available as class methods.

对象#extend做的是将模块的方法添加到单个实例。在本例中,该实例是类本身,这就是为什么这些方法可以作为类方法使用。