Module: ForestAdminAgent::AuditTrail::Diff
- Defined in:
- lib/forest_admin_agent/audit_trail/diff.rb
Overview
Minimal structural diff. Nested hashes and arrays of hashes are recursed into, so only the keys/indexes whose leaf value actually changed are kept — a single sub-field change does not store the whole object/array. Scalars, primitive arrays, dates and other values are compared and kept as a whole.
Ruby's == already performs deep, key-order-independent equality on Hash and (ordered) equality
on Array, so it is used directly as the equality primitive.
Constant Summary collapse
- ABSENT =
A key that does not exist on one side of the diff. It is never stored: the key is simply left out of that side's hash, so
{"flag" => nil}(a key holding nil) and{}(no key) stay tellable apart in the database — which a revert needs and a string sentinel would only fake. Object.new.freeze
Class Method Summary collapse
-
.changed_values(before, patch, columns) ⇒ Hash{Symbol=>Hash}
Build the previous/new value hashes for the writable columns that actually changed.
-
.diff(before, after) ⇒ Hash{Symbol=>Object}?
{ previous:, next: } of the changed leaves, or nil when equal.
-
.object_array?(value) ⇒ Boolean
Arrays whose every element is a hash (record-like collections, e.g. a workflow history).
-
.revert(current, previous, changed) ⇒ Object
Undo one recorded change: given the value as it stands now and the two sides of the diff that produced it, return the value as it was before.
Class Method Details
.changed_values(before, patch, columns) ⇒ Hash{Symbol=>Hash}
Build the previous/new value hashes for the writable columns that actually changed.
35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/forest_admin_agent/audit_trail/diff.rb', line 35 def changed_values(before, patch, columns) previous_values = {} new_values = {} columns.each do |column| delta = patch.key?(column) ? diff(before[column], patch[column]) : nil next unless delta previous_values[column] = delta[:previous] new_values[column] = delta[:next] end { previous_values: previous_values, new_values: new_values } end |
.diff(before, after) ⇒ Hash{Symbol=>Object}?
Returns { previous:, next: } of the changed leaves, or nil when equal.
19 20 21 22 23 24 25 26 27 |
# File 'lib/forest_admin_agent/audit_trail/diff.rb', line 19 def diff(before, after) return nil if before == after return diff_hashes(before, after) if before.is_a?(Hash) && after.is_a?(Hash) return diff_object_arrays(before, after) if object_array?(before) && object_array?(after) { previous: before.nil? ? nil : before, next: after.nil? ? nil : after } end |
.object_array?(value) ⇒ Boolean
Arrays whose every element is a hash (record-like collections, e.g. a workflow history).
51 52 53 |
# File 'lib/forest_admin_agent/audit_trail/diff.rb', line 51 def object_array?(value) value.is_a?(Array) && !value.empty? && value.all?(Hash) end |
.revert(current, previous, changed) ⇒ Object
Undo one recorded change: given the value as it stands now and the two sides of the diff that
produced it, return the value as it was before. Nested hashes are walked so untouched keys keep
their current value; a key missing from previous was added by the change, so it goes away.
105 106 107 108 109 110 |
# File 'lib/forest_admin_agent/audit_trail/diff.rb', line 105 def revert(current, previous, changed) return revert_array(current, previous, changed) if current.is_a?(Array) && partial?(previous, changed) return revert_hash(current, previous, changed) if current.is_a?(Hash) && partial?(previous, changed) previous end |