iso-jsonpath 1.1.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0c58f2694eac62a528c76cbdd58c827a9269302570cf765ce4b65611696fac39
4
+ data.tar.gz: 8331f78eb483639bb0b453c3ec72887bfbf91692982454a1476608ca2d32d012
5
+ SHA512:
6
+ metadata.gz: ede9bb040400866ea9a8f4674b86dc6b8d63c5774f984eb20c0a59238994a609fd5e2da42a6390d67d2e8133bb3e00acd8baa3650b8e452aa1cdfa65f3f7d74a
7
+ data.tar.gz: 4b3f0ca17ee3229794e06b620a12e975bdd67d655ca5b0f82c63ffdddd932cc2267864afe4833788bd2ea99c9c11293c5c3437f7e2b5b89230a1c5e5ea3f008b
data/.gemtest ADDED
File without changes
@@ -0,0 +1,33 @@
1
+ name: test
2
+ on:
3
+ - push
4
+ - pull_request
5
+ jobs:
6
+ test:
7
+ strategy:
8
+ fail-fast: false
9
+ matrix:
10
+ ruby-version:
11
+ - '2.6'
12
+ - '2.7'
13
+ - '3.2'
14
+ - '3.1'
15
+ - '3.0'
16
+ - ruby-head
17
+ - jruby-head
18
+ - truffleruby-head
19
+ runs-on:
20
+ - ubuntu-latest
21
+
22
+ runs-on: ${{ matrix.runs-on }}
23
+
24
+ steps:
25
+
26
+ - uses: actions/checkout@v2
27
+
28
+ - uses: ruby/setup-ruby@v1
29
+ with:
30
+ ruby-version: ${{ matrix.ruby-version }}
31
+ bundler-cache: true
32
+
33
+ - run: bundle exec rake test
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ pkg/*
2
+ Gemfile.lock
3
+ coverage/*
4
+ doc/*
5
+ .yardoc
6
+ .DS_Store
7
+ .idea
8
+ vendor
9
+ .tags
10
+ *.gem
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --colour
2
+ --format doc
3
+ --backtrace
data/.rubocop.yml ADDED
@@ -0,0 +1 @@
1
+ inherit_from: .rubocop_todo.yml
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,127 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2019-01-25 09:23:04 +0100 using RuboCop version 0.63.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 15
10
+ # Configuration parameters: AllowSafeAssignment.
11
+ Lint/AssignmentInCondition:
12
+ Exclude:
13
+ - 'lib/jsonpath.rb'
14
+ - 'lib/jsonpath/parser.rb'
15
+
16
+ # Offense count: 1
17
+ Lint/IneffectiveAccessModifier:
18
+ Exclude:
19
+ - 'lib/jsonpath.rb'
20
+
21
+ # Offense count: 17
22
+ Metrics/AbcSize:
23
+ Max: 60
24
+
25
+ # Offense count: 2
26
+ # Configuration parameters: CountComments, ExcludedMethods.
27
+ # ExcludedMethods: refine
28
+ Metrics/BlockLength:
29
+ Max: 37
30
+
31
+ # Offense count: 1
32
+ # Configuration parameters: CountBlocks.
33
+ Metrics/BlockNesting:
34
+ Max: 4
35
+
36
+ # Offense count: 3
37
+ # Configuration parameters: CountComments.
38
+ Metrics/ClassLength:
39
+ Max: 739
40
+
41
+ # Offense count: 7
42
+ Metrics/CyclomaticComplexity:
43
+ Max: 20
44
+
45
+ # Offense count: 26
46
+ # Configuration parameters: CountComments, ExcludedMethods.
47
+ Metrics/MethodLength:
48
+ Max: 52
49
+
50
+ # Offense count: 1
51
+ # Configuration parameters: CountKeywordArgs.
52
+ Metrics/ParameterLists:
53
+ Max: 6
54
+
55
+ # Offense count: 6
56
+ Metrics/PerceivedComplexity:
57
+ Max: 21
58
+
59
+ # Offense count: 1
60
+ # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
61
+ # AllowedNames: io, id, to, by, on, in, at, ip, db
62
+ Naming/UncommunicativeMethodParamName:
63
+ Exclude:
64
+ - 'lib/jsonpath/parser.rb'
65
+
66
+ # Offense count: 15
67
+ # Configuration parameters: AllowedChars.
68
+ Style/AsciiComments:
69
+ Exclude:
70
+ - 'lib/jsonpath/parser.rb'
71
+
72
+ # Offense count: 2
73
+ Style/Documentation:
74
+ Exclude:
75
+ - 'spec/**/*'
76
+ - 'test/**/*'
77
+ - 'lib/jsonpath/enumerable.rb'
78
+ - 'lib/jsonpath/proxy.rb'
79
+
80
+ # Offense count: 3
81
+ # Configuration parameters: MinBodyLength.
82
+ Style/GuardClause:
83
+ Exclude:
84
+ - 'lib/jsonpath/enumerable.rb'
85
+ - 'lib/jsonpath/parser.rb'
86
+
87
+ # Offense count: 2
88
+ # Cop supports --auto-correct.
89
+ Style/IfUnlessModifier:
90
+ Exclude:
91
+ - 'lib/jsonpath/enumerable.rb'
92
+
93
+ # Offense count: 1
94
+ Style/MultipleComparison:
95
+ Exclude:
96
+ - 'lib/jsonpath/parser.rb'
97
+
98
+ # Offense count: 3
99
+ # Cop supports --auto-correct.
100
+ # Configuration parameters: AutoCorrect, EnforcedStyle, IgnoredMethods.
101
+ # SupportedStyles: predicate, comparison
102
+ Style/NumericPredicate:
103
+ Exclude:
104
+ - 'spec/**/*'
105
+ - 'lib/jsonpath/enumerable.rb'
106
+ - 'lib/jsonpath/parser.rb'
107
+
108
+ # Offense count: 3
109
+ # Cop supports --auto-correct.
110
+ # Configuration parameters: EnforcedStyle, AllowInnerSlashes.
111
+ # SupportedStyles: slashes, percent_r, mixed
112
+ Style/RegexpLiteral:
113
+ Exclude:
114
+ - 'lib/jsonpath/parser.rb'
115
+
116
+ # Offense count: 4
117
+ # Cop supports --auto-correct.
118
+ Style/RescueModifier:
119
+ Exclude:
120
+ - 'lib/jsonpath/enumerable.rb'
121
+ - 'lib/jsonpath/parser.rb'
122
+
123
+ # Offense count: 89
124
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
125
+ # URISchemes: http, https
126
+ Metrics/LineLength:
127
+ Max: 296
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'http://rubygems.org'
4
+ gemspec
5
+ # gem 'rubocop', require: true, group: :test
6
+ gem 'simplecov', require: false, group: :test
data/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Joshua Lin & Gergely Brautigam
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,301 @@
1
+ # JsonPath
2
+
3
+ This is an implementation of http://goessner.net/articles/JsonPath/.
4
+
5
+ ## What is JsonPath?
6
+
7
+ JsonPath is a way of addressing elements within a JSON object. Similar to xpath
8
+ of yore, JsonPath lets you traverse a json object and manipulate or access it.
9
+
10
+ ## Usage
11
+
12
+ ### Command-line
13
+
14
+ There is stand-alone usage through the binary `jsonpath`
15
+
16
+ jsonpath [expression] (file|string)
17
+
18
+ If you omit the second argument, it will read stdin, assuming one valid JSON
19
+ object per line. Expression must be a valid jsonpath expression.
20
+
21
+ ### Library
22
+
23
+ To use JsonPath as a library simply include and get goin'!
24
+
25
+ ```ruby
26
+ require 'jsonpath'
27
+
28
+ json = <<-HERE_DOC
29
+ {"store":
30
+ {"bicycle":
31
+ {"price":19.95, "color":"red"},
32
+ "book":[
33
+ {"price":8.95, "category":"reference", "title":"Sayings of the Century", "author":"Nigel Rees"},
34
+ {"price":12.99, "category":"fiction", "title":"Sword of Honour", "author":"Evelyn Waugh"},
35
+ {"price":8.99, "category":"fiction", "isbn":"0-553-21311-3", "title":"Moby Dick", "author":"Herman Melville","color":"blue"},
36
+ {"price":22.99, "category":"fiction", "isbn":"0-395-19395-8", "title":"The Lord of the Rings", "author":"Tolkien"}
37
+ ]
38
+ }
39
+ }
40
+ HERE_DOC
41
+ ```
42
+
43
+ Now that we have a JSON object, let's get all the prices present in the object.
44
+ We create an object for the path in the following way.
45
+
46
+ ```ruby
47
+ path = JsonPath.new('$..price')
48
+ ```
49
+
50
+ Now that we have a path, let's apply it to the object above.
51
+
52
+ ```ruby
53
+ path.on(json)
54
+ # => [19.95, 8.95, 12.99, 8.99, 22.99]
55
+ ```
56
+
57
+ Or reuse it later on some other object (thread safe) ...
58
+
59
+ ```ruby
60
+ path.on('{"books":[{"title":"A Tale of Two Somethings","price":18.88}]}')
61
+ # => [18.88]
62
+ ```
63
+
64
+ You can also just combine this into one mega-call with the convenient
65
+ `JsonPath.on` method.
66
+
67
+ ```ruby
68
+ JsonPath.on(json, '$..author')
69
+ # => ["Nigel Rees", "Evelyn Waugh", "Herman Melville", "Tolkien"]
70
+ ```
71
+
72
+ Of course the full JsonPath syntax is supported, such as array slices
73
+
74
+ ```ruby
75
+ JsonPath.new('$..book[::2]').on(json)
76
+ # => [
77
+ # {"price" => 8.95, "category" => "reference", "title" => "Sayings of the Century", "author" => "Nigel Rees"},
78
+ # {"price" => 8.99, "category" => "fiction", "isbn" => "0-553-21311-3", "title" => "Moby Dick", "author" => "Herman Melville","color" => "blue"},
79
+ # ]
80
+ ```
81
+
82
+ ...and evals, including those with conditional operators
83
+
84
+ ```ruby
85
+ JsonPath.new("$..price[?(@ < 10)]").on(json)
86
+ # => [8.95, 8.99]
87
+
88
+ JsonPath.new("$..book[?(@['price'] == 8.95 || @['price'] == 8.99)].title").on(json)
89
+ # => ["Sayings of the Century", "Moby Dick"]
90
+
91
+ JsonPath.new("$..book[?(@['price'] == 8.95 && @['price'] == 8.99)].title").on(json)
92
+ # => []
93
+ ```
94
+
95
+ There is a convenience method, `#first` that gives you the first element for a
96
+ JSON object and path.
97
+
98
+ ```ruby
99
+ JsonPath.new('$..color').first(json)
100
+ # => "red"
101
+ ```
102
+
103
+ As well, we can directly create an `Enumerable` at any time using `#[]`.
104
+
105
+ ```ruby
106
+ enum = JsonPath.new('$..color')[json]
107
+ # => #<JsonPath::Enumerable:...>
108
+ enum.first
109
+ # => "red"
110
+ enum.any?{ |c| c == 'red' }
111
+ # => true
112
+ ```
113
+
114
+ For more usage examples and variations on paths, please visit the tests. There
115
+ are some more complex ones as well.
116
+
117
+ ### Querying ruby data structures
118
+
119
+ If you have ruby hashes with symbolized keys as input, you
120
+ can use `:use_symbols` to make JsonPath work fine on them too:
121
+
122
+ ```ruby
123
+ book = { title: "Sayings of the Century" }
124
+
125
+ JsonPath.new('$.title').on(book)
126
+ # => []
127
+
128
+ JsonPath.new('$.title', use_symbols: true).on(book)
129
+ # => ["Sayings of the Century"]
130
+ ```
131
+
132
+ JsonPath also recognizes objects responding to `dig` (introduced
133
+ in ruby 2.3), and therefore works out of the box with Struct,
134
+ OpenStruct, and other Hash-like structures:
135
+
136
+ ```ruby
137
+ book_class = Struct.new(:title)
138
+ book = book_class.new("Sayings of the Century")
139
+
140
+ JsonPath.new('$.title').on(book)
141
+ # => ["Sayings of the Century"]
142
+ ```
143
+
144
+ JsonPath is able to query pure ruby objects and uses `__send__`
145
+ on them. The option is enabled by default in JsonPath 1.x, but
146
+ we encourage to enable it explicitly:
147
+
148
+ ```ruby
149
+ book_class = Class.new{ attr_accessor :title }
150
+ book = book_class.new
151
+ book.title = "Sayings of the Century"
152
+
153
+ JsonPath.new('$.title', allow_send: true).on(book)
154
+ # => ["Sayings of the Century"]
155
+ ```
156
+
157
+ ### Other available options
158
+
159
+ By default, JsonPath does not return null values on unexisting paths.
160
+ This can be changed using the `:default_path_leaf_to_null` option
161
+
162
+ ```ruby
163
+ JsonPath.new('$..book[*].isbn').on(json)
164
+ # => ["0-553-21311-3", "0-395-19395-8"]
165
+
166
+ JsonPath.new('$..book[*].isbn', default_path_leaf_to_null: true).on(json)
167
+ # => [nil, nil, "0-553-21311-3", "0-395-19395-8"]
168
+ ```
169
+
170
+ When JsonPath returns a Hash, you can ask to symbolize its keys
171
+ using the `:symbolize_keys` option
172
+
173
+ ```ruby
174
+ JsonPath.new('$..book[0]').on(json)
175
+ # => [{"category" => "reference", ...}]
176
+
177
+ JsonPath.new('$..book[0]', symbolize_keys: true).on(json)
178
+ # => [{category: "reference", ...}]
179
+ ```
180
+
181
+ ### Selecting Values
182
+
183
+ It's possible to select results once a query has been defined after the query. For
184
+ example given this JSON data:
185
+
186
+ ```bash
187
+ {
188
+ "store": {
189
+ "book": [
190
+ {
191
+ "category": "reference",
192
+ "author": "Nigel Rees",
193
+ "title": "Sayings of the Century",
194
+ "price": 8.95
195
+ },
196
+ {
197
+ "category": "fiction",
198
+ "author": "Evelyn Waugh",
199
+ "title": "Sword of Honour",
200
+ "price": 12.99
201
+ }
202
+ ]
203
+ }
204
+ ```
205
+
206
+ ... and this query:
207
+
208
+ ```ruby
209
+ "$.store.book[*](category,author)"
210
+ ```
211
+
212
+ ... the result can be filtered as such:
213
+
214
+ ```bash
215
+ [
216
+ {
217
+ "category" : "reference",
218
+ "author" : "Nigel Rees"
219
+ },
220
+ {
221
+ "category" : "fiction",
222
+ "author" : "Evelyn Waugh"
223
+ }
224
+ ]
225
+ ```
226
+
227
+ ### Manipulation
228
+
229
+ If you'd like to do substitution in a json object, you can use `#gsub`
230
+ or `#gsub!` to modify the object in place.
231
+
232
+ ```ruby
233
+ JsonPath.for('{"candy":"lollipop"}').gsub('$..candy') {|v| "big turks" }.to_hash
234
+ ```
235
+
236
+ The result will be
237
+
238
+ ```ruby
239
+ {'candy' => 'big turks'}
240
+ ```
241
+
242
+ If you'd like to remove all nil keys, you can use `#compact` and `#compact!`.
243
+ To remove all keys under a certain path, use `#delete` or `#delete!`. You can
244
+ even chain these methods together as follows:
245
+
246
+ ```ruby
247
+ json = '{"candy":"lollipop","noncandy":null,"other":"things"}'
248
+ o = JsonPath.for(json).
249
+ gsub('$..candy') {|v| "big turks" }.
250
+ compact.
251
+ delete('$..other').
252
+ to_hash
253
+ # => {"candy" => "big turks"}
254
+ ```
255
+
256
+ ### Fetch all paths
257
+
258
+ To fetch all possible paths in given json, you can use `fetch_all_path`` method.
259
+
260
+ data:
261
+
262
+ ```bash
263
+ {
264
+ "store": {
265
+ "book": [
266
+ {
267
+ "category": "reference",
268
+ "author": "Nigel Rees"
269
+ },
270
+ {
271
+ "category": "fiction",
272
+ "author": "Evelyn Waugh"
273
+ }
274
+ ]
275
+ }
276
+ ```
277
+
278
+ ... and this query:
279
+
280
+ ```ruby
281
+ JsonPath.fetch_all_path(data)
282
+ ```
283
+
284
+ ... the result will be:
285
+
286
+ ```bash
287
+ ["$", "$.store", "$.store.book", "$.store.book[0].category", "$.store.book[0].author", "$.store.book[0]", "$.store.book[1].category", "$.store.book[1].author", "$.store.book[1]"]
288
+ ```
289
+
290
+
291
+
292
+ # Contributions
293
+
294
+ Please feel free to submit an Issue or a Pull Request any time you feel like
295
+ you would like to contribute. Thank you!
296
+
297
+ ## Running an individual test
298
+
299
+ ```ruby
300
+ ruby -Ilib:../lib test/test_jsonpath.rb --name test_wildcard_on_intermediary_element_v6
301
+ ```
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ desc 'run rubocop'
4
+ task(:rubocop) do
5
+ require 'rubocop'
6
+ cli = RuboCop::CLI.new
7
+ cli.run
8
+ end
9
+
10
+ require 'simplecov'
11
+ SimpleCov.start do
12
+ add_filter '/test/'
13
+ end
14
+
15
+ require 'bundler'
16
+ Bundler::GemHelper.install_tasks
17
+
18
+ task :test do
19
+ $LOAD_PATH << 'lib'
20
+ Dir['./test/**/test_*.rb'].each { |test| require test }
21
+ end
22
+
23
+ task default: %i[test rubocop]
data/bin/jsonpath ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'jsonpath'
5
+ require 'multi_json'
6
+
7
+ def usage
8
+ puts "jsonpath [expression] (file|string)
9
+
10
+ If you omit the second argument, it will read stdin, assuming one valid JSON object
11
+ per line. Expression must be a valid jsonpath expression."
12
+ exit!
13
+ end
14
+
15
+ usage unless ARGV[0]
16
+
17
+ jsonpath = JsonPath.new(ARGV[0])
18
+ case ARGV[1]
19
+ when nil # stdin
20
+ puts MultiJson.encode(jsonpath.on(MultiJson.decode(STDIN.read)))
21
+ when String
22
+ puts MultiJson.encode(jsonpath.on(MultiJson.decode(File.exist?(ARGV[1]) ? File.read(ARGV[1]) : ARGV[1])))
23
+ end
data/jsonpath.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.join(File.dirname(__FILE__), 'lib', 'jsonpath', 'version')
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'iso-jsonpath'
7
+ s.version = JsonPath::VERSION
8
+ s.required_ruby_version = '>= 2.6'
9
+ s.authors = ['Joshua Hull', 'Gergely Brautigam']
10
+ s.summary = 'Ruby implementation of http://goessner.net/articles/JsonPath/'
11
+ s.description = 'Ruby implementation of http://goessner.net/articles/JsonPath/.'
12
+ s.email = ['joshbuddy@gmail.com', 'skarlso777@gmail.com']
13
+ s.extra_rdoc_files = ['README.md']
14
+ s.files = `git ls-files`.split("\n")
15
+ s.homepage = 'https://github.com/joshbuddy/jsonpath'
16
+ s.test_files = `git ls-files`.split("\n").select { |f| f =~ /^spec/ }
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
18
+ s.licenses = ['MIT']
19
+
20
+ # dependencies
21
+ s.add_runtime_dependency 'multi_json'
22
+ s.add_development_dependency 'bundler'
23
+ s.add_development_dependency 'code_stats'
24
+ s.add_development_dependency 'minitest', '~> 2.2.0'
25
+ s.add_development_dependency 'phocus'
26
+ s.add_development_dependency 'racc'
27
+ s.add_development_dependency 'rake'
28
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ class JsonPath
4
+ module Dig
5
+
6
+ # Similar to what Hash#dig or Array#dig
7
+ def dig(context, *keys)
8
+ keys.inject(context){|memo,k|
9
+ dig_one(memo, k)
10
+ }
11
+ end
12
+
13
+ # Returns a hash mapping each key from keys
14
+ # to its dig value on context.
15
+ def dig_as_hash(context, keys)
16
+ keys.each_with_object({}) do |k, memo|
17
+ memo[k] = dig_one(context, k)
18
+ end
19
+ end
20
+
21
+ # Dig the value of k on context.
22
+ def dig_one(context, k)
23
+ case context
24
+ when Hash
25
+ context[@options[:use_symbols] ? k.to_sym : k]
26
+ when Array
27
+ context[k.to_i]
28
+ else
29
+ if context.respond_to?(:dig)
30
+ context.dig(k)
31
+ elsif @options[:allow_send]
32
+ context.__send__(k)
33
+ end
34
+ end
35
+ end
36
+
37
+ # Yields the block if context has a diggable
38
+ # value for k
39
+ def yield_if_diggable(context, k, &blk)
40
+ case context
41
+ when Array
42
+ nil
43
+ when Hash
44
+ k = @options[:use_symbols] ? k.to_sym : k
45
+ return yield if context.key?(k) || @options[:default_path_leaf_to_null]
46
+ else
47
+ if context.respond_to?(:dig)
48
+ digged = dig_one(context, k)
49
+ yield if !digged.nil? || @options[:default_path_leaf_to_null]
50
+ elsif @options[:allow_send] && context.respond_to?(k.to_s) && !Object.respond_to?(k.to_s)
51
+ yield
52
+ end
53
+ end
54
+ end
55
+
56
+ end
57
+ end