json_p3 0.3.0 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d20426353cc6a3b8f7be130f6b5d3dd8ff37dcad13ac2b81a7a421f70d0462c
4
- data.tar.gz: c93805a6ca467e830881176da0b9203bf3cfce7d85690749621bf3609de43e1c
3
+ metadata.gz: afd5de1be45132d6071616dc7aa21425dc3d9beaed525315f017b343b434a42f
4
+ data.tar.gz: 19f0fc51ac0f8680e47808beda7f206ea9d911b33ebebaccb5803cd7bcd012c0
5
5
  SHA512:
6
- metadata.gz: 329b8946de0220d55a7d1358bfd3062bced99420247b14d95ec023ab7fbb22a336534d227ebbf37766890f2c2c68bf5dc482504e10860b12845830930c560d5f
7
- data.tar.gz: d248901e8c95aaa4ca7336d56439542cb4c6208c94993a6324e48b9dcbf95c00ed84040f36d697c99fe8b629e48c58f5c01bd0d55a6e8eac2ffa2af23ce06779
6
+ metadata.gz: 9c6458e2fc86fa09988edbc032ad434cca4125f1d6a368e2f8e37e79e65b80fb404d12a3ec9dfb42ef007dbfc2977a4492814c9a8d689900921e2cfa885b6c11
7
+ data.tar.gz: 54580931a9a37165dcbb10f8a240c8142a8de013cbc9e76776f59a5f60b43743f98db65daae7d85968d61a6eda1983df26f9ae8d8aaf1571b3a45106236a8ed1
checksums.yaml.gz.sig CHANGED
Binary file
data/.rubocop.yml CHANGED
@@ -23,7 +23,7 @@ Metrics/CyclomaticComplexity:
23
23
  Max: 15
24
24
 
25
25
  Metrics/MethodLength:
26
- Max: 35
26
+ Max: 50
27
27
 
28
28
  Metrics/PerceivedComplexity:
