json_select 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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