1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2024-12-01 12:24:28 +01:00
fab-manager/app/services/footprint_service.rb

78 lines
3.1 KiB
Ruby

# frozen_string_literal: true
require 'integrity/checksum'
# Provides helper methods to compute footprints
class FootprintService
class << self
# Compute the footprint
# @param klass {Class} a class inheriting from Footprintable
# @param item {Footprintable} an instance of the provided class
def compute_footprint(klass, item)
Integrity::Checksum.text(FootprintService.footprint_data(klass, item))
end
# Return the original data string used to compute the footprint
# @param klass {Class} a class inheriting from Footprintable
# @param item {Footprintable} an instance of the provided class
# @param array {Boolean} if true, the result is return on the form of an array, otherwise a concatenated string is returned
def footprint_data(klass, item, array: false)
raise TypeError unless item.is_a? klass
sort_on = item.sort_on_field
previous = klass.where("#{sort_on} < ?", item[sort_on])
.order("#{sort_on} DESC")
.limit(1)
columns = FootprintService.footprint_columns(klass)
columns = columns.map do |c|
comparable(item[c])
rescue ActiveModel::MissingAttributeError
nil
end
res = columns.push(previous.first ? previous.first.footprint : '')
array ? res.map(&:to_s) : res.join.to_s
end
# Return an ordered array of the columns used in the footprint computation
# @param klass {Class} a class inheriting from Footprintable
def footprint_columns(klass)
%w[id].concat(klass.columns.map(&:name).delete_if { |c| %w[id footprint updated_at].concat(klass.columns_out_of_footprint).include? c }.sort)
end
# Logs a debugging message to help finding why a footprint is invalid
# @param klass {Class} a class inheriting from Footprintable
# @param item {Footprintable} an instance of the provided class
def debug_footprint(klass, item)
columns = FootprintService.footprint_columns(klass)
current = FootprintService.footprint_data(klass, item, array: true)
saved = FootprintDebug.find_by(footprint: item.footprint, klass: klass.name)
return saved if Rails.env.test?
if saved.nil?
Rails.logger.debug { "Debug data not found for #{klass} [ id: #{item.id} ]" }
else
Rails.logger.debug do
"Debug footprint for #{klass} [ id: #{item.id} ]\n" \
"-----------------------------------------\ncolumns: [ #{columns.join(', ')} ]\n" \
"current: #{current}\n saved: #{saved.format_data(item.id)}\n" \
'-----------------------------------------'
end
item.footprint_children.map(&:debug_footprint)
end
others = FootprintDebug.where('klass = ? AND data LIKE ? AND id != ?', klass, "#{item.id}%", saved&.id)
Rails.logger.debug { "other possible matches IDs: #{others.map(&:id)}\n-----------------------------------------" }
end
private
# Return a comparable value for jsonb fields (with keys ordered alphabetically)
def comparable(value)
return value unless value.is_a? Hash
value.sort.to_h
end
end
end