Ruby 扩展并包含跟踪代码

Ruby extend & include tracing code

我对使用 "include" 与“扩展”感到困惑,搜索了几个小时后,我得到的只是与 class 实例一起使用的模块方法,包括模块,以及与当 class 扩展这些方法的模块时 class 本身。

但这并没有帮助我弄清楚为什么这段代码在注释“#extend Inventoryable”中的扩展模块行时出错 在取消注释时工作,这是代码

module Inventoryable

    def create(attributes)
      object = new(attributes)
      instances.push(object)
      return object
    end

    def instances
      @instances ||= []
    end

  def stock_count
    @stock_count ||= 0
  end

  def stock_count=(number)
    @stock_count = number
  end

  def in_stock?
    stock_count > 0
  end
end

class Shirt
  #extend Inventoryable
  include Inventoryable
  attr_accessor :attributes

  def initialize(attributes)
    @attributes = attributes
  end
end

 shirt1 = Shirt.create(name: "MTF", size: "L")
 shirt2 = Shirt.create(name: "MTF", size: "M")
 puts Shirt.instances.inspect

输出是

store2.rb:52:in `<main>': undefined method `create' for Shirt:Class (NoMethodError)

而当取消注释 "extend Inventoryable" 以使代码工作时:

module Inventoryable

    def create(attributes)
      object = new(attributes)
      instances.push(object)
      return object
    end

    def instances
      @instances ||= []
    end

  def stock_count
    @stock_count ||= 0
  end

  def stock_count=(number)
    @stock_count = number
  end

  def in_stock?
    stock_count > 0
  end
end

class Shirt
  extend Inventoryable
  include Inventoryable
  attr_accessor :attributes

  def initialize(attributes)
    @attributes = attributes
  end
end

 shirt1 = Shirt.create(name: "MTF", size: "L")
 shirt2 = Shirt.create(name: "MTF", size: "M")
 puts Shirt.instances.inspect

使代码运行并输出以下内容

[#<Shirt:0x0055792cb93890 @attributes={:name=>"MTF", :size=>"L"}>, #<Shirt:0x0055792cb937a0 @attributes={:name=>"MTF", :size=>"M"}>] 

这有点令人困惑,但我需要知道的是,为什么我需要扩展模块以避免错误?,以及如何编辑此代码以使其在没有扩展方法的情况下工作? ,代码中还剩下什么仍然依赖于扩展?

当您 extend 一个模块时,该模块中的方法变为 "class methods"**。因此,当您 extend Inventoryable 时,create 可用作 Shirt class.

上的方法

当您 include 一个模块时,该模块中的方法将变为 "instance methods"**。因此,当您 include Inventoryable 时,createShirt class 上不可用(但 在 [= 的实例上可用16=]).

要在使用 include 时使 createShirt class 上可用,您可以使用 included 挂钩。这可能看起来像:

module Inventoryable
  module ClassMethods

    def create
      puts "create!"
    end

  end

  module InstanceMethods

  end

  def self.included(receiver)
    receiver.extend ClassMethods
    receiver.include InstanceMethods
  end
end

那么如果你这样做:

class Shirt
  include Invetoryable
end

你可以这样做:

> Shirt.create
create!
 => nil 

** 人群中的 ruby 纯粹主义者会正确地指出,在 ruby 中,一切都是实例方法,没有 class 方法。这在形式上是 100% 正确的,但我们将在这里使用 classinstance 方法的通俗含义。

当您在 class 中扩展模块时,您会将模块的方法公开为 class 方法,但如果您包含该模块,那么您会将模块的方法作为实例方法获取,在您的示例中为了能够调用 Inventoryable class 的 create 方法,您需要使用 Shirt class 的实例调用它(如果包含模块)

shirt1 = Shirt.new(attributes).create(attributes)

没有更多信息,我无法判断您要做什么,但您需要重新设计 initializecreate 方法来决定在这些方法中的位置或操作。

我会尝试用一个简单的例子来解释它

module A
  def test
    puts "ok"
  end
end

class B
  include A
end

class C
  extend A
end

puts C.test # here you invoke the method against the class itself
puts B.new.test #here you create an instance to do it

希望对您有所帮助。

归根结底,这真的很简单:

  • C.include(M) 使 C 的当前超类成为 M 的超类,而 M 成为 C 的超类。换句话说,它将 M 插入到 C 的祖先链中。
  • obj.extend(M)obj.singleton_class.include(M).
  • (大致)相同