简体   繁体   English

Ruby:在使用模块扩展实例时做一些事情

[英]Ruby: Do something when extending an instance with a module

I'd like to execute some code when an instance is extended with Object#extend .当使用Object#extend 扩展实例时,我想执行一些代码。 A bit like initialize when instantiating a class but for a module.有点像实例化类时的initialize ,但用于模块。

Here is the extended documentation example :这是扩展文档示例

module Mod
  def hello
    "Hello from Mod.\n"
  end
end

class GoodKlass
  def hello
    "Hello from GoodKlass.\n"
  end
end

class BadKlass
  # something totally different
end

good = GoodKlass.new
good.hello         #=> "Hello from GoodKlass.\n"
good.extend(Mod)   #=> #<GoodKlass:0x401b3bc8>
good.hello         #=> "Hello from Mod.\n"

For example I'd like to display a warning or raise if Mod is used to extend something else than an instance of GoodKlass :例如,如果Mod用于extend GoodKlass实例以外的其他内容,我想显示警告或加注:

bad = BadKlass.new
bad.extend(Mod)   #=> raise "Mod cannot extend BadKlass"

You can define self.extended in the module:您可以在模块中定义self.extended

module Mod
  def self.extended(base)
    raise "Cannot extend #{base}" unless base.is_a?(GoodKlass)
  end

  def hello
    "Hello from Mod.\n"
  end
end

Your comment on the question, replying to my comment, confirmed a suspicion I had.你对这个问题的评论,对我的评论的回复,证实了我的怀疑。 What you have done is not to extend the class, but to extend a particular instance of the class.您所做的不是扩展类,而是扩展类的特定实例。 Let's see what your code does.让我们看看你的代码做了什么。

good = GoodKlass.new
good.hello       #=> "Hello from GoodKlass.\n"
GoodKlass.hello  #=> NoMethodError: undefined method `hello' for GoodKlass:Class
good.extend(Mod)
GoodKlass.hello  #=> NoMethodError: undefined method `hello' for GoodKlass:Class
good.hello       #=> "Hello from Mod.\n"
very_good = GoodKlass.new
very_good.hello  #=> "Hello from GoodKlass.\n"

As you see, hello is only defined on the instance good .如您所见, hello仅在实例good上定义。

Note笔记

GoodKlass.methods.include?(:hello)          #=> false
good.methods.include?(:hello)               #=> true 

If that's not what you want, there are two possibilities.如果这不是您想要的,则有两种可能性。 I reference我参考

class VeryGoodKlass
end

in discussing both.在讨论两者。

1. Extend the class 1. Extend

In your application (ref comments on the question), this approach would allow you to a create a class method File::jpg?在您的应用程序中(对问题的引用评论),这种方法将允许您创建一个类方法File::jpg? which would be invoked File.jpeg?("cat.jpg") .这将被调用File.jpeg?("cat.jpg")

To convert Mod 's instance method hello to a class method of GoodKlass you need to extend the class (not an instance of the class), using Object#extend .要将Mod的实例方法hello转换为GoodKlass的类方法,您需要使用Object#extend扩展类(不是类的实例)。 To prevent other classes from extending the module, use the callback method Module#extended in the module.要防止其他类扩展模块,请在模块中使用回调方法Module#extended

module Mod
  def self.extended(base)
    raise ArgumentError, "Cannot extend #{base}" unless base == GoodKlass
  end
  def hello
    "Hello from Mod"
  end
end

class GoodKlass
  def self.hello
    "Hello from GoodKlass"
  end
end

GoodKlass.hello           #=> "Hello from GoodKlass"
GoodKlass.extend(Mod)
GoodKlass.hello           #=> "Hello from Mod"
VeryGoodKlass.extend(Mod) #=> ArgumentError: Cannot extend VeryGoodKlass

2. Include the module in the class 2.在类中包含模块

To add Mod 's instance methods to GoodKlass (keeping them instance methods) you need to include the module, using Module#include .要将Mod的实例方法添加到GoodKlass (保留它们的实例方法),您需要使用Module#include包含该模块。 To prevent other classes from including the module, use the callback method #included in the module.要防止其他类包含该模块,请在模块中使用回调方法#included

In your application this would allow you to write an instance method File#jpg?在您的应用程序中,这将允许您编写一个实例方法File#jpg? 1 used as follows: 1使用如下:

f = File.new('cat.jpg')
f.jpg?

You could do that as follows.您可以按如下方式执行此操作。

module Mod
  def self.included(base)
    raise ArgumentError, "Cannot include #{base}" unless base == GoodKlass
  end
  def hello
    "Hello"
  end
end

class GoodKlass
end

good = GoodKlass.new
GoodKlass.include(Mod)
GoodKlass.hello            #=> NoMethodError: undefined method `hello' for GoodKlass:Class
good.hello                 #=> "Hello" 
VeryGoodKlass.include(Mod) #=> ArgumentError: Cannot include VeryGoodKlass

1. Perhaps File.basename(f.path).end_with?(".jpg") . 1. 也许File.basename(f.path).end_with?(".jpg")

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

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