0

我想将一些功能从 Rails 模型移到服务模块中。

对此担心不是正确的事情,因为它仅适用于一个模型,我只想在其他地方整理一些代码。

我似乎无法对模型进行基本调用,这是我的设置:

/app/models/account.rb

class Account < ApplicationRecord
    include SomeService
end

在不同的位置:

app/services/some_service.rb

module SomeService

  def test_code
    "abc"
  end

  def self.test_code_2
    "xyz"
  end

end

在这种情况下,我会期望Account.first.test_code输出abcAccount.test_code_2输出zyx

我如何将这样的功能从模型中移出而不是关注?我觉得我非常接近这项工作。

4

2 回答 2

2

这段代码实际上并没有定义类方法:

module SomeService
  def test_code
    "abc"
  end

  def self.test_code_2
    "xyz"
  end
end

它在模块本身上声明了一个方法,您可以通过运行来验证它SomeService.test_code_2。那是因为self它不像static其他语言那样是“类方法关键字”——它只是对当前词法范围的引用。在这种情况下,模块本身。

在类中声明方法时:

class Foo
  def self.bar
    "Hello world"
  end
end

self是所谓的单例类 - Class 类的一个实例。所以它在Foo类上定义了一个方法。

当您include是类中的一个模块时,您将该模块添加到该类的祖先链中。因此,它可以调用模块的实例方法,就好像它在它自己的位置一样。您可以将其与extend将模块的方法导入类(test_code成为类方法)进行对比。

因此,在包含时ClassMethods使用内部模块扩展类的模式:

module SomeService
  def self.included(base)
    base.extend ClassMethods
  end

  def test_code
    "abc"
  end

  module ClassMethods
    def test_code_2
      "xyz"
    end
  end
end

我如何将这样的功能从模型中移出而不是关注?

你在做什么是一个问题。Rails 中的“关注”一词实际上只是模糊地表示“混合到类中的模块”之类的东西。唯一真正的定义是自动加载根和app/models/concerns存在,它只是简化了编写 mixin 时所需的样板代码。例如,您可以使用它的宏来缩短上述代码。app/controllers/concernsActiveSupport::Concernclass_methods

对于关注点应该包含什么或其在应用程序中的角色没有实际定义,也没有任何要求将其混合到多个类中。

但...

如果将逻辑从模型(或任何类)中移出并移入模块中,那么如果您将其重新包含到模型中,则实际上什么也做不了。您只是通过将代码改组为多个文件来掩盖代码。

责任的数量和复杂性保持不变。

如果你真的想重新分配责任,你想创建一个可以独立存在并执行工作单元的对象:

class SomeService
  def initialize(thing)
     @thing = thing
  end

  def perform
    # do something awesome with @thing
  end

  def self.perform(thing)
    new(thing).perform
  end
end

这通常被称为服务对象模式- 服务模块不是 AFAIK 的东西。这具有一组定义的职责并卸载模型。ActiveJob 就是这种模式的一个例子。

您正在做的事情被称为方法提取 - 基本上只是将像上帝一样的对象拆分为模块,因为模块是好的,大类是邪恶的。正确的?没有。依旧是神级。这在 Rails 引入关注文件夹的时候变得非常流行。

另一个不应忽视的解决方案是查看模型是否确实做了很多工作,并且应该将其拆分为具有更明确定义的职责的多个模型。

于 2021-11-14T14:46:04.260 回答
0

您可以在模块中定义一个ClassMethods模块,并include在基类中定义它。这样,普通的模块方法将作为实例方法使用,而内部定义的方法ClassMethods将作为基类中的类方法使用。

app/services/some_service.rb

module SomeService

  def self.included(base)
    base.extend ClassMethods
  end

  def test_code
    "abc"
  end

  module ClassMethods
    def test_code_2
      "xyz"
    end
  end
end
于 2021-11-14T10:56:49.957 回答