29
29
  Max: 20
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.3.6
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## [0.3.2] - 2025-01-29
2
+
3
+ - Fix normalized string representations of node locations as returned by `JSONPathNode.path`.
4
+ - Fix canonical string representations of instances of `JSONPath`, as returned by `to_s`.
5
+ - Fixed filter queries with multiple bracketed segments. Previously we were failing to tokenize queries like `$[?@[0][0]]`. See [#15](https://github.com/jg-rp/ruby-json-p3/issues/15).
6
+
7
+ ## [0.3.1] - 2024-12-05
8
+
9
+ - Fix JSON Patch `move` and `copy` operations when using the special JSON Pointer token `-`.
10
+
1
11
  ## [0.3.0] - 2024-11-25
2
12
 
3
13
  - Implement JSON Pointer and Relative JSON Pointer
data/README.md CHANGED
@@ -474,6 +474,12 @@ Print memory usage to the terminal.
474
474
  bundle exec ruby performance/memory_profile.rb
475
475
  ```
476
476
 
477
- ### TruffleRuby
477
+ ### Notes to self
478
+
479
+ #### Build
480
+
481
+ `bundle exec rake release` and `bundle exec rake build` will look for `gem-private_key.pem` and `gem-public_cert.pem` in `~/.gem`.
482
+
483
+ #### TruffleRuby
478
484
 
479
485
  On macOS Sonoma using MacPorts and `rbenv`, `LIBYAML_PREFIX=/opt/local/lib` is needed to install TruffleRuby and when executing any `bundle` command.
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json"
4
3
  require_relative "function"
4
+ require_relative "serialize"
5
5
 
6
6
  module JSONP3 # rubocop:disable Style/Documentation
7
7
  # Base class for all filter expression nodes.
@@ -33,7 +33,7 @@ module JSONP3 # rubocop:disable Style/Documentation
33
33
  end
34
34
 
35
35
  def to_s
36
- @expression.to_s
36
+ to_canonical_string(@expression, Precedence::LOWEST)
37
37
  end
38
38
 
39
39
  def ==(other)
@@ -47,6 +47,39 @@ module JSONP3 # rubocop:disable Style/Documentation
47
47
  def hash
48
48
  [@expression, @token].hash
49
49
  end
50
+
51
+ private
52
+
53
+ class Precedence
54
+ LOWEST = 1
55
+ LOGICAL_OR = 3
56
+ LOGICAL_AND = 4
57
+ PREFIX = 7
58
+ end
59
+
60
+ def to_canonical_string(expression, parent_precedence)
61
+ if expression.instance_of? LogicalAndExpression
62
+ left = to_canonical_string(expression.left, Precedence::LOGICAL_AND)
63
+ right = to_canonical_string(expression.right, Precedence::LOGICAL_AND)
64
+ expr = "#{left} && #{right}"
65
+ return parent_precedence >= Precedence::LOGICAL_AND ? "(#{expr})" : expr
66
+ end
67
+
68
+ if expression.instance_of? LogicalOrExpression
69
+ left = to_canonical_string(expression.left, Precedence::LOGICAL_OR)
70
+ right = to_canonical_string(expression.right, Precedence::LOGICAL_OR)
71
+ expr = "#{left} || #{right}"
72
+ return parent_precedence >= Precedence::LOGICAL_OR ? "(#{expr})" : expr
73
+ end
74
+
75
+ if expression.instance_of? LogicalNotExpression
76
+ operand = to_canonical_string(expression.expression, Precedence::PREFIX)
77
+ expr = "!#{operand}"
78
+ return parent_precedence > Precedence::PREFIX ? `(#{expr})` : expr
79
+ end
80
+
81
+ expression.to_s
82
+ end
50
83
  end
51
84
 
52
85
  # Base class for expression literals.
@@ -85,7 +118,7 @@ module JSONP3 # rubocop:disable Style/Documentation
85
118
  # A double or single quoted string literal.
86
119
  class StringLiteral < FilterExpressionLiteral
87
120
  def to_s
88
- JSON.generate(@value)
121
+ JSONP3.canonical_string(@value)
89
122
  end
90
123
  end
91
124
 
data/lib/json_p3/lexer.rb CHANGED
@@ -209,7 +209,7 @@ module JSONP3 # rubocop:disable Style/Documentation
209
209
  case c
210
210
  when "]"
211
211
  emit(:token_rbracket, "]")
212
- return @filter_depth.positive? ? :lex_inside_filter : :lex_segment
212
+ return :lex_segment
213
213
  when ""
214
214
  error "unclosed bracketed selection"
215
215
  return nil
data/lib/json_p3/node.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "serialize"
4
+
3
5
  module JSONP3
4
6
  # A JSON-like value and its location.
5
7
  class JSONPathNode
@@ -19,7 +21,7 @@ module JSONP3
19
21
  # Return the normalized path to this node.
20
22
  # @return [String] the normalized path.
21
23
  def path
22
- segments = @location.flatten.map { |i| i.is_a?(String) ? "['#{i}']" : "[#{i}]" }
24
+ segments = @location.flatten.map { |i| i.is_a?(String) ? "[#{JSONP3.canonical_string(i)}]" : "[#{i}]" }
23
25
  "$#{segments.join}"
24
26
  end
25
27
 
@@ -116,7 +116,7 @@ module JSONP3
116
116
  end
117
117
  end
118
118
 
119
- def parse_bracketed_selection(stream) # rubocop:disable Metrics/MethodLength
119
+ def parse_bracketed_selection(stream)
120
120
  stream.expect(:token_lbracket)
121
121
  segment_token = stream.next
122
122
 
@@ -245,7 +245,7 @@ module JSONP3
245
245
  FilterSelector.new(@env, token, FilterExpression.new(token, expression))
246
246
  end
247
247
 
248
- def parse_filter_expression(stream, precedence = Precedence::LOWEST) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
248
+ def parse_filter_expression(stream, precedence = Precedence::LOWEST) # rubocop:disable Metrics/CyclomaticComplexity
249
249
  left = case stream.peek.type
250
250
  when :token_double_quote_string, :token_single_quote_string
251
251
  token = stream.next
@@ -313,7 +313,7 @@ module JSONP3
313
313
  end
314
314
  end
315
315
 
316
- def parse_function_expression(stream) # rubocop:disable Metrics/MethodLength
316
+ def parse_function_expression(stream)
317
317
  token = stream.next
318
318
  args = [] # : Array[Expression]
319
319
 
data/lib/json_p3/patch.rb CHANGED
@@ -220,7 +220,11 @@ module JSONP3
220
220
 
221
221
  # Write the source value to the destination.
222
222
  if dest_parent.is_a?(Array)
223
- dest_parent[dest_target.to_i] = source_obj
223
+ if dest_target == "-"
224
+ dest_parent << source_obj
225
+ else
226
+ dest_parent[dest_target.to_i] = source_obj
227
+ end
224
228
  elsif dest_parent.is_a?(Hash)
225
229
  dest_parent[dest_target] = source_obj
226
230
  end
@@ -267,7 +271,11 @@ module JSONP3
267
271
 
268
272
  # Write the source value to the destination.
269
273
  if dest_parent.is_a?(Array)
270
- dest_parent.insert(dest_target.to_i, deep_copy(source_obj))
274
+ if dest_target == "-"
275
+ dest_parent << source_obj
276
+ else
277
+ dest_parent.insert(dest_target.to_i, deep_copy(source_obj))
278
+ end
271
279
  elsif dest_parent.is_a?(Hash)
272
280
  dest_parent[dest_target] = deep_copy(source_obj)
273
281
  else
@@ -46,7 +46,7 @@ module JSONP3
46
46
  end
47
47
 
48
48
  def to_s
49
- @name.inspect
49
+ JSONP3.canonical_string(@name)
50
50
  end
51
51
 
52
52
  def ==(other)
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module JSONP3 # rubocop:disable Style/Documentation
6
+ TRANS = { "\\\"" => "\"", "'" => "\\'" }.freeze
7
+
8
+ # Return _value_ formatted as a canonical string literal.
9
+ # @param value [String]
10
+ def self.canonical_string(value)
11
+ "'#{(JSON.dump(value)[1..-2] || raise).gsub(/('|\\")/, TRANS)}'"
12
+ end
13
+ end
@@ -6,7 +6,7 @@ module JSONP3 # rubocop:disable Style/Documentation
6
6
  # @param quote [String] one of '"' or "'".
