yujideveloper/operator_recordable

View on GitHub
lib/operator_recordable/recorder.rb

Summary

Maintainability
A
45 mins
Test Coverage
# frozen_string_literal: true

require "operator_recordable/configuration"

module OperatorRecordable
  class Recorder < ::Module
    def initialize(config)
      super()
      define_activate_method(config)
    end

    private

    def define_activate_method(config) # rubocop:disable Metrics/MethodLength
      m = self

      define_method :record_operator_on do |*actions| # rubocop:disable Metrics/MethodLength
        c = Configuration::Model.new(actions)

        if c.record_creator?
          m.__send__(:run_creator_dsl, self, config)
          m.__send__(:define_creator_instance_methods, self, config)
        end

        if c.record_updater?
          m.__send__(:run_updater_dsl, self, config)
          m.__send__(:define_updater_instance_methods, self, config)
        end

        if c.record_deleter?
          m.__send__(:run_deleter_dsl, self, config)
          m.__send__(:define_deleter_instance_methods, self, config)
        end

        if c.record_discarder?
          m.__send__(:run_discarder_dsl, self, config)
          m.__send__(:define_discarder_instance_methods, self, config)
        end
      end
    end

    def run_creator_dsl(klass, config)
      klass.before_create :"assign_#{config.creator_association_name}"
      run_add_association_dsl(:creator, klass, config)
    end

    def run_updater_dsl(klass, config)
      klass.before_save :"assign_#{config.updater_association_name}"
      run_add_association_dsl(:updater, klass, config)
    end

    def run_deleter_dsl(klass, config)
      klass.before_destroy :"assign_#{config.deleter_association_name}"
      run_add_association_dsl(:deleter, klass, config)
    end

    def run_discarder_dsl(klass, config)
      klass.before_discard :"assign_#{config.discarder_association_name}"
      run_add_association_dsl(:discarder, klass, config)
    end

    def run_add_association_dsl(type, klass, config)
      klass.belongs_to config.association_name_for(type).to_sym,
                       config.operator_association_scope,
                       **{ foreign_key: config.column_name_for(type),
                           class_name: config.operator_class_name,
                           optional: true }.merge(config.operator_association_options)
    end

    def define_creator_instance_methods(klass, config)
      klass.class_eval <<~END_OF_DEF, __FILE__, __LINE__ + 1
        private def assign_#{config.creator_association_name}  # private def assign_creator
          return unless (op = OperatorRecordable.operator)     #   return unless (op = OperatorRecordable.operator)
                                                               #
          self.#{config.creator_column_name} = op.id           #   self.created_by = op.id
        end                                                    # end
      END_OF_DEF
    end

    def define_updater_instance_methods(klass, config)
      klass.class_eval <<~END_OF_DEF, __FILE__, __LINE__ + 1
        private def assign_#{config.updater_association_name}  # private def assign_updater
          return if !self.new_record? && !self.changed?        #   return if !self.new_record? && !self.changed?
          return unless (op = OperatorRecordable.operator)     #   return unless (op = OperatorRecordable.operator)
                                                               #
          self.#{config.updater_column_name} = op.id           #   self.updated_by = op.id
        end                                                    # end
      END_OF_DEF
    end

    def define_deleter_instance_methods(klass, config)
      klass.class_eval <<~END_OF_DEF, __FILE__, __LINE__ + 1
        private def assign_#{config.deleter_association_name}      # private def assign_deleter
          return if self.frozen?                                   #   return if self.frozen?
          return unless (op = OperatorRecordable.operator)         #   return unless (op = OperatorRecordable.operator)
                                                                   #
          self                                                     #   self
            .class                                                 #     .class
            .where(self.class.primary_key => id)                   #     .where(self.class.primary_key => id)
            .update_all('#{config.deleter_column_name}' => op.id)  #     .update_all('deleted_by' => op.id)
          self.#{config.deleter_column_name} = op.id               #   self.deleted_by = op.id
        end                                                        # end
      END_OF_DEF
    end

    def define_discarder_instance_methods(klass, config)
      klass.class_eval <<~END_OF_DEF, __FILE__, __LINE__ + 1
        private def assign_#{config.discarder_association_name}      # private def assign_discarder
          return if self.frozen?                                     #   return if self.frozen?
          return unless (op = OperatorRecordable.operator)           #   return unless (op = OperatorRecordable.operator)
                                                                     #
          self.#{config.discarder_column_name} = op.id               #   self.discarded_by = op.id
        end                                                          # end
      END_OF_DEF
    end
  end
end