Ruby在模型中包含模块的单一方法

Posted

技术标签:

【中文标题】Ruby在模型中包含模块的单一方法【英文标题】:Ruby include module's single method in model 【发布时间】:2011-09-24 20:05:38 【问题描述】:

我有一个关注模块

module SimpleTask
    def task1
    end
    def task2
    end
    def task3
    end
end

我有一个模型,它只需要模块SimpleTasktask2 方法。

我知道在我的模型中包含SimpleTaskinclude SimpleTask 可以完成这项工作。

但我想知道我是否只能在我的模型中包含特定的 task2 方法。

【问题讨论】:

Can I invoke an instance method on a Ruby module without including it? 的可能重复项 【参考方案1】:

听起来您需要将#task2 重构为一个单独的模块(例如,BaseTask)。然后你可以很容易地只包含BaseTask,你只需要#task2

module BaseTask
  def task2
    ...
  end
end

module SimpleTask
  include BaseTask

  def task1
    ...
  end

  def task3
    ...
  end
end

如果没有更具体的问题(例如SimpleTask 的方法之间的相互依赖等),很难提供更多帮助。

可以include SimpleTask 处进行一些元编程,然后取消定义您不想要的方法,但这在 IMO 中是相当丑陋的。

【讨论】:

这个问题没有错,很好,很清楚。我到了这个页面,因为我有完全相同的问题。我不想创建新模块,也不想在类命名空间中不必要地包含其他方法。我只想包含模块中的一个方法。【参考方案2】:

你可以添加

module SimpleTask
    def task1
    end
    def task2
    end
    def task3
    end
    module_function :task2
end

这样您就可以像调用模块上的类方法一样调用该方法,也可以将其作为实例 方法在你确实想要所有三种方法的地方,即:

class Foo
   include SimpleTask
end #=> Foo.new.task2
class LessFoo
   def only_needs_the_one_method
      SimpleTask.task2
   end
end #=> LessFoo.new.only_needs_the_one_method

或者,如果模块中确实没有共享状态,并且您不介意总是使用模块名称本身,则可以像这样声明所有类级别的方法:

module SimpleTask
    def self.task1
    end
    def self.task2
    end
    def self.task3
    end
end

class Foo
   include SimpleTask # Does, more or less nothing now
   def do_something
     SimpleTask.task1
   end
end 
#=> Foo.new.task2 #=> "task2 not a method or variable in Foo"
#=> Foo.new.do_something does, however, work
class LessFoo
   def only_needs_the_one_method
      SimpleTask.task2
   end
end #=> LessFoo.new.only_needs_the_one_method works as well in this case

但在这种情况下,您必须更改所有调用者。

【讨论】:

module_function 很好,只要所有模块实例方法都是私有的就可以了。如果您希望它们公开,您可以使用extend self。必须小心,任何方法都不依赖于包含它们的实例的状态;在这种情况下,类方法将不起作用。【参考方案3】:

我要从delegate.rb 那里偷一个例子,它限制了它包含的内容

...
class Delegator < BasicObject
  kernel = ::Kernel.dup
  kernel.class_eval do
    [:to_s,:inspect,:=~,:!~,:===,:<=>,:eql?,:hash].each do |m|
      undef_method m
    end
  end
  include kernel
...

变成

module PreciseInclude

  def include_except(mod, *except)
    the_module = mod.dup
    the_module.class_eval do
      except.each do |m|
        remove_method m # was undef_method, that prevents parent calls
      end
    end
    include the_module
  end
end

class Foo
  extend PreciseInclude

  include_except(SimpleTask, :task1, :task2)
end

Foo.instance_methods.grep(/task/) => [:task3]

你总是可以翻转它,而不是包含它变成include_only

需要注意的是 remove_method 不适用于嵌套模块,使用 undef 将阻止在整个层次结构中搜索该方法。

【讨论】:

【参考方案4】:

一个简单的解决方案是

define_method :task2, SimpleTask.instance_method(:task2)

【讨论】:

以上是关于Ruby在模型中包含模块的单一方法的主要内容,如果未能解决你的问题,请参考以下文章

访问模块中包含的 ruby​​ 方法的符号

如何在 node.js 中包含默认模型作为参考名称?

为啥在 Ruby 中包含模块的顺序会有所不同?

从单独的文件中包含一个 Ruby 类

在 pydantic 模型中包含非验证方法是不好的做法吗?

如何在模型中包含 Session 组件? CakePHP