json_select 0.1.0 → 0.1.1

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.
@@ -0,0 +1,7 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.1
4
+ - 1.9.2
5
+ - jruby
6
+ - rbx
7
+ - ree
data/Gemfile CHANGED
@@ -3,5 +3,6 @@ source "http://rubygems.org"
3
3
  # Specify your gem's dependencies in json_select.gemspec
4
4
  gemspec
5
5
 
6
+ gem 'rake', '0.8.7'
6
7
  gem 'rspec'
7
8
  gem 'yajl-ruby'
data/Rakefile CHANGED
@@ -11,6 +11,11 @@ end
11
11
 
12
12
  task :build_parser do
13
13
  sh "bundle exec tt lib/json_select/selector_parser.tt -o lib/json_select/selector_parser.rb"
14
+
15
+ path = 'lib/json_select/selector_parser.rb'
16
+ src = File.read(path)
17
+ src.gsub!('JSONSelectSelector', 'JSONSelect::Selector')
18
+ File.open(path, 'w+') { |f| f.write src }
14
19
  end
15
20
 
16
21
  task :spec => :build_parser
@@ -1,12 +1,11 @@
1
- module JSONSelect
2
-
1
+ class JSONSelect
2
+
3
3
  require 'treetop'
4
-
4
+
5
5
  require 'json_select/version'
6
6
  require 'json_select/selector_parser'
7
7
  require 'json_select/selector'
8
- require 'json_select/parser'
9
-
8
+
10
9
  module Ast
11
10
  require 'json_select/ast/combination_selector'
12
11
  require 'json_select/ast/simple_selector'
@@ -15,21 +14,17 @@ module JSONSelect
15
14
  require 'json_select/ast/hash_selector'
16
15
  require 'json_select/ast/pseudo_selector'
17
16
  require 'json_select/ast/universal_selector'
18
-
17
+
19
18
  require 'json_select/ast/odd_expr'
20
19
  require 'json_select/ast/even_expr'
21
20
  require 'json_select/ast/simple_expr'
22
21
  require 'json_select/ast/complex_expr'
23
22
  end
24
-
23
+
25
24
  ParseError = Class.new(RuntimeError)
26
-
27
- def self.parse(selector)
28
- JSONSelect::Parser.new(selector).parse
29
- end
30
-
25
+
31
26
  end
32
27
 
33
28
  def JSONSelect(selector)
34
- JSONSelect.parse(selector)
29
+ JSONSelect.new(selector)
35
30
  end
@@ -1,14 +1,14 @@
1
1
  module JSONSelect::Ast::CombinationSelector
2
-
2
+
3
3
  def to_ast
4
4
  ast = [a.to_ast]
5
-
5
+
6
6
  b.elements.each do |comb|
7
- ast.push('>') if comb.c.text_value.strip == '>'
7
+ ast.push(:>) if comb.c.text_value.strip == '>'
8
8
  ast.push(comb.d.to_ast)
9
9
  end
10
-
10
+
11
11
  ast
12
12
  end
13
-
13
+
14
14
  end
@@ -1,27 +1,27 @@
1
1
  module JSONSelect::Ast::ComplexExpr
2
-
2
+
3
3
  def to_ast
4
4
  _a = ""
5
-
5
+
6
6
  if a.text_value.size > 0
7
7
  _a += a.text_value
8
8
  else
9
9
  _a += '+'
10
10
  end
11
-
11
+
12
12
  if b.text_value.size > 0
13
13
  _a += b.text_value
14
14
  else
15
15
  _a += '1'
16
16
  end
17
-
17
+
18
18
  if c.text_value.size > 0
19
19
  _b = c.text_value.to_i
20
20
  else
21
21
  _b = 0
22
22
  end
23
-
24
- { 'a' => _a.to_i, 'b' => _b }
23
+
24
+ { :a => _a.to_i, :b => _b }
25
25
  end
26
-
26
+
27
27
  end
@@ -1,7 +1,7 @@
1
1
  module JSONSelect::Ast::EvenExpr
2
-
2
+
3
3
  def to_ast
4
- { 'a' => 2, 'b' => 0 }
4
+ { :a => 2, :b => 0 }
5
5
  end
6
-
6
+
7
7
  end
@@ -3,10 +3,10 @@ module JSONSelect::Ast::HashSelector
3
3
  def to_ast
4
4
  tv = self.text_value[1..-1]
5
5
  if tv[0,1] == '"'
6
- { "class" => eval(tv) }
6
+ { :class => eval(tv) }
7
7
  else
8
- { "class" => tv }
8
+ { :class => tv }
9
9
  end
10
10
  end
11
-
11
+
12
12
  end
@@ -1,7 +1,7 @@
1
1
  module JSONSelect::Ast::OddExpr
2
-
2
+
3
3
  def to_ast
4
- { 'a' => 2, 'b' => 1 }
4
+ { :a => 2, :b => 1 }
5
5
  end
6
-
6
+
7
7
  end
@@ -2,23 +2,23 @@ module JSONSelect::Ast::PseudoSelector
2
2
 
3
3
  def to_ast
4
4
  if respond_to?(:e)
