djc 0.3.0 → 0.4.0

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.
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