json_select 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Build Status](http://travis-ci.org/fd/json_select.png)](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
|