json_select 0.1.2 → 0.1.3
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.
- data/.travis.yml +3 -3
- data/README.md +4 -2
- data/lib/json_select.rb +6 -0
- data/lib/json_select/helpers/depth.rb +7 -0
- data/lib/json_select/helpers/key.rb +7 -0
- data/lib/json_select/helpers/position.rb +24 -0
- data/lib/json_select/helpers/size.rb +24 -0
- data/lib/json_select/helpers/type.rb +31 -0
- data/lib/json_select/selector.rb +28 -129
- data/lib/json_select/version.rb +1 -1
- data/spec/fixtures/basic_first-child.output +0 -4
- data/spec/fixtures/basic_last-child.output +0 -4
- data/spec/fixtures/basic_nth-child-2.output +0 -9
- data/spec/fixtures/basic_nth-child.output +0 -4
- data/spec/fixtures/basic_nth-last-child.output +0 -4
- data/spec/fixtures/basic_universal.output +1 -1
- data/spec/ruby_extensions_spec.rb +18 -2
- metadata +43 -29
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# JSONSelect
|
1
|
+
# JSONSelect [](http://travis-ci.org/fd/json_select)
|
2
2
|
|
3
3
|
**CSS-like selectors for JSON.**
|
4
4
|
|
@@ -48,7 +48,9 @@ json = { # This would normally be loaded with something like yajl-ruby
|
|
48
48
|
"weight" => 172
|
49
49
|
}
|
50
50
|
|
51
|
-
JSONSelect('string:first-child').
|
51
|
+
JSONSelect('string:first-child').test(json) # => true
|
52
|
+
JSONSelect('string:first-child').match(json) # => "window"
|
53
|
+
JSONSelect('string:first-child').matches(json) # => ["window", "beer"]
|
52
54
|
```
|
53
55
|
|
54
56
|
## Note on Patches/Pull Requests
|
data/lib/json_select.rb
CHANGED
@@ -5,6 +5,12 @@ class JSONSelect
|
|
5
5
|
require 'json_select/version'
|
6
6
|
require 'json_select/selector_parser'
|
7
7
|
require 'json_select/selector'
|
8
|
+
|
9
|
+
require 'json_select/helpers/size'
|
10
|
+
require 'json_select/helpers/key'
|
11
|
+
require 'json_select/helpers/type'
|
12
|
+
require 'json_select/helpers/depth'
|
13
|
+
require 'json_select/helpers/position'
|
8
14
|
|
9
15
|
module Ast
|
10
16
|
require 'json_select/ast/combination_selector'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module JSONSelect::PositionHelpers
|
2
|
+
|
3
|
+
def nth_child(object, test, key, idx, size, depth)
|
4
|
+
return false unless idx and size
|
5
|
+
|
6
|
+
idx += 1
|
7
|
+
|
8
|
+
a = test[:a]
|
9
|
+
b = test[:b]
|
10
|
+
|
11
|
+
if a == 0
|
12
|
+
(b == idx)
|
13
|
+
else
|
14
|
+
(((idx - b) % a) == 0) and ((idx * a + b) >= 0)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def nth_last_child(object, test, key, idx, size, depth)
|
19
|
+
return false unless idx and size
|
20
|
+
|
21
|
+
nth_child(object, test, key, (size - idx) - 1, size, depth)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module JSONSelect::SizeHelpers
|
2
|
+
|
3
|
+
def only_child(object, test, key, idx, size, depth)
|
4
|
+
return false unless size
|
5
|
+
|
6
|
+
size == 1
|
7
|
+
end
|
8
|
+
|
9
|
+
def empty(object, test, key, idx, size, depth)
|
10
|
+
return false unless size
|
11
|
+
|
12
|
+
case object
|
13
|
+
when Array then return object.empty?
|
14
|
+
else
|
15
|
+
if object.respond_to?(:json_select_each)
|
16
|
+
object.json_select_each { return false }
|
17
|
+
return true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
return false
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module JSONSelect::TypeHelpers
|
2
|
+
|
3
|
+
def instance_of_type(object, test, key, idx, size, depth)
|
4
|
+
test[:n] == type_of(object)
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def type_of(object)
|
10
|
+
if object.respond_to?(:json_select_each)
|
11
|
+
return 'array'
|
12
|
+
end
|
13
|
+
|
14
|
+
if object.respond_to?(:json_select_each_pair)
|
15
|
+
return 'object'
|
16
|
+
end
|
17
|
+
|
18
|
+
case object
|
19
|
+
when Hash then 'object'
|
20
|
+
when Array then 'array'
|
21
|
+
when String then 'string'
|
22
|
+
when Symbol then 'string'
|
23
|
+
when Numeric then 'number'
|
24
|
+
when TrueClass then 'boolean'
|
25
|
+
when FalseClass then 'boolean'
|
26
|
+
when NilClass then 'null'
|
27
|
+
else raise "Invalid object of class #{object.class} for JSONSelect: #{object.inspect}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/lib/json_select/selector.rb
CHANGED
@@ -42,6 +42,19 @@ class JSONSelect
|
|
42
42
|
raise ArgumentError, "Expected a string for ast"
|
43
43
|
|
44
44
|
end
|
45
|
+
|
46
|
+
@helpers = Module.new
|
47
|
+
@helpers.send(:extend,
|
48
|
+
JSONSelect::KeyHelpers,
|
49
|
+
JSONSelect::TypeHelpers,
|
50
|
+
JSONSelect::SizeHelpers,
|
51
|
+
JSONSelect::DepthHelpers,
|
52
|
+
JSONSelect::PositionHelpers)
|
53
|
+
|
54
|
+
@helper_methods = {}
|
55
|
+
(@helpers.public_methods - Module.public_methods).map do |name|
|
56
|
+
@helper_methods[name.to_sym] = @helpers.method(name)
|
57
|
+
end
|
45
58
|
end
|
46
59
|
|
47
60
|
# Returns the first matching child in `object`
|
@@ -79,32 +92,6 @@ class JSONSelect
|
|
79
92
|
|
80
93
|
private
|
81
94
|
|
82
|
-
# function forEach(sel, obj, fun, id, num, tot) {
|
83
|
-
# var a = (sel[0] === ',') ? sel.slice(1) : [sel];
|
84
|
-
# var a0 = [];
|
85
|
-
# var call = false;
|
86
|
-
# for (var i = 0; i < a.length; i++) {
|
87
|
-
# var x = mn(obj, a[i], id, num, tot);
|
88
|
-
# if (x[0]) call = true;
|
89
|
-
# for (var j = 0; j < x[1].length; j++) a0.push(x[1][j]);
|
90
|
-
# }
|
91
|
-
# if (a0.length && typeof obj === 'object') {
|
92
|
-
# if (a0.length >= 1) a0.unshift(",");
|
93
|
-
# if (isArray(obj)) {
|
94
|
-
# for (var i = 0; i < obj.length; i++) forEach(a0, obj[i], fun, undefined, i, obj.length);
|
95
|
-
# } else {
|
96
|
-
# // it's a shame to do this for :last-child and other
|
97
|
-
# // properties which count from the end when we don't
|
98
|
-
# // even know if they're present. Also, the stream
|
99
|
-
# // parser is going to be pissed.
|
100
|
-
# var l = 0;
|
101
|
-
# for (var k in obj) if (obj.hasOwnProperty(k)) l++;
|
102
|
-
# var i = 0;
|
103
|
-
# for (var k in obj) if (obj.hasOwnProperty(k)) forEach(a0, obj[k], fun, k, i++, l);
|
104
|
-
# }
|
105
|
-
# }
|
106
|
-
# if (call && fun) fun(obj);
|
107
|
-
# };
|
108
95
|
def _each(selector, object, id, number, total, depth, &block)
|
109
96
|
a0 = (selector[0] == ',' ? selector[1..-1] : [selector])
|
110
97
|
a1 = []
|
@@ -131,27 +118,33 @@ private
|
|
131
118
|
|
132
119
|
when Hash
|
133
120
|
a1.unshift(',')
|
134
|
-
size = object.size
|
135
121
|
|
136
|
-
object.
|
137
|
-
_each(a1, child, key,
|
122
|
+
object.each do |key, child|
|
123
|
+
_each(a1, child, key, nil, nil, depth + 1, &block)
|
138
124
|
end
|
139
125
|
|
140
126
|
else
|
141
127
|
if object.respond_to?(:json_select_each)
|
142
128
|
children = []
|
143
|
-
object.json_select_each do |
|
144
|
-
children <<
|
129
|
+
object.json_select_each do |value|
|
130
|
+
children << value
|
145
131
|
end
|
146
132
|
|
147
133
|
a1.unshift(',')
|
148
134
|
size = children.size
|
149
135
|
|
150
|
-
children.each_with_index do |
|
151
|
-
_each(a1, child,
|
136
|
+
children.each_with_index do |child, idx|
|
137
|
+
_each(a1, child, nil, idx, size, depth + 1, &block)
|
138
|
+
end
|
139
|
+
|
140
|
+
elsif object.respond_to?(:json_select_each_pair)
|
141
|
+
a1.unshift(',')
|
142
|
+
|
143
|
+
object.json_select_each_pair do |key, child|
|
144
|
+
_each(a1, child, key, nil, nil, depth + 1, &block)
|
152
145
|
end
|
146
|
+
|
153
147
|
end
|
154
|
-
|
155
148
|
end
|
156
149
|
end
|
157
150
|
|
@@ -160,34 +153,6 @@ private
|
|
160
153
|
end
|
161
154
|
end
|
162
155
|
|
163
|
-
|
164
|
-
# function mn(node, sel, id, num, tot) {
|
165
|
-
# var sels = [];
|
166
|
-
# var cs = (sel[0] === '>') ? sel[1] : sel[0];
|
167
|
-
# var m = true;
|
168
|
-
# if (cs.type) m = m && (cs.type === mytypeof(node));
|
169
|
-
# if (cs.id) m = m && (cs.id === id);
|
170
|
-
# if (m && cs.pf) {
|
171
|
-
# if (cs.pf === ":nth-last-child") num = tot - num;
|
172
|
-
# else num++;
|
173
|
-
# if (cs.a === 0) {
|
174
|
-
# m = cs.b === num;
|
175
|
-
# } else {
|
176
|
-
# m = (!((num - cs.b) % cs.a) && ((num*cs.a + cs.b) >= 0));
|
177
|
-
# }
|
178
|
-
# }
|
179
|
-
#
|
180
|
-
# // should we repeat this selector for descendants?
|
181
|
-
# if (sel[0] !== '>' && sel[0].pc !== ":root") sels.push(sel);
|
182
|
-
#
|
183
|
-
# if (m) {
|
184
|
-
# // is there a fragment that we should pass down?
|
185
|
-
# if (sel[0] === '>') { if (sel.length > 2) { m = false; sels.push(sel.slice(2)); } }
|
186
|
-
# else if (sel.length > 1) { m = false; sels.push(sel.slice(1)); }
|
187
|
-
# }
|
188
|
-
#
|
189
|
-
# return [m, sels];
|
190
|
-
# }
|
191
156
|
def _match(object, selector, id, number, total, depth)
|
192
157
|
selectors = []
|
193
158
|
current_selector = (selector[0] == :> ? selector[1] : selector[0])
|
@@ -200,7 +165,7 @@ private
|
|
200
165
|
end
|
201
166
|
|
202
167
|
if match
|
203
|
-
match =
|
168
|
+
match = !!@helper_methods[test[:f].to_sym].call(object, test, id, number, total, depth)
|
204
169
|
end
|
205
170
|
end
|
206
171
|
|
@@ -235,70 +200,4 @@ private
|
|
235
200
|
return [match, selectors]
|
236
201
|
end
|
237
202
|
|
238
|
-
def type_of(object)
|
239
|
-
if object.respond_to?(:json_select_each)
|
240
|
-
return 'object'
|
241
|
-
end
|
242
|
-
|
243
|
-
case object
|
244
|
-
when Hash then 'object'
|
245
|
-
when Array then 'array'
|
246
|
-
when String then 'string'
|
247
|
-
when Symbol then 'string'
|
248
|
-
when Numeric then 'number'
|
249
|
-
when TrueClass then 'boolean'
|
250
|
-
when FalseClass then 'boolean'
|
251
|
-
when NilClass then 'null'
|
252
|
-
else raise "Invalid object of class #{object.class} for JSONSelect: #{object.inspect}"
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
def only_child(object, test, key, idx, size, depth)
|
257
|
-
size == 1
|
258
|
-
end
|
259
|
-
|
260
|
-
def empty(object, test, key, idx, size, depth)
|
261
|
-
case object
|
262
|
-
when Array then return object.empty?
|
263
|
-
when Hash then return object.empty?
|
264
|
-
else
|
265
|
-
if object.respond_to?(:json_select_each)
|
266
|
-
object.json_select_each { return false }
|
267
|
-
return true
|
268
|
-
end
|
269
|
-
end
|
270
|
-
return false
|
271
|
-
end
|
272
|
-
|
273
|
-
def is_root(object, test, key, idx, size, depth)
|
274
|
-
depth == 0
|
275
|
-
end
|
276
|
-
|
277
|
-
def instance_of_type(object, test, key, idx, size, depth)
|
278
|
-
test[:n] == type_of(object)
|
279
|
-
end
|
280
|
-
|
281
|
-
def has_class(object, test, key, idx, size, depth)
|
282
|
-
test[:n] == key.to_s
|
283
|
-
end
|
284
|
-
|
285
|
-
def nth_child(object, test, key, idx, size, depth)
|
286
|
-
return false unless idx
|
287
|
-
|
288
|
-
idx += 1
|
289
|
-
|
290
|
-
a = test[:a]
|
291
|
-
b = test[:b]
|
292
|
-
|
293
|
-
if a == 0
|
294
|
-
(b == idx)
|
295
|
-
else
|
296
|
-
(((idx - b) % a) == 0) and ((idx * a + b) >= 0)
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
def nth_last_child(object, test, key, idx, size, depth)
|
301
|
-
nth_child(object, test, key, (size - idx) - 1, size, depth)
|
302
|
-
end
|
303
|
-
|
304
203
|
end
|
data/lib/json_select/version.rb
CHANGED
@@ -12,9 +12,9 @@ describe "JSONSelect", "Ruby extensions" do
|
|
12
12
|
should == :hello
|
13
13
|
end
|
14
14
|
|
15
|
-
it "treats Objects which respond to #
|
15
|
+
it "treats Objects which respond to #json_select_each_pair as Objects" do
|
16
16
|
class Person < Struct.new(:name, :age)
|
17
|
-
def
|
17
|
+
def json_select_each_pair
|
18
18
|
yield(:name, self.name)
|
19
19
|
yield(:age, self.age)
|
20
20
|
end
|
@@ -25,4 +25,20 @@ describe "JSONSelect", "Ruby extensions" do
|
|
25
25
|
should == 'Simon Menke'
|
26
26
|
end
|
27
27
|
|
28
|
+
it "treats Objects which respond to #json_select_each as Arrays" do
|
29
|
+
class List
|
30
|
+
def initialize(items)
|
31
|
+
@items = items
|
32
|
+
end
|
33
|
+
|
34
|
+
def json_select_each
|
35
|
+
@items.each { |i| yield(i) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
JSONSelect('array.person string:first-child').
|
40
|
+
match(:person => List.new(%w( foo bar baz ))).
|
41
|
+
should == 'foo'
|
42
|
+
end
|
43
|
+
|
28
44
|
end
|
metadata
CHANGED
@@ -1,34 +1,39 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: json_select
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.2
|
3
|
+
version: !ruby/object:Gem::Version
|
5
4
|
prerelease:
|
5
|
+
version: 0.1.3
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- Simon Menke
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
|
13
|
+
date: 2011-05-27 00:00:00 +02:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
15
17
|
name: treetop
|
16
|
-
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
17
20
|
none: false
|
18
|
-
requirements:
|
19
|
-
- -
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version:
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
22
25
|
type: :runtime
|
23
|
-
|
24
|
-
version_requirements: *70235358530860
|
26
|
+
version_requirements: *id001
|
25
27
|
description: JSONSelect implementation for Ruby
|
26
|
-
email:
|
28
|
+
email:
|
27
29
|
- simon.menke@gmail.com
|
28
30
|
executables: []
|
31
|
+
|
29
32
|
extensions: []
|
33
|
+
|
30
34
|
extra_rdoc_files: []
|
31
|
-
|
35
|
+
|
36
|
+
files:
|
32
37
|
- .gitignore
|
33
38
|
- .travis.yml
|
34
39
|
- Gemfile
|
@@ -48,6 +53,11 @@ files:
|
|
48
53
|
- lib/json_select/ast/simple_selector.rb
|
49
54
|
- lib/json_select/ast/type_selector.rb
|
50
55
|
- lib/json_select/ast/universal_selector.rb
|
56
|
+
- lib/json_select/helpers/depth.rb
|
57
|
+
- lib/json_select/helpers/key.rb
|
58
|
+
- lib/json_select/helpers/position.rb
|
59
|
+
- lib/json_select/helpers/size.rb
|
60
|
+
- lib/json_select/helpers/type.rb
|
51
61
|
- lib/json_select/selector.rb
|
52
62
|
- lib/json_select/selector_parser.rb
|
53
63
|
- lib/json_select/selector_parser.tt
|
@@ -110,31 +120,35 @@ files:
|
|
110
120
|
- spec/fixtures/basic_universal.selector
|
111
121
|
- spec/ruby_extensions_spec.rb
|
112
122
|
- spec/spec_helper.rb
|
123
|
+
has_rdoc: true
|
113
124
|
homepage: http://github.com/fd/json_select
|
114
125
|
licenses: []
|
126
|
+
|
115
127
|
post_install_message:
|
116
128
|
rdoc_options: []
|
117
|
-
|
129
|
+
|
130
|
+
require_paths:
|
118
131
|
- lib
|
119
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
120
133
|
none: false
|
121
|
-
requirements:
|
122
|
-
- -
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version:
|
125
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: "0"
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
126
139
|
none: false
|
127
|
-
requirements:
|
128
|
-
- -
|
129
|
-
- !ruby/object:Gem::Version
|
130
|
-
version:
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: "0"
|
131
144
|
requirements: []
|
145
|
+
|
132
146
|
rubyforge_project: json_select
|
133
|
-
rubygems_version: 1.
|
147
|
+
rubygems_version: 1.6.2
|
134
148
|
signing_key:
|
135
149
|
specification_version: 3
|
136
150
|
summary: JSONSelect implementation for Ruby
|
137
|
-
test_files:
|
151
|
+
test_files:
|
138
152
|
- spec/conformance_spec.rb
|
139
153
|
- spec/fixtures/README.md
|
140
154
|
- spec/fixtures/alltests.txt
|