7
7
  # @param token [Token]
8
8
  # @return [String] A new string without escape sequences.
9
- def self.unescape_string(value, quote, token) # rubocop:disable Metrics/MethodLength
9
+ def self.unescape_string(value, quote, token)
10
10
  unescaped = String.new(encoding: "UTF-8")
11
11
  index = 0
12
12
  length = value.length
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSONP3
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.2"
5
5
  end
data/sig/json_p3.rbs CHANGED
@@ -126,7 +126,7 @@ module JSONP3
126
126
 
127
127
  def initialize: (Token token) -> void
128
128
 
129
- # Evaluate the filter expressin in the given context.
129
+ # Evaluate the filter expression in the given context.
130
130
  def evaluate: (FilterContext _context) -> untyped
131
131
  end
132
132
 
@@ -147,6 +147,10 @@ module JSONP3
147
147
  alias eql? ==
148
148
 
149
149
  def hash: () -> Integer
150
+
151
+ private
152
+
153
+ def to_canonical_string: (Expression expression, Integer parent_precedence) -> String
150
154
  end
151
155
 
152
156
  # Base class for expression literals.
@@ -394,7 +398,7 @@ module JSONP3
394
398
  # @return [Array<Token>]
395
399
  def self.tokenize: (String query) -> Array[Token]
396
400
 
397
- # JSONPath query expreession lexical scanner.
401
+ # JSONPath query expression lexical scanner.
398
402
  #
399
403
  # @see tokenize
400
404
  class Lexer
@@ -428,7 +432,7 @@ module JSONP3
428
432
  # Generate a new token with the given type.
429
433
  # @param token_type [Symbol] one of the constants defined on the _Token_ class.
430
434
  # @param value [String | nil] a the token's value, if it is known, otherwise the
