自动创建缺少的联接模型

时间:2018-12-19 23:54:39

标签: ruby-on-rails ruby metaprogramming

最近我已经厌倦了用无意义的样板模型填充app/models目录,例如:

  • 加入始终包含两个belongs_to且没有其他内容的模型。
  • 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

我可以对ThingSupport宏有一个has_things的关注:

  1. 根据类名和has_many :model_things的一些选项创建has_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创建(如果找不到)。

所有模型和关联名称都可以使用通常的Rails约定从调用者的类名以及某些特殊情况的has_things选项中构建。

问题是我在这里玩火吗?这种骗子怎么办?

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

1 个答案:

答案 0 :(得分:3)

我不知道是否关注您。因此,如果这离目标很远,请这样说,我将删除。

关注加入模型位时,我也厌倦了那种火焰。因此,我创建了一个模型,例如:

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位。

无论如何,我不知道它是否在玩火。我什至不知道我是有道理还是在解决您的概念。但是,我大约三年前开始创业,我并不后悔。所以,就是这样。