json-patch 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +18 -0
- data/.gitmodules +3 -0
- data/.travis.yml +9 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +82 -0
- data/Rakefile +11 -0
- data/json-patch.gemspec +23 -0
- data/lib/json/patch.rb +187 -0
- data/lib/json/patch/railtie.rb +14 -0
- data/lib/json/patch/version.rb +5 -0
- data/test/ietf_spec_test.rb +77 -0
- data/test/ietf_test.rb +71 -0
- data/test/json-patch_test.rb +355 -0
- data/test/ruby_ietf_spec_test.rb +0 -0
- data/test/test_helper.rb +11 -0
- metadata +100 -0
data/.gitignore
ADDED
data/.gitmodules
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2013 Guille Carlos
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# JSON::Patch (Version 1 Coming Soon)
|
|
2
|
+
[](https://travis-ci.org/guillec/json-patch)
|
|
3
|
+
[](https://codeclimate.com/github/guillec/json-patch)
|
|
4
|
+
[](https://coveralls.io/r/guillec/json-patch)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
This gem augments Ruby's built-in JSON library to support JSON Patch
|
|
8
|
+
(identified by the json-patch+json media type). http://tools.ietf.org/html/rfc6902
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
Add this line to your application's Gemfile:
|
|
13
|
+
|
|
14
|
+
gem 'json-patch'
|
|
15
|
+
|
|
16
|
+
And then execute:
|
|
17
|
+
|
|
18
|
+
$ bundle
|
|
19
|
+
|
|
20
|
+
Or install it yourself as:
|
|
21
|
+
|
|
22
|
+
$ gem install json-patch
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
Then, use it:
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
# The example from http://tools.ietf.org/html/rfc6902#appendix-A
|
|
30
|
+
|
|
31
|
+
# Add Object Member
|
|
32
|
+
target_document = <<-JSON
|
|
33
|
+
{ "foo": "bar"}
|
|
34
|
+
JSON
|
|
35
|
+
|
|
36
|
+
operations_document = <<-JSON
|
|
37
|
+
[
|
|
38
|
+
{ "op": "add", "path": "/baz", "value": "qux" }
|
|
39
|
+
]
|
|
40
|
+
JSON
|
|
41
|
+
|
|
42
|
+
JSON.patch(target_document, operations_document)
|
|
43
|
+
# =>
|
|
44
|
+
{ "baz": "qux", "foo": "bar" }
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# Add Array Element
|
|
48
|
+
target_document = <<-JSON
|
|
49
|
+
{ "foo": [ "bar", "baz" ] }
|
|
50
|
+
JSON
|
|
51
|
+
|
|
52
|
+
operations_document = <<-JSON
|
|
53
|
+
[
|
|
54
|
+
{ "op": "add", "path": "/foo/1", "value": "qux" }
|
|
55
|
+
]
|
|
56
|
+
JSON
|
|
57
|
+
|
|
58
|
+
JSON.patch(target_document, operations_document)
|
|
59
|
+
# =>
|
|
60
|
+
{ "foo": [ "bar", "qux", "baz" ] }
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
If you'd prefer to operate on pure Ruby objects rather than JSON
|
|
64
|
+
strings, you can construct a JSON::Patch object instead.
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
target_document = { "foo" => [ "bar", "baz" ] }
|
|
68
|
+
operations_document = [{ "op" => "add", "path" => "/foo/1", "value" => "qux" }]
|
|
69
|
+
|
|
70
|
+
JSON::Patch.new(target_document, operations_document).call
|
|
71
|
+
# =>
|
|
72
|
+
{ "foo" => [ "bar", "qux", "baz" ] }
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
## Contributing
|
|
77
|
+
|
|
78
|
+
1. Fork it
|
|
79
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
80
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
81
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
82
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/json-patch.gemspec
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'json/patch/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "json-patch"
|
|
8
|
+
spec.version = Json::Patch::VERSION
|
|
9
|
+
spec.authors = ["Guille Carlos"]
|
|
10
|
+
spec.email = ["guille@bitpop.in"]
|
|
11
|
+
spec.description = %q{An implementation of RFC 6902: JSON Patch.}
|
|
12
|
+
spec.summary = %q{An implementation of RFC 6902: JSON Patch.}
|
|
13
|
+
spec.homepage = "https://github.com/guillec/json-patch"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files`.split($/)
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
|
22
|
+
spec.add_development_dependency "rake"
|
|
23
|
+
end
|
data/lib/json/patch.rb
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'json/patch/railtie' if defined?(Rails)
|
|
3
|
+
|
|
4
|
+
module JSON
|
|
5
|
+
PatchError = Class.new(StandardError)
|
|
6
|
+
PatchOutOfBoundException = Class.new(StandardError)
|
|
7
|
+
PatchObjectOperationOnArrayException = Class.new(StandardError)
|
|
8
|
+
|
|
9
|
+
def self.patch(target_doc, operations_doc)
|
|
10
|
+
target_doc = JSON.parse(target_doc)
|
|
11
|
+
operations_doc = JSON.parse(operations_doc)
|
|
12
|
+
result_doc = JSON::Patch.new(target_doc, operations_doc).call
|
|
13
|
+
JSON.dump(result_doc)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class Patch
|
|
17
|
+
|
|
18
|
+
def initialize(target_doc, operations_doc)
|
|
19
|
+
@target_doc = target_doc
|
|
20
|
+
@operations_doc = operations_doc
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def call
|
|
24
|
+
return @target_doc if @operations_doc.empty?
|
|
25
|
+
@operations_doc.each do |operation|
|
|
26
|
+
operation = operation.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
|
27
|
+
if allowed?(operation)
|
|
28
|
+
@target_doc = send(operation[:op].to_sym, @target_doc, operation)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
return @target_doc
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
def allowed?(operation)
|
|
36
|
+
operation.fetch(:op) { raise JSON::PatchError }
|
|
37
|
+
raise JSON::PatchError unless ["add","remove","replace","move","copy","test"].include?(operation[:op])
|
|
38
|
+
operation.fetch(:path) { raise JSON::PatchError }
|
|
39
|
+
true
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def add(target_doc, operation_doc)
|
|
43
|
+
path = operation_doc[:path]
|
|
44
|
+
value = operation_doc.fetch(:value) { raise JSON::PatchError }
|
|
45
|
+
|
|
46
|
+
add_operation(target_doc, path, value)
|
|
47
|
+
target_doc
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def remove(target_doc, operation_doc)
|
|
51
|
+
path = operation_doc.fetch(:path) { raise JSON::PatchError }
|
|
52
|
+
|
|
53
|
+
remove_operation(target_doc, path)
|
|
54
|
+
target_doc
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def replace(target_doc, operation_doc)
|
|
58
|
+
remove(target_doc, operation_doc)
|
|
59
|
+
add(target_doc, operation_doc)
|
|
60
|
+
target_doc
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def move(target_doc, operation_doc)
|
|
64
|
+
src = operation_doc.fetch(:from) { raise JSON::PatchError }
|
|
65
|
+
dest = operation_doc[:path]
|
|
66
|
+
value = remove_operation(target_doc, src)
|
|
67
|
+
|
|
68
|
+
add_operation(target_doc, dest, value)
|
|
69
|
+
target_doc
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def copy(target_doc, operation_doc)
|
|
73
|
+
src = operation_doc.fetch(:from) { raise JSON::PatchError }
|
|
74
|
+
dest = operation_doc[:path]
|
|
75
|
+
value = find_value(target_doc, operation_doc, src)
|
|
76
|
+
|
|
77
|
+
add_operation(target_doc, dest, value)
|
|
78
|
+
target_doc
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def test(target_doc, operation_doc)
|
|
82
|
+
path = operation_doc[:path]
|
|
83
|
+
value = find_value(target_doc, operation_doc, path)
|
|
84
|
+
test_value = operation_doc.fetch(:value) { raise JSON::PatchError }
|
|
85
|
+
|
|
86
|
+
raise JSON::PatchError if value != test_value
|
|
87
|
+
target_doc if value == test_value
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def add_operation(target_doc, path, value)
|
|
91
|
+
path_array = split_path(path)
|
|
92
|
+
ref_token = path_array.pop
|
|
93
|
+
target_item = build_target_array(path_array, target_doc)
|
|
94
|
+
|
|
95
|
+
add_array(target_doc, path_array, target_item, ref_token, value) if target_item.kind_of? Array
|
|
96
|
+
add_object(target_doc, target_item, ref_token, value) unless target_item.kind_of? Array
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def add_object(target_doc, target_item, ref_token, value)
|
|
100
|
+
raise JSON::PatchError if target_item.nil?
|
|
101
|
+
if ref_token.nil?
|
|
102
|
+
target_doc.replace(value)
|
|
103
|
+
else
|
|
104
|
+
target_item[ref_token] = value
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def add_array(doc, path_array, target_item, ref_token, value)
|
|
109
|
+
return unless valid_index?(target_item, ref_token)
|
|
110
|
+
if ref_token == "-"
|
|
111
|
+
new_array = target_item << value
|
|
112
|
+
else
|
|
113
|
+
new_array = target_item.insert ref_token.to_i, value
|
|
114
|
+
end
|
|
115
|
+
add_to_target_document(doc, path_array, target_item, new_array)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def valid_index?(item_array, index)
|
|
119
|
+
raise JSON::PatchObjectOperationOnArrayException unless index =~ /\A-?\d+\Z/ || index == "-"
|
|
120
|
+
index = index == "-" ? item_array.length : index.to_i
|
|
121
|
+
raise JSON::PatchOutOfBoundException if index.to_i > item_array.length || index.to_i < 0
|
|
122
|
+
true
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def remove_operation(target_doc, path)
|
|
126
|
+
path_array = split_path(path)
|
|
127
|
+
ref_token = path_array.pop
|
|
128
|
+
target_item = build_target_array(path_array, target_doc)
|
|
129
|
+
raise JSON::PatchObjectOperationOnArrayException if target_item.nil?
|
|
130
|
+
|
|
131
|
+
if Array === target_item
|
|
132
|
+
target_item.delete_at ref_token.to_i if valid_index?(target_item, ref_token)
|
|
133
|
+
else
|
|
134
|
+
target_item.delete ref_token
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def find_value(target_doc, operation_doc, path)
|
|
139
|
+
path_array = split_path(path)
|
|
140
|
+
ref_token = path_array.pop
|
|
141
|
+
target_item = build_target_array(path_array, target_doc)
|
|
142
|
+
if Array === target_item
|
|
143
|
+
if is_a_number?(ref_token)
|
|
144
|
+
target_item.at ref_token.to_i
|
|
145
|
+
else
|
|
146
|
+
raise JSON::PatchObjectOperationOnArrayException
|
|
147
|
+
end
|
|
148
|
+
else
|
|
149
|
+
target_item[ref_token]
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def is_a_number?(s)
|
|
154
|
+
s.to_s.match(/\A[+-]?\d+?(\.\d+)?\Z/) == nil ? false : true
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def build_target_array(path_array, target_doc)
|
|
158
|
+
path_array.inject(target_doc) do |doc, item|
|
|
159
|
+
key = (doc.kind_of?(Array) ? item.to_i : item)
|
|
160
|
+
if doc.kind_of?(Array)
|
|
161
|
+
doc[key]
|
|
162
|
+
else
|
|
163
|
+
doc.has_key?(key) ? doc[key] : doc[key.to_sym] unless doc.kind_of?(Array)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def add_to_target_document(doc, path, target_item, array)
|
|
169
|
+
path.inject(doc) do |obj, part|
|
|
170
|
+
key = (Array === doc ? part.to_i : part)
|
|
171
|
+
doc[key]
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def split_path(path)
|
|
176
|
+
escape_characters = {'^/' => '/', '^^' => '^', '~0' => '~', '~1' => '/'}
|
|
177
|
+
if path == '/'
|
|
178
|
+
['']
|
|
179
|
+
else
|
|
180
|
+
path.sub(/^\//, '').split(/(?<!\^)\//).map! { |part|
|
|
181
|
+
part.gsub!(/\^[\/^]|~[01]/) { |m| escape_characters[m] }
|
|
182
|
+
part
|
|
183
|
+
}
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'rails'
|
|
2
|
+
|
|
3
|
+
module JSON
|
|
4
|
+
class Patch
|
|
5
|
+
# This class registers our gem with Rails.
|
|
6
|
+
class Railtie < ::Rails::Railtie
|
|
7
|
+
# When the application loads, this will cause Rails to know
|
|
8
|
+
# # how to serve up the proper type.
|
|
9
|
+
initializer 'json-patch' do
|
|
10
|
+
Mime::Type.register 'application/json-patch+json', :json_patch
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
require 'json/patch'
|
|
3
|
+
|
|
4
|
+
describe "IETF JSON Patch Test" do
|
|
5
|
+
|
|
6
|
+
TESTDIR = File.dirname File.expand_path __FILE__
|
|
7
|
+
spec_json = File.read File.join TESTDIR, 'json-patch-tests', 'spec_tests.json'
|
|
8
|
+
specs = JSON.load spec_json
|
|
9
|
+
|
|
10
|
+
describe "JSON.patch" do
|
|
11
|
+
specs.each_with_index do |spec, index|
|
|
12
|
+
next unless spec['doc']
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
#This test I am skipping
|
|
16
|
+
#Because it test if a operations object has two op members
|
|
17
|
+
#Since the first step in the process is to convert to hash
|
|
18
|
+
#it gets rid of the similar keys.
|
|
19
|
+
next if spec['comment'] == 'A.13 Invalid JSON Patch Document'
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
comment = spec['comment']
|
|
23
|
+
unless spec['disabled']
|
|
24
|
+
|
|
25
|
+
describe "A JSON String " do
|
|
26
|
+
it "#{comment || spec['error'] || index}" do
|
|
27
|
+
|
|
28
|
+
target_doc = JSON.dump(spec['doc']) if spec['doc']
|
|
29
|
+
operations_doc = JSON.dump(spec['patch']) if spec['patch']
|
|
30
|
+
expected_doc = JSON.dump(spec['expected']) if spec['expected']
|
|
31
|
+
|
|
32
|
+
if spec['error']
|
|
33
|
+
assert_raises(ex(spec['error'])) do
|
|
34
|
+
JSON.patch(target_doc, operations_doc)
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
result_doc = JSON.patch(target_doc, operations_doc)
|
|
38
|
+
assert_equal JSON.parse(expected_doc || target_doc), JSON.parse(result_doc)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe "JSON::Patch.new" do
|
|
44
|
+
it "#{comment || spec['error'] || index}" do
|
|
45
|
+
|
|
46
|
+
target_doc = eval(spec['doc'].to_s) if spec['doc']
|
|
47
|
+
operations_doc = eval(spec['patch'].to_s) if spec['patch']
|
|
48
|
+
expected_doc = eval(spec['expected'].to_s) if spec['expected']
|
|
49
|
+
|
|
50
|
+
if spec['error']
|
|
51
|
+
assert_raises(ex(spec['error'])) do
|
|
52
|
+
JSON::Patch.new(target_doc, operations_doc).call
|
|
53
|
+
end
|
|
54
|
+
else
|
|
55
|
+
result_doc = JSON::Patch.new(target_doc, operations_doc).call
|
|
56
|
+
assert_equal (expected_doc || target_doc), result_doc
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
def ex msg
|
|
67
|
+
case msg
|
|
68
|
+
when /Out of bounds/i then
|
|
69
|
+
JSON::PatchOutOfBoundException
|
|
70
|
+
when /Object operation on array target/ then
|
|
71
|
+
JSON::PatchObjectOperationOnArrayException
|
|
72
|
+
else
|
|
73
|
+
JSON::PatchError
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
data/test/ietf_test.rb
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
require 'json/patch'
|
|
3
|
+
|
|
4
|
+
describe "IETF JSON Patch Test" do
|
|
5
|
+
|
|
6
|
+
TESTDIR = File.dirname File.expand_path __FILE__
|
|
7
|
+
spec_json = File.read File.join TESTDIR, 'json-patch-tests', 'tests.json'
|
|
8
|
+
specs = JSON.load spec_json
|
|
9
|
+
|
|
10
|
+
describe "Test JSON File" do
|
|
11
|
+
specs.each_with_index do |spec, index|
|
|
12
|
+
next unless spec['doc']
|
|
13
|
+
comment = spec['comment']
|
|
14
|
+
unless spec['disabled']
|
|
15
|
+
|
|
16
|
+
describe "JSON.patch" do
|
|
17
|
+
it "#{comment || spec['error'] || index}" do
|
|
18
|
+
|
|
19
|
+
target_doc = JSON.dump(spec['doc']) if spec['doc']
|
|
20
|
+
operations_doc = JSON.dump(spec['patch']) if spec['patch']
|
|
21
|
+
expected_doc = JSON.dump(spec['expected']) if spec['expected']
|
|
22
|
+
|
|
23
|
+
if spec['error']
|
|
24
|
+
assert_raises(ex(spec['error'])) do
|
|
25
|
+
JSON.patch(target_doc, operations_doc)
|
|
26
|
+
end
|
|
27
|
+
else
|
|
28
|
+
result_doc = JSON.patch(target_doc, operations_doc)
|
|
29
|
+
assert_equal JSON.parse(expected_doc || target_doc), JSON.parse(result_doc)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe "JSON::Patch.new" do
|
|
35
|
+
it "#{comment || spec['error'] || index}" do
|
|
36
|
+
|
|
37
|
+
target_doc = spec['doc'] if spec['doc']
|
|
38
|
+
operations_doc = spec['patch'] if spec['patch']
|
|
39
|
+
expected_doc = spec['expected'] if spec['expected']
|
|
40
|
+
|
|
41
|
+
if spec['error']
|
|
42
|
+
assert_raises(ex(spec['error'])) do
|
|
43
|
+
JSON::Patch.new(target_doc, operations_doc).call
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
result_doc = JSON::Patch.new(target_doc, operations_doc).call
|
|
47
|
+
assert_equal (expected_doc || target_doc), result_doc
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
def ex msg
|
|
57
|
+
case msg
|
|
58
|
+
when /Out of bounds/i then
|
|
59
|
+
JSON::PatchOutOfBoundException
|
|
60
|
+
when /Object operation on array target/i then
|
|
61
|
+
JSON::PatchObjectOperationOnArrayException
|
|
62
|
+
when /with bad number/i then
|
|
63
|
+
JSON::PatchObjectOperationOnArrayException
|
|
64
|
+
when /get array element 1/ then
|
|
65
|
+
JSON::PatchObjectOperationOnArrayException
|
|
66
|
+
else
|
|
67
|
+
JSON::PatchError
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
require 'json/patch'
|
|
3
|
+
|
|
4
|
+
describe "Section 4: Operation objects" do
|
|
5
|
+
|
|
6
|
+
describe "Operation objects MUST have at least one 'op' member" do
|
|
7
|
+
let(:target_document) { %q'{}' }
|
|
8
|
+
let(:operation_document) { %q'[{"path":"/a/b/c"}]' }
|
|
9
|
+
|
|
10
|
+
it "will raise exception when no 'op' member exist" do
|
|
11
|
+
assert_raises(JSON::PatchError) do
|
|
12
|
+
JSON.patch(target_document, operation_document)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe "Operation objects 'op' member MUST be one of the correct values" do
|
|
18
|
+
let(:target_document) { %q'{ "foo":["bar","baz"] }' }
|
|
19
|
+
let(:add_operation_document) { %q'[{"op":"add","path":"/foo/1","value":"qux"}]' }
|
|
20
|
+
let(:remove_operation_document) { %q'[{ "op": "remove", "path": "/baz" }]' }
|
|
21
|
+
let(:replace_operation_document) { %q'[{"op":"replace","path":"/foo/1","value":"qux"}]' }
|
|
22
|
+
let(:move_operation_document) { %q'[{"op":"replace","from":"foo","path":"/foo/1","value":"qux"}]' }
|
|
23
|
+
let(:copy_operation_document) { %q'[{"op":"replace","from":"foo","path":"/foo/1","value":"qux"}]' }
|
|
24
|
+
let(:test_operation_document) { %q'[{"op":"test", "path":"/foo/1","value":"baz"}]' }
|
|
25
|
+
let(:error_operation_document) { %q'[{"op": "hammer time"}]' }
|
|
26
|
+
|
|
27
|
+
it "can contain a 'add' value" do
|
|
28
|
+
assert JSON.patch(target_document, add_operation_document)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "can contain a 'remove' value" do
|
|
32
|
+
assert JSON.patch(target_document, remove_operation_document)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "can contain a 'replace' value" do
|
|
36
|
+
assert JSON.patch(target_document, replace_operation_document)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "can contain a 'move' value" do
|
|
40
|
+
assert JSON.patch(target_document, move_operation_document)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "can contain a 'copy' value" do
|
|
44
|
+
assert JSON.patch(target_document, copy_operation_document)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "can contain a 'test' value" do
|
|
48
|
+
assert JSON.patch(target_document, test_operation_document)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "will raise exception when 'op' member contains invalid 'hammer time' value" do
|
|
52
|
+
assert_raises(JSON::PatchError) do
|
|
53
|
+
JSON.patch(target_document, error_operation_document)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe "Operation objects MUST have at least one path member" do
|
|
59
|
+
let(:target_document) { %q'{ "foo":["bar","baz"] }' }
|
|
60
|
+
let(:operation_document) { %q'[{"op":"add", "value":"qux"}]' }
|
|
61
|
+
|
|
62
|
+
it "will raise exception when no 'path' member exist" do
|
|
63
|
+
assert_raises(JSON::PatchError) do
|
|
64
|
+
JSON.patch(target_document, operation_document)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
describe "Operation members not define by the action MUST be ignored" do
|
|
70
|
+
let(:target_document) { %q'{ "foo":["bar","baz"] }' }
|
|
71
|
+
let(:operation_document) { %q'[{"op":"add","path":"/foo/1","value":"qux", "ignore":"This please"}]' }
|
|
72
|
+
|
|
73
|
+
it "ignores the 'ignore' member of the add operation_document" do
|
|
74
|
+
expected = %q'{"foo":["bar","qux","baz"]}'
|
|
75
|
+
assert_equal expected, JSON.patch(target_document, operation_document)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
describe "Section 4.1: The add operation" do
|
|
82
|
+
|
|
83
|
+
describe "If the target location specifies an array index" do
|
|
84
|
+
let(:target_document) { %q'{ "foo":["bar","baz"] }' }
|
|
85
|
+
let(:operation_document) { %q'[{"op":"add","path":"/foo/1","value":"qux"}]' }
|
|
86
|
+
|
|
87
|
+
it "inserts the value into the array at specified index" do
|
|
88
|
+
expected = %q'{"foo":["bar","qux","baz"]}'
|
|
89
|
+
assert_equal expected, JSON.patch(target_document, operation_document)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe "If the target location species a object member that does not exist" do
|
|
94
|
+
let(:target_document) { %q'{"foo":"bar"}' }
|
|
95
|
+
let(:operation_document) { %q'[{ "op": "add", "path": "/baz", "value": "qux" }]' }
|
|
96
|
+
|
|
97
|
+
it "it will add the object to the target_document" do
|
|
98
|
+
expected = %q'{"foo":"bar","baz":"qux"}'
|
|
99
|
+
assert_equal expected, JSON.patch(target_document, operation_document)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
describe "If the target location species a member that does exist" do
|
|
104
|
+
let(:target_document) { %q'{"foo":"bar","baz":"wat"}' }
|
|
105
|
+
let(:operation_document) { %q'[{ "op": "add", "path": "/baz", "value": "qux" }]' }
|
|
106
|
+
|
|
107
|
+
it "it replaces the value" do
|
|
108
|
+
expected = %q'{"foo":"bar","baz":"qux"}'
|
|
109
|
+
assert_equal expected, JSON.patch(target_document, operation_document)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
describe "The add operation MUST contina a 'value' member" do
|
|
114
|
+
let(:target_document) { %q'{"foo":"bar","baz":"wat"}' }
|
|
115
|
+
let(:operation_document) { %q'[{ "op": "add", "path": "/baz" }]' }
|
|
116
|
+
|
|
117
|
+
it "will raise exception if no 'value' member" do
|
|
118
|
+
assert_raises(JSON::PatchError) do
|
|
119
|
+
JSON.patch(target_document, operation_document)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
=begin
|
|
125
|
+
TODO
|
|
126
|
+
When the operation is applied, the target location MUST reference one of:
|
|
127
|
+
|
|
128
|
+
1. The root of the target document - whereupon the specified value
|
|
129
|
+
becomes the entire content of the target document.
|
|
130
|
+
|
|
131
|
+
2. A member to add to an existing object - whereupon the supplied
|
|
132
|
+
value is added to that object at the indicated location. If the
|
|
133
|
+
member already exists, it is replaced by the specified value.
|
|
134
|
+
|
|
135
|
+
3. An element to add to an existing array - whereupon the supplied
|
|
136
|
+
value is added to the array at the indicated location. Any
|
|
137
|
+
elements at or above the specified index are shifted one position
|
|
138
|
+
to the right. The specified index MUST NOT be greater than the
|
|
139
|
+
number of elements in the array. If the "-" character is used to
|
|
140
|
+
index the end of the array (see [RFC6901]), this has the effect of
|
|
141
|
+
appending the value to the array.
|
|
142
|
+
=end
|
|
143
|
+
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
describe "Section 4.2: The remove operation" do
|
|
147
|
+
|
|
148
|
+
describe "Removing a object member" do
|
|
149
|
+
let(:target_document) { %q'{"foo":"bar","baz":"qux"}' }
|
|
150
|
+
let(:operation_document) { %q'[{ "op": "remove", "path": "/baz" }]' }
|
|
151
|
+
|
|
152
|
+
it "will remove memeber of object at the target location" do
|
|
153
|
+
expected = %q'{"foo":"bar"}'
|
|
154
|
+
assert_equal expected, JSON.patch(target_document, operation_document)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
describe "Removing a array element" do
|
|
159
|
+
let(:target_document) { %q'{"foo":["bar","qux","baz"]}' }
|
|
160
|
+
let(:operation_document) { %q'[{ "op": "remove", "path": "/foo/1" }]' }
|
|
161
|
+
|
|
162
|
+
it "will remove object in array at the target location" do
|
|
163
|
+
expected = %q'{"foo":["bar","baz"]}'
|
|
164
|
+
assert_equal expected, JSON.patch(target_document, operation_document)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
describe "Target location MUST exist for the remove operation" do
|
|
169
|
+
let(:target_document) { %q'{"foo":["bar","qux","baz"]}' }
|
|
170
|
+
let(:operation_document) { %q'[{ "op": "remove"}]' }
|
|
171
|
+
|
|
172
|
+
it "will raise an exception if no target is specified" do
|
|
173
|
+
assert_raises(JSON::PatchError) do
|
|
174
|
+
JSON.patch(target_document, operation_document)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
describe "Section 4.3: The replace operation" do
|
|
182
|
+
|
|
183
|
+
describe "Replacing a value" do
|
|
184
|
+
let(:target_document) { %q'{"foo":"bar","baz":"qux"}' }
|
|
185
|
+
let(:operation_document) { %q'[{ "op": "replace", "path": "/baz", "value": "boo" }]' }
|
|
186
|
+
|
|
187
|
+
it "will replace old value with a new value at target location" do
|
|
188
|
+
expected = %q'{"foo":"bar","baz":"boo"}'
|
|
189
|
+
assert_equal expected, JSON.patch(target_document, operation_document)
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
describe "The replace operation document MUST contain a 'value' member" do
|
|
194
|
+
let(:target_document) { %q'{"foo":"bar","baz":"qux"}' }
|
|
195
|
+
let(:operation_document) { %q'[{ "op": "replace", "path": "/baz" }]' }
|
|
196
|
+
|
|
197
|
+
it "will raise an exception if no 'value' is specified" do
|
|
198
|
+
assert_raises(JSON::PatchError) do
|
|
199
|
+
JSON.patch(target_document, operation_document)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
describe "The replace operation MUST have a target location" do
|
|
205
|
+
let(:target_document) { %q'{"foo":"bar","baz":"qux"}' }
|
|
206
|
+
let(:operation_document) { %q'[{ "op": "replace", "value": "boo" }]' }
|
|
207
|
+
|
|
208
|
+
it "will raise an exception if no target is specified" do
|
|
209
|
+
assert_raises(JSON::PatchError) do
|
|
210
|
+
JSON.patch(target_document, operation_document)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
describe "Section 4.4: The move operation" do
|
|
217
|
+
|
|
218
|
+
describe "The move operation" do
|
|
219
|
+
let(:target_document) { %q'{"foo":{"bar":"baz","waldo":"fred"},"qux":{"corge":"grault"}}' }
|
|
220
|
+
let(:operation_document) { %q'[{ "op": "move", "from":"/foo/waldo", "path": "/qux/thud" }]' }
|
|
221
|
+
|
|
222
|
+
it "will remove the value at a specified location and add it to the target location" do
|
|
223
|
+
expected = %q'{"foo":{"bar":"baz"},"qux":{"corge":"grault","thud":"fred"}}'
|
|
224
|
+
assert_equal expected, JSON.patch(target_document, operation_document)
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
describe "The move operation" do
|
|
229
|
+
let(:target_document) { %q'{"foo":["add","grass","cows","eat"]}' }
|
|
230
|
+
let(:operation_document) { %q'[{ "op": "move", "from":"/foo/1", "path": "/foo/3" }]' }
|
|
231
|
+
|
|
232
|
+
it "will move a array element to new location" do
|
|
233
|
+
expected = %q'{"foo":["add","cows","eat","grass"]}'
|
|
234
|
+
assert_equal expected, JSON.patch(target_document, operation_document)
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
describe "The move operation MUST hav a 'from' memeber" do
|
|
239
|
+
let(:target_document) { %q'{"foo":"bar","baz":"qux"}' }
|
|
240
|
+
let(:operation_document) { %q'[{ "op": "move", "value": "boo" }]' }
|
|
241
|
+
|
|
242
|
+
it "will raise an exception if no from 'from' location is specified" do
|
|
243
|
+
assert_raises(JSON::PatchError) do
|
|
244
|
+
JSON.patch(target_document, operation_document)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
=begin
|
|
250
|
+
TODO The "from" location MUST NOT be a proper prefix of the "path"
|
|
251
|
+
location; i.e., a location cannot be moved into one of its children.
|
|
252
|
+
=end
|
|
253
|
+
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
describe "Section 4.5: The copy operation" do
|
|
257
|
+
|
|
258
|
+
describe "The copy operation" do
|
|
259
|
+
let(:target_document) { %q'{"foo":{"bar":"baz","waldo":"fred"},"qux":{"corge":"grault"}}' }
|
|
260
|
+
let(:operation_document) { %q'[{ "op": "copy", "from":"/foo/waldo", "path": "/qux/waldo" }]' }
|
|
261
|
+
|
|
262
|
+
it "will copy a value from a specified location to the target location" do
|
|
263
|
+
expected = %q'{"foo":{"bar":"baz","waldo":"fred"},"qux":{"corge":"grault","waldo":"fred"}}'
|
|
264
|
+
assert_equal expected, JSON.patch(target_document, operation_document)
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
describe "The copy operation MUST have a 'from' member" do
|
|
269
|
+
let(:target_document) { %q'{"foo":"bar","baz":"qux"}' }
|
|
270
|
+
let(:operation_document) { %q'[{ "op": "copy", "path": "/foo" }]' }
|
|
271
|
+
|
|
272
|
+
it "will raise an exception if no 'from' location is specified" do
|
|
273
|
+
assert_raises(JSON::PatchError) do
|
|
274
|
+
JSON.patch(target_document, operation_document)
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
describe "Section 4.6: The test operation" do
|
|
282
|
+
|
|
283
|
+
#The "test" operation tests that a value at the target location is equal to a specified value.
|
|
284
|
+
|
|
285
|
+
describe "The test operation MUST contain a 'value' member" do
|
|
286
|
+
let(:target_document) { %q'{"baz":"qux","foo":["a",2,"c"]}' }
|
|
287
|
+
let(:operation_document) { %q'[{ "op": "test", "path": "/baz"}, {"op": "test", "path": "/foo/1"}]' }
|
|
288
|
+
|
|
289
|
+
it "will raise a exception if no 'value' is specified" do
|
|
290
|
+
assert_raises(JSON::PatchError) do
|
|
291
|
+
JSON.patch(target_document, operation_document)
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
describe "Testing that strings have the same number of Unicode characters and their code points are byte-to-byte equal" do
|
|
297
|
+
let(:target_document) { %q'{"baz":"qux","foo":["a",2,"c"]}' }
|
|
298
|
+
let(:operation_document) { %q'[{ "op": "test", "path": "/baz", "value": "qux"}]' }
|
|
299
|
+
|
|
300
|
+
it "will return true since the strings are equal" do
|
|
301
|
+
assert JSON.patch(target_document, operation_document)
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
describe "Testing that numbers are equal if their values are numerically equal" do
|
|
306
|
+
let(:target_document) { %q'{"baz": 1,"foo":["a",2,"c"]}' }
|
|
307
|
+
let(:operation_document) { %q'[{ "op": "test", "path": "/baz", "value": 1}]' }
|
|
308
|
+
|
|
309
|
+
it "will return true since the numbers are equal" do
|
|
310
|
+
assert JSON.patch(target_document, operation_document)
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
describe "Testing that arrays are equal if they contain then same number of values and these values are equal" do
|
|
315
|
+
let(:target_document) { %q'{"baz": 1,"foo":["a",2,"c"]}' }
|
|
316
|
+
let(:operation_document) { %q'[{"op": "test", "path": "/foo", "value": ["a",2,"c"]}]' }
|
|
317
|
+
|
|
318
|
+
it "will return true since arrays and values are equal" do
|
|
319
|
+
assert JSON.patch(target_document, operation_document)
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
describe "Testing that objects are equal if they contain then same number of members and each member has same keys and values" do
|
|
324
|
+
let(:target_document) { %q'{"baz": 1,"foo":{"foo": "bar","hammer": "time"}}' }
|
|
325
|
+
let(:operation_document) { %q'[{"op": "test", "path": "/foo", "value": {"foo": "bar", "hammer":"time"}}]' }
|
|
326
|
+
|
|
327
|
+
it "will return true since objects equal" do
|
|
328
|
+
assert JSON.patch(target_document, operation_document)
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
=begin
|
|
333
|
+
TODO
|
|
334
|
+
5 literals (false, true, and null): are considered equal if they are
|
|
335
|
+
the same.
|
|
336
|
+
|
|
337
|
+
Also, note that ordering of the serialization of object members is
|
|
338
|
+
not significant.
|
|
339
|
+
=end
|
|
340
|
+
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
describe "JSON::Patch object" do
|
|
344
|
+
|
|
345
|
+
describe "JSON::Patch.new " do
|
|
346
|
+
let(:target_document) { {"foo" => { "bar" => "baz", "waldo" => "fred" }, "qux" => { "corge" => "grault" } } }
|
|
347
|
+
let(:operation_document) { [{ "op"=> "copy", "from" => "/foo/waldo", "path" => "/qux/waldo" }] }
|
|
348
|
+
|
|
349
|
+
it "can handle plain ruby objects" do
|
|
350
|
+
expected = {"foo"=>{"bar"=>"baz","waldo"=>"fred"},"qux"=>{"corge"=>"grault","waldo"=>"fred"}}
|
|
351
|
+
assert_equal expected, JSON::Patch.new(target_document, operation_document).call
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
end
|
|
File without changes
|
data/test/test_helper.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: json-patch
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Guille Carlos
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2013-05-26 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: bundler
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ~>
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '1.3'
|
|
22
|
+
type: :development
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ~>
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '1.3'
|
|
30
|
+
- !ruby/object:Gem::Dependency
|
|
31
|
+
name: rake
|
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
|
33
|
+
none: false
|
|
34
|
+
requirements:
|
|
35
|
+
- - ! '>='
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: '0'
|
|
38
|
+
type: :development
|
|
39
|
+
prerelease: false
|
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ! '>='
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
description: ! 'An implementation of RFC 6902: JSON Patch.'
|
|
47
|
+
email:
|
|
48
|
+
- guille@bitpop.in
|
|
49
|
+
executables: []
|
|
50
|
+
extensions: []
|
|
51
|
+
extra_rdoc_files: []
|
|
52
|
+
files:
|
|
53
|
+
- .gitignore
|
|
54
|
+
- .gitmodules
|
|
55
|
+
- .travis.yml
|
|
56
|
+
- Gemfile
|
|
57
|
+
- LICENSE.txt
|
|
58
|
+
- README.md
|
|
59
|
+
- Rakefile
|
|
60
|
+
- json-patch.gemspec
|
|
61
|
+
- lib/json/patch.rb
|
|
62
|
+
- lib/json/patch/railtie.rb
|
|
63
|
+
- lib/json/patch/version.rb
|
|
64
|
+
- test/ietf_spec_test.rb
|
|
65
|
+
- test/ietf_test.rb
|
|
66
|
+
- test/json-patch_test.rb
|
|
67
|
+
- test/ruby_ietf_spec_test.rb
|
|
68
|
+
- test/test_helper.rb
|
|
69
|
+
homepage: https://github.com/guillec/json-patch
|
|
70
|
+
licenses:
|
|
71
|
+
- MIT
|
|
72
|
+
post_install_message:
|
|
73
|
+
rdoc_options: []
|
|
74
|
+
require_paths:
|
|
75
|
+
- lib
|
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
77
|
+
none: false
|
|
78
|
+
requirements:
|
|
79
|
+
- - ! '>='
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0'
|
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
|
+
none: false
|
|
84
|
+
requirements:
|
|
85
|
+
- - ! '>='
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '0'
|
|
88
|
+
requirements: []
|
|
89
|
+
rubyforge_project:
|
|
90
|
+
rubygems_version: 1.8.25
|
|
91
|
+
signing_key:
|
|
92
|
+
specification_version: 3
|
|
93
|
+
summary: ! 'An implementation of RFC 6902: JSON Patch.'
|
|
94
|
+
test_files:
|
|
95
|
+
- test/ietf_spec_test.rb
|
|
96
|
+
- test/ietf_test.rb
|
|
97
|
+
- test/json-patch_test.rb
|
|
98
|
+
- test/ruby_ietf_spec_test.rb
|
|
99
|
+
- test/test_helper.rb
|
|
100
|
+
has_rdoc:
|