431
- # value will be sliced from @query. This is a performance optimisation.
435
+ # value will be sliced from @query. This is a performance optimization.
432
436
  def emit: (token_t token_type, ?untyped value) -> void
433
437
 
434
438
  def next: () -> String
@@ -488,7 +492,7 @@ module JSONP3
488
492
 
489
493
  # @param value [JSON-like] the value at this node.
490
494
  # @param location [Array<String | Integer | Array<String | Integer>>] the sequence of
491
- # names and/or indicies leading to _value_ in _root_.
495
+ # names and/or indices leading to _value_ in _root_.
492
496
  # @param root [JSON-like] the root value containing _value_ at _location_.
493
497
  def initialize: (untyped value, Array[location_element] location, untyped root) -> void
494
498
 
@@ -498,7 +502,7 @@ module JSONP3
498
502
 
499
503
  # Return a new node that is a child of this node.
500
504
  # @param value the JSON-like value at the new node.
501
- # @param key [Integer, String] the array index or hash key assiciated with _value_.
505
+ # @param key [Integer, String] the array index or hash key associated with _value_.
502
506
  def new_child: (untyped value, String | Integer key) -> JSONPathNode
503
507
 
504
508
  def to_s: () -> ::String
@@ -926,7 +930,7 @@ module JSONP3
926
930
  # @param value [String]
927
931
  # @param quote [String] one of '"' or "'".
928
932
  # @param token [Token]
929
- # @return [String] A new string without escape seqeuences.
933
+ # @return [String] A new string without escape sequences.
930
934
  def self.unescape_string: (String value, String quote, Token token) -> String
931
935
 
932
936
  def self.decode_hex_char: (untyped value, untyped index, Token token) -> ::Array[untyped]
@@ -938,6 +942,8 @@ module JSONP3
938
942
  def self.low_surrogate?: (untyped code_point) -> untyped
939
943
 
940
944
  def self.code_point_to_string: (untyped code_point, Token token) -> untyped
945
+
946
+ def self.canonical_string: (untyped String) -> ::String
941
947
  end
942
948
 
943
949
  module JSONP3
@@ -1162,7 +1168,7 @@ module JSONP3
1162
1168
  class OpRemove < Op
1163
1169
  @pointer: JSONPointer
1164
1170
 
1165
- # @param papointerth [JSONPointer]
1171
+ # @param pointer [JSONPointer]
1166
1172
  def initialize: (JSONPointer pointer) -> void
1167
1173
 
1168
1174
  def name: () -> "remove"
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: 0.3.0
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Prior
@@ -36,7 +36,7 @@ cert_chain:
36
36
  6dM18fnfBc3yA4KI7AO8UAmRkTscMYV6f/K4YZR6ZYCNWRpY7rkg+arhf05aoSQf
37
37
  vn9bO1bzwdnG
38
38
  -----END CERTIFICATE-----
39
- date: 2024-11-25 00:00:00.000000000 Z
39
+ date: 2025-01-29 00:00:00.000000000 Z
40
40
  dependencies: []
41
41
  description: JSONPath following RFC 9535
42
42
  email:
@@ -46,6 +46,7 @@ extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
48
  - ".rubocop.yml"
49
+ - ".ruby-version"
49
50
  - ".yardopts"
50
51
  - CHANGELOG.md
51
52
  - LICENCE
@@ -73,6 +74,7 @@ files:
73
74
  - lib/json_p3/pointer.rb
74
75
  - lib/json_p3/segment.rb
75
76
  - lib/json_p3/selector.rb
77
+ - lib/json_p3/serialize.rb
76
78
  - lib/json_p3/token.rb
77
79
  - lib/json_p3/unescape.rb
78
80
  - lib/json_p3/version.rb
@@ -106,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
108
  - !ruby/object:Gem::Version
107
109
  version: '0'
108
110
  requirements: []
109
- rubygems_version: 3.5.16
111
+ rubygems_version: 3.5.22
110
112
  signing_key:
111
113
  specification_version: 4
112
114
  summary: 'JSONPath: Query Expressions for JSON in Ruby'
metadata.gz.sig CHANGED
Binary file