module Approval class Item < ApplicationRecord class UnexistResource < StandardError; end self.table_name = :approval_items EVENTS = %w[create update destroy perform].freeze belongs_to :request, class_name: :"Approval::Request", inverse_of: :items belongs_to :resource, polymorphic: true, optional: true serialize :params, Hash validates :resource_type, presence: true validates :resource_id, presence: true, if: ->(item) { item.update_event? || item.destroy_event? } validates :event, presence: true, inclusion: { in: EVENTS } validates :params, presence: true, if: :update_event? validate :ensure_resource_be_valid, if: ->(item) { item.create_event? || item.update_event? } EVENTS.each do |event_name| define_method "#{event_name}_event?" do event_name.to_s == event.to_s end end def apply send("exec_#{event}") end private def exec_create resource_model.create!(params).tap do |created_resource| update!(resource_id: created_resource.id) end end def exec_update raise UnexistResource unless resource resource.update!(params) end def exec_destroy raise UnexistResource unless resource resource.destroy end def exec_perform raise NotImplementedError unless resource_model.respond_to?(:perform) if resource_model.method(:perform).arity > 0 resource_model.perform(params) else resource_model.perform end end def resource_model @resource_model ||= resource_type.to_s.safe_constantize end def ensure_resource_be_valid return unless resource_model record = if resource_id.present? resource_model.find(resource_id).tap {|m| m.assign_attributes(params) } else resource_model.new(params || {}) end unless record.valid? errors.add(:base, :invalid) record.errors.full_messages.each do |message| request.errors.add(:base, message) end end end end end