djc 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/djc.rb +82 -69
  2. metadata +2 -2
data/lib/djc.rb CHANGED
@@ -12,47 +12,54 @@ module DJC
12
12
 
13
13
  class ::Array
14
14
 
15
+ def select_first(&block)
16
+ selected = nil
17
+ each do |item|
18
+ selected = block.call(item)
19
+ break if selected
20
+ end
21
+ selected
22
+ end
23
+
15
24
  def walk(obj)
16
25
  path = self.dup
17
- key = path.shift.to_s
18
-
19
- val = if obj.is_a? Array
20
- if key == '*'
21
- if path.empty?
22
- obj
23
- else
24
- sub = obj.map do |inner|
25
- path.dup.walk(inner)
26
- end
27
- path.clear
28
- sub
29
- end
30
- elsif /^[\d,\+-\.]+$/.match(key)
31
- selected = nil
32
- key.split('|').each do |option|
33
- locators = option.split(',').map do |dex|
34
- range = /(?<start>\d+)(?:-|\+|\.\.(?<exclusive>\.)?)?(?<end>-?\d+)/.match(dex)
35
- range ? Range.new(range['start'].to_i, (range['end'] || -1).to_i, range['exclusive']) : dex.to_i
36
- end
37
- selected = obj.values_at(*locators)
38
- break if selected
39
- end
40
- selected.size == 1 ? selected.first : selected
41
- end
42
- elsif obj.is_a? Hash
43
- match = key[/\/(.*)\//, 1]
44
- if match.nil?
45
- obj[key]
46
- else
47
- found = obj.keys.select { |k| Regexp.new(match).match(k) }
48
- found = found.map { |k| path.empty? ? obj[k] : path.walk(obj[k]) }
49
- path.clear
50
- found = found.first if found.size < 2
51
- found
26
+ keys = path.shift.to_s.split('|')
27
+
28
+ val = keys.select_first do |key|
29
+ if obj.is_a? Array
30
+ if key == '*'
31
+ if path.empty?
32
+ obj
33
+ else
34
+ sub = obj.map do |inner|
35
+ path.dup.walk(inner)
52
36
  end
53
- elsif obj.respond_to? key
54
- obj.send(key)
37
+ path.clear
38
+ sub
39
+ end
40
+ elsif /^[\d,\+-\.]+$/.match(key)
41
+ locators = key.split(',').map do |dex|
42
+ range = /(?<start>\d+)(?:-|\+|\.\.(?<exclusive>\.)?)?(?<end>-?\d+)/.match(dex)
43
+ range ? Range.new(range['start'].to_i, (range['end'] || -1).to_i, range['exclusive']) : dex.to_i
55
44
  end
45
+ selected = obj.values_at(*locators)
46
+ selected.size == 1 ? selected.first : selected
47
+ end
48
+ elsif obj.is_a? Hash
49
+ match = key[/\/(.*)\//, 1]
50
+ if match.nil?
51
+ obj[key]
52
+ else
53
+ found = obj.keys.select { |k| Regexp.new(match).match(k) }
54
+ found = found.map { |k| path.empty? ? obj[k] : path.walk(obj[k]) }
55
+ path.clear
56
+ found = found.first if found.size < 2
57
+ found
58
+ end
59
+ elsif obj.respond_to? key
60
+ obj.send(key)
61
+ end
62
+ end
56
63
 
57
64
  path.empty? ? val : path.walk(val)
58
65
  end
@@ -103,65 +110,73 @@ module DJC
103
110
 
104
111
  class Rule
105
112
  def parse(paths)
106
-
107
113
  regex = /\/[^\/]+\//
108
114
  lookup = /\<[^\<]\>/
109
115
  indexes = /(?:-?\d+(?:(?:\.\.\.?|-|\+)(?:-?\d+)?)?,?)+/
110
- node = /[^\[\]\{\}\|\.]+/
111
- rules = paths.split('||').map do |path|
116
+ node = /[^\[\]\{\}\.]+/
117
+ paths.split('||').map do |path|
112
118
  path.scan(/#{regex}|#{lookup}|#{indexes}|#{node}/)
113
119
  end
114
120
 
115
- rules
116
121
  end
117
122
 
118
- attr_reader :paths, :type, :block
119
- def initialize(type='lookup', rules, &block)
123
+ attr_reader :type, :paths, :blocks
124
+ def initialize(type, rules, &block)
120
125
  if rules.is_a?(String) && rules[0] == '#'
121
- @type, @block, @paths = 'literal', proc { rules[1..-1] }, nil
126
+ @type, @blocks, @paths = 'LITERAL', [proc { rules[1..-1] }], nil
122
127
  else
123
- @type, @block, @paths = type, block, rules.is_a?(String) ? parse(rules) : rules
128
+ @type, @blocks, @paths = type, [block].compact, (rules.is_a?(String) ? parse(rules) : rules)
124
129
  end
125
130
  end
126
131
 
127
132
  def to_s
128
- "#{paths}:#{type}:#{block}"
133
+ rules = if type == 'LITERAL'
134
+ @blocks.first.call
135
+ elsif paths
136
+ paths.join(paths.first.is_a?(Rule) ? ' + ' : '.')
137
+ end
138
+
139
+ "#{type}(#{rules})"
129
140
  end
130
141
 
131
142
  def sum
132
- @type, @block = 'sum', proc { |array| array.map(&:to_i).inject(0, :+) if array }
143
+ @type = 'SUM'
144
+ @blocks << proc { |array| array.map(&:to_i).inject(0, :+) if array }
133
145
  self
134
146
  end
135
147
 
136
148
  def avg
137
- @type, @block = 'avg', proc { |array| ( array.map(&:to_i).inject(0.0, :+) / array.size) if array }
149
+ @type = 'AVG'
150
+ @blocks << proc { |array| ( array.map(&:to_i).inject(0.0, :+) / array.size) if array }
138
151
  self
139
152
  end
140
153
 
141
154
  def each(&each_block)
142
- @type, @block = 'each', proc { |array| array.map { |val| each_block.call(val) } if array }
155
+ @type = 'EACH'
156
+ @blocks << proc { |array| array.map { |val| each_block.call(val) } if array }
143
157
  self
144
158
  end
145
159
 
146
160
  def join(sep = '')
147
- @type, @block = 'join', proc { |vals| vals.is_a?(Array) ? vals.compact.join(sep) : vals }
161
+ @type = 'JOIN'
162
+ @blocks << proc { |vals| vals.is_a?(Array) ? vals.compact.join(sep) : vals }
148
163
  self
149
164
  end
150
165
 
151
166
  def sort(&sort_block)
152
- @type, @block = 'sort', proc { |sort| sort.is_a?(Array) ? sort.compact.sort(&sort_block) : sort.sort(&sort_block) }
167
+ @type = 'SORT'
168
+ @blocks << proc { |sort| sort.is_a?(Array) ? sort.compact.sort(&sort_block) : (sort.nil? ? nil : sort.sort(&sort_block)) }
153
169
  self
154
170
  end
155
171
 
156
172
  def match(matcher)
157
- @type, @block = 'match', proc do |val|
173
+ @type = 'MATCH'
174
+ @blocks << proc do |val|
158
175
  if val
159
176
  if val.is_a?(Array)
160
- val.map do |v|
161
- if v
162
- match = v.scan(matcher).flatten
163
- match.size == 1 ? match.first : match
164
- end
177
+ val.compact.map do |v|
178
+ match = v.scan(matcher).flatten
179
+ match.size == 1 ? match.first : match
165
180
  end
166
181
  else
167
182
  match = val.scan(matcher).flatten
@@ -173,17 +188,16 @@ module DJC
173
188
  end
174
189
 
175
190
  def apply(obj)
176
- value = nil
177
- if type == 'lookup'
191
+ if @blocks.empty?
178
192
  walker = paths.dup
179
193
  value = nil
180
- while value.nil? && path = walker.shift
181
- value = path.walk(obj)
194
+ while value.nil? && (path = walker.shift)
195
+ value = path.is_a?(Rule) ? path.apply(obj) : path.walk(obj)
182
196
  end
183
197
  value
184
198
  else
185
199
  if paths.nil? || paths.empty?
186
- value = block.call(obj)
200
+ value = blocks.inject(obj) { |val, block| block.call(val) }
187
201
  elsif paths.length > 1
188
202
  value = [[]]
189
203
  paths.each_with_index do |rule, index|
@@ -198,23 +212,22 @@ module DJC
198
212
  value.first << val
199
213
  end
200
214
  end
201
- value = value.map { |val| block.call(val) } unless block.nil?
215
+ value = value.map { |val| blocks.inject(val) { |v, block| block.call(v) }} unless blocks.empty?
202
216
  value = value.first if value.length == 1
203
217
  value
204
218
  else
205
219
  value = paths.first.apply(obj)
206
- value = block.call(value) unless block.nil?
220
+ value = blocks.inject(value) { |val, block| block.call(val) } unless blocks.empty?
207
221
  end
208
222
  end
209
223
  value
210
224
  end
211
-
212
225
  end
213
226
 
214
227
  class Column
215
228
  attr_reader :name, :rule
216
229
  def initialize(name, rule)
217
- @name, @rule = name, rule.is_a?(Rule) ? rule : Rule.new(rule)
230
+ @name, @rule = name, rule.is_a?(Rule) ? rule : Rule.new('LOOKUP', rule)
218
231
  end
219
232
  end
220
233
 
@@ -226,7 +239,7 @@ module DJC
226
239
  end
227
240
 
228
241
  def initialize(path = nil)
229
- @path = Rule.new(path) if path
242
+ @path = Rule.new('USING', path) if path
230
243
  end
231
244
 
232
245
  attr_reader :columns
@@ -252,11 +265,11 @@ module DJC
252
265
  end
253
266
 
254
267
  def with(*paths, &block)
255
- Rule.new('with', paths.map { |path| Rule.new(path) }, &block)
268
+ Rule.new('WITH', paths.map { |path| Rule.new('LOOKUP', path) }, &block)
256
269
  end
257
270
 
258
271
  def rule(&block)
259
- Rule.new('rule', nil, &block)
272
+ Rule.new('RULE', nil, &block)
260
273
  end
261
274
 
262
275
  def build(json)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: djc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-20 00:00:00.000000000 Z
12
+ date: 2012-10-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec