自动创建缺失的连接模型
Automatically creating missing join models
最近我厌倦了在我的 app/models
目录中乱扔毫无意义的样板模型,例如:
- 加入总是包含一对
belongs_to
且没有其他任何东西的模型。
- 仅
include SomeConcern
并进行几个宏调用的状态日志模型。
- 修订跟踪模型,再次包含一个问题并调用一个宏。
这些模型仅用于支持 has_many
和 has_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
宏:
根据 class 名称和 has_things
的一些选项创建 has_many :model_things
关联。
创建 has_many :things, through: :model_things
关联。
查找或创建 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 我并没有后悔。所以,就是这样。
最近我厌倦了在我的 app/models
目录中乱扔毫无意义的样板模型,例如:
- 加入总是包含一对
belongs_to
且没有其他任何东西的模型。 - 仅
include SomeConcern
并进行几个宏调用的状态日志模型。 - 修订跟踪模型,再次包含一个问题并调用一个宏。
这些模型仅用于支持 has_many
和 has_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
宏:
根据 class 名称和
has_things
的一些选项创建has_many :model_things
关联。创建
has_many :things, through: :model_things
关联。查找或创建
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 我并没有后悔。所以,就是这样。