lilit-sql 0.0.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.
- checksums.yaml +7 -0
- data/lib/lilit_sql.rb +630 -0
- metadata +48 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8055fa9bdd9f4c7dd21d9e1036ca22ebd423637994ec0aeda7894062a8c3b2fa
|
4
|
+
data.tar.gz: 2683c306cb68f4d0e7b2e17fd678e8951a67c92a28acdafc87ffbf0c2b466ba0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 67a42477621655368a38a0ef15c55af70046b859b69f7bddd780546debd7bb8b81267112896a358effb95ae0b5820ec73618bd8f074412b0f10ebda78ff177b4
|
7
|
+
data.tar.gz: bab58672731d51626c65ee9855aa7fc7ba88c11bec2d917dbdfb6349923dc60424bbc9fee0e591e4b556224959a28315a0b259b68e956a5a51cab6899095b019
|
data/lib/lilit_sql.rb
ADDED
@@ -0,0 +1,630 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ruby2ruby'
|
4
|
+
require 'ruby_parser'
|
5
|
+
require 'sourcify'
|
6
|
+
|
7
|
+
class From
|
8
|
+
attr_accessor :source
|
9
|
+
attr_accessor :join_type
|
10
|
+
attr_accessor :condition
|
11
|
+
|
12
|
+
def initialize(source, join_type = nil, condition = nil, alias_name = nil)
|
13
|
+
@source = source
|
14
|
+
@join_type = join_type
|
15
|
+
@condition = condition
|
16
|
+
@alias_name = alias_name
|
17
|
+
end
|
18
|
+
|
19
|
+
def alias_name=(value)
|
20
|
+
@alias_name = value
|
21
|
+
end
|
22
|
+
|
23
|
+
def raw_alias_name
|
24
|
+
@alias_name
|
25
|
+
end
|
26
|
+
|
27
|
+
def alias_name
|
28
|
+
@alias_name || @source.subquery_name
|
29
|
+
end
|
30
|
+
|
31
|
+
def rows
|
32
|
+
source.rows
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class GroupBy
|
37
|
+
attr_accessor :query
|
38
|
+
attr_accessor :keys
|
39
|
+
|
40
|
+
def initialize(query, keys)
|
41
|
+
@query = query
|
42
|
+
@keys = keys
|
43
|
+
end
|
44
|
+
|
45
|
+
def aggregate(&blk)
|
46
|
+
result = blk.call(@keys, *@query.rows)
|
47
|
+
|
48
|
+
Query.new(
|
49
|
+
@query.froms + [],
|
50
|
+
@query.conditions + [],
|
51
|
+
@keys,
|
52
|
+
Row.new(result.class.members, result)
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class Row
|
58
|
+
attr_accessor :columns
|
59
|
+
|
60
|
+
def initialize(columns, origins = [])
|
61
|
+
@columns = columns.zip(origins).map do |col, origin|
|
62
|
+
if col.is_a?(Symbol)
|
63
|
+
Column.new(col, origin)
|
64
|
+
elsif col.is_a?(Column)
|
65
|
+
col
|
66
|
+
else
|
67
|
+
raise NotImplementedError
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def col(name)
|
73
|
+
found = @columns.select {|c| c.name == name}.first
|
74
|
+
|
75
|
+
raise ArgumentError.new("#{name} is not found in the columns: #{@columns.map {|c|c.name}.inspect}") if found.nil?
|
76
|
+
|
77
|
+
found
|
78
|
+
end
|
79
|
+
|
80
|
+
def with_from(from)
|
81
|
+
Row.new(@columns.map {|c| c.with_from(from)})
|
82
|
+
end
|
83
|
+
|
84
|
+
private def method_missing(symbol, *args)
|
85
|
+
begin
|
86
|
+
col(symbol)
|
87
|
+
rescue ArgumentError
|
88
|
+
super
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def has?(name)
|
93
|
+
@columns.any? {|c| c.name == name}
|
94
|
+
end
|
95
|
+
|
96
|
+
def decl_sql
|
97
|
+
@columns.map {|c|c.decl_sql}.join(', ')
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class Column
|
102
|
+
attr_accessor :name
|
103
|
+
attr_accessor :origin
|
104
|
+
|
105
|
+
def initialize(name, origin = nil, from = nil)
|
106
|
+
@name = name
|
107
|
+
@origin = origin
|
108
|
+
@from = from
|
109
|
+
end
|
110
|
+
|
111
|
+
def with_from(from)
|
112
|
+
Column.new(@name, @origin, from)
|
113
|
+
end
|
114
|
+
|
115
|
+
def eq(other)
|
116
|
+
Expr.new(self, :eq, other)
|
117
|
+
end
|
118
|
+
|
119
|
+
def in(list)
|
120
|
+
Expr.new(self, :in, list)
|
121
|
+
end
|
122
|
+
|
123
|
+
def *(other)
|
124
|
+
Expr.new(self, :*, other)
|
125
|
+
end
|
126
|
+
|
127
|
+
def <=(other)
|
128
|
+
Expr.new(self, :<=, other)
|
129
|
+
end
|
130
|
+
|
131
|
+
def ref_sql
|
132
|
+
"#{@from.alias_name}.#{@name}"
|
133
|
+
end
|
134
|
+
|
135
|
+
def decl_sql
|
136
|
+
s = ''
|
137
|
+
if origin
|
138
|
+
origin_sql = if origin.is_a?(Proc)
|
139
|
+
origin.call.ref_sql
|
140
|
+
else
|
141
|
+
origin.ref_sql
|
142
|
+
end
|
143
|
+
if origin_sql != @name.to_s
|
144
|
+
s += "#{origin_sql} as "
|
145
|
+
end
|
146
|
+
end
|
147
|
+
s += @name.to_s
|
148
|
+
s
|
149
|
+
end
|
150
|
+
|
151
|
+
def ==(other)
|
152
|
+
other.class == self.class && other.state == self.state
|
153
|
+
end
|
154
|
+
|
155
|
+
def state
|
156
|
+
self.instance_variables.map { |variable| self.instance_variable_get variable }
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
class Count < Column
|
161
|
+
|
162
|
+
def initialize
|
163
|
+
super(nil)
|
164
|
+
end
|
165
|
+
|
166
|
+
def ref_sql
|
167
|
+
decl_sql
|
168
|
+
end
|
169
|
+
|
170
|
+
def decl_sql
|
171
|
+
"count(*)"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
class Sum < Column
|
176
|
+
|
177
|
+
def initialize(col)
|
178
|
+
super(nil, col)
|
179
|
+
end
|
180
|
+
|
181
|
+
def ref_sql
|
182
|
+
decl_sql
|
183
|
+
end
|
184
|
+
|
185
|
+
def decl_sql
|
186
|
+
"sum(#{@origin.ref_sql})"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
module Aggregate
|
191
|
+
def self.count
|
192
|
+
Count.new
|
193
|
+
end
|
194
|
+
|
195
|
+
def self.sum(col)
|
196
|
+
Sum.new(col)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
class Literal
|
201
|
+
attr_accessor :value
|
202
|
+
|
203
|
+
def initialize(value)
|
204
|
+
@value = value
|
205
|
+
end
|
206
|
+
|
207
|
+
def ref_sql
|
208
|
+
decl_sql
|
209
|
+
end
|
210
|
+
|
211
|
+
def decl_sql
|
212
|
+
if @value.nil?
|
213
|
+
"null"
|
214
|
+
elsif @value.is_a?(Integer) || @value.is_a?(Float)
|
215
|
+
"#{@value}"
|
216
|
+
elsif @value.is_a?(String)
|
217
|
+
"'#{@value}'"
|
218
|
+
else
|
219
|
+
raise NotImplementedError.new("Literal doesn't support render #{@value.class} (#{@value})")
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def ==(other)
|
224
|
+
other.class == self.class && other.state == self.state
|
225
|
+
end
|
226
|
+
|
227
|
+
def state
|
228
|
+
self.instance_variables.map { |variable| self.instance_variable_get variable }
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
class Expr
|
233
|
+
attr_accessor :left
|
234
|
+
attr_accessor :op
|
235
|
+
attr_accessor :right
|
236
|
+
|
237
|
+
def initialize(left, op, right)
|
238
|
+
@left = left
|
239
|
+
@op = op
|
240
|
+
@right = right
|
241
|
+
end
|
242
|
+
|
243
|
+
def and(other)
|
244
|
+
Expr.new(self, :and, other)
|
245
|
+
end
|
246
|
+
|
247
|
+
def ref_sql
|
248
|
+
if op == :and
|
249
|
+
"#{left.ref_sql} and #{right.ref_sql}"
|
250
|
+
elsif op == :eq
|
251
|
+
if right.is_a?(Literal) && right.value.nil?
|
252
|
+
"#{left.ref_sql} is #{right.ref_sql}"
|
253
|
+
else
|
254
|
+
"#{left.ref_sql} = #{right.ref_sql}"
|
255
|
+
end
|
256
|
+
elsif op == :ne
|
257
|
+
if right.is_a?(Literal) && right.value.nil?
|
258
|
+
"#{left.ref_sql} is not #{right.ref_sql}"
|
259
|
+
else
|
260
|
+
"#{left.ref_sql} != #{right.ref_sql}"
|
261
|
+
end
|
262
|
+
elsif op == :*
|
263
|
+
"#{left.ref_sql} * #{right.ref_sql}"
|
264
|
+
elsif op == :<=
|
265
|
+
"#{left.ref_sql} <= #{right.ref_sql}"
|
266
|
+
elsif op == :in
|
267
|
+
"#{left.ref_sql} in (#{right.map {|r|r.ref_sql}.join(', ')})"
|
268
|
+
else
|
269
|
+
raise ArgumentError.new("#{op} is not supported by Expr")
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def ==(other)
|
274
|
+
other.class == self.class && other.state == self.state
|
275
|
+
end
|
276
|
+
|
277
|
+
def state
|
278
|
+
self.instance_variables.map { |variable| self.instance_variable_get variable }
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
class Table
|
283
|
+
attr_accessor :table_name
|
284
|
+
attr_accessor :rows
|
285
|
+
|
286
|
+
def initialize(struct, table_name)
|
287
|
+
@table_name = table_name
|
288
|
+
@rows = [Row.new(struct.members)]
|
289
|
+
end
|
290
|
+
|
291
|
+
def subquery_name
|
292
|
+
@table_name.to_s
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
class Query
|
297
|
+
attr_accessor :froms
|
298
|
+
attr_accessor :conditions
|
299
|
+
attr_accessor :grouped_keys
|
300
|
+
attr_accessor :row
|
301
|
+
|
302
|
+
def initialize(froms, conditions = [], grouped_keys = [], row = nil)
|
303
|
+
@froms = froms + []
|
304
|
+
@conditions = conditions + []
|
305
|
+
@grouped_keys = grouped_keys + []
|
306
|
+
@row = row
|
307
|
+
@subquery_name = nil
|
308
|
+
end
|
309
|
+
|
310
|
+
def self.from(query)
|
311
|
+
new([From.new(query)])
|
312
|
+
end
|
313
|
+
|
314
|
+
def is_vanilla
|
315
|
+
@froms.size == 1 && @conditions.empty? && @grouped_keys.empty? && @row.nil?
|
316
|
+
end
|
317
|
+
|
318
|
+
def map(&blk)
|
319
|
+
if @row
|
320
|
+
return Query.from(self).map(&blk)
|
321
|
+
end
|
322
|
+
|
323
|
+
result = expr(&blk).call(*get_from_rows)
|
324
|
+
Query.new(
|
325
|
+
@froms,
|
326
|
+
@conditions,
|
327
|
+
@grouped_keys,
|
328
|
+
Row.new(result.class.members, result)
|
329
|
+
)
|
330
|
+
end
|
331
|
+
|
332
|
+
def has?(column_name)
|
333
|
+
rows.any? {|r| r.has?(column_name)}
|
334
|
+
end
|
335
|
+
|
336
|
+
def rows
|
337
|
+
if @row
|
338
|
+
return [@row]
|
339
|
+
end
|
340
|
+
|
341
|
+
get_from_rows
|
342
|
+
end
|
343
|
+
|
344
|
+
def group_by(&blk)
|
345
|
+
if @row
|
346
|
+
return Query.from(self).group_by(&blk)
|
347
|
+
end
|
348
|
+
|
349
|
+
result = expr(&blk).call(*get_from_rows)
|
350
|
+
|
351
|
+
if result.is_a?(Column)
|
352
|
+
GroupBy.new(self, [result])
|
353
|
+
elsif result.is_a?(Array)
|
354
|
+
GroupBy.new(self, result)
|
355
|
+
else
|
356
|
+
raise NotImplementedError
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
def join(other, &blk)
|
361
|
+
perform_join(:join, other, &blk)
|
362
|
+
end
|
363
|
+
|
364
|
+
def left_join(other, &blk)
|
365
|
+
perform_join(:left_join, other, &blk)
|
366
|
+
end
|
367
|
+
|
368
|
+
def where(&blk)
|
369
|
+
if @row
|
370
|
+
return Query.from(self).where(&blk)
|
371
|
+
end
|
372
|
+
|
373
|
+
condition = expr(&blk).call(*get_from_rows)
|
374
|
+
Query.new(
|
375
|
+
@froms,
|
376
|
+
@conditions + [condition],
|
377
|
+
@grouped_keys,
|
378
|
+
@row
|
379
|
+
)
|
380
|
+
end
|
381
|
+
|
382
|
+
def subquery_name
|
383
|
+
if is_vanilla
|
384
|
+
return @froms.first.source.subquery_name
|
385
|
+
end
|
386
|
+
|
387
|
+
if @subquery_name.nil?
|
388
|
+
raise ArgumentError.new("The query #{self.inspect} doesn't have a subquery name")
|
389
|
+
end
|
390
|
+
|
391
|
+
@subquery_name
|
392
|
+
end
|
393
|
+
|
394
|
+
def subquery_name=(value)
|
395
|
+
@subquery_name = value
|
396
|
+
end
|
397
|
+
|
398
|
+
def sql
|
399
|
+
s = "select "
|
400
|
+
s += rows.map {|r| r.decl_sql}.join(', ')
|
401
|
+
s += " from"
|
402
|
+
|
403
|
+
@froms.each_with_index do |from, index|
|
404
|
+
if index >= 1
|
405
|
+
if from.join_type == :join
|
406
|
+
s += " join"
|
407
|
+
elsif from.join_type == :left_join
|
408
|
+
s += " left join"
|
409
|
+
else
|
410
|
+
raise ArgumentError.new("The join type #{from.join_type} is not supoprted.")
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
s += " #{from.source.subquery_name}"
|
415
|
+
|
416
|
+
if from.source.subquery_name != from.alias_name
|
417
|
+
s += " #{from.alias_name}"
|
418
|
+
end
|
419
|
+
|
420
|
+
if from.condition
|
421
|
+
s += " on #{from.condition.ref_sql}"
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
if @conditions.size > 0
|
426
|
+
s += " where #{@conditions.map {|c| c.ref_sql}.join(' and ')}"
|
427
|
+
end
|
428
|
+
|
429
|
+
if @grouped_keys.size > 0
|
430
|
+
s += " group by #{@grouped_keys.map {|k| k.ref_sql}.join(', ')}"
|
431
|
+
end
|
432
|
+
|
433
|
+
s
|
434
|
+
end
|
435
|
+
|
436
|
+
private
|
437
|
+
def get_from_rows
|
438
|
+
@froms.map {|f| f.rows.map {|r|r.with_from(f)}}.flatten
|
439
|
+
end
|
440
|
+
|
441
|
+
def get_next_alias
|
442
|
+
alias_names = @froms.map {|f|f.raw_alias_name}.compact
|
443
|
+
index = 0
|
444
|
+
alias_names.sort.each do |name|
|
445
|
+
if name == "alias#{index}"
|
446
|
+
index += 1
|
447
|
+
end
|
448
|
+
end
|
449
|
+
"alias#{index}"
|
450
|
+
end
|
451
|
+
|
452
|
+
def perform_join(join_type, other, &blk)
|
453
|
+
if @row || @conditions.size > 0
|
454
|
+
return Query.from(self).send(:perform_join, join_type, other, &blk)
|
455
|
+
end
|
456
|
+
|
457
|
+
alias_name = nil
|
458
|
+
@froms.each do |from|
|
459
|
+
if from.source == other
|
460
|
+
alias_name = get_next_alias
|
461
|
+
break
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
other_from = From.new(other, join_type, nil, alias_name)
|
466
|
+
condition = expr(&blk).call(*(get_from_rows + other_from.rows.map {|r|r.with_from(other_from)}))
|
467
|
+
other_from.condition = condition
|
468
|
+
|
469
|
+
Query.new(
|
470
|
+
@froms + [other_from],
|
471
|
+
@conditions,
|
472
|
+
@grouped_keys,
|
473
|
+
@row
|
474
|
+
)
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
class IfElse
|
479
|
+
def initialize(cond, true_result, false_result)
|
480
|
+
@condition = cond
|
481
|
+
@true_result = true_result
|
482
|
+
@false_result = false_result
|
483
|
+
end
|
484
|
+
|
485
|
+
def ref_sql
|
486
|
+
"if(#{@condition.ref_sql}, #{@true_result.ref_sql}, #{@false_result.ref_sql})"
|
487
|
+
end
|
488
|
+
|
489
|
+
def decl_sql
|
490
|
+
ref_sql
|
491
|
+
end
|
492
|
+
|
493
|
+
def ==(other)
|
494
|
+
other.class == self.class && other.state == self.state
|
495
|
+
end
|
496
|
+
|
497
|
+
def state
|
498
|
+
self.instance_variables.map { |variable| self.instance_variable_get variable }
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
def ifElse(cond, true_result, false_result)
|
503
|
+
IfElse.new(cond, true_result, false_result)
|
504
|
+
end
|
505
|
+
|
506
|
+
def generate_sql(query)
|
507
|
+
queries = fill(query)
|
508
|
+
last_query = queries.pop
|
509
|
+
|
510
|
+
sql = ''
|
511
|
+
|
512
|
+
if queries.size > 0
|
513
|
+
sql += 'with '
|
514
|
+
end
|
515
|
+
|
516
|
+
queries.map.with_index do |query, index|
|
517
|
+
if index > 0
|
518
|
+
sql += ', '
|
519
|
+
end
|
520
|
+
query.subquery_name = "subquery#{index}"
|
521
|
+
sql += "#{query.subquery_name} as (\n#{query.sql}\n)\n"
|
522
|
+
end
|
523
|
+
|
524
|
+
sql += last_query.sql
|
525
|
+
|
526
|
+
sql
|
527
|
+
end
|
528
|
+
|
529
|
+
def fill(query)
|
530
|
+
return [] if query.is_vanilla
|
531
|
+
|
532
|
+
queries = []
|
533
|
+
query.froms.each do |from|
|
534
|
+
if from.source.is_a?(Query)
|
535
|
+
subqueries = fill(from.source)
|
536
|
+
subqueries.each do |subquery|
|
537
|
+
queries.push(subquery)
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
541
|
+
queries.push(query)
|
542
|
+
queries.uniq
|
543
|
+
end
|
544
|
+
|
545
|
+
$ruby2ruby = Ruby2Ruby.new
|
546
|
+
|
547
|
+
def search_for_expr_block(parsed)
|
548
|
+
# s(:iter, s(:call, nil, :expr)
|
549
|
+
|
550
|
+
if parsed[0] == :iter && parsed[1][0] == :call && parsed[1][1].nil? && parsed[1][2] == :expr
|
551
|
+
return parsed[3]
|
552
|
+
end
|
553
|
+
|
554
|
+
parsed.each do |component|
|
555
|
+
if component.is_a?(Sexp)
|
556
|
+
return search_for_expr_block(component)
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
nil
|
561
|
+
end
|
562
|
+
|
563
|
+
def rewrite(parsed)
|
564
|
+
parsed = parsed.map do |component|
|
565
|
+
if component.is_a?(Sexp)
|
566
|
+
rewrite(component)
|
567
|
+
else
|
568
|
+
component
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
572
|
+
if parsed[0] == :call && parsed[2] == :==
|
573
|
+
parsed[2] = :eq
|
574
|
+
elsif parsed[0] == :and
|
575
|
+
parsed = Sexp.new(
|
576
|
+
:call,
|
577
|
+
parsed[1],
|
578
|
+
:and,
|
579
|
+
parsed[2]
|
580
|
+
)
|
581
|
+
elsif parsed[0] == :str
|
582
|
+
parsed = Sexp.new(
|
583
|
+
:call,
|
584
|
+
Sexp.new(:const, :Literal),
|
585
|
+
:new,
|
586
|
+
Sexp.new(:str, parsed[1])
|
587
|
+
)
|
588
|
+
elsif parsed[0] == :lit && (parsed[1].is_a?(Integer) || parsed[1].is_a?(Float))
|
589
|
+
parsed = Sexp.new(
|
590
|
+
:call,
|
591
|
+
Sexp.new(:const, :Literal),
|
592
|
+
:new,
|
593
|
+
Sexp.new(:lit, parsed[1])
|
594
|
+
)
|
595
|
+
elsif parsed[0] == :case && parsed[2] && parsed[2][0] == :in
|
596
|
+
parsed = Sexp.new(
|
597
|
+
:call,
|
598
|
+
parsed[1],
|
599
|
+
:in,
|
600
|
+
parsed[2][1]
|
601
|
+
)
|
602
|
+
elsif parsed[0] == :if
|
603
|
+
parsed = Sexp.new(
|
604
|
+
:call,
|
605
|
+
nil,
|
606
|
+
:ifElse,
|
607
|
+
parsed[1],
|
608
|
+
parsed[2],
|
609
|
+
parsed[3],
|
610
|
+
)
|
611
|
+
elsif parsed[0] == :nil
|
612
|
+
parsed = Sexp.new(
|
613
|
+
:call,
|
614
|
+
Sexp.new(:const, :Literal),
|
615
|
+
:new,
|
616
|
+
Sexp.new(:nil)
|
617
|
+
)
|
618
|
+
end
|
619
|
+
|
620
|
+
parsed
|
621
|
+
end
|
622
|
+
|
623
|
+
def expr(&blk)
|
624
|
+
parsed = blk.to_sexp
|
625
|
+
|
626
|
+
parsed = rewrite(parsed)
|
627
|
+
|
628
|
+
code = $ruby2ruby.process(parsed)
|
629
|
+
eval(code, blk.binding)
|
630
|
+
end
|
metadata
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lilit-sql
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tanin Na Nakorn
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-06-27 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: |-
|
14
|
+
lilit-sql is a Ruby DSL for composing production-grade analytical SQLs
|
15
|
+
|
16
|
+
The DSL supports higher order primitives like parameterization and meta-programming, which makes writing production-grade analytical SQLs easier.
|
17
|
+
|
18
|
+
This is suitable for an application that builds analytics on top of SQL-supported data warehouses like Presto.
|
19
|
+
email: "@tanin"
|
20
|
+
executables: []
|
21
|
+
extensions: []
|
22
|
+
extra_rdoc_files: []
|
23
|
+
files:
|
24
|
+
- lib/lilit_sql.rb
|
25
|
+
homepage: https://github.com/tanin47/lilit-sql
|
26
|
+
licenses:
|
27
|
+
- MIT
|
28
|
+
metadata: {}
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
requirements: []
|
44
|
+
rubygems_version: 3.4.10
|
45
|
+
signing_key:
|
46
|
+
specification_version: 4
|
47
|
+
summary: lilit-sql is a Ruby DSL for composing production-grade analytical SQLs
|
48
|
+
test_files: []
|