5
- ast = { "pseudo_function" => a.text_value, 'a' => 0 , 'b' => 0 }
5
+ ast = { :pseudo_function => a.text_value, :a => 0 , :b => 0 }
6
6
  ast.merge!(e.to_ast)
7
7
  ast
8
8
  else
9
9
  case a.text_value
10
-
10
+
11
11
  when 'first-child'
12
- { "pseudo_function" => 'nth-child', 'a' => 0, 'b' => 1 }
13
-
12
+ { :pseudo_function => 'nth-child', :a => 0, :b => 1 }
13
+
14
14
  when 'last-child'
15
- { "pseudo_function" => 'nth-last-child', 'a' => 0, 'b' => 1 }
16
-
15
+ { :pseudo_function => 'nth-last-child', :a => 0, :b => 1 }
16
+
17
17
  else
18
- { "pseudo_class" => a.text_value }
19
-
18
+ { :pseudo_class => a.text_value }
19
+
20
20
  end
21
21
  end
22
22
  end
23
-
23
+
24
24
  end
@@ -1,7 +1,7 @@
1
1
  module JSONSelect::Ast::SimpleExpr
2
-
2
+
3
3
  def to_ast
4
- { 'a' => 0, 'b' => text_value.to_i }
4
+ { :a => 0, :b => text_value.to_i }
5
5
  end
6
-
6
+
7
7
  end
@@ -1,8 +1,8 @@
1
1
  module JSONSelect::Ast::TypeSelector
2
-
2
+
3
3
  # `object` | `array` | `number` | `string` | `boolean` | `null`
4
4
  def to_ast
5
- { "type" => self.text_value }
5
+ { :type => self.text_value }
6
6
  end
7
-
7
+
8
8
  end
@@ -1,27 +1,84 @@
1
- class JSONSelect::Selector
2
-
1
+ class JSONSelect
2
+
3
3
  attr_reader :ast
4
-
5
- def initialize(ast)
6
- @ast = ast
4
+
5
+ @@parser_cache = {}
6
+
7
+ def self.reset_cache!
8
+ @@parser_cache.clear
7
9
  end
8
-
10
+
11
+ def initialize(src, use_parser_cache=true)
12
+ case src
13
+
14
+ when String
15
+ ast = nil
16
+
17
+ if use_parser_cache
18
+ ast = @@parser_cache[src]
19
+ end
20
+
21
+ if ast
22
+ @ast = ast
23
+
24
+ else
25
+ parser = JSONSelect::SelectorParser.new
26
+ tree = parser.parse(src)
27
+ unless tree
28
+ raise JSONSelect::ParseError, parser.failure_reason
29
+ end
30
+
31
+ @ast = tree.to_ast
32
+
33
+ if use_parser_cache
34
+ @@parser_cache[src] = ast
35
+ end
36
+ end
37
+
38
+ when Array
39
+ @ast = src
40
+
41
+ else
42
+ raise ArgumentError, "Expected a string for ast"
43
+
44
+ end
45
+ end
46
+
47
+ # Returns the first matching child in `object`
9
48
  def match(object)
49
+ _each(@ast, object, nil, nil, nil) do |object|
50
+ return object
51
+ end
52
+
53
+ return nil
54
+ end
55
+
56
+ alias_method :=~, :match
57
+
58
+ # Returns all matching children in `object`
59
+ def matches(object)
10
60
  matches = []
11
-
61
+
12
62
  _each(@ast, object, nil, nil, nil) do |object|
13
63
  matches << object
14
64
  end
15
-
65
+
16
66
  matches
17
67
  end
18
-
19
- alias_method :evaluate, :match
20
- alias_method :=~, :match
21
- alias_method :===, :match
22
-
68
+
69
+ # Returns true if `object` has any matching children.
70
+ def test(object)
71
+ _each(@ast, object, nil, nil, nil) do |object|
72
+ return true
73
+ end
74
+
75
+ return false
76
+ end
77
+
78
+ alias_method :===, :test
79
+
23
80
  private
24
-
81
+
25
82
  # function forEach(sel, obj, fun, id, num, tot) {
26
83
  # var a = (sel[0] === ',') ? sel.slice(1) : [sel];
27
84
  # var a0 = [];
@@ -51,40 +108,59 @@ private
51
108
  def _each(selector, object, id, number, total, &block)
52
109
  a0 = (selector[0] == ',' ? selector[1..-1] : [selector])
53
110
  a1 = []
54
-
111
+
55
112
  call = false
56
-
113
+
57
114
  a0.each do |selector|
58
115
  ok, extra = _match(object, selector, id, number, total)
59
-
116
+
60
117
  call = true if ok
61
118
  a1.concat extra
62
119
  end
63
-
64
- if (Array === object or Hash === object) and a1.any?
65
- a1.unshift(',')
66
- size = object.size
67
-
68
- if Array === object
120
+
121
+ if a1.any?
122
+ case object
123
+
124
+ when Array
125
+ a1.unshift(',')
126
+ size = object.size
127
+
69
128
  object.each_with_index do |child, idx|
