【问题标题】:Automatically creating missing join models自动创建缺失的连接模型
【发布时间】:2018-12-19 23:54:38
【问题描述】:

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

  • 加入始终包含一对 belongs_tos 而没有其他内容的模型。
  • include SomeConcern 并进行几个宏调用的状态日志模型。
  • 同样是修订跟踪模型,只需包含关注点并调用宏。

这些模型仅用于支持 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

我可以通过has_things 宏关注ThingSupport

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

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

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

     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

所有模型和关联名称都可以使用调用者的类名和has_things 的一些选项(用于特殊情况)中的常用 Rails 约定来构建。

问题是我在这里玩火吗?这种诡计会有什么问题?

我已经遇到的一个问题是,生成的模型类本身并不存在,因此它们不能直接从 ActiveJob(例如 deliver_later 邮件程序)中引用。例如,如果加载Model 会创建ModelThing 关联模型,那么您不能在邮件参数中引用ModelThing,因为ActiveJob 不会知道您必须在ModelThing 存在之前加载Model 类.但是,这可以通过使用Model::Thing 来解决,这样constantize 将在尝试查找Model::Thing 之前查找Model(并在app/models/model.rb 中找到它)(这将存在,因为constantize 将只有加载了Model,它创建了Model::Thing)。我还缺少其他东西吗?

【问题讨论】:

  • 哇,金 rubyruby-on-rails 会员在问问题????????????
  • has_and_belongs_to_many 在这里不起作用有什么原因吗? (我假设有,我只是错过了)
  • “生成的模型类本身不存在”您可以使用initializers 中的文件来处理这部分。您是否还考虑过查看has_many 关系的扩展选项?似乎你可以使这个逻辑模块化和跨类适用
  • @engineersmnky 或者只使用Model::Thing 以便'Model::Thing'.constantize'Model'.constantize(从而创建Model::Thing)一路走来。我更喜欢命名空间而不是使用初始化程序使事情变得更加分散。是的,我确实将扩展块与所有这些结合使用,有时我会在模块中捕获扩展块,可以混合到生成的类中。
  • @muistooshort extend 可以将完整的模块作为参数,这就是我建议它的原因

标签: ruby-on-rails ruby metaprogramming


【解决方案1】:

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

专注于连接模型位,我也厌倦了那个薄薄的火焰。所以,我创建了一个模型:

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...

然后,我把所有的垃圾都包成一个宝石。现在我很少创建连接模型,除非它们有一些我无法将其插入我的acts_as_having 位的特定要求。

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

【讨论】:

  • 你肯定和我在同一个地方玩耍,所以你说得有道理。我也一直在使用我的ModuleUtil.find_or_create chicanery 来避免多态关联在数据库中造成的不愉快(在我看来,将数据库绑定到单个应用程序有点令人厌恶,因此多态关联和 STI 之类的东西很少适用) .在我的例子中,Foo::Event 类可以由acts_as_having 生成。真正的基本思想是不要手动编写样板。
  • 有趣的东西。哦,有一种方法可以进行更长时间、更详细的讨论。所以问答并没有完全做到这一点。很想听听更多。
猜你喜欢
  • 2022-01-23
  • 2018-06-04
  • 2020-11-09
  • 2023-03-15
  • 1970-01-01
  • 2015-05-20
  • 2017-12-23
  • 2015-05-19
  • 2012-09-27
相关资源
最近更新 更多