自动创建缺失的连接模型

Automatically creating missing join models

最近我厌倦了在我的 app/models 目录中乱扔毫无意义的样板模型,例如:

这些模型仅用于支持 has_manyhas_many ... through: 关联。

添加根据需要生成这些模型的模型关注点可以简化 app/models 目录。所以而不是:

has_many :model_things
has_many :things, through: :model_things

和一个琐碎的 app/models/model_thing.rb 说:

class ModelThing < ApplicationRecord
  belongs_to :model
  belongs_to :thing
end

我可以 ThingSupport 关注 has_things 宏:

  1. 根据 class 名称和 has_things 的一些选项创建 has_many :model_things 关联。

  2. 创建 has_many :things, through: :model_things 关联。

  3. 查找或创建 Model::Thing(请参阅下文了解为何使用此名称)class,调用如下:

     ModuleUtil.find_or_create(join_model_name) do
      Class.new(ApplicationRecord) do
        # Set the table name, call belongs_to as needed, call concern methods, ...
      end
    end
    

    其中 ModuleUtil.find_or_create 是一个简单的方法,它使用 String#constantize 找到所需的模块(如果存在)或使用块创建它,如果不存在则使用 Object#const_set找到了。

所有模型和关联名称都可以使用来自调用者 class 名称的通常 Rails 约定和特殊情况下 has_things 的一些选项来构建。

问题是我这是在玩火吗?这种欺骗能出什么问题?

我已经遇到的一个问题是生成的模型 classes 本身并不存在,因此不能直接从 ActiveJob 引用它们(例如 deliver_later 邮件)。例如,如果加载 Model 创建 ModelThing 关联模型,那么您不能在邮件参数中引用 ModelThing 因为 ActiveJob 不知道您必须加载 Model class 在 ModelThing 存在之前。但是,这可以通过使用 Model::Thing 来解决,这样 constantize 将在尝试查找 Model::Thing(它将存在,因为 constantize 将刚刚加载 Model,这会创建 Model::Thing)。我还漏掉了什么吗?

我不知道我是否在关注你。所以,如果这偏离目标,请说出来,我会删除。

专注于连接模型位,我也厌倦了那种轻浮。所以,我创建了一个模型:

module ActsAsHaving
  class HasA < ActiveRecord::Base
    validates :haser_type, :haser_id, :hased_type, :hased_id, presence: true
    belongs_to :hased, polymorphic: true
    belongs_to :haser, polymorphic: true

    acts_as_taggable

    def haser=(thing)
      self.haser_type = thing.class.name
      self.haser_id   = thing.id
    end

    def haser
      haser_type.constantize.find_by(id: haser_id)
    end

    def hased=(thing)
      self.hased_type = thing.class.name
      self.hased_id   = thing.id
    end

    def hased
      hased_type.constantize.find_by(id: hased_id)
    end

  end
end

我没有使用内置访问器和验证,因为我有时会用它来加入非 AR 记录(我从远程 API 服务中获取这些记录,其中一些属于我,另一些不要,但那是一个更长的故事)。

无论如何,我然后写了一个 acts_as_having 宏,让我可以做这样的事情:

class Person < ActiveRecord::Base

  acts_as_having :health_events, class_name: "Foo::Event", tag_with: "health_event", remote: true
  acts_as_having :program_events, class_name: "Foo::Event", tag_with: "program_event", remote: true
  acts_as_having :email_addresses, :phone_numbers, :physical_addresses

end

这给了我这样的东西:

@person.email_addresses
@person.email_addresses << @email_address 
etc...

我可以像这样进行逆运算:

class EmailAddress < ActiveRecord::Base

  acts_as_had_by :person

end

这给了我这样的东西:

@email_address.person
etc...

然后,我将所有这些垃圾打包成 gem。现在我很少创建连接模型,除非它们有一些我不能强行插入我的 acts_as_having 位的特定要求。

反正就是不知道是不是在玩火。我什至不知道我是否有道理或解决您的概念。但是,大约三年前我开始了我的 gem 我并没有后悔。所以,就是这样。