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.4.0'
Class Method Summary collapse
-
.apply(target, operations) ⇒ Hash, Array
Apply an RFC 6902 JSON Patch to a target document.
-
.compact(operations) ⇒ Array<Hash>
Remove redundant operations from a patch.
-
.diff(source, target) ⇒ Array<Hash>
Generate RFC 6902 patch operations that transform source into target.
-
.invert(target, operations) ⇒ Array<Hash>
Generate reverse operations that undo a given patch.
-
.merge_diff(source, target) ⇒ Hash
Generate an RFC 7396 merge patch that transforms source into target.
-
.merge_patch(target, patch) ⇒ Hash
Apply an RFC 7396 merge patch to a target document.
-
.paths(operations) ⇒ Array<String>
List the distinct document paths touched by a patch sequence.
-
.read(doc, path, default: nil) ⇒ Object
Read a value from a document via RFC 6901 JSON Pointer.
-
.validate(target, operations) ⇒ Hash
Validate patch operations without modifying the target.
-
.write(doc, path, value) ⇒ Hash, Array
Write a value into a document at an RFC 6901 JSON Pointer.
Class Method Details
.apply(target, operations) ⇒ Hash, Array
Apply an RFC 6902 JSON Patch to a target document
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
198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/philiprehberger/json_merge.rb', line 198 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
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
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
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
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.
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 |
.read(doc, path, default: nil) ⇒ Object
Read a value from a document via RFC 6901 JSON Pointer.
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/philiprehberger/json_merge.rb', line 107 def self.read(doc, path, default: nil) tokens = parse_pointer(path) tokens.reduce(doc) do |obj, token| case obj when Hash return default unless obj.key?(token) obj[token] when Array return default unless token.match?(/\A\d+\z/) idx = token.to_i return default if idx >= obj.length obj[idx] else return default end end end |
.validate(target, operations) ⇒ Hash
Validate patch operations without modifying the target
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 |
.write(doc, path, value) ⇒ Hash, Array
Write a value into a document at an RFC 6901 JSON Pointer.
Intermediate hash containers are created as needed; the supplied document is mutated and returned. Use ‘deep_clone` first if you need to preserve the original.
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/philiprehberger/json_merge.rb', line 139 def self.write(doc, path, value) tokens = parse_pointer(path) return value if tokens.empty? parent = tokens[0..-2].each_with_index.reduce(doc) do |obj, (token, _)| descend_for_write(obj, token) end last = tokens.last case parent when Hash parent[last] = value when Array raise Error, "Invalid array index: '#{last}'" unless last.match?(/\A\d+\z|\A-\z/) last == '-' ? parent.push(value) : parent[last.to_i] = value else raise Error, "Cannot write into #{parent.class}" end doc end |