70
129
  _each(a1, child, nil, idx, size, &block)
71
130
  end
72
- end
73
-
74
- if Hash === object
131
+
132
+ when Hash
133
+ a1.unshift(',')
134
+ size = object.size
135
+
75
136
  object.each_with_index do |(key, child), idx|
76
137
  _each(a1, child, key, idx, size, &block)
77
138
  end
139
+
140
+ else
141
+ if object.respond_to?(:json_select_each)
142
+ children = []
143
+ object.json_select_each do |key, value|
144
+ children << [key, value]
145
+ end
146
+
147
+ a1.unshift(',')
148
+ size = children.size
149
+
150
+ children.each_with_index do |(key, child), idx|
151
+ _each(a1, child, key, idx, size, &block)
152
+ end
153
+ end
154
+
78
155
  end
79
-
80
156
  end
81
-
157
+
82
158
  if call and block
83
159
  block.call(object)
84
160
  end
85
161
  end
86
-
87
-
162
+
163
+
88
164
  # function mn(node, sel, id, num, tot) {
89
165
  # var sels = [];
90
166
  # var cs = (sel[0] === '>') ? sel[1] : sel[0];
@@ -100,77 +176,83 @@ private
100
176
  # m = (!((num - cs.b) % cs.a) && ((num*cs.a + cs.b) >= 0));
101
177
  # }
102
178
  # }
103
- #
179
+ #
104
180
  # // should we repeat this selector for descendants?
105
181
  # if (sel[0] !== '>' && sel[0].pc !== ":root") sels.push(sel);
106
- #
182
+ #
107
183
  # if (m) {
108
184
  # // is there a fragment that we should pass down?
109
185
  # if (sel[0] === '>') { if (sel.length > 2) { m = false; sels.push(sel.slice(2)); } }
110
186
  # else if (sel.length > 1) { m = false; sels.push(sel.slice(1)); }
111
187
  # }
112
- #
188
+ #
113
189
  # return [m, sels];
114
190
  # }
115
191
  def _match(object, selector, id, number, total)
116
192
  selectors = []
117
- current_selector = (selector[0] == '>' ? selector[1] : selector[0])
193
+ current_selector = (selector[0] == :> ? selector[1] : selector[0])
118
194
  match = true
119
-
120
- if current_selector.key?('type')
121
- match = (match and current_selector['type'] == _type_of(object))
195
+
196
+ if current_selector.key?(:type)
197
+ match = (match and current_selector[:type] == _type_of(object))
122
198
  end
123
-
124
- if current_selector.key?('class')
125
- match = (match and current_selector['class'] == id)
199
+
200
+ if current_selector.key?(:class)
201
+ match = (match and current_selector[:class] == id.to_s)
126
202
  end
127
-
128
- if match and current_selector.key?('pseudo_function')
129
- pseudo_function = current_selector['pseudo_function']
130
-
203
+
204
+ if match and current_selector.key?(:pseudo_function)
205
+ pseudo_function = current_selector[:pseudo_function]
206
+
131
207
  if pseudo_function == 'nth-last-child'
132
208
  number = total - number
133
209
  else
134
210
  number += 1
135
211
  end
136
-
137
- if current_selector['a'] == 0
138
- match = current_selector['b'] == number
212
+
213
+ if current_selector[:a] == 0
214
+ match = (current_selector[:b] == number)
139
215
  else
140
216
  # WTF!
141
- match = ((((number - current_selector['b']) % current_selector['a']) == 0) && ((number * current_selector['a'] + current_selector['b']) >= 0))
217
+ match = ((((number - current_selector[:b]) % current_selector[:a]) == 0) && ((number * current_selector[:a] + current_selector[:b]) >= 0))
142
218
  end
143
219
  end
144
-
145
- if selector[0] != '>' and selector[0]['pseudo_class'] != 'root'
220
+
221
+ if Hash === selector[0] and selector[0][:pseudo_class] != 'root'
146
222
  selectors.push selector
147
223
  end
148
-
224
+
149
225
  if match
150
- if selector[0] == '>'
151
- if selector.length > 2
152
- m = false
226
+ if selector[0] == :>
227
+ if selector.length > 2
228
+ match = false
153
229
  selectors.push selector[2..-1]
154
230
  end
155
231
  elsif selector.length > 1
156
- m = false
232
+ match = false
157
233
  selectors.push selector[1..-1]
158
234
  end
159
235
  end
160
-
236
+
161
237
  return [match, selectors]
162
238
  end
163
-
239
+
164
240
  def _type_of(object)
241
+ if object.respond_to?(:json_select_each)
242
+ return 'object'
243
+ end
244
+
165
245
  case object
166
246
  when Hash then 'object'
167
247
  when Array then 'array'
168
248
  when String then 'string'
249
+ when Symbol then 'string'
169
250
  when Numeric then 'number'
170
251
  when TrueClass then 'boolean'
171
252
  when FalseClass then 'boolean'
172
253
  when NilClass then 'null'
254
+ else raise "Invalid object of class #{object.class} for JSONSelect: #{object.inspect}"
173
255
  end
174
256
  end
175
-
257
+
176
258
  end