在 Rails 中的 model/class 中定义自定义方法 4

Defining custom methods within a model/class in Rails 4

我有以下型号:

class ActivityLog < ActiveRecord::Base
  validates :user_id, :instance_id, :action, presence: true
  validates :user_id, :instance_id, :action, numericality: true

  def log
    ActivityLog.create(
      user_id: current_user ? current_user.id : -1,
      instance_id: instance_id,
      action: actions.index(action)
      )
  end

  private 

  def actions
    ['start','stop','create','destroy']
  end

end

当我从 rails 控制台调用以下行时,出现错误:

ActivityLog.log(user_id: 1, instance_id:1, action: 'create')

# Error returned from console
NoMethodError: undefined method `log' for #<Class:0x007fb4755a26a8>

为什么我的方法调用不起作用?我在 class 中定义了它,但它说它未定义。我错过了什么或误解了什么?谢谢。

log是定义的实例方法;只有当你有一个 ActivityLog.

的具体实例时它才会起作用

如果你想让它成为一个 class 方法,你应该通过 self 关键字将它附加到 class。

def self.log
    # code here
end

您正在编写实例方法并像 class 方法一样调用它。

要编写class方法,您需要在方法名称前添加selfself.logself.actions)。这将使您能够像预期的那样调用方法,并且可能是编写这样的替代构造函数的最佳方式。如果您的方法不依赖于 class 的特定实例,您在这里似乎正在这样做,那么最好使它们成为 class 方法。

或者,您可以创建 class 的实例并调用您定义的实例方法。使用 Activity.new 创建一个新记录器,而不是 ActivityLog.log,并在其上调用 log 方法。在一行中,这看起来像 Activity.new.log,但您可能应该将新对象存储在一个变量中以跟踪它。

最后一个替代方法是使用 initialize 方法。通过编写 def initialize,您可以更改 class 的构造函数,这样您就可以调用 ActivityLog.new 而不是调用 ActivityLog.log。这更清楚地表明您正在构造一个新对象,并且在 ruby 中是惯用的。但是,它确实删除了描述性方法名称。如果你不打算用任何其他方法构建你的 class,我会推荐这条路线,但如果你想要多个,请使用 class 方法。

创建方法log

假设您有一个 class User,并且在 class 中定义方法 has_cell_phone。 (该方法的内容无关紧要。)当您将 class 中的方法定义为 def has_cell_phone 时,可以在任何 User 对象上调用该方法。虽然 class User 本身是一个 class 对象,但您可以在直接 class 是 User 的对象上调用它。用正确的术语来说,您将为 User class.[=40 的 实例 编写 实例方法 =]

您收到该错误是因为您定义的方法 log 仅适用于 ActivityLog class 的 _instance。如果您执行以下操作,您可以正确调用 log,给定您当前的代码:

activity_log = ActivityLog.create  # with required params
activity_log.log

其次,您调用 log 时带有参数,而您的方法定义不需要任何参数。 (看起来像 def log(params)。)

现在,您可以在此处修改现有代码。当你想调用 entire class 上的方法时(意思是 class 本身),你将关键字 self 添加到 class-方法定义。例如,对于 User class,它将是 def self.create_user_with_cell_phone。您还可以向该方法添加参数。您在 "method call" 行中提供的参数,我会将它们添加到您的 class 方法中,如下所示:

def self.log(instance_id, action)
  # ...
end

ActivityLog.log(1, 'create')

您不需要包含 user_id,因为根据您的逻辑,它会检查 current_user 对象是否为 true,并从那里开始。

创建一个 class 常量

再看你的问题,我发现你在定义一个方法actions。还记得我说过的实例方法吗?由于 actions 似乎将始终保持不变,因此我建议您将其设为一个!为此,建议您在 class 中的任何方法定义之前放置以下行。

ACTIONS = ['start','stop','create','destroy']

然后,任何时候您想在 ActivityLog class 中调用 ACTIONS,您可以执行以下操作:ACTIONS.index(action)。如果您想在 class 的 外部 调用此常量,您可以这样做:ActivityLog::ACTION。它是 与 class 方法调用相似的 语法,而是使用 :: 将 class 与常量分开。重新检查您的代码,它现在应该如下所示:

class ActivityLog < ActiveRecord::Base
  ACTIONS = ['start','stop','create','destroy']

  validates :user_id, :instance_id, :action, presence: true
  validates :user_id, :instance_id, :action, numericality: true

  def self.log(instance_id, action)
    ActivityLog.create(
      user_id: (current_user ? current_user.id : -1),
      instance_id: instance_id,
      action: ACTIONS.index(action)
    )
  end
end