Фон
Я хочу сохранить навыки на различных типах объектов.Как правило, пользователь может иметь проект или опыт и пометить его различными навыками и т. Д. Ruby, Python или Excel.Эти навыки являются глобальными и используются в различных возможных объектах, которые «умелые».
Задача состоит из трех моделей:
- Project - содержит информацию о проекте
- Skill - содержит название навыка
- SkillObject - таблица соединений между навыком и проектами (полиморфная)
Пожалуйста, посмотрите на эту ER-диаграмму дляболее детальная картина.
Проблема
Когда я создаю или обновляю проект, я также хочу, чтобы навыки были добавлены в той же транзакции, простоотправив имена навыков из внешнего интерфейса.
Я хочу что-то вроде find_or_create_by, чтобы избежать дублирования навыков.В то же время проверки должны гарантировать, что два одинаковых навыка не могут быть применены к одному и тому же проекту ( skill_object.rb ) и что имя навыка не может быть нулевым ( skill.rb )
Требуемое поведение: если проверка навыков не удалась, тогда ни проект, ни навыки не сохраняются в базе данных.Если вы введете два разных имени навыка, и одно уже находится в базе данных, то существующее должно быть найдено и подключено к проекту, а другое создано и подключено через таблицу объединения, если не произойдет сбой проверки.
project.rb class Project < ApplicationRecord
include Skillable
[...]
end
skill_object.rb
class SkillObject < ApplicationRecord
belongs_to :skill, inverse_of: :skill_objects
belongs_to :skillable, polymorphic: true
delegate :delete_if_empty_skill_objects, to: :skill
after_destroy :delete_if_empty_skill_objects
# Avoiding duplicates of the same skill
validates :skill, uniqueness: { scope: :skillable }
end
skill.rb
class Skill < ApplicationRecord
has_many :skill_objects, inverse_of: :skill
has_many :projects, through: :skill_objects, source: :sectorable, source_type: 'Project'
has_many :experiences, through: :skill_objects, source: :sectorable, source_type: 'Experience'
validates :name, uniqueness: true, presence: true
def delete_if_empty_skill_objects
self.destroy if self.skill_objects.empty? and not self.is_global
end
end
Я использую вопрос, который включен в различные типы умелых объектов:
Concercns / skillable.rb
module Skillable
extend ActiveSupport::Concern
included do
attr_accessor :skills_attributes
after_save :set_skills
# Adding needed relations for skillable objects
has_many :skill_objects, -> { order(created_at: :asc) }, as: :skillable, dependent: :destroy
has_many :skills, through: :skill_objects
private
def set_skills
# THIS CODE IS THE ONE I AM STRUGGLING WITH
# Parsing out all the skill names
skill_names = skills_attributes.pluck(:name)
# Removing existing skills
self.skills = []
# Building new skills
skill_names.each do |name|
existing = Skill.find_by(name: name)
if existing
self.skills << existing
else
self.skills.new(name: name)
end
end
raise ActiveRecord::Rollback unless self.valid?
self.skills.each(&:save)
end
end
end
Кто-нибудь знает, как я могу написать функцию set_skill в skillable.rb, чтобы иметь возможность сохранять и обновлять навыки, проверять родительский объект и навыки, делать откат, если он не был проверен, и добавлять соответствующие ошибки в объект проекта, если он не работает?