【发布时间】:2019-11-14 15:39:15
【问题描述】:
我正在尝试在一个大型生产 Rails 6.0 网站中简化我的生活。我有一堆从 Redis 作为非规范化哈希提供的数据,因为带有所有包含和关联的 Rails 非常慢。
为了保持干燥,我想使用可以包含在ApplicationRecord 中的关注(或模块),它允许我为我想要存储的数据动态定义收集方法。
这是我目前所拥有的:
class ApplicationRecord < ActiveRecord::Base
include DenormalizableCollection
# ...
end
# The model
class News < ApplicationRecord
denormalizable_collection :most_popular
# ...
end
# The Concern
module DenormalizableCollection
extend ActiveSupport::Concern
class_methods do
def denormalizable_collection(*actions)
actions.each do |action|
# define News.most_popular
define_singleton_method "#{action}" do
collection = Redis.current.get(send("#{action}_key"))
return [] unless collection.present?
JSON.parse(collection).map { |h| DenormalizedHash.new(h) }
end
# define News.set_most_popular
define_singleton_method "set_#{action}" do
Redis.current.set(send("#{action}_key"), send("#{action}_data").to_json)
end
# define News.most_popular_data, which is a method that returns an array of hashes
define_singleton_method "#{action}_data" do
raise NotImplementedError, "#{action}_data is required"
end
# define News.most_popular_key, the index key to use inside of redis
define_singleton_method "#{action}_key" do
"#{name.underscore}_#{action}".to_sym
end
end
end
end
end
这可行,但我似乎不正确,因为我也不能定义实例方法或 ActiveRecord after_commit 回调来更新 Redis 内部的集合。
我想添加如下内容:
after_commit :set_#{action}
after_destroy :set_#{action}
但显然这些回调需要实例方法,而after_commit :"self.class.set_most_popular" 会导致抛出错误。所以我想添加一个实例方法,如下所示:
class News
# ...
def reset_most_popular
self.class.send("set_most_popular")
end
end
我一直在阅读尽可能多的文章,并通过 Rails 源查看我遗漏了什么 - 因为我知道我错过了一些东西!
【问题讨论】:
-
define_method定义实例方法的方式与define_singleton_method定义类方法的方式相同。 -
可以使用
included块定义回调,defined_method定义实例方法api.rubyonrails.org/classes/ActiveSupport/Concern.html -
您的意思是从
class_methods块调用define_method? -
class_methods在幕后确实做了类似ClassMethods = Module.new do ... end的事情。因此,当您调用define_singleton_method时,您定义的是模块的单例方法,而不是模块扩展的类。就像在模块内调用def self.foo。 -
@max,我宁愿不使用
define_singleton_method,但这是我让它以基本方式运行的唯一方法。我确实尝试过class_eval <<-CODE, __FILE__, __LINE__ + 1 ... CODE和普通define_method,但在检查News.methods.include?(:most_popular)时都没有注册方法
标签: ruby-on-rails ruby metaprogramming