Module: Philiprehberger::JsonMerge

Defined in:
lib/philiprehberger/json_merge.rb,
lib/philiprehberger/json_merge/diff.rb,
lib/philiprehberger/json_merge/version.rb,
lib/philiprehberger/json_merge/json_patch.rb,
lib/philiprehberger/json_merge/merge_patch.rb

Defined Under Namespace

Modules: Diff, JsonPatch, MergePatch Classes: Error

Constant Summary collapse

VERSION =
'0.3.0'

Class Method Summary collapse

Class Method Details

.apply(target, operations) ⇒ Hash, Array

Apply an RFC 6902 JSON Patch to a target document

Parameters:

  • target (Hash, Array)

    the target document

  • operations (Array<Hash>)

    array of patch operations

Returns:

  • (Hash, Array)

    the patched document

Raises:

  • (Error)

    if an operation fails



27
28
29
# File 'lib/philiprehberger/json_merge.rb', line 27

def self.apply(target, operations)
  JsonPatch.call(target, operations)
end

.compact(operations) ⇒ Array<Hash>

Remove redundant operations from a patch

Parameters:

  • operations (Array<Hash>)

    RFC 6902 patch operations

Returns:

  • (Array<Hash>)

    optimized operations



105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/philiprehberger/json_merge.rb', line 105

def self.compact(operations)
  result = []
  operations.each do |op|
    existing = result.rindex { |r| r['path'] == op['path'] }
    if existing && op['op'] == 'remove'
      result.delete_at(existing)
    elsif existing && %w[replace add].include?(op['op'])
      result[existing] = op
    else
      result << op
    end
  end
  result
end

.diff(source, target) ⇒ Array<Hash>

Generate RFC 6902 patch operations that transform source into target

Parameters:

  • source (Hash, Array)

    the source document

  • target (Hash, Array)

    the target document

Returns:

  • (Array<Hash>)

    array of patch operations



36
37
38
# File 'lib/philiprehberger/json_merge.rb', line 36

def self.diff(source, target)
  Diff.call(source, target)
end

.invert(target, operations) ⇒ Array<Hash>

Generate reverse operations that undo a given patch

Parameters:

  • target (Hash, Array)

    the original document before patch

  • operations (Array<Hash>)

    RFC 6902 patch operations

Returns:

  • (Array<Hash>)

    reverse operations



68
69
70
71
72
73
74
75
76
77
# File 'lib/philiprehberger/json_merge.rb', line 68

def self.invert(target, operations)
  inverse = []
  current = deep_clone(target)
  operations.each do |op|
    inverse_op = build_inverse(current, op)
    inverse.unshift(inverse_op) if inverse_op
    current = apply(current, [op])
  end
  inverse
end

.merge_diff(source, target) ⇒ Hash

Generate an RFC 7396 merge patch that transforms source into target

Parameters:

  • source (Hash)

    the source document

  • target (Hash)

    the target document

Returns:

  • (Hash)

    the merge patch



45
46
47
# File 'lib/philiprehberger/json_merge.rb', line 45

def self.merge_diff(source, target)
  MergePatch.generate(source, target)
end

.merge_patch(target, patch) ⇒ Hash

Apply an RFC 7396 merge patch to a target document

Parameters:

  • target (Hash)

    the target document

  • patch (Hash)

    the merge patch

Returns:

  • (Hash)

    the patched document



17
18
19
# File 'lib/philiprehberger/json_merge.rb', line 17

def self.merge_patch(target, patch)
  MergePatch.call(target, patch)
end

.paths(operations) ⇒ Array<String>

List the distinct document paths touched by a patch sequence

Collects the ‘path` from every operation. For `move` and `copy` operations the `from` pointer is also included. Operations that are missing a `path` (or `from` where applicable) are skipped silently; this method does not validate op shape.

Parameters:

  • operations (Array<Hash>)

    RFC 6902 patch operations

Returns:

  • (Array<String>)

    sorted, deduplicated list of paths



88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/philiprehberger/json_merge.rb', line 88

def self.paths(operations)
  result = []
  operations.each do |op|
    path = op['path'] || op[:path]
    result << path if path
    if %w[move copy].include?(op['op'] || op[:op])
      from = op['from'] || op[:from]
      result << from if from
    end
  end
  result.uniq.sort
end

.validate(target, operations) ⇒ Hash

Validate patch operations without modifying the target

Parameters:

  • target (Hash, Array)

    the document to validate against

  • operations (Array<Hash>)

    RFC 6902 patch operations

Returns:

  • (Hash)

    { valid: Boolean, errors: Array<String> }



54
55
56
57
58
59
60
61
# File 'lib/philiprehberger/json_merge.rb', line 54

def self.validate(target, operations)
  errors = []
  operations.each_with_index do |op, idx|
    error = validate_operation(deep_clone(target), op, idx)
    errors << error if error
  end
  { valid: errors.empty?, errors: errors }
end