json_select 0.1.1 → 0.1.2
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/lib/json_select/ast/hash_selector.rb +2 -2
- data/lib/json_select/ast/pseudo_selector.rb +9 -6
- data/lib/json_select/ast/simple_selector.rb +8 -5
- data/lib/json_select/ast/type_selector.rb +1 -1
- data/lib/json_select/ast/universal_selector.rb +1 -1
- data/lib/json_select/selector.rb +88 -42
- data/lib/json_select/selector_parser.rb +13 -2
- data/lib/json_select/selector_parser.tt +1 -1
- data/lib/json_select/version.rb +1 -1
- data/spec/fixtures/basic_children.ast +10 -2
- data/spec/fixtures/basic_combination.ast +10 -2
- data/spec/fixtures/basic_first-child.ast +9 -4
- data/spec/fixtures/basic_grouping.ast +15 -5
- data/spec/fixtures/basic_id.ast +5 -1
- data/spec/fixtures/basic_id_multiple.ast +5 -1
- data/spec/fixtures/basic_id_quotes.ast +5 -1
- data/spec/fixtures/basic_id_with_type.ast +8 -2
- data/spec/fixtures/basic_last-child.ast +10 -5
- data/spec/fixtures/basic_nth-child-2.ast +9 -4
- data/spec/fixtures/basic_nth-child.ast +9 -4
- data/spec/fixtures/basic_nth-last-child.ast +9 -4
- data/spec/fixtures/basic_root_pseudo.ast +4 -1
- data/spec/fixtures/basic_type.ast +5 -1
- data/spec/fixtures/basic_type2.ast +5 -1
- data/spec/fixtures/basic_type3.ast +5 -1
- data/spec/fixtures/basic_universal.ast +4 -1
- metadata +29 -38
@@ -2,20 +2,23 @@ module JSONSelect::Ast::PseudoSelector
|
|
2
2
|
|
3
3
|
def to_ast
|
4
4
|
if respond_to?(:e)
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
test = { :f => a.text_value.gsub('-', '_') }
|
6
|
+
test.merge!(e.to_ast)
|
7
|
+
test
|
8
8
|
else
|
9
9
|
case a.text_value
|
10
10
|
|
11
11
|
when 'first-child'
|
12
|
-
{ :
|
12
|
+
{ :f => :nth_child, :a => 0, :b => 1 }
|
13
13
|
|
14
14
|
when 'last-child'
|
15
|
-
{ :
|
15
|
+
{ :f => :nth_last_child, :a => 0, :b => 1 }
|
16
|
+
|
17
|
+
when 'root'
|
18
|
+
{ :f => :is_root }
|
16
19
|
|
17
20
|
else
|
18
|
-
{ :
|
21
|
+
{ :f => a.text_value.gsub('-', '_') }
|
19
22
|
|
20
23
|
end
|
21
24
|
end
|
@@ -1,23 +1,26 @@
|
|
1
1
|
module JSONSelect::Ast::SimpleSelector
|
2
2
|
|
3
3
|
def to_ast
|
4
|
-
|
4
|
+
tests = []
|
5
|
+
node = {:tests => tests}
|
5
6
|
|
6
7
|
if respond_to?(:a) and respond_to?(:b)
|
7
|
-
|
8
|
+
tests << a.to_ast
|
8
9
|
|
9
10
|
b.elements.each do |s|
|
10
|
-
|
11
|
+
tests << s.to_ast
|
11
12
|
end
|
12
13
|
|
13
14
|
else
|
14
15
|
self.elements.each do |s|
|
15
|
-
|
16
|
+
tests << s.to_ast
|
16
17
|
end
|
17
18
|
|
18
19
|
end
|
19
20
|
|
20
|
-
|
21
|
+
tests.compact!
|
22
|
+
|
23
|
+
node
|
21
24
|
end
|
22
25
|
|
23
26
|
end
|
data/lib/json_select/selector.rb
CHANGED
@@ -46,7 +46,7 @@ class JSONSelect
|
|
46
46
|
|
47
47
|
# Returns the first matching child in `object`
|
48
48
|
def match(object)
|
49
|
-
_each(@ast, object, nil, nil, nil) do |object|
|
49
|
+
_each(@ast, object, nil, nil, nil, 0) do |object|
|
50
50
|
return object
|
51
51
|
end
|
52
52
|
|
@@ -59,7 +59,7 @@ class JSONSelect
|
|
59
59
|
def matches(object)
|
60
60
|
matches = []
|
61
61
|
|
62
|
-
_each(@ast, object, nil, nil, nil) do |object|
|
62
|
+
_each(@ast, object, nil, nil, nil, 0) do |object|
|
63
63
|
matches << object
|
64
64
|
end
|
65
65
|
|
@@ -68,7 +68,7 @@ class JSONSelect
|
|
68
68
|
|
69
69
|
# Returns true if `object` has any matching children.
|
70
70
|
def test(object)
|
71
|
-
_each(@ast, object, nil, nil, nil) do |object|
|
71
|
+
_each(@ast, object, nil, nil, nil, 0) do |object|
|
72
72
|
return true
|
73
73
|
end
|
74
74
|
|
@@ -105,14 +105,14 @@ private
|
|
105
105
|
# }
|
106
106
|
# if (call && fun) fun(obj);
|
107
107
|
# };
|
108
|
-
def _each(selector, object, id, number, total, &block)
|
108
|
+
def _each(selector, object, id, number, total, depth, &block)
|
109
109
|
a0 = (selector[0] == ',' ? selector[1..-1] : [selector])
|
110
110
|
a1 = []
|
111
111
|
|
112
112
|
call = false
|
113
113
|
|
114
114
|
a0.each do |selector|
|
115
|
-
ok, extra = _match(object, selector, id, number, total)
|
115
|
+
ok, extra = _match(object, selector, id, number, total, depth)
|
116
116
|
|
117
117
|
call = true if ok
|
118
118
|
a1.concat extra
|
@@ -126,7 +126,7 @@ private
|
|
126
126
|
size = object.size
|
127
127
|
|
128
128
|
object.each_with_index do |child, idx|
|
129
|
-
_each(a1, child, nil, idx, size, &block)
|
129
|
+
_each(a1, child, nil, idx, size, depth + 1, &block)
|
130
130
|
end
|
131
131
|
|
132
132
|
when Hash
|
@@ -134,7 +134,7 @@ private
|
|
134
134
|
size = object.size
|
135
135
|
|
136
136
|
object.each_with_index do |(key, child), idx|
|
137
|
-
_each(a1, child, key, idx, size, &block)
|
137
|
+
_each(a1, child, key, idx, size, depth + 1, &block)
|
138
138
|
end
|
139
139
|
|
140
140
|
else
|
@@ -148,7 +148,7 @@ private
|
|
148
148
|
size = children.size
|
149
149
|
|
150
150
|
children.each_with_index do |(key, child), idx|
|
151
|
-
_each(a1, child, key, idx, size, &block)
|
151
|
+
_each(a1, child, key, idx, size, depth + 1, &block)
|
152
152
|
end
|
153
153
|
end
|
154
154
|
|
@@ -188,56 +188,54 @@ private
|
|
188
188
|
#
|
189
189
|
# return [m, sels];
|
190
190
|
# }
|
191
|
-
def _match(object, selector, id, number, total)
|
191
|
+
def _match(object, selector, id, number, total, depth)
|
192
192
|
selectors = []
|
193
193
|
current_selector = (selector[0] == :> ? selector[1] : selector[0])
|
194
194
|
match = true
|
195
|
+
has_root_test = false
|
195
196
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
if current_selector.key?(:class)
|
201
|
-
match = (match and current_selector[:class] == id.to_s)
|
202
|
-
end
|
203
|
-
|
204
|
-
if match and current_selector.key?(:pseudo_function)
|
205
|
-
pseudo_function = current_selector[:pseudo_function]
|
206
|
-
|
207
|
-
if pseudo_function == 'nth-last-child'
|
208
|
-
number = total - number
|
209
|
-
else
|
210
|
-
number += 1
|
197
|
+
current_selector[:tests].each do |test|
|
198
|
+
if test[:f] == :is_root
|
199
|
+
has_root_test = true
|
211
200
|
end
|
212
|
-
|
213
|
-
if
|
214
|
-
match = (
|
215
|
-
else
|
216
|
-
# WTF!
|
217
|
-
match = ((((number - current_selector[:b]) % current_selector[:a]) == 0) && ((number * current_selector[:a] + current_selector[:b]) >= 0))
|
201
|
+
|
202
|
+
if match
|
203
|
+
match = !!send(test[:f], object, test, id, number, total, depth)
|
218
204
|
end
|
219
205
|
end
|
220
206
|
|
221
|
-
|
207
|
+
# continue search for decendants
|
208
|
+
if Hash === selector[0] and !has_root_test
|
222
209
|
selectors.push selector
|
223
210
|
end
|
224
211
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
212
|
+
# break search for terminals
|
213
|
+
if selector.size <= 1
|
214
|
+
return [match, selectors]
|
215
|
+
end
|
216
|
+
|
217
|
+
# break search (no match)
|
218
|
+
unless match
|
219
|
+
return [match, selectors]
|
220
|
+
end
|
221
|
+
|
222
|
+
# continue search for decendants
|
223
|
+
unless selector[0] == :>
|
224
|
+
match = false
|
225
|
+
selectors.push selector[1..-1]
|
226
|
+
return [match, selectors]
|
227
|
+
end
|
228
|
+
|
229
|
+
# continue search for children
|
230
|
+
if selector.length > 2
|
231
|
+
match = false
|
232
|
+
selectors.push selector[2..-1]
|
235
233
|
end
|
236
234
|
|
237
235
|
return [match, selectors]
|
238
236
|
end
|
239
237
|
|
240
|
-
def
|
238
|
+
def type_of(object)
|
241
239
|
if object.respond_to?(:json_select_each)
|
242
240
|
return 'object'
|
243
241
|
end
|
@@ -254,5 +252,53 @@ private
|
|
254
252
|
else raise "Invalid object of class #{object.class} for JSONSelect: #{object.inspect}"
|
255
253
|
end
|
256
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
|
257
303
|
|
258
304
|
end
|
@@ -794,8 +794,19 @@ module JSONSelect::Selector
|
|
794
794
|
if r4
|
795
795
|
r0 = r4
|
796
796
|
else
|
797
|
-
|
798
|
-
|
797
|
+
if has_terminal?('empty', false, index)
|
798
|
+
r5 = instantiate_node(SyntaxNode,input, index...(index + 5))
|
799
|
+
@index += 5
|
800
|
+
else
|
801
|
+
terminal_parse_failure('empty')
|
802
|
+
r5 = nil
|
803
|
+
end
|
804
|
+
if r5
|
805
|
+
r0 = r5
|
806
|
+
else
|
807
|
+
@index = i0
|
808
|
+
r0 = nil
|
809
|
+
end
|
799
810
|
end
|
800
811
|
end
|
801
812
|
end
|
@@ -69,7 +69,7 @@ grammar JSONSelectSelector
|
|
69
69
|
# pseudo_class_name
|
70
70
|
# : `root` | `first-child` | `last-child` | `only-child`
|
71
71
|
rule pseudo_class_name
|
72
|
-
'root' / 'first-child' / 'last-child' / 'only-child'
|
72
|
+
'root' / 'first-child' / 'last-child' / 'only-child' / 'empty'
|
73
73
|
end
|
74
74
|
|
75
75
|
# pseudo_function_name
|
data/lib/json_select/version.rb
CHANGED
@@ -1,12 +1,22 @@
|
|
1
1
|
[ ","
|
2
2
|
|
3
|
-
, [ { "
|
4
|
-
|
3
|
+
, [ { "tests" :
|
4
|
+
[ { "f" : "instance_of_type"
|
5
|
+
, "n" : "string"
|
6
|
+
}
|
7
|
+
, { "f" : "has_class"
|
8
|
+
, "n" : "level"
|
9
|
+
}
|
10
|
+
]
|
5
11
|
}
|
6
12
|
]
|
7
|
-
|
8
|
-
, [ { "
|
13
|
+
|
14
|
+
, [ { "tests" :
|
15
|
+
[ { "f" : "instance_of_type"
|
16
|
+
, "n" : "number"
|
17
|
+
}
|
18
|
+
]
|
9
19
|
}
|
10
20
|
]
|
11
|
-
|
21
|
+
|
12
22
|
]
|
data/spec/fixtures/basic_id.ast
CHANGED
metadata
CHANGED
@@ -1,39 +1,34 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: json_select
|
3
|
-
version: !ruby/object:Gem::Version
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
4
5
|
prerelease:
|
5
|
-
version: 0.1.1
|
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
|
-
|
15
|
-
dependencies:
|
16
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2011-05-26 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
17
15
|
name: treetop
|
18
|
-
|
19
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: &70235358530860 !ruby/object:Gem::Requirement
|
20
17
|
none: false
|
21
|
-
requirements:
|
22
|
-
- -
|
23
|
-
- !ruby/object:Gem::Version
|
24
|
-
version:
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
25
22
|
type: :runtime
|
26
|
-
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70235358530860
|
27
25
|
description: JSONSelect implementation for Ruby
|
28
|
-
email:
|
26
|
+
email:
|
29
27
|
- simon.menke@gmail.com
|
30
28
|
executables: []
|
31
|
-
|
32
29
|
extensions: []
|
33
|
-
|
34
30
|
extra_rdoc_files: []
|
35
|
-
|
36
|
-
files:
|
31
|
+
files:
|
37
32
|
- .gitignore
|
38
33
|
- .travis.yml
|
39
34
|
- Gemfile
|
@@ -115,35 +110,31 @@ files:
|
|
115
110
|
- spec/fixtures/basic_universal.selector
|
116
111
|
- spec/ruby_extensions_spec.rb
|
117
112
|
- spec/spec_helper.rb
|
118
|
-
has_rdoc: true
|
119
113
|
homepage: http://github.com/fd/json_select
|
120
114
|
licenses: []
|
121
|
-
|
122
115
|
post_install_message:
|
123
116
|
rdoc_options: []
|
124
|
-
|
125
|
-
require_paths:
|
117
|
+
require_paths:
|
126
118
|
- lib
|
127
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
128
120
|
none: false
|
129
|
-
requirements:
|
130
|
-
- -
|
131
|
-
- !ruby/object:Gem::Version
|
132
|
-
version:
|
133
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ! '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
134
126
|
none: false
|
135
|
-
requirements:
|
136
|
-
- -
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
version:
|
127
|
+
requirements:
|
128
|
+
- - ! '>='
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
139
131
|
requirements: []
|
140
|
-
|
141
132
|
rubyforge_project: json_select
|
142
|
-
rubygems_version: 1.
|
133
|
+
rubygems_version: 1.7.2
|
143
134
|
signing_key:
|
144
135
|
specification_version: 3
|
145
136
|
summary: JSONSelect implementation for Ruby
|
146
|
-
test_files:
|
137
|
+
test_files:
|
147
138
|
- spec/conformance_spec.rb
|
148
139
|
- spec/fixtures/README.md
|
149
140
|
- spec/fixtures/alltests.txt
|