hana 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5593991a2078ce9a518e6815df0eae13a5a6b016
4
+ data.tar.gz: b03db1e4ec9245bde4757190b51ec19b5084a932
5
+ SHA512:
6
+ metadata.gz: 9a71e96247335c047ba05d71beb6eab4f65813e667c021b9e6a36769b0564d93a099326a1a06ac474dbf7a645978255ca4d193ada800e32837bcd7bf601edb0c
7
+ data.tar.gz: 86379cd00f01187cd5e504ba301c1041170b36f05b6d1ef53c8025cea539577375edc3b4d6f69d94a6fefa60df1d61417ceccfa4792a882b4fcd6ad5f62718f8
@@ -5,5 +5,8 @@ README.md
5
5
  Rakefile
6
6
  lib/hana.rb
7
7
  test/helper.rb
8
+ test/json-patch-tests/README.md
9
+ test/json-patch-tests/spec_tests.json
10
+ test/json-patch-tests/tests.json
8
11
  test/test_hana.rb
9
- test/test_patch.rb
12
+ test/test_ietf.rb
@@ -1,5 +1,5 @@
1
1
  module Hana
2
- VERSION = '1.0.1'
2
+ VERSION = '1.1.0'
3
3
 
4
4
  class Pointer
5
5
  include Enumerable
@@ -8,23 +8,24 @@ module Hana
8
8
  @path = Pointer.parse path
9
9
  end
10
10
 
11
- def each
12
- @path.each { |x| yield x }
13
- end
14
-
15
- def to_a; @path.dup; end
11
+ def each(&block); @path.each(&block); end
12
+ def to_a; @path.dup; end
16
13
 
17
14
  def eval object
18
15
  Pointer.eval @path, object
19
16
  end
20
17
 
18
+ ESC = {'^/' => '/', '^^' => '^', '~0' => '~', '~1' => '/'} # :nodoc:
19
+
21
20
  def self.eval list, object
22
21
  list.inject(object) { |o, part| o[(Array === o ? part.to_i : part)] }
23
22
  end
24
23
 
25
24
  def self.parse path
26
- path.sub(/^\//, '').split(/(?<!\^)\//).map { |part|
27
- part.gsub(/\^([\/^])/, '\1')
25
+ return [''] if path == '/'
26
+
27
+ path.sub(/^\//, '').split(/(?<!\^)\//).map! { |part|
28
+ part.gsub!(/\^[\/^]|~[01]/) { |m| ESC[m] }; part
28
29
  }
29
30
  end
30
31
  end
@@ -33,73 +34,119 @@ module Hana
33
34
  class Exception < StandardError
34
35
  end
35
36
 
37
+ class FailedTestException < Exception
38
+ attr_accessor :path, :value
39
+
40
+ def initialize path, value
41
+ super "expected #{value} at #{path}"
42
+ @path = path
43
+ @value = value
44
+ end
45
+ end
46
+
47
+ class OutOfBoundsException < Exception
48
+ end
49
+
50
+ class ObjectOperationOnArrayException < Exception
51
+ end
52
+
36
53
  def initialize is
37
54
  @is = is
38
55
  end
39
56
 
40
- VALID = Hash[%w{ add move test replace remove }.map { |x| [x,x]}] # :nodoc:
57
+ VALID = Hash[%w{ add move test replace remove copy }.map { |x| [x,x]}] # :nodoc:
41
58
 
42
59
  def apply doc
43
- @is.each_with_object(doc) { |ins, doc|
44
- send VALID.fetch(ins.keys.sort.first) { |k|
60
+ @is.each_with_object(doc) { |ins, d|
61
+ send VALID.fetch(ins['op'].strip) { |k|
45
62
  raise Exception, "bad method `#{k}`"
46
- }, ins, doc
63
+ }, ins, d
47
64
  }
48
65
  end
49
66
 
50
67
  private
51
68
 
52
69
  def add ins, doc
53
- list = Pointer.parse ins['add']
70
+ list = Pointer.parse ins['path']
54
71
  key = list.pop
55
- obj = Pointer.eval list, doc
72
+ dest = Pointer.eval list, doc
73
+ obj = ins['value']
56
74
 
57
- if Array === obj
58
- obj.insert key.to_i, ins['value']
59
- else
60
- obj[key] = ins['value']
61
- end
75
+ add_op dest, key, obj
62
76
  end
63
77
 
64
78
  def move ins, doc
65
- from = Pointer.parse ins['move']
66
- to = Pointer.parse ins['to']
79
+ from = Pointer.parse ins['from']
80
+ to = Pointer.parse ins['path']
67
81
  from_key = from.pop
68
- to_key = to.pop
82
+ key = to.pop
83
+ src = Pointer.eval from, doc
84
+ dest = Pointer.eval to, doc
69
85
 
70
- src = Pointer.eval(from, doc)
86
+ obj = rm_op src, from_key
87
+ add_op dest, key, obj
88
+ end
89
+
90
+ def copy ins, doc
91
+ from = Pointer.parse ins['from']
92
+ to = Pointer.parse ins['path']
93
+ from_key = from.pop
94
+ key = to.pop
95
+ src = Pointer.eval from, doc
96
+ dest = Pointer.eval to, doc
71
97
 
72
98
  if Array === src
73
- obj = src.delete_at from_key.to_i
99
+ obj = src.fetch from_key.to_i
74
100
  else
75
- obj = src.delete from_key
101
+ obj = src.fetch from_key
76
102
  end
77
103
 
78
- dest = Pointer.eval(to, doc)
79
-
80
- if Array === dest
81
- dest.insert to_key.to_i, obj
82
- else
83
- dest[to_key] = obj
84
- end
104
+ add_op dest, key, obj
85
105
  end
86
106
 
87
107
  def test ins, doc
88
- expected = Pointer.new(ins['test']).eval doc
89
- raise Exception unless expected == ins['value']
108
+ expected = Pointer.new(ins['path']).eval doc
109
+
110
+ unless expected == ins['value']
111
+ raise FailedTestException.new(ins['value'], ins['path'])
112
+ end
90
113
  end
91
114
 
92
115
  def replace ins, doc
93
- list = Pointer.parse ins['replace']
116
+ list = Pointer.parse ins['path']
94
117
  key = list.pop
95
- Pointer.eval(list, doc)[key] = ins['value']
118
+ obj = Pointer.eval list, doc
119
+
120
+ if Array === obj
121
+ obj[key.to_i] = ins['value']
122
+ else
123
+ obj[key] = ins['value']
124
+ end
96
125
  end
97
126
 
98
127
  def remove ins, doc
99
- list = Pointer.parse ins['remove']
128
+ list = Pointer.parse ins['path']
100
129
  key = list.pop
101
130
  obj = Pointer.eval list, doc
131
+ rm_op obj, key
132
+ end
133
+
134
+ def check_index obj, key
135
+ raise ObjectOperationOnArrayException unless key =~ /\A-?\d+\Z/
136
+ idx = key.to_i
137
+ raise OutOfBoundsException if idx > obj.length || idx < 0
138
+ idx
139
+ end
140
+
141
+ def add_op dest, key, obj
142
+ if Array === dest
143
+ dest.insert check_index(dest, key), obj
144
+ else
145
+ dest[key] = obj
146
+ end
147
+ end
102
148
 
149
+ def rm_op obj, key
103
150
  if Array === obj
104
151
  obj.delete_at key.to_i
105
152
  else
@@ -0,0 +1,50 @@
1
+ JSON Patch Tests
2
+ ================
3
+
4
+ These are test cases for implementations of the [IETF JSON Patch
5
+ draft](http://tools.ietf.org/html/draft-ietf-appsawg-json-patch).
6
+
7
+
8
+ Test Format
9
+ -----------
10
+
11
+ Each test file is a JSON document that contains an array of test records. A
12
+ test record is an object with the following members:
13
+
14
+ - doc: The JSON document to test against
15
+ - patch: The patch(es) to apply
16
+ - expected: The expected resulting document, OR
17
+ - error: A string describing an expected error
18
+ - comment: A string describing the test
19
+ - disabled: True if the test should be skipped
20
+
21
+ All fields except 'doc' and 'patch' are optional. Test records consisting only
22
+ of a comment are also OK.
23
+
24
+ These tests are not complete, or even correct - help welcome!
25
+
26
+
27
+ Credits
28
+ -------
29
+
30
+ The seed test set was adapted from Byron Ruth's
31
+ [jsonpatch-js](https://github.com/bruth/jsonpatch-js/blob/master/test.js) and
32
+ extended by [Mike McCabe](https://github.com/mikemccabe).
33
+
34
+
35
+ License
36
+ -------
37
+
38
+ Copyright 2012 The Authors
39
+
40
+ Licensed under the Apache License, Version 2.0 (the "License");
41
+ you may not use this file except in compliance with the License.
42
+ You may obtain a copy of the License at
43
+
44
+ http://www.apache.org/licenses/LICENSE-2.0
45
+
46
+ Unless required by applicable law or agreed to in writing, software
47
+ distributed under the License is distributed on an "AS IS" BASIS,
48
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
49
+ See the License for the specific language governing permissions and
50
+ limitations under the License.
@@ -0,0 +1,203 @@
1
+ [
2
+ {
3
+ "comment": "A.1. Adding an Object Member",
4
+ "doc": {
5
+ "foo": "bar"
6
+ },
7
+ "patch": [
8
+ { "op": "add", "path": "/baz", "value": "qux" }
9
+ ],
10
+ "expected": {
11
+ "baz": "qux",
12
+ "foo": "bar"
13
+ }
14
+ },
15
+
16
+ {
17
+ "comment": "A.2. Adding an Array Element",
18
+ "doc": {
19
+ "foo": [ "bar", "baz" ]
20
+ },
21
+ "patch": [
22
+ { "op": "add", "path": "/foo/1", "value": "qux" }
23
+ ],
24
+ "expected": {
25
+ "foo": [ "bar", "qux", "baz" ]
26
+ }
27
+ },
28
+
29
+ {
30
+ "comment": "A.3. Removing an Object Member",
31
+ "doc": {
32
+ "baz": "qux",
33
+ "foo": "bar"
34
+ },
35
+ "patch": [
36
+ { "op": "remove", "path": "/baz" }
37
+ ],
38
+ "expected": {
39
+ "foo": "bar"
40
+ }
41
+ },
42
+
43
+ {
44
+ "comment": "A.4. Removing an Array Element",
45
+ "doc": {
46
+ "foo": [ "bar", "qux", "baz" ]
47
+ },
48
+ "patch": [
49
+ { "op": "remove", "path": "/foo/1" }
50
+ ],
51
+ "expected": {
52
+ "foo": [ "bar", "baz" ]
53
+ }
54
+ },
55
+
56
+ {
57
+ "comment": "A.5. Replacing a Value",
58
+ "doc": {
59
+ "baz": "qux",
60
+ "foo": "bar"
61
+ },
62
+ "patch": [
63
+ { "op": "replace", "path": "/baz", "value": "boo" }
64
+ ],
65
+ "expected": {
66
+ "baz": "boo",
67
+ "foo": "bar"
68
+ }
69
+ },
70
+
71
+ {
72
+ "comment": "A.6. Moving a Value",
73
+ "doc": {
74
+ "foo": {
75
+ "bar": "baz",
76
+ "waldo": "fred"
77
+ },
78
+ "qux": {
79
+ "corge": "grault"
80
+ }
81
+ },
82
+ "patch": [
83
+ { "op": "move", "from": "/foo/waldo", "path": "/qux/thud" }
84
+ ],
85
+ "expected": {
86
+ "foo": {
87
+ "bar": "baz"
88
+ },
89
+ "qux": {
90
+ "corge": "grault",
91
+ "thud": "fred"
92
+ }
93
+ }
94
+ },
95
+
96
+ {
97
+ "comment": "A.7. Moving an Array Element",
98
+ "doc": {
99
+ "foo": [ "all", "grass", "cows", "eat" ]
100
+ },
101
+ "patch": [
102
+ { "op": "move", "from": "/foo/1", "path": "/foo/3" }
103
+ ],
104
+ "expected": {
105
+ "foo": [ "all", "cows", "eat", "grass" ]
106
+ }
107
+
108
+ },
109
+
110
+ {
111
+ "comment": "A.8. Testing a Value: Success",
112
+ "doc": {
113
+ "baz": "qux",
114
+ "foo": [ "a", 2, "c" ]
115
+ },
116
+ "patch": [
117
+ { "op": "test", "path": "/baz", "value": "qux" },
118
+ { "op": "test", "path": "/foo/1", "value": 2 }
119
+ ],
120
+ "expected": {
121
+ "baz": "qux",
122
+ "foo": [ "a", 2, "c" ]
123
+ }
124
+ },
125
+
126
+ {
127
+ "comment": "A.9. Testing a Value: Error",
128
+ "doc": {
129
+ "baz": "qux"
130
+ },
131
+ "patch": [
132
+ { "op": "test", "path": "/baz", "value": "bar" }
133
+ ],
134
+ "error": "string not equivalent"
135
+ },
136
+
137
+ {
138
+ "comment": "A.10. Adding a nested Member Object",
139
+ "doc": {
140
+ "foo": "bar"
141
+ },
142
+ "patch": [
143
+ { "op": "add", "path": "/child", "value": { "grandchild": { } } }
144
+ ],
145
+ "expected": {
146
+ "foo": "bar",
147
+ "child": {
148
+ "grandchild": {
149
+ }
150
+ }
151
+ }
152
+ },
153
+
154
+ {
155
+ "comment": "A.11. Ignoring Unrecognized Elements",
156
+ "doc": {
157
+ "foo":"bar"
158
+ },
159
+ "patch": [
160
+ { "op": "add", "path": "/baz", "value": "qux", "xyz": 123 }
161
+ ],
162
+ "expected": {
163
+ "foo":"bar",
164
+ "baz":"qux"
165
+ }
166
+ },
167
+
168
+ {
169
+ "comment": "A.12. Adding to a Non-existant Target",
170
+ "doc": {
171
+ "foo": "bar"
172
+ },
173
+ "patch": [
174
+ { "op": "add", "path": "/baz/bat", "value": "qux" }
175
+ ],
176
+ "error": "add to a non-existant target"
177
+ },
178
+
179
+ {
180
+ "comment": "Invalid JSON Patch Document",
181
+ "doc": {
182
+ "foo": "bar"
183
+ },
184
+ "patch": [
185
+ { "op": "add", "path": "/baz", "value": "qux", "op": "remove" }
186
+ ],
187
+ "error": "operation has two 'op' members"
188
+ },
189
+
190
+ {
191
+ "comment": "~ Escape Ordering",
192
+ "doc": {
193
+ "/": 9,
194
+ "~1": 10
195
+ },
196
+ "patch": [{"op": "test", "path": "/~01", "value":"10"}],
197
+ "expected": {
198
+ "/": 9,
199
+ "~1": 10
200
+ }
201
+ }
202
+
203
+ ]
@@ -0,0 +1,242 @@
1
+ [
2
+ { "comment": "empty list, empty docs",
3
+ "doc": {},
4
+ "patch": [],
5
+ "expected": {} },
6
+
7
+ { "comment": "empty patch list",
8
+ "doc": {"foo": 1},
9
+ "patch": [],
10
+ "expected": {"foo": 1} },
11
+
12
+ { "comment": "rearrangements OK?",
13
+ "doc": {"foo": 1, "bar": 2},
14
+ "patch": [],
15
+ "expected": {"bar":2, "foo": 1} },
16
+
17
+ { "comment": "rearrangements OK? How about one level down ... array",
18
+ "doc": [{"foo": 1, "bar": 2}],
19
+ "patch": [],
20
+ "expected": [{"bar":2, "foo": 1}] },
21
+
22
+ { "comment": "rearrangements OK? How about one level down...",
23
+ "doc": {"foo":{"foo": 1, "bar": 2}},
24
+ "patch": [],
25
+ "expected": {"foo":{"bar":2, "foo": 1}} },
26
+
27
+ { "comment": "add replaces any existing field",
28
+ "doc": {"foo": null},
29
+ "patch": [{"op": "add", "path": "/foo", "value":1}],
30
+ "expected": {"foo": 1} },
31
+
32
+ { "comment": "toplevel array",
33
+ "doc": [],
34
+ "patch": [{"op": "add", "path": "/0", "value": "foo"}],
35
+ "expected": ["foo"] },
36
+
37
+ { "comment": "toplevel array, no change",
38
+ "doc": ["foo"],
39
+ "patch": [],
40
+ "expected": ["foo"] },
41
+
42
+ { "comment": "toplevel object, numeric string",
43
+ "doc": {},
44
+ "patch": [{"op": "add", "path": "/foo", "value": "1"}],
45
+ "expected": {"foo":"1"} },
46
+
47
+ { "comment": "toplevel object, integer",
48
+ "doc": {},
49
+ "patch": [{"op": "add", "path": "/foo", "value": 1}],
50
+ "expected": {"foo":1} },
51
+
52
+ { "comment": "Toplevel scalar values OK?",
53
+ "doc": "foo",
54
+ "patch": [{"op": "replace", "path": "", "value": "bar"}],
55
+ "expected": "bar",
56
+ "disabled": true },
57
+
58
+ { "comment": "Add, / target",
59
+ "doc": {},
60
+ "patch": [ {"op": "add", "path": "/", "value":1 } ],
61
+ "expected": {"":1} },
62
+
63
+ { "comment": "Add composite value at top level",
64
+ "doc": {"foo": 1},
65
+ "patch": [{"op": "add", "path": "/bar", "value": [1, 2]}],
66
+ "expected": {"foo": 1, "bar": [1, 2]} },
67
+
68
+ { "comment": "Add into composite value",
69
+ "doc": {"foo": 1, "baz": [{"qux": "hello"}]},
70
+ "patch": [{"op": "add", "path": "/baz/0/foo", "value": "world"}],
71
+ "expected": {"foo": 1, "baz": [{"qux": "hello", "foo": "world"}]} },
72
+
73
+ { "doc": {"bar": [1, 2]},
74
+ "patch": [{"op": "add", "path": "/bar/8", "value": "5"}],
75
+ "error": "Out of bounds (upper)" },
76
+
77
+ { "doc": {"bar": [1, 2]},
78
+ "patch": [{"op": "add", "path": "/bar/-1", "value": "5"}],
79
+ "error": "Out of bounds (lower)" },
80
+
81
+ { "doc": {"foo": 1},
82
+ "patch": [{"op": "add", "path": "/bar", "value": true}],
83
+ "expected": {"foo": 1, "bar": true} },
84
+
85
+ { "doc": {"foo": 1},
86
+ "patch": [{"op": "add", "path": "/bar", "value": false}],
87
+ "expected": {"foo": 1, "bar": false} },
88
+
89
+ { "doc": {"foo": 1},
90
+ "patch": [{"op": "add", "path": "/bar", "value": null}],
91
+ "expected": {"foo": 1, "bar": null} },
92
+
93
+ { "comment": "0 can be an array index or object element name",
94
+ "doc": {"foo": 1},
95
+ "patch": [{"op": "add", "path": "/0", "value": "bar"}],
96
+ "expected": {"foo": 1, "0": "bar" } },
97
+
98
+ { "doc": ["foo"],
99
+ "patch": [{"op": "add", "path": "/1", "value": "bar"}],
100
+ "expected": ["foo", "bar"] },
101
+
102
+ { "doc": ["foo", "sil"],
103
+ "patch": [{"op": "add", "path": "/1", "value": "bar"}],
104
+ "expected": ["foo", "bar", "sil"] },
105
+
106
+ { "doc": ["foo", "sil"],
107
+ "patch": [{"op": "add", "path": "/0", "value": "bar"}],
108
+ "expected": ["bar", "foo", "sil"] },
109
+
110
+ { "doc": ["foo", "sil"],
111
+ "patch": [{"op":" add", "path": "/2", "value": "bar"}],
112
+ "expected": ["foo", "sil", "bar"] },
113
+
114
+ { "doc": ["foo", "sil"],
115
+ "patch": [{"op": "add", "path": "/bar", "value": 42}],
116
+ "error": "Object operation on array target" },
117
+
118
+ { "doc": ["foo", "sil"],
119
+ "patch": [{"op": "add", "path": "/1", "value": ["bar", "baz"]}],
120
+ "expected": ["foo", ["bar", "baz"], "sil"],
121
+ "comment": "value in array add not flattened" },
122
+
123
+ { "doc": {"foo": 1, "bar": [1, 2, 3, 4]},
124
+ "patch": [{"op": "remove", "path": "/bar"}],
125
+ "expected": {"foo": 1} },
126
+
127
+ { "doc": {"foo": 1, "baz": [{"qux": "hello"}]},
128
+ "patch": [{"op": "remove", "path": "/baz/0/qux"}],
129
+ "expected": {"foo": 1, "baz": [{}]} },
130
+
131
+ { "doc": {"foo": 1, "baz": [{"qux": "hello"}]},
132
+ "patch": [{"op": "replace", "path": "/foo", "value": [1, 2, 3, 4]}],
133
+ "expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}]} },
134
+
135
+ { "doc": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}]},
136
+ "patch": [{"op": "replace", "path": "/baz/0/qux", "value": "world"}],
137
+ "expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "world"}]} },
138
+
139
+ { "doc": ["foo"],
140
+ "patch": [{"op": "replace", "path": "/0", "value": "bar"}],
141
+ "expected": ["bar"] },
142
+
143
+ { "doc": [""],
144
+ "patch": [{"op": "replace", "path": "/0", "value": 0}],
145
+ "expected": [0] },
146
+
147
+ { "doc": [""],
148
+ "patch": [{"op": "replace", "path": "/0", "value": true}],
149
+ "expected": [true] },
150
+
151
+ { "doc": [""],
152
+ "patch": [{"op": "replace", "path": "/0", "value": false}],
153
+ "expected": [false] },
154
+
155
+ { "doc": [""],
156
+ "patch": [{"op": "replace", "path": "/0", "value": null}],
157
+ "expected": [null] },
158
+
159
+ { "doc": ["foo", "sil"],
160
+ "patch": [{"op": "replace", "path": "/1", "value": ["bar", "baz"]}],
161
+ "expected": ["foo", ["bar", "baz"]],
162
+ "comment": "value in array replace not flattened" },
163
+
164
+ { "comment": "spurious patch properties",
165
+ "doc": {"foo": 1},
166
+ "patch": [{"op": "test", "path": "/foo", "value": 1, "spurious": 1}],
167
+ "expected": {"foo": 1} },
168
+
169
+ { "doc": {"foo": null},
170
+ "patch": [{"op": "test", "path": "/foo", "value": null}],
171
+ "comment": "null value should still be valid obj property" },
172
+
173
+ { "doc": {"foo": {"foo": 1, "bar": 2}},
174
+ "patch": [{"op": "test", "path": "/foo", "value": {"bar": 2, "foo": 1}}],
175
+ "comment": "test should pass despite rearrangement" },
176
+
177
+ { "doc": {"foo": [{"foo": 1, "bar": 2}]},
178
+ "patch": [{"op": "test", "path": "/foo", "value": [{"bar": 2, "foo": 1}]}],
179
+ "comment": "test should pass despite (nested) rearrangement" },
180
+
181
+ { "doc": {"foo": {"bar": [1, 2, 5, 4]}},
182
+ "patch": [{"op": "test", "path": "/foo", "value": {"bar": [1, 2, 5, 4]}}],
183
+ "comment": "test should pass - no error" },
184
+
185
+ { "doc": {"foo": {"bar": [1, 2, 5, 4]}},
186
+ "patch": [{"op": "test", "path": "/foo", "value": [1, 2]}],
187
+ "error": "test op should fail" },
188
+
189
+ { "comment": "json-pointer tests" },
190
+
191
+ { "comment": "Whole document",
192
+ "doc": { "foo": 1 },
193
+ "patch": [{"op": "test", "path": "", "value": {"foo": 1}}],
194
+ "disabled": true },
195
+
196
+ { "comment": "Empty-string element",
197
+ "doc": { "": 1 },
198
+ "patch": [{"op": "test", "path": "/", "value": 1}] },
199
+
200
+ { "doc": {
201
+ "foo": ["bar", "baz"],
202
+ "": 0,
203
+ "a/b": 1,
204
+ "c%d": 2,
205
+ "e^f": 3,
206
+ "g|h": 4,
207
+ "i\\j": 5,
208
+ "k\"l": 6,
209
+ " ": 7,
210
+ "m~n": 8
211
+ },
212
+ "patch": [{"op": "test", "path": "/foo", "value": ["bar", "baz"]},
213
+ {"op": "test", "path": "/foo/0", "value": "bar"},
214
+ {"op": "test", "path": "/", "value": 0},
215
+ {"op": "test", "path": "/a~1b", "value": 1},
216
+ {"op": "test", "path": "/c%d", "value": 2},
217
+ {"op": "test", "path": "/e^f", "value": 3},
218
+ {"op": "test", "path": "/g|h", "value": 4},
219
+ {"op": "test", "path": "/i\\j", "value": 5},
220
+ {"op": "test", "path": "/k\"l", "value": 6},
221
+ {"op": "test", "path": "/ ", "value": 7},
222
+ {"op": "test", "path": "/m~0n", "value": 8}] },
223
+
224
+ { "comment": "Move to same location has no effect",
225
+ "doc": {"foo": 1},
226
+ "patch": [{"op": "move", "from": "/foo", "path": "/foo"}],
227
+ "expected": {"foo": 1} },
228
+
229
+ { "doc": {"foo": 1, "baz": [{"qux": "hello"}]},
230
+ "patch": [{"op": "move", "from": "/foo", "path": "/bar"}],
231
+ "expected": {"baz": [{"qux": "hello"}], "bar": 1} },
232
+
233
+ { "doc": {"baz": [{"qux": "hello"}], "bar": 1},
234
+ "patch": [{"op": "move", "from": "/baz/0/qux", "path": "/baz/1"}],
235
+ "expected": {"baz": [{}, "hello"], "bar": 1} },
236
+
237
+ { "doc": {"baz": [{"qux": "hello"}], "bar": 1},
238
+ "patch": [{"op": "copy", "from": "/baz/0", "path": "/boo"}],
239
+ "expected": {"baz":[{"qux":"hello"}],"bar":1,"boo":{"qux":"hello"}} },
240
+
241
+ { "comment": "tests complete" }
242
+ ]
@@ -1,6 +1,15 @@
1
1
  require 'helper'
2
2
 
3
3
  class TestHana < Hana::TestCase
4
+ def test_no_eval
5
+ patch = Hana::Patch.new [
6
+ { 'op' => 'eval', 'value' => '1' }
7
+ ]
8
+ assert_raises(Hana::Patch::Exception) do
9
+ patch.apply('foo' => 'bar')
10
+ end
11
+ end
12
+
4
13
  def test_split_many
5
14
  pointer = Hana::Pointer.new '/foo/bar/baz'
6
15
  assert_equal %w{ foo bar baz }, pointer.to_a
@@ -8,7 +17,7 @@ class TestHana < Hana::TestCase
8
17
 
9
18
  def test_root
10
19
  pointer = Hana::Pointer.new '/'
11
- assert_equal [], pointer.to_a
20
+ assert_equal [''], pointer.to_a
12
21
  end
13
22
 
14
23
  def test_escape
@@ -0,0 +1,42 @@
1
+ require 'helper'
2
+ require 'json'
3
+
4
+ module Hana
5
+ class TestIETF < TestCase
6
+ TESTDIR = File.dirname File.expand_path __FILE__
7
+ json = File.read File.join TESTDIR, 'json-patch-tests', 'tests.json'
8
+ tests = JSON.load json
9
+ tests.each_with_index do |test, i|
10
+ next unless test['doc']
11
+
12
+ define_method("test_#{test['comment'] || i }") do
13
+ skip "disabled" if test['disabled']
14
+
15
+ doc = test['doc']
16
+ patch = test['patch']
17
+
18
+ patch = Hana::Patch.new patch
19
+
20
+ if test['error']
21
+ assert_raises(ex(test['error'])) do
22
+ patch.apply doc
23
+ end
24
+ else
25
+ assert_equal(test['expected'] || doc, patch.apply(doc))
26
+ end
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def ex msg
33
+ case msg
34
+ when /Out of bounds/i then Hana::Patch::OutOfBoundsException
35
+ when /Object operation on array/ then
36
+ Hana::Patch::ObjectOperationOnArrayException
37
+ else
38
+ Hana::Patch::FailedTestException
39
+ end
40
+ end
41
+ end
42
+ end
metadata CHANGED
@@ -1,64 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hana
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
5
- prerelease:
4
+ version: 1.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Aaron Patterson
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-09-08 00:00:00.000000000 Z
11
+ date: 2013-05-23 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: minitest
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
21
- version: '3.4'
19
+ version: '5.0'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ~>
28
25
  - !ruby/object:Gem::Version
29
- version: '3.4'
26
+ version: '5.0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rdoc
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ~>
36
32
  - !ruby/object:Gem::Version
37
- version: '3.10'
33
+ version: '4.0'
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ~>
44
39
  - !ruby/object:Gem::Version
45
- version: '3.10'
40
+ version: '4.0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: hoe
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
45
  - - ~>
52
46
  - !ruby/object:Gem::Version
53
- version: '3.0'
47
+ version: '3.6'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
52
  - - ~>
60
53
  - !ruby/object:Gem::Version
61
- version: '3.0'
54
+ version: '3.6'
62
55
  description: Implementation of [JSON Patch][1] and [JSON Pointer][2] drafts.
63
56
  email:
64
57
  - aaron@tenderlovemaking.com
@@ -67,6 +60,7 @@ extensions: []
67
60
  extra_rdoc_files:
68
61
  - CHANGELOG.rdoc
69
62
  - Manifest.txt
63
+ - README.md
70
64
  files:
71
65
  - .autotest
72
66
  - CHANGELOG.rdoc
@@ -75,11 +69,15 @@ files:
75
69
  - Rakefile
76
70
  - lib/hana.rb
77
71
  - test/helper.rb
72
+ - test/json-patch-tests/README.md
73
+ - test/json-patch-tests/spec_tests.json
74
+ - test/json-patch-tests/tests.json
78
75
  - test/test_hana.rb
79
- - test/test_patch.rb
76
+ - test/test_ietf.rb
80
77
  - .gemtest
81
78
  homepage: http://github.com/tenderlove/hana
82
79
  licenses: []
80
+ metadata: {}
83
81
  post_install_message:
84
82
  rdoc_options:
85
83
  - --main
@@ -87,23 +85,21 @@ rdoc_options:
87
85
  require_paths:
88
86
  - lib
89
87
  required_ruby_version: !ruby/object:Gem::Requirement
90
- none: false
91
88
  requirements:
92
- - - ! '>='
89
+ - - '>='
93
90
  - !ruby/object:Gem::Version
94
91
  version: '0'
95
92
  required_rubygems_version: !ruby/object:Gem::Requirement
96
- none: false
97
93
  requirements:
98
- - - ! '>='
94
+ - - '>='
99
95
  - !ruby/object:Gem::Version
100
96
  version: '0'
101
97
  requirements: []
102
98
  rubyforge_project: hana
103
- rubygems_version: 1.8.24
99
+ rubygems_version: 2.0.2
104
100
  signing_key:
105
- specification_version: 3
101
+ specification_version: 4
106
102
  summary: Implementation of [JSON Patch][1] and [JSON Pointer][2] drafts.
107
103
  test_files:
108
104
  - test/test_hana.rb
109
- - test/test_patch.rb
105
+ - test/test_ietf.rb
@@ -1,162 +0,0 @@
1
- require 'helper'
2
-
3
- module Hana
4
- class TestPatch < TestCase
5
- def test_no_eval
6
- patch = Hana::Patch.new [
7
- { 'eval' => '1' }
8
- ]
9
- assert_raises(Hana::Patch::Exception) do
10
- patch.apply('foo' => 'bar')
11
- end
12
- end
13
-
14
- def test_add_member
15
- patch = Hana::Patch.new [
16
- { 'add' => '/baz', 'value' => 'qux' }
17
- ]
18
-
19
- result = patch.apply('foo' => 'bar')
20
- assert_equal({'baz' => 'qux', 'foo' => 'bar'}, result)
21
- end
22
-
23
- def test_add_array
24
- patch = Hana::Patch.new [
25
- { "add" => "/foo/1", "value" => "qux" }
26
- ]
27
-
28
- result = patch.apply({ "foo" => [ "bar", "baz" ] })
29
-
30
- assert_equal({ "foo" => [ "bar", "qux", "baz" ] }, result)
31
- end
32
-
33
- def test_remove_object_member
34
- patch = Hana::Patch.new [ { "remove" => "/baz" } ]
35
-
36
- result = patch.apply({ 'baz' => 'qux', 'foo' => 'bar' })
37
-
38
- assert_equal({ "foo" => 'bar' }, result)
39
- end
40
-
41
- def test_remove_array_element
42
- patch = Hana::Patch.new [ { "remove" => "/foo/1" } ]
43
- result = patch.apply({ "foo" => [ "bar", "qux", "baz" ] })
44
- assert_equal({ "foo" => [ "bar", "baz" ] }, result)
45
- end
46
-
47
- def test_replace_value
48
- patch = Hana::Patch.new [ { "replace" => "/baz", "value" => "boo" } ]
49
- result = patch.apply({ "baz" => "qux", "foo" => "bar" })
50
- assert_equal({ "baz" => "boo", "foo" => "bar" }, result)
51
- end
52
-
53
- def test_moving_a_value
54
- doc = {
55
- "foo" => {
56
- "bar" => "baz",
57
- "waldo" => "fred"
58
- },
59
- "qux" => {
60
- "corge" => "grault"
61
- }
62
- }
63
-
64
- patch = [ { "move" => "/foo/waldo", 'to' => "/qux/thud" } ]
65
-
66
- expected = {
67
- "foo" => {
68
- "bar" => "baz"
69
- },
70
- "qux" => {
71
- "corge" => "grault",
72
- "thud" => "fred"
73
- }
74
- }
75
-
76
- patch = Hana::Patch.new patch
77
- result = patch.apply doc
78
- assert_equal expected, result
79
- end
80
-
81
- def test_move_an_array_element
82
- # An example target JSON document:
83
- doc = {
84
- "foo" => [ "all", "grass", "cows", "eat" ]
85
- }
86
-
87
- # A JSON Patch document:
88
- patch = [
89
- { "move" => "/foo/1", "to" => "/foo/3" }
90
- ]
91
-
92
- # The resulting JSON document:
93
- expected = {
94
- "foo" => [ "all", "cows", "eat", "grass" ]
95
- }
96
-
97
- patch = Hana::Patch.new patch
98
- result = patch.apply doc
99
- assert_equal expected, result
100
- end
101
-
102
- def test_testing_a_value_success
103
- # An example target JSON document:
104
- doc = {
105
- "baz" => "qux",
106
- "foo" => [ "a", 2, "c" ]
107
- }
108
-
109
- # A JSON Patch document that will result in successful evaluation:
110
- patch = [
111
- { "test" => "/baz", "value" => "qux" },
112
- { "test" => "/foo/1", "value" => 2 },
113
- { "add" => "/bar", "value" => 2 },
114
- ]
115
-
116
- expected = {
117
- "baz" => "qux",
118
- "foo" => [ "a", 2, "c" ],
119
- 'bar' => 2
120
- }
121
-
122
- patch = Hana::Patch.new patch
123
- result = patch.apply doc
124
- assert_equal expected, result
125
- end
126
-
127
- def test_testing_a_value_error
128
- # An example target JSON document:
129
- doc = { "baz" => "qux" }
130
-
131
- # A JSON Patch document that will result in an error condition:
132
- patch = [
133
- { "test" => "/baz", "value" => "bar" }
134
- ]
135
-
136
- patch = Hana::Patch.new patch
137
-
138
- assert_raises(Hana::Patch::Exception) do
139
- patch.apply doc
140
- end
141
- end
142
-
143
- def test_add_nested_member_object
144
- # An example target JSON document:
145
- doc = { "foo" => "bar" }
146
- # A JSON Patch document:
147
- patch = [
148
- { "add" => "/child", "value" => { "grandchild" => { } } }
149
- ]
150
-
151
- # The resulting JSON document:
152
- expected = {
153
- "foo" => "bar",
154
- "child" => { "grandchild" => { } }
155
- }
156
-
157
- patch = Hana::Patch.new patch
158
- result = patch.apply doc
159
- assert_equal expected, result
160
- end
161
- end
162
- end