У меня есть система HR с моделью Person и моделью Field. Person имеет некоторые атрибуты, которые хранятся в обычных столбцах базы данных, а некоторые могут быть добавлены динамически.
В таблице полей есть запись для каждого столбца базы данных в таблице сотрудников. Это обязательные для заполнения поля. Администраторы могут добавлять столько полей (не обязательных для заполнения), сколько им нужно при настройке приложения. Они также могут устанавливать свойства для полей, например, являются ли они обязательными.
Для необязательных полей администраторы могут добавить виджет на домашнюю страницу пользователя, который показывает, сколько людей пропустили этот атрибут. Например, администратор может добавить поле: personal_email и виджет, который показывает, сколько людей не ввели это поле.
Поля могут быть добавлены в приложение во время выполнения, а области используются для фильтрации таблицы сотрудников на предмет отсутствия записей. Все это делается с помощью модуля PersonField. Когда добавляется новое поле и запрашивается виджет, приложение выдает ошибку NoMethodError: undefined method `missing_personal_email' for #<Person::ActiveRecord_Relation>
.
При перезапуске сервера rails ошибка не появляется. Я думал, что это может быть связано с cache_classes, однако это происходит в разработке, где оно ложно. Как можно реорганизовать модуль PersonField, чтобы избежать этой проблемы?
class Person < ActiveRecord::Base
include PersonField
end
class Field < ActiveRecord::Base
enum field_type: {:boolean => 1, :integer => 2, :string => 3, :date => 4, :time => 5, :datetime => 6, :float => 7, :decimal => 8, :reference => 9, :any => 10, :email => 11, :phone => 12, :text => 13, :currency => 14, :postcode => 15}
enum widget: { :not_set => 0, :missing => 1, :not_missing => 2 }
scope :widget, -> { where.not(widget: 0) }
scope :system_required, -> {where(system_required: 1)}
scope :not_system_required, -> {where(system_required: 0)}
end
module PersonField
included do
typed_store :data do |s|
Field.active.each do |f|
case f.field_type.to_sym
when :integer, :reference
s.integer f.name.to_sym
when :string, :text, :email, :phone, :postcode
s.string f.name.to_sym
when :datetime
s.datetime f.name.to_sym
# etc for all field types
else
s.any f.name.to_sym
end
end
end
end
Field.active.system_required.widget.uniq.each do |f|
scope "#{f.widget}_#{f.name}", -> { where("people.#{f.name} IS NULL") }
end
Field.active.not_system_required.widget.pluck(:name).uniq.each do |f|
# EG for :personal_email field this gives the SQL condition: people.data NOT LIKE '%personal_email%' OR people.data LIKE '%personal_email: \n%'
scope "#{f.widget}_#{f.name}", -> { where("people.data NOT LIKE '%#{f.name}%' OR people.data LIKE '%#{f.name}: \n%'") }
end
end