af83/chouette-core

View on GitHub
app/models/concerns/checksum_support.rb

Summary

Maintainability
A
1 hr
Test Coverage
module ChecksumSupport
  extend ActiveSupport::Concern
  SEPARATOR = '|'
  VALUE_FOR_NIL_ATTRIBUTE = '-'

  included do |into|
    before_save do
      Chouette::ChecksumManager.watch self
    end

    after_create do
      Chouette::ChecksumManager.after_create self
    end

    after_destroy do
      Chouette::ChecksumManager.after_destroy self
    end

    Referential.register_model_with_checksum self
    into.extend ClassMethods
  end

  module ClassMethods
    def is_checksum_enabled?; true end

    def has_checksum_children klass, opts={}
      Chouette::ChecksumManager.current.log "Define callback in #{klass} to update checksums #{self.model_name}"
      unless klass.respond_to?(:checksum_parent_relations)
        klass.define_singleton_method :checksum_parent_relations do
          @checksum_parent_relations ||= {}
        end
      end
      klass.checksum_parent_relations[self] = opts

      klass.after_save     { Chouette::ChecksumManager.child_after_save(self) }
      klass.before_destroy { Chouette::ChecksumManager.child_before_destroy(self) }
      klass.after_destroy  { Chouette::ChecksumManager.child_after_destroy(self) }
    end
  end

  def checksum_attributes(db_lookup = true)
    self.attributes.values
  end

  def checksum_replace_nil_or_empty_values values
    # Replace empty array by nil & nil by VALUE_FOR_NIL_ATTRIBUTE
    values
      .map { |x| x.present? && x || VALUE_FOR_NIL_ATTRIBUTE }
      .map do |item|
        item =
          if item.kind_of?(Array)
            checksum_replace_nil_or_empty_values(item)
          else
            item
          end
      end
  end

  def current_checksum_source(db_lookup: true)
    source = checksum_replace_nil_or_empty_values(self.checksum_attributes(db_lookup))
    source += self.custom_fields_checksum if self.respond_to?(:custom_fields_checksum)
    source.map{ |item|
      if item.kind_of?(Array)
        item.map{ |x| x.kind_of?(Array) ? "(#{x.join(',')})" : x }.join(',')
      else
        item
      end
    }.join(SEPARATOR)
  end

  def set_current_checksum_source(db_lookup: true)
    self.checksum_source = self.current_checksum_source(db_lookup: db_lookup)
  end

  def update_checksum(force: false, silent: false)
    if self.checksum_source_changed? || force
      self.checksum = Digest::SHA256.new.hexdigest(self.checksum_source)
    end
    if (self.checksum_source_changed? || self.checksum_changed?) && !silent
      Chouette::ChecksumManager.current.log("Changed #{self.class.name}:#{id} checksum: #{self.checksum}, checksum_source: #{self.checksum_source}")
    end
  end

  def update_checksum!
    _checksum_source = current_checksum_source
    update checksum_source: _checksum_source, checksum: Digest::SHA256.new.hexdigest(_checksum_source)
    Chouette::ChecksumManager.current.log("Updated #{self.class.name}:#{id} checksum: #{self.checksum}")
  end

  def update_checksum_without_callbacks!(db_lookup: true)
    set_current_checksum_source(db_lookup: db_lookup)
    _checksum = Digest::SHA256.new.hexdigest(checksum_source)
    Chouette::ChecksumManager.current.log("Compute checksum for #{self.class.name}:#{id} checksum_source:'#{checksum_source}' checksum: #{_checksum}")
    if _checksum != self.checksum
      self.checksum = _checksum
      self.update_columns(checksum: _checksum, checksum_source: checksum_source) unless self.new_record?
      Chouette::ChecksumManager.current.log("Updated without callback #{self.class.name}:#{id} checksum: #{self.checksum}, checksum_source: #{self.checksum_source}")
    else
      Chouette::ChecksumManager.current.log("Checksum remained unchanged: #{_checksum}")
    end
  end
end