json_p3 1.0.0 → 1.0.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1c8afa8205821093144f9c2ccbe427e7d726251e48e8cc1480152d0efc8ade7c
4
- data.tar.gz: 77639923975a6f89d10cbad38891c59015f06797f084ccaacc78073a5e4161ed
3
+ metadata.gz: 506b34d62097639d18aae756967a490cacd9d810ffa49361d9556149d83c8802
4
+ data.tar.gz: 4a5565093f304714305a7664a6f7a868aa70c0a9aa8c99a67c9ff067166edfcf
5
5
  SHA512:
6
- metadata.gz: 178a01fe4d486b0d2787d46edbe93dce36a6fe6b2b30410daaa57f44da385649aad995c1187ac55253d8cdbe2448f86c73bdd56f1142ba066364c24cd9a2dfe9
7
- data.tar.gz: 82d38fc46b9d1049135e79d1b3b2aae814ef65285153f5b57242021b7b9b5da5f55f2b5c092cd0e301b6f5ef0b047d59d354bb998c92fe522ecdb2804bc688e9
6
+ metadata.gz: a21a879a120bd3209e2066ad26d1cceac3e44686bf5fd4f5ecb939f4094156a9ee25c17d099113911dadba34dc0a9c2ff44d28c7d8680f29682ef9993f30b874
7
+ data.tar.gz: 49668d5a557ec389d644e5a6965f3433ac20165d724868551fb1fbda5b1c99dcab92b7bbf296ecabc2694c50ff76166d76a5e562b60c54afe0770458b5d2ff87
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,51 +1,65 @@
1
+ ## [1.0.1] - 2026-10-18
2
+
3
+ - Fixed the JSON Patch `move` operation overwriting the item at its target array index instead of inserting before it, which corrupted arrays when reordering their items. [RFC 6902 section 4.4](https://datatracker.ietf.org/doc/html/rfc6902#section-4.4) defines `move` as a `remove` followed by an `add`, and [section 4.1](https://datatracker.ietf.org/doc/html/rfc6902#section-4.1) says that when an `add` targets an array index "a new value is inserted into the array at the specified index".
4
+ - Fixed the JSON Patch `add` operation rejecting an array index equal to the length of the array. [RFC 6902 section 4.1](https://datatracker.ietf.org/doc/html/rfc6902#section-4.1) only requires that the specified index "MUST NOT be greater than the number of elements in the array", so an index equal to the length is valid and appends to the array.
5
+ - Fixed the JSON Patch `move` and `copy` operations padding an array with `nil` when their target index was past the end of the array. Both are defined in terms of `add` (RFC 6902 [section 4.4](https://datatracker.ietf.org/doc/html/rfc6902#section-4.4) and [section 4.5](https://datatracker.ietf.org/doc/html/rfc6902#section-4.5)), whose index "MUST NOT be greater than the number of elements in the array" ([section 4.1](https://datatracker.ietf.org/doc/html/rfc6902#section-4.1)). Under [section 5](https://datatracker.ietf.org/doc/html/rfc6902#section-5), violating a normative requirement means the patch "SHALL NOT be deemed successful", so they now raise a `JSONP3::Patch::Error`, as `add` does.
6
+ - Fixed the JSON Patch `move` and `copy` operations replacing the whole document with the moved or copied value when the parent of the target does not exist. [RFC 6902 section 4.1](https://datatracker.ietf.org/doc/html/rfc6902#section-4.1) says the object or array being added to "does need to exist, and it remains an error for that not to be the case", and `move` and `copy` inherit that from `add` ([section 4.4](https://datatracker.ietf.org/doc/html/rfc6902#section-4.4) and [section 4.5](https://datatracker.ietf.org/doc/html/rfc6902#section-4.5)). They now raise a `JSONP3::Patch::Error`.
7
+ - Fixed the JSON Patch `move` operation silently discarding the moved value when the parent of the target is neither an array nor an object. [RFC 6902 section 4.1](https://datatracker.ietf.org/doc/html/rfc6902#section-4.1) says the target location "MUST reference one of" the document root, a member of an existing object or an element of an existing array, and [section 5](https://datatracker.ietf.org/doc/html/rfc6902#section-5) requires an operation that cannot succeed to fail the patch. It now raises a `JSONP3::Patch::Error`, as `add` and `copy` already did.
8
+ - Fixed the JSON Patch `copy` operation not copying the value when appending it to an array with `-`, so the copy and its source shared nested arrays and objects. [RFC 6902 section 4.5](https://datatracker.ietf.org/doc/html/rfc6902#section-4.5) defines `copy` as an `add` of the value at `from`, which leaves the document with two independent values. With the shared objects, a later operation on one changed the other, and copying an array to its own end made the array contain itself.
9
+ - Fixed JSON Patch operations targeting an object member whose name is a non-negative integer, like `/foo/1`. `add`, `copy` and `move` wrote the value under an integer key instead of the member name, duplicating the member when it already existed, `replace` added an integer key alongside the existing member, `remove` did nothing, and `move` left its source member in place. Under [RFC 6901 section 4](https://datatracker.ietf.org/doc/html/rfc6901#section-4), when a reference token is applied to an object, "the new referenced value is the object member with the name identified by the reference token". A token is only read as an array index when it is applied to an array. [RFC 8259 section 4](https://datatracker.ietf.org/doc/html/rfc8259#section-4) says "A name is a string" and "The names within an object SHOULD be unique", so a second key for the same name produced JSON with duplicate member names.
10
+ - Fixed trailing comma detection in JSONPath bracketed segments. Previously we failed to consume whitespace after a comma and before checking for `]`.
11
+ - Fixed JSONPath `\u` escape sequence rejection of code points less than or equal to 0x1F. The spec requires us to reject literals of 0x1F or lower, not escape sequences.
12
+ - Fixed the JSONPath shorthand name selector to accept the full range of allow Unicode code points.
13
+ - Fixed the JSONPath slice selector when a start index equal to the target array's length and default end and step values would result in selecting the last value in the array.
14
+
1
15
  ## [1.0.0] - 2026-04-21
2
16
 
3
17
  This major release includes breaking changes. No new features have been added.
4
18
 
5
19
  - Changed the JSONPath tokenizer and parser. The new parser is faster and more JIT friendly.
6
20
  - Changed our module and class layout to better separate JSONPath, JSON Pointer and JSON Patch, and to remove "stuttering". For example, previously we had `JSONP3::JSONPathEnvironment` and `JSONP3::JSONPathNode`. Now we have `JSONP3::Path::Environment` and `JSONP3::Path:Node`. This ASCII tree view show our new module and class hierarchy.
7
-
8
- ```
9
- module JSONP3
10
- ├── class Error < StandardError
11
- │
12
- ├── module Path # JSONPath (RFC 9535)
13
- │ ├── class Environment
14
- │ ├── class Query
15
- │ ├── class Node
16
- │ ├── class NodeList
17
- │ ├── class Error < JSONP3::Error
18
- │ │ ├── class SyntaxError
19
- │ │ ├── class TypeError
20
- │ │ ├── class NameError
21
- │ │ └── class RecursionError
22
- │ ├── def self.find
23
- │ ├── def self.find_enum
24
- │ ├── def self.compile
25
- │ ├── def self.match
26
- │ ├── def self.match?
27
- │ └── def self.first
28
- │
29
- ├── class Pointer # JSON Pointer (RFC 6901)
30
- │ ├── class Error < JSONP3::Error
31
- │ │ ├── class IndexError
32
- │ │ ├── class SyntaxError
33
- │ │ └── class TypeError
34
- │ └── def self.resolve
35
- │
36
- └── class Patch # JSON Patch (RFC 6902)
37
- ├── class OpAdd
38
- ├── class OpCopy
39
- ├── class OpMove
40
- ├── class OpRemove
41
- ├── class OpReplace
42
- ├── class OpTest
43
- ├── class Error < JSONP3::Error
44
- │ └── class TestFailure
45
- └── def !self.apply
46
- ```
47
-
48
- Note that top-level convenience methods - like `JSONP3.find` and `JSONP3.compile` - are unchanged. Most dependent projects should only need to change error class names.
21
+
22
+ ```
23
+ module JSONP3
24
+ ├── class Error < StandardError
25
+ │
26
+ ├── module Path # JSONPath (RFC 9535)
27
+ │ ├── class Environment
28
+ │ ├── class Query
29
+ │ ├── class Node
30
+ │ ├── class NodeList
31
+ │ ├── class Error < JSONP3::Error
32
+ │ │ ├── class SyntaxError
33
+ │ │ ├── class TypeError
34
+ │ │ ├── class NameError
35
+ │ │ └── class RecursionError
36
+ │ ├── def self.find
37
+ │ ├── def self.find_enum
38
+ │ ├── def self.compile
39
+ │ ├── def self.match
40
+ │ ├── def self.match?
41
+ │ └── def self.first
42
+ │
43
+ ├── class Pointer # JSON Pointer (RFC 6901)
44
+ │ ├── class Error < JSONP3::Error
45
+ │ │ ├── class IndexError
46
+ │ │ ├── class SyntaxError
47
+ │ │ └── class TypeError
48
+ │ └── def self.resolve
49
+ │
50
+ └── class Patch # JSON Patch (RFC 6902)
51
+ ├── class OpAdd
52
+ ├── class OpCopy
53
+ ├── class OpMove
54
+ ├── class OpRemove
55
+ ├── class OpReplace
56
+ ├── class OpTest
57
+ ├── class Error < JSONP3::Error
58
+ │ └── class TestFailure
59
+ └── def !self.apply
60
+ ```
61
+
62
+ Note that top-level convenience methods - like `JSONP3.find` and `JSONP3.compile` - are unchanged. Most dependent projects should only need to change error class names.
49
63
 
50
64
  - Renamed `JSONP3::JSONPath` to `JSONP3::Path::Query`.
51
65
  - Renamed `JSONP3::RecursiveDescentSegment` to `JSONP3::Path::DescendantSegment` to better match the spec.
data/certs/jgrp.pem CHANGED
@@ -1,27 +1,27 @@
1
1
  -----BEGIN CERTIFICATE-----
2
2
  MIIEhTCCAu2gAwIBAgIBATANBgkqhkiG9w0BAQsFADBEMRYwFAYDVQQDDA1qYW1l
3
3
  c2dyLnByaW9yMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZ
4
- FgNjb20wHhcNMjUxMDI1MDc0ODI4WhcNMjYxMDI1MDc0ODI4WjBEMRYwFAYDVQQD
4
+ FgNjb20wHhcNMjYxMDE4MTMzMTExWhcNMjcxMDE4MTMzMTExWjBEMRYwFAYDVQQD
5
5
  DA1qYW1lc2dyLnByaW9yMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJ
6
- k/IsZAEZFgNjb20wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDYTvQW
7
- 0JzsHenYu4w0iHgJRtU9uK8vkDx1wlMioKlPFkaiMfxqUcTTfg80U/n8n/KKmpdq
8
- y2rpUqv+VPE2nrfCUncf7ukqs+gF8uehbciMXV4iOzsZmqmtlPUJiGuSyO8REe/O
9
- ERAyxYV8y5psF+WnW4uQzsWCKTnKbexIc6rf237veuYvYoBpnodJu0mJ0m9tBCFU
10
- qH3Nk1sHBRIEEwn+nTiIiOMb+QMU5p0NIQkIhJO3el5nEQgIGxMPnie7PnwxPquQ
11
- 1rouuISbLmQy5oKYc9kScwnHdAu2PtnnJFNNuQ37vED375iQgMem/tK8EgMm9/Qr
12
- Qxd2DQqqehgtEySWntGHTxNLXQouqHIRyqzkbcCatJqC+PBBql1x2N1KqgyvbLM3
13
- B0dZCdJECr5J8zrluMxYsy7uGMmLz3LZ7PskGaJg4XxXP3j/B/e03/AxDj0neZZu
14
- xuDLIfMDSNILO1pskMc+Xp0xdnkbS4IjiMDTrhqABuEcdNYH/GpyTiA/I0cCAwEA
15
- AaOBgTB/MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBR4j7UhnpT9
16
- mLcAkshwkFtwVpMdejAiBgNVHREEGzAZgRdqYW1lc2dyLnByaW9yQGdtYWlsLmNv
6
+ k/IsZAEZFgNjb20wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDAXkrS
7
+ W5E5Ts892bcQSSt/+RoE6T7yf6rWBxWs6PSNksUM05toEX7Mmsq+u5hJ2oVit9Tp
8
+ /Gvpq4eANMv95vQnLmiT+1J3YauckaTB0TaArf77ytdiOxlfgYRt0Di6QmZVkinR
9
+ BDA9qTcsuwmia5IBG6mcjpmAoiNeBtz6jv/PqCYBitnJEW0dpaqd7r3Lmzh9B562
10
+ Z/kGD8nmVpA6Wj/i5PjBlhMfQ7TRdAZi4xji96gAoFo4v3JY8r8baH6ww/uT4cFC
11
+ O0vIh2FpSyrWT/nAqEqfjHl5XShLTUdDbcTyl5rnU1lZr66A0gl4x/gKYglYsRfO
12
+ xo9TPH2P0G3U+iTI/a0jeNlXi9cxZMw1LaUChB+rI9qa/xpvOYUb76pu7BJmtpp/
13
+ QyBHujNa0VCtCmfWJnVaR6JNPKza5VP38BPvS1YriXDnkLAxZ4ruAXrTtneQyemP
14
+ 4umgDSJ9ESXdNfgZkGcXsNmXSU7vLYZAp4OajBhUp5hnRSw4y6HWKt8oG2kCAwEA
15
+ AaOBgTB/MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBROcgTGvg8n
16
+ 3d5QbkQz4JzlE7Jk0TAiBgNVHREEGzAZgRdqYW1lc2dyLnByaW9yQGdtYWlsLmNv
17
17
  bTAiBgNVHRIEGzAZgRdqYW1lc2dyLnByaW9yQGdtYWlsLmNvbTANBgkqhkiG9w0B
18
- AQsFAAOCAYEAbTkg946BsGPQFX3jskBjwINeFnMP0IppuStonPQv3SNQu1SWP0xS
19
- o1Qji1dxkWDfOcp1ffrRMUo7ecrJbTqQ5Sdx+7zw0OLodSVqSu1LMIVDw/T73smg
20
- ADO2r3eZ8vxCKh7z4wUeKXvmBXi5NvpmvhH7D+qeG4OMLI8jnfmfbSHZQZbe2tpd
21
- tJC+MYVok/m91fBrfv5wqLsR5QiJl3vcVhG+xACd46gr3JZFOxBqpSJrCYL8Gwqx
22
- BaKEySCLGqkb0d1Rnt2/zTR2KtW2QJVH9rBMnJ+LsPEFKs1UHH4qHzxmwmmaQLon
23
- MoE4UJ51GHGrjJ4WsHOUSOy5UbjaQec3kuIDwTXKbHws0J0Ut9S4S38opZKdQHDD
24
- PUBpY0QA1CgE/VjKAYEDHOJyfUCNOl+sEESM+WxEXZAUVZLrFF7MljJqyQq+JyTD
25
- vFKHe1ZAA76NFrPscB/ODPTBK8i+u4gwrD1UAsVAHuIbOpQu4LYAujluCu5cl2A8
26
- HhmlCT7sTkqG
18
+ AQsFAAOCAYEAZLTr3Z6ZNmlI+AT9ci2wCC7A74OVNME8eZaqcBxGuXE/9jWycIQX
19
+ RTggqZcofJpgKNMFIrelQDCa/Jf3fbCtdVfbcEb6vITlr7Zt/M/mHzu/QjjIFpmT
20
+ gTXlP8APjoQxWaRrhOAm5nbgwYKrOPdLtBu3uzN3PAhbZQlLHyM49bWSux75Ibxy
21
+ nL0cYGR05H3ym7U9XRRuEExHJ0gCW67CwchWNav0yEgeko5Sn3BSjMISBdy9jF5c
22
+ bJgFKiVQFTN+5+uHtBUu5YrtCgsMTgr09g6DgpUUlALHOyg7+XEp/MeqZqE3g5Qu
23
+ JubJmxfW7PrLuyBLkHeIxpvVdpORTaH3/U5qb45XPJ1z2IHC91IlBIoFSOsDrCrL
24
+ kPjfyWVsL/uWyRIDnXRoTTepSBND6NmHrGWuTiYDB7r/Yeqj714R6GF1qe2jZlDW
25
+ yE12qcU40zRwg3LPrEwjuaF4Lo29G666PBWxGcLbakvRUDuO5GG5rqJydmX2KV/J
26
+ NlOr4XEE+abN
27
27
  -----END CERTIFICATE-----
@@ -18,6 +18,33 @@ module JSONP3
18
18
  def to_h
19
19
  raise "JSON Patch operations must implement #to_h"
20
20
  end
21
+
22
+ private
23
+
24
+ # Add _obj_ to _parent_ at _target_ following the semantics of the JSON
25
+ # Patch _add_ operation: insert into arrays, set object members.
26
+ def add_member(parent, target, obj, index)
27
+ if parent.is_a?(Array)
28
+ if target == "-"
29
+ parent << obj
30
+ elsif target.is_a?(Integer) && target <= parent.length
31
+ parent.insert(target, obj)
32
+ else
33
+ raise JSONP3::Patch::Error, "index out of range (#{name}:#{index})"
34
+ end
35
+ elsif parent.is_a?(Hash)
36
+ parent[member_key(parent, target)] = obj
37
+ else
38
+ raise JSONP3::Patch::Error, "unexpected operation on #{parent.class} (#{name}:#{index})"
39
+ end
40
+ end
41
+
42
+ # Return the key of _parent_ identified by pointer token _target_.
43
+ # Pointer tokens that look like array indices are integers, but object
44
+ # member names are strings.
45
+ def member_key(parent, target)
46
+ parent.key?(target) ? target : target.to_s
47
+ end
21
48
  end
22
49
  end
23
50
  end
@@ -17,7 +17,7 @@ module JSONP3
17
17
  end
18
18
 
19
19
  def apply!(value, index)
20
- parent, obj = @pointer.resolve_with_parent(value)
20
+ parent, _obj = @pointer.resolve_with_parent(value)
21
21
  return @value if parent == JSONP3::Pointer::UNDEFINED && @pointer.tokens.empty?
22
22
 
23
23
  if parent == JSONP3::Pointer::UNDEFINED
@@ -25,21 +25,7 @@ module JSONP3
25
25
  "no such property or item '#{@pointer.parent}' (#{name}:#{index})"
26
26
  end
27
27
 
28
- target = @pointer.tokens.last
29
- if parent.is_a?(Array)
30
- if obj == JSONP3::Pointer::UNDEFINED
31
- raise JSONP3::Patch::Error, "index out of range (#{name}:#{index})" unless target == "-"
32
-
33
- parent << @value
34
- else
35
- parent.insert(target.to_i, @value)
36
- end
37
- elsif parent.is_a?(Hash)
38
- parent[target] = @value
39
- else
40
- raise JSONP3::Patch::Error, "unexpected operation on #{parent.class} (#{name}:#{index})"
41
- end
42
-
28
+ add_member(parent, @pointer.tokens.last, @value, index)
43
29
  value
44
30
  end
45
31
 
@@ -26,27 +26,15 @@ module JSONP3
26
26
 
27
27
  # Find the parent of the destination pointer.
28
28
  dest_parent, _dest_obj = @pointer.resolve_with_parent(value)
29
- return deep_copy(source_obj) if dest_parent == JSONP3::Pointer::UNDEFINED
29
+ return deep_copy(source_obj) if @pointer.tokens.empty?
30
30
 
31
- dest_target = @pointer.tokens.last
32
- if dest_target == JSONP3::Pointer::UNDEFINED
31
+ if dest_parent == JSONP3::Pointer::UNDEFINED
33
32
  raise JSONP3::Patch::Error,
34
- "unexpected operation (#{name}:#{index})"
33
+ "no such property or item '#{@pointer.parent}' (#{name}:#{index})"
35
34
  end
36
35
 
37
36
  # Write the source value to the destination.
38
- if dest_parent.is_a?(Array)
39
- if dest_target == "-"
40
- dest_parent << source_obj
41
- else
42
- dest_parent.insert(dest_target.to_i, deep_copy(source_obj))
43
- end
44
- elsif dest_parent.is_a?(Hash)
45
- dest_parent[dest_target] = deep_copy(source_obj)
46
- else
47
- raise JSONP3::Patch::Error, "unexpected operation on #{dest_parent.class} (#{name}:#{index})"
48
- end
49
-
37
+ add_member(dest_parent, @pointer.tokens.last, deep_copy(source_obj), index)
50
38
  value
51
39
  end
52
40
 
@@ -39,30 +39,20 @@ module JSONP3
39
39
  if source_parent.is_a?(Array)
40
40
  source_parent.delete_at(source_target.to_i)
41
41
  elsif source_parent.is_a?(Hash)
42
- source_parent.delete(source_target)
42
+ source_parent.delete(member_key(source_parent, source_target))
43
43
  end
44
44
 
45
45
  # Find the parent of the destination pointer.
46
46
  dest_parent, _dest_obj = @pointer.resolve_with_parent(value)
47
- return source_obj if dest_parent == JSONP3::Pointer::UNDEFINED
47
+ return source_obj if @pointer.tokens.empty?
48
48
 
49
- dest_target = @pointer.tokens.last
50
- if dest_target == JSONP3::Pointer::UNDEFINED
49
+ if dest_parent == JSONP3::Pointer::UNDEFINED
51
50
  raise JSONP3::Patch::Error,
52
- "unexpected operation (#{name}:#{index})"
51
+ "no such property or item '#{@pointer.parent}' (#{name}:#{index})"
53
52
  end
54
53
 
55
54
  # Write the source value to the destination.
56
- if dest_parent.is_a?(Array)
57
- if dest_target == "-"
58
- dest_parent << source_obj
59
- else
60
- dest_parent[dest_target.to_i] = source_obj
61
- end
62
- elsif dest_parent.is_a?(Hash)
63
- dest_parent[dest_target] = source_obj
64
- end
65
-
55
+ add_member(dest_parent, @pointer.tokens.last, source_obj, index)
66
56
  value
67
57
  end
68
58
 
@@ -40,7 +40,7 @@ module JSONP3
40
40
  elsif parent.is_a?(Hash)
41
41
  raise JSONP3::Patch::Error, "no property to remove (#{name}:#{index})" if obj == JSONP3::Pointer::UNDEFINED
42
42
 
43
- parent.delete(target)
43
+ parent.delete(member_key(parent, target))
44
44
  else
45
45
  raise JSONP3::Patch::Error, "unexpected operation on #{parent.class} (#{name}:#{index})"
46
46
  end
@@ -38,7 +38,7 @@ module JSONP3
38
38
  elsif parent.is_a?(Hash)
39
39
  raise JSONP3::Patch::Error, "no property to replace (#{name}:#{index})" if obj == JSONP3::Pointer::UNDEFINED
40
40
 
41
- parent[target] = @value
41
+ parent[member_key(parent, target)] = @value
42
42
  else
43
43
  raise JSONP3::Patch::Error, "unexpected operation on #{parent.class} (#{name}:#{index})"
44
44
  end
@@ -194,7 +194,11 @@ module JSONP3
194
194
  end
195
195
 
196
196
  def self.name_first?(ch)
197
- (ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122) || ch == 95 || (ch >= 0x80 && ch <= 0xffff)
197
+ (ch >= 65 && ch <= 90) ||
198
+ (ch >= 97 && ch <= 122) ||
199
+ ch == 95 ||
200
+ (ch >= 0x80 && ch <= 0xD7FF) ||
201
+ (ch >= 0xE000 && ch <= 0x10FFFF)
198
202
  end
199
203
 
200
204
  def self.name_ch?(ch)
@@ -202,7 +206,7 @@ module JSONP3
202
206
  (ch >= 65 && ch <= 90) ||
203
207
  (ch >= 97 && ch <= 122) ||
204
208
  ch == 95 ||
205
- (ch >= 0x80 && ch <= 0xffff)
209
+ (ch >= 0x80 && ch <= 0x10FFFF)
206
210
  end
207
211
 
208
212
  def self.number_ch?(ch)
@@ -237,11 +237,12 @@ module JSONP3
237
237
  when :token_rbracket
238
238
  break
239
239
  else
240
- eat(:token_comma)
240
+ comma_token = eat(:token_comma)
241
+ skip(:token_trivia)
241
242
  if peek.first == :token_rbracket
242
243
  raise SyntaxError.new(
243
244
  "unexpected trailing comma",
244
- peek,
245
+ comma_token,
245
246
  @query
246
247
  )
247
248
  end
@@ -364,7 +365,7 @@ module JSONP3
364
365
  func = @env.function_extensions[name]
365
366
 
366
367
  unless func
367
- raise JSONPathNameError.new(
368
+ raise NameError.new(
368
369
  "unknown function extension #{name}",
369
370
  token,
370
371
  @query
@@ -199,23 +199,15 @@ module JSONP3
199
199
  length = node.value.length
200
200
  return [] if length.zero? || @step.zero?
201
201
 
202
- normalized_start = if @start.nil?
203
- @step.negative? ? length - 1 : 0
204
- elsif @start&.negative?
205
- [length + (@start || raise), 0].max
206
- else
207
- [@start || raise, length - 1].min
208
- end
209
-
210
- normalized_stop = if @stop.nil?
211
- @step.negative? ? -1 : length
212
- elsif @stop&.negative?
213
- [length + (@stop || raise), -1].max
214
- else
215
- [@stop || raise, length].min
216
- end
202
+ if step.positive?
203
+ norm_start = normalized_index(@start, 0, length, @step)
204
+ norm_end = normalized_index(@stop, length, length, @step)
205
+ else
206
+ norm_start = normalized_index(@start, length - 1, length, @step)
207
+ norm_end = normalized_index(@stop, -1, length, @step)
208
+ end
217
209
 
218
- (normalized_start...normalized_stop).step(@step).map { |i| node.new_child(node.value[i], i) }
210
+ (norm_start...norm_end).step(@step).map { |i| node.new_child(node.value[i], i) }
219
211
  end
220
212
 
221
213
  def to_s
@@ -238,6 +230,19 @@ module JSONP3
238
230
  def hash
239
231
  [@start, @stop, @step, @token].hash
240
232
  end
233
+
234
+ private
235
+
236
+ #: (Integer?, Integer, Integer, Integer) -> Integer
237
+ def normalized_index(num, default, length, step)
238
+ return default if num.nil?
239
+
240
+ if num.negative?
241
+ return step.negative? ? [num + length, -1].max : [num + length, 0].max
242
+ end
243
+
244
+ step.negative? ? [num, length - 1].min : [num, length].min
245
+ end
241
246
  end
242
247
 
243
248
  # Select array elements or hash values according to a filter expression.
@@ -48,14 +48,6 @@ module JSONP3
48
48
  )
49
49
  end
50
50
 
51
- if code_point <= 0x1f
52
- raise JSONP3::Path::SyntaxError.new(
53
- "invalid character #{code_point}",
54
- token,
55
- query
56
- )
57
- end
58
-
59
51
  unescaped << code_point.chr(Encoding::UTF_8)
60
52
  next
61
53
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSONP3
4
- VERSION = "1.0.0"
4
+ VERSION = "1.0.1"
5
5
  end
@@ -15,6 +15,13 @@ module JSONP3
15
15
 
16
16
  # Return a JSON-like representation of this patch operation.
17
17
  def to_h: () -> untyped
18
+
19
+ # Add _obj_ to _parent_ at _target_ following the semantics of the JSON
20
+ # Patch _add_ operation: insert into arrays, set object members.
21
+ private def add_member: (untyped parent, String | Integer target, untyped obj, Integer index) -> void
22
+
23
+ # Return the key of _parent_ identified by pointer token _target_.
24
+ private def member_key: (Hash[untyped, untyped] parent, String | Integer target) -> (String | Integer)
18
25
  end
19
26
 
20
27
  # The JSON Patch _add_ operation.
@@ -29,6 +29,9 @@ module JSONP3
29
29
  alias eql? ==
30
30
 
31
31
  def hash: () -> Integer
32
+
33
+ #: (Integer?, Integer, Integer, Integer) -> Integer
34
+ def normalized_index: (Integer? num, Integer default, Integer length, Integer step) -> Integer
32
35
  end
33
36
 
34
37
  # The name selector select values from hashes given a key.
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json_p3
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Prior
@@ -11,29 +11,29 @@ cert_chain:
11
11
  -----BEGIN CERTIFICATE-----
12
12
  MIIEhTCCAu2gAwIBAgIBATANBgkqhkiG9w0BAQsFADBEMRYwFAYDVQQDDA1qYW1l
13
13
  c2dyLnByaW9yMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZ
14
- FgNjb20wHhcNMjUxMDI1MDc0ODI4WhcNMjYxMDI1MDc0ODI4WjBEMRYwFAYDVQQD
14
+ FgNjb20wHhcNMjYxMDE4MTMzMTExWhcNMjcxMDE4MTMzMTExWjBEMRYwFAYDVQQD
15
15
  DA1qYW1lc2dyLnByaW9yMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJ
16
- k/IsZAEZFgNjb20wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDYTvQW
17
- 0JzsHenYu4w0iHgJRtU9uK8vkDx1wlMioKlPFkaiMfxqUcTTfg80U/n8n/KKmpdq
18
- y2rpUqv+VPE2nrfCUncf7ukqs+gF8uehbciMXV4iOzsZmqmtlPUJiGuSyO8REe/O
19
- ERAyxYV8y5psF+WnW4uQzsWCKTnKbexIc6rf237veuYvYoBpnodJu0mJ0m9tBCFU
20
- qH3Nk1sHBRIEEwn+nTiIiOMb+QMU5p0NIQkIhJO3el5nEQgIGxMPnie7PnwxPquQ
21
- 1rouuISbLmQy5oKYc9kScwnHdAu2PtnnJFNNuQ37vED375iQgMem/tK8EgMm9/Qr
22
- Qxd2DQqqehgtEySWntGHTxNLXQouqHIRyqzkbcCatJqC+PBBql1x2N1KqgyvbLM3
23
- B0dZCdJECr5J8zrluMxYsy7uGMmLz3LZ7PskGaJg4XxXP3j/B/e03/AxDj0neZZu
24
- xuDLIfMDSNILO1pskMc+Xp0xdnkbS4IjiMDTrhqABuEcdNYH/GpyTiA/I0cCAwEA
25
- AaOBgTB/MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBR4j7UhnpT9
26
- mLcAkshwkFtwVpMdejAiBgNVHREEGzAZgRdqYW1lc2dyLnByaW9yQGdtYWlsLmNv
16
+ k/IsZAEZFgNjb20wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDAXkrS
17
+ W5E5Ts892bcQSSt/+RoE6T7yf6rWBxWs6PSNksUM05toEX7Mmsq+u5hJ2oVit9Tp
18
+ /Gvpq4eANMv95vQnLmiT+1J3YauckaTB0TaArf77ytdiOxlfgYRt0Di6QmZVkinR
19
+ BDA9qTcsuwmia5IBG6mcjpmAoiNeBtz6jv/PqCYBitnJEW0dpaqd7r3Lmzh9B562
20
+ Z/kGD8nmVpA6Wj/i5PjBlhMfQ7TRdAZi4xji96gAoFo4v3JY8r8baH6ww/uT4cFC
21
+ O0vIh2FpSyrWT/nAqEqfjHl5XShLTUdDbcTyl5rnU1lZr66A0gl4x/gKYglYsRfO
22
+ xo9TPH2P0G3U+iTI/a0jeNlXi9cxZMw1LaUChB+rI9qa/xpvOYUb76pu7BJmtpp/
23
+ QyBHujNa0VCtCmfWJnVaR6JNPKza5VP38BPvS1YriXDnkLAxZ4ruAXrTtneQyemP
24
+ 4umgDSJ9ESXdNfgZkGcXsNmXSU7vLYZAp4OajBhUp5hnRSw4y6HWKt8oG2kCAwEA
25
+ AaOBgTB/MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBROcgTGvg8n
26
+ 3d5QbkQz4JzlE7Jk0TAiBgNVHREEGzAZgRdqYW1lc2dyLnByaW9yQGdtYWlsLmNv
27
27
  bTAiBgNVHRIEGzAZgRdqYW1lc2dyLnByaW9yQGdtYWlsLmNvbTANBgkqhkiG9w0B
28
- AQsFAAOCAYEAbTkg946BsGPQFX3jskBjwINeFnMP0IppuStonPQv3SNQu1SWP0xS
29
- o1Qji1dxkWDfOcp1ffrRMUo7ecrJbTqQ5Sdx+7zw0OLodSVqSu1LMIVDw/T73smg
30
- ADO2r3eZ8vxCKh7z4wUeKXvmBXi5NvpmvhH7D+qeG4OMLI8jnfmfbSHZQZbe2tpd
31
- tJC+MYVok/m91fBrfv5wqLsR5QiJl3vcVhG+xACd46gr3JZFOxBqpSJrCYL8Gwqx
32
- BaKEySCLGqkb0d1Rnt2/zTR2KtW2QJVH9rBMnJ+LsPEFKs1UHH4qHzxmwmmaQLon
33
- MoE4UJ51GHGrjJ4WsHOUSOy5UbjaQec3kuIDwTXKbHws0J0Ut9S4S38opZKdQHDD
34
- PUBpY0QA1CgE/VjKAYEDHOJyfUCNOl+sEESM+WxEXZAUVZLrFF7MljJqyQq+JyTD
35
- vFKHe1ZAA76NFrPscB/ODPTBK8i+u4gwrD1UAsVAHuIbOpQu4LYAujluCu5cl2A8
36
- HhmlCT7sTkqG
28
+ AQsFAAOCAYEAZLTr3Z6ZNmlI+AT9ci2wCC7A74OVNME8eZaqcBxGuXE/9jWycIQX
29
+ RTggqZcofJpgKNMFIrelQDCa/Jf3fbCtdVfbcEb6vITlr7Zt/M/mHzu/QjjIFpmT
30
+ gTXlP8APjoQxWaRrhOAm5nbgwYKrOPdLtBu3uzN3PAhbZQlLHyM49bWSux75Ibxy
31
+ nL0cYGR05H3ym7U9XRRuEExHJ0gCW67CwchWNav0yEgeko5Sn3BSjMISBdy9jF5c
32
+ bJgFKiVQFTN+5+uHtBUu5YrtCgsMTgr09g6DgpUUlALHOyg7+XEp/MeqZqE3g5Qu
33
+ JubJmxfW7PrLuyBLkHeIxpvVdpORTaH3/U5qb45XPJ1z2IHC91IlBIoFSOsDrCrL
34
+ kPjfyWVsL/uWyRIDnXRoTTepSBND6NmHrGWuTiYDB7r/Yeqj714R6GF1qe2jZlDW
35
+ yE12qcU40zRwg3LPrEwjuaF4Lo29G666PBWxGcLbakvRUDuO5GG5rqJydmX2KV/J
36
+ NlOr4XEE+abN
37
37
  -----END CERTIFICATE-----
38
38
  date: 1980-01-02 00:00:00.000000000 Z
39
39
  dependencies: []
metadata.gz.sig CHANGED
Binary file