Module: Lutaml::Model::ComparableModel::StructuralDiff

Defined in:
lib/lutaml/model/comparable_model.rb

Overview

Structured, path-addressed diff between two model trees (lutaml-model#18). Used by ComparableModel#diff.

Defined Under Namespace

Classes: Entry

Class Method Summary collapse

Class Method Details

.comparable_pair?(left, right) ⇒ Boolean

Returns:



219
220
221
222
# File 'lib/lutaml/model/comparable_model.rb', line 219

def comparable_pair?(left, right)
  left.is_a?(ComparableModel) && right.is_a?(ComparableModel) &&
    left.instance_of?(right.class)
end

.diff(left, right, path = []) ⇒ Object



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/lutaml/model/comparable_model.rb', line 181

def diff(left, right, path = [])
  return [] if left.equal?(right)
  return [Entry.new(path_string(path), left, right)] unless comparable_pair?(left, right)

  entries = []
  left.class.attributes.each_key do |attr|
    lval = left.public_send(attr)
    rval = right.public_send(attr)
    child_path = path + [attr]

    if comparable_pair?(lval, rval)
      entries.concat(diff(lval, rval, child_path))
    elsif lval.is_a?(::Array) && rval.is_a?(::Array)
      entries.concat(diff_collections(lval, rval, child_path))
    elsif differing?(lval, rval)
      entries << Entry.new(path_string(child_path), lval, rval)
    end
  end
  entries
end

.diff_collections(lval, rval, path) ⇒ Object



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/lutaml/model/comparable_model.rb', line 202

def diff_collections(lval, rval, path)
  entries = []
  max = [lval.size, rval.size].max
  max.times do |i|
    li = lval[i]
    ri = rval[i]
    child_path = path + [i]

    if comparable_pair?(li, ri)
      entries.concat(diff(li, ri, child_path))
    elsif differing?(li, ri)
      entries << Entry.new(path_string(child_path), li, ri)
    end
  end
  entries
end

.differing?(lval, rval) ⇒ Boolean

Returns:



224
225
226
227
228
229
230
231
# File 'lib/lutaml/model/comparable_model.rb', line 224

def differing?(lval, rval)
  if lval.is_a?(::Array) && rval.is_a?(::Array)
    lval.size != rval.size ||
      lval.zip(rval).any? { |l, r| l != r }
  else
    lval != rval
  end
end

.path_string(path) ⇒ Object



233
234
235
236
237
# File 'lib/lutaml/model/comparable_model.rb', line 233

def path_string(path)
  path.reduce("") do |acc, seg|
    seg.is_a?(::Integer) ? "#{acc}[#{seg}]" : "#{acc}.#{seg}"
  end.delete_prefix(".")
end