简体   繁体   English

从gem继承一个类并添加本地方法

[英]Inherit a class from a gem and add local methods

I use a gem to manage certain attributes of a gmail api integration, and I'm pretty happy with the way it works. 我使用gem来管理gmail api集成的某些属性,我对它的工作方式非常满意。

I want to add some local methods to act on the Gmail::Message class that is used in that gem. 我想添加一些本地方法来处理该gem中使用的Gmail :: Message类。

ie I want to do something like this. 即我想做这样的事情。

models/GmailMessage.rb
  class GmailMessage < Gmail::Message

    def initialize(gmail)
      #create a Gmail::Message instance as a GmailMessage instance 
      self = gmail
    end

    def something_clever
      #do something clever utilising the Gmail::Message methods
    end
  end

I don't want to persist it. 我不想坚持下去。 But obviously I can't define self in that way. 但显然我无法以这种方式定义自我。

To clarify, I want to take an instance of Gmail::Message and create a GmailMessage instance which is a straight copy of that other message. 为了澄清,我想采用Gmail :: Message的实例并创建一个GmailMessage实例,该实例是该其他消息的直接副本。

I can then run methods like @gmail.subject and @gmail.html, but also run @gmail.something_clever... and save local attributes if necessary. 然后我可以运行@ gmail.subject和@gmail.html等方法,但也可以运行@ gmail.something_clever ...并在必要时保存本地属性。

Am I completely crazy? 我完全疯了吗?

You can use concept of mixin , wherein you include a Module in another class to enhance it with additional functions. 您可以使用mixin概念,其中您在另一个类中包含一个Module ,以通过其他功能来增强它。

Here is how to do it. 这是怎么做的。 To create a complete working example, I have created modules that resemble what you may have in your code base. 为了创建一个完整的工作示例,我创建了类似于您的代码库中的模块。

# Assumed to be present in 3rd party gem, dummy implementation used for demonstration
module Gmail
    class Message
        def initialize
            @some_var = "there"
        end
        def subject
            "Hi"
        end
    end
end

# Your code
module GmailMessage
    # You can code this method assuming as if it is an instance method
    # of Gmail::Message. Once we include this module in that class, it
    # will be able to call instance methods and access instance variables.
    def something_clever
        puts "Subject is #{subject} and @some_var = #{@some_var}"
    end
end

# Enhance 3rd party class with your code by including your module
Gmail::Message.include(GmailMessage)

# Below gmail object will actually be obtained by reading the user inbox
# Lets create it explicitly for demonstration purposes.
gmail = Gmail::Message.new

# Method can access methods and instance variables of gmail object
p gmail.something_clever  
#=> Subject is Hi and @some_var = there

# You can call the methods of original class as well on same object
p gmail.subject
#=> "Hi"

Following should work: 以下应该工作:

class GmailMessage < Gmail::Message

  def initialize(extra)
    super
    # some additional stuff
    @extra = extra
  end

  def something_clever
    #do something clever utilising the Gmail::Message methods
  end

end

GmailMessage.new # => will call first the initializer of Gmail::Message class..

I'm not sure why you can't just have a simple wrapper class... 我不确定为什么你不能只有一个简单的包装类......

class GmailMessage

  def initialize(message)
    @message = message
  end

  def something_clever
    # do something clever here
  end

  def method_missing(m, *args, &block)  
    if @message.class.instance_methods.include?(m)
      @message.send(m, *args, &block)
    else
      super
    end  
  end 
end

Then you can do... 然后你可以......

  @my_message = GmailMessage.new(@original_message)

@my_message will correctly respond to all the methods that were supported with @original_message and you can add your own methods to the class. @my_message将正确响应@original_message支持的所有方法,您可以将自己的方法添加到类中。

EDIT - changed thanks to @jeeper's observations in the comments 编辑 - 感谢@ jeeper在评论中的观察结果

Building upon what the other posters have said, you can use built-in class SimpleDelegator in ruby to wrap an existing message: 基于其他海报所说的内容,您可以在ruby中使用内置类SimpleDelegator来包装现有消息:

require 'delegate'

class MyMessage < SimpleDelegator
  def my_clever_method
    some_method_on_the_original_message + "woohoo"
  end
end

class OriginalMessage
  def some_method_on_the_original_message
    "hey"
  end

  def another_original_method
    "zoink"
  end
end

original = OriginalMessage.new
wrapper = MyMessage.new(original)

puts wrapper.my_clever_method
# => "heywoohoo"

puts wrapper.another_original_method
# => "zoink"

As you can see, the wrapper automatically forwards method calls to the wrapped object. 如您所见,包装器自动将方法调用转发给包装对象。

It's not the prettiest, but it works... 这不是最漂亮的,但它有效......

class GmailMessage < Gmail::Message

  def initialize(message)
    message.instance_variables.each do |variable|
      self.instance_variable_set(
        variable, 
        message.instance_variable_get(variable)
        )
    end
  end

  def something_clever
    # do something clever here
  end

end

Thanks for all your help guys. 谢谢你们的帮助。

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

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