在 class 定义之外调用私有方法

Call Private methods outside class definition

我正在尝试使用 Gem 为我提供 DSL,我需要在我的某些 classes 上应用。但是直接使用它会使我的 class 定义不像我希望的那样干净,所以我想将使用 DSL 的代码移到其他地方,这让我遇到了这个问题,我将在 abstract/general方式:

如果我有一个 ruby class 包含来自另一个 gem 的私有方法,并且在文档中他们告诉我在 class 定义中调用这些方法。

例如:

class A
  include ModuleB::PrivateMethods
end

class B < A
  do_z :param do
    set_property 'a', :x, :y, false
    set_property 'b', :x, :y, false
    set_property 'only for class B', :x, :y, true
  end

  def whatever
  end
end

# this is from the gem
module ModuleZ
  module PrivateMethods
    def self.included(base)
      base.extend Zmethods
    end
    module Zmethods
      private
      def do_z(param1, &block)
        # this method do something and calls the block
      end
    end
  end
end

有没有办法干掉对 do_z 的那些调用,例如 任何继承自 A 的 class 都必须这样做:

  do_z :param do
    set_property 'a', :x, :y, false
    set_property 'b', :x, :y, false
  end

  do_z :param do
    set_property 'only for class B', :x, :y, true
  end

仅 class B 需要,我不想在 class 定义中编写此调用,而是在其他地方?

就像另一个模块一样,当包含时即使这些方法是私有的,也会进行这些调用?

所以我可以这样写 class 定义?

class B < A
  include TheModuleForAllClases::AndTheOneForBclass

  def whatever
  end
end

我可以在基础 class 上调用#do_z,然后再为每个专门的 class 调用一次,以便只在每个实现上进行所需的调用,但它们仍然很多而且这些块非常大,所以我的 class 定义变得很长,class 的实际方法实现被隐藏在这些调用之后。

如果想知道,Gem 是 Rails 上的 swagger-docs look: documenting-a-controller

您好!

像这样的东西应该可以工作

module AllClassesMethods
  def self.included(base)
    base.class_eval do
      do_z :param do
        set_property 'a', :x, :y, false
        set_property 'b', :x, :y, false
      end
    end
  end
end

module OnlyBMethods
  def self.included(base)
    base.class_eval do
      do_z :param do
        set_property 'only for class B', :x, :y, true
      end
    end
  end
end

class A
  include ModuleB::PrivateMethods
  include AllClassesMethods

  def self.inherited(klass)
    klass.include AllClassesMethods
  end
end

class B < A
  include OnlyBMethods
end

A 和任何继承自 A 的 class 将在其 included 方法中包含 AllClassesMethods、运行 代码。它必须显式包含在每个继承的 class 中,否则 included 方法只会被父 A 调用。 class_eval 块在包含 class 的上下文中执行,因此它就像在 class 定义中打开 class 一样。只有 B 包含 OnlyBMethods,因此是唯一触发两个模块的 included 实现的模块。

您可以使用另一种方法。如果您在 extended 模块中定义一个 class 方法宏,class 方法将在 class 上下文中执行,同时让您轻松访问它的私有方法(我说 "easy" 访问是因为在 Ruby 中,您始终可以使用 send)

从任何上下文访问对象的私有方法
module AllClassesMethods
  def does_z
    do_z :param do
      set_property 'a', :x, :y, false
      set_property 'b', :x, :y, false
    end
  end

  def does_z_for_b
    do_z :param do
      set_property 'only for class B', :x, :y, true
    end
  end
end

class A
  include ModuleB::PrivateMethods
  extend AllClassesMethods

  does_z

  def self.inherited(klass)
    klass.does_z
  end
end

class B < A
  does_z_for_b
end 

使用发送方法

obj = SomeClass.new
obj.send(:private_method)