У меня есть эта строка:
factory.workers.where.not(confirmed_at:nil).where(job_roles: {heavy_lifting:true, sensitive_area: false}).pluck(:work_capacity)
Но таблица ассоциаций job_roles
зависит от названия класса фабрики, а у меня 5 типов фабрик. Итак, если factory.class.name == "DistributionCenter"
, то job_roles
на самом деле будет distribution_center_roles
, а если factory.class.name == "AutomotiveAssemblyPlant"
, то job_roles
будет automotive_assembly_plant_roles
и т. д.
Есть ли способ добавить имя класса в имя таблицы связей в коде запроса ActiveRecord, чтобы вместо job_roles
было написано distribution_center_roles
или automotive_assembly_plant_roles
(и т. д.), в зависимости от имени класса factory
?
лиииике
factory.workers.where.not(confirmed_at:nil).where((factory.class.name + "_roles").string_to_tablename_rails_method: {heavy_lifting:true, sensitive_area: false}).pluck(:work_capacity)
или мне нужно сделать:
if factory.is_a?(DistributionCenter)
factory.workers.where.not(confirmed_at:nil).where(distribution_center_roles: {heavy_lifting:true, sensitive_area: false}).pluck(:work_capacity)
elsif factory.is_a?(AutomotiveAssemblyPlant)
factory.workers.where.not(confirmed_at:nil).where(automotive_assembly_plant_roles: {heavy_lifting:true, sensitive_area: false}).pluck(:work_capacity)
etc
Если название стола основано на названии модели, вы можете просто подчеркнуть его.
job_roles = :"#{factory.class.name.underscore}_roles"
Если для модели используется какое-то пространство имен, вам, вероятно, также потребуется параметризовать
job_roles = :"#{factory.class.name.undercore.parameterize(separator: '_')}_roles"
Сравнивать:
Asdf::FooBar.name.underscore
# => "asdf/foo_bar"
Asdf::FooBar.name.underscore.parameterize(separator: '_')
# => "asdf_foo_bar"
Если имя таблицы основано на имени таблицы, вы можете таблизировать ее.
job_roles = :"#{factory.class.name.tableize}_roles"
Также можно использовать ActiveRecord::Base::table_name
Это также будет работать, даже если вы переопределите table_name
внутри модели или используете пользовательские table_name_prefix
/ table_name_suffix
job_roles = :"#{factory.class.table_name}_roles"
После этого вы можете применить его к своему запросу.
factory.workers.where.not(confirmed_at:nil).where(job_roles => {heavy_lifting:true, sensitive_area: false}).pluck(:work_capacity)
Я бы использовал ActiveModel::Naming и отражение, если вы хотите сделать это менее хакерским способом:
module RoleScoped
def with_roles(**conditions)
reflect_on_assocation(:"#{model_name.singular}_roles")
.compute_class
.where(conditions)
end
end
class DistributionCenter < ApplicationModel
extend RoleScoped
end
class AutomotiveAssemblyPlant < ApplicationModel
extend RoleScoped
end
factory.workers
.where.not(confirmed_at:nil)
.merge(factory.class.with_roles(heavy_lifting:true, sensitive_area: false))
.pluck(:work_capacity)
Это позволяет избежать предположений об именах таблиц. Еще лучше было бы назвать ассоциацию одним и тем же в двух классах, чтобы вам вообще не приходилось возиться.
class DistributionCenter
has_many :roles, class_name: 'DistributionCenterRole'
end
class AutomotiveAssemblyPlant
has_many :roles, class_name: 'AutomotiveAssemblyPlantRole'
end
factory.workers
.where.not(confirmed_at:nil)
.merge(factory.roles.where(heavy_lifting:true, sensitive_area: false))
.pluck(:work_capacity)
Второй прекрасен, первый — следующий уровень, о котором мне придется прочитать.
reflect_on_assocation(:"#{model_name.singular}_roles")
на самом деле это просто способ получить метаданные об ассоциации. И затем compute_class
получает целевой класс ассоциации.
Хотя это один из способов сделать это, помните, что вы создаете довольно хрупкое решение, поскольку в результате из моделей свисает множество движущихся частей.