djc 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/djc.rb +82 -69
- 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
|
-
|
18
|
-
|
19
|
-
val =
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
54
|
-
|
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
|
-
|
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 :
|
119
|
-
def initialize(type
|
123
|
+
attr_reader :type, :paths, :blocks
|
124
|
+
def initialize(type, rules, &block)
|
120
125
|
if rules.is_a?(String) && rules[0] == '#'
|
121
|
-
@type, @
|
126
|
+
@type, @blocks, @paths = 'LITERAL', [proc { rules[1..-1] }], nil
|
122
127
|
else
|
123
|
-
@type, @
|
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
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
-
|
162
|
-
|
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
|
-
|
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(
|
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(
|
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(
|
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('
|
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('
|
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.
|
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-
|
12
|
+
date: 2012-10-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|