在 ruby 中对模型进行方法调用之前和之后 rails,执行 "thing"

before and after method call on models in ruby on rails, do "thing"

我正在考虑一种在 RoR 中包装模型方法调用的方法。 (这是一个自定义模型缓存系统的原因,它允许我在我的客户正在使用的一些大型模型创建的数据上做很多幕后工作,而不必不断加载这些模型并解析所有非常胖的模型数据来获取输出。我可以只发送已经计算并存储在不同位置的输出,以及在幕后进行计算和存储的延迟作业,由于数据的收集方式或令人震惊的方式,这并不是一个真正的选择体积有问题..很难解释。)

鉴于我有一个这个极其简单的实例 class;

class Person < ApplicationRecord
  attr_accessor: :name
  has_many :books
  has_many :friends
end

我将如何着手编写一些允许我以编程方式“拦截”ALL(或除我特别声明的以外的所有)方法调用给定 class.

所以如果有人这样做;

bob = Person.find 1
puts bob.name
puts bob.friends.count

我可以让这个系统知道在 .name 被调用之前或 .friends 被调用之前做一些事情。比如检查我们是否已经在其他地方找到了这个问题的答案..

我正在考虑使用 before_hook 并覆盖 method_added 挂钩以根据这个问题在我的代码前面加上: 但问题是它适用于 .name但不适用于 .friends。我假设这是由于一些 rails 魔法,因为朋友只是在没有实际执行挂钩的情况下给你正常的活动记录调用..

我希望能够做类似的事情;

class Person < ApplicationRecord

  include CustomCachableThingy
  before_hook :friends, :name, except: :books

  attr_accessor: :name
  has_many :books
  has_many :friends
end

这也是一个非常简单的例子。有问题的模型太大了,数据太多了。所以想找一些我可以为我的客户工作的东西。

想法?

如果你真的想包装每个方法调用(甚至是目标 class 本身定义的方法调用),你可以定义一个模块来做两件事,

  • 获取所有 instance_methods 并用您的自定义代码包装它们
  • 覆盖 method_added 方法调用并包装使用相同自定义代码添加的每个新方法

回答了一个使用此策略的好例子 here。您可以对其进行自定义以采用额外的 except 配置并排除这些方法。

这里进行的meta-programming是重写每个方法,因为它被注入到实例中。


另一种选择是,如果您知道需要缓存哪些方法调用,则只用您的自定义代码包装特定的方法调用。它将减少开销和更清洁的实施。

对于实施,您可以使用与第一个示例中所述相同的 meta-programming 方法。另一种方法是为原始方法添加别名并重写该方法以包含您的钩子。像,

def add_before_hook(method_name)
  class_eval do
    without = :"#{method_name}_without_before_each_method"
    alias_method without, method_name

    define_method method_name do |*args, &block|
      puts 'called before'
      send without, *args, &block
    end
  end
end

第二种方法的问题在于,它会通过为每个实例方法添加一个重复方法来污染您的方法名称空间。