hana 1.0.1 → 1.1.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.
@@ -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