i_dig_sql 3.0.0 → 4.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5a72cee2c11ff760fcb5227b539fa976a39dfb56
4
- data.tar.gz: a418aba79878b9361f980f137c7be1b108e3ee74
3
+ metadata.gz: 31d1e3efcf21763358024010c06ded12c96db386
4
+ data.tar.gz: 406fb62530629737bc65194b61f971d30d817963
5
5
  SHA512:
6
- metadata.gz: 1e7ef6053d12c991ab562da0c882bc510babae4c315e108e7bb34c67855e998106ea51ccc1f5c4384a88d9f5c0fdbaf8b2f183539d1c3a40b208b4be73431de5
7
- data.tar.gz: 43c7bca10bbb494ab492e7ad1852ecbd992ba80b96832056bf6760b7f8c2a533023faeb8df91ab595bf30e04c38081d927367ad522f269245b1f6b5c8f113aae
6
+ metadata.gz: a0d7425d98fd9de91ad8a03169215bfeb34c895d54fb8fd3bcca9e58746e7dd819f96928ed091934b60058ed2e4529eccb73105766fb0475ca08a3e3cca38e8c
7
+ data.tar.gz: 0d429c6906c64c71beaf531b34a88a51313ad5ce559db376bc6e79dc28890db099c9221d4ca575e71ffcaae500434f822e48eccf3a6f661397bfeaa83cb48e90
data/README.md CHANGED
@@ -24,24 +24,40 @@ Please note that none of this is ready yet.
24
24
  require 'i_dig_sql'
25
25
 
26
26
  sql = I_Dig_Sql.new
27
- sql[:HEROES] = "SELECT id FROM hero WHERE id = :PERSON_ID"
28
- sql[:VILLIANS] = "SELECT id FROM villian WHERE id = :PERSON_ID"
29
- sql << %^
27
+ sql.sql :HEROES, "SELECT id FROM hero WHERE id = :PERSON_ID"
28
+ sql.sql :VILLIANS, "SELECT id FROM villian WHERE id = :PERSON_ID"
29
+
30
+ sql.sql(:SIDE_KICKS) {
31
+
32
+ FROM :HEROES, :good_guys do
33
+ SELECT :nick_name, :name
34
+ end
35
+
36
+ INNER :PEOPLE, :others do
37
+ ON '_.heroe_id == __.id'
38
+ SELECT :nick_name, :name
39
+ end
40
+
41
+ SELECT 'field' => 'a_field'
42
+
43
+ GROUP_BY 'id DESC', 'created_at ASC'
44
+
45
+ }
46
+
47
+ sql.sql :SQL, %^
30
48
  SELECT *
31
49
  FROM people
32
50
  WHERE
33
- id IN ( << HEROES >> AND status = :ALIVE)
34
- OR
35
51
  id IN (SELECT ID FROM {{ HEROES }} AND status = :ALIVE)
36
52
  OR
37
- id IN ( << * HEROES >> )
53
+ id IN ( {{ HEROES * }} )
38
54
  OR
39
- id IN ( << patron_id VILLIANS >> )
55
+ id IN ( {{ VILLIANS patron_id }} )
40
56
  OR
41
- id IN ( << VILLIANS >> )
57
+ id IN ( {{ VILLIANS * }} )
42
58
  ^
43
59
 
44
- sql.to_sql
60
+ puts sql.pair
45
61
 
46
62
  ```
47
63
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.0.0
1
+ 4.0.0
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env bash
2
+ # -*- bash -*-
3
+ #
4
+ #
5
+
6
+ action="$1"
7
+ shift
8
+
9
+ if [[ -z "$action" ]]; then
10
+ action="test"
11
+ fi
12
+ # ===============================================================
13
+
14
+ set -u -e -o pipefail
15
+
16
+ case "$action" in
17
+
18
+ "help")
19
+ echo " ====================================================="
20
+ echo ""
21
+ echo " $ i_dig_sql watch"
22
+ echo ""
23
+ echo " $ i_dig_sql test"
24
+ echo " $ i_dig_sql test name"
25
+ echo ""
26
+ echo " ====================================================="
27
+ echo ""
28
+ exit 0
29
+ ;; # === start
30
+
31
+ "watch")
32
+ echo ""
33
+ echo "=== Watching: $@"
34
+
35
+ echo -e "\n=== Running test:"
36
+ bin/i_dig_sql test "$@" || echo ""
37
+
38
+ inotifywait -q -m -e close_write,close --exclude .git/ -r . | while read CHANGE
39
+ do
40
+
41
+ dir=$(echo "$CHANGE" | cut -d' ' -f 1)
42
+ op=$(echo "$CHANGE" | cut -d' ' -f 2)
43
+ file=$(echo "$CHANGE" | cut -d' ' -f 3)
44
+ path="${dir}$file"
45
+
46
+ if [[ "$op" == *CLOSE_WRITE* && ( $file == *.rb* || $file == *.md* ) ]]; then
47
+ echo -e "\n=== Running test:"
48
+ bin/i_dig_sql test "$@" || echo ""
49
+ fi
50
+
51
+ done # === do
52
+
53
+ echo ""
54
+ exit 0
55
+ ;;
56
+
57
+
58
+ "test")
59
+ files="$(echo specs/*-$@.rb)"
60
+ if [[ -f "$files" ]]; then
61
+ shift
62
+ else
63
+ files="$(echo specs/*-*.rb)"
64
+ fi
65
+
66
+ bundle exec bacon -rpry -ri_dig_sql specs/helpers.rb $files "$@"
67
+ ;; # === test
68
+
69
+
70
+ *)
71
+ echo "=== Unknown action: $action" 1>&2
72
+ exit 1
73
+ ;;
74
+
75
+ esac # =========================================================
76
+
@@ -21,12 +21,13 @@ Gem::Specification.new do |spec|
21
21
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
22
  spec.require_paths = ["lib"]
23
23
 
24
+ spec.add_dependency "boxomojo" , "> 1.0.1"
25
+
24
26
  spec.add_development_dependency "bundler" , "> 1.5"
25
27
  spec.add_development_dependency "bacon" , '> 1.0'
26
28
  spec.add_development_dependency "Bacon_Colored" , '> 0'
27
29
  spec.add_development_dependency "pry" , '> 0'
28
30
  spec.add_development_dependency "awesome_print" , '> 0'
29
- spec.add_development_dependency "anbt-sql-formatter" , '> 0'
30
31
  spec.add_development_dependency "rouge" , '> 0.0.0'
31
32
  spec.add_development_dependency "unindent" , '> 0.0.0'
32
33
  end
@@ -1,448 +1,193 @@
1
1
 
2
- require "i_dig_sql/H"
3
2
 
4
- class I_Dig_Sql
5
-
6
- include Enumerable
3
+ require 'i_dig_sql/H'
4
+ require 'boxomojo'
7
5
 
8
- HAS_VAR = /(\{\{|\<\<)[^\}\>]+(\}\}|\>\>)/
9
- SELECT_FROM_REG = /SELECT.+FROM.+/
6
+ class I_Dig_Sql
10
7
 
8
+ Box = Boxomojo.new(
9
+ :SELECT,
10
+ :FROM, :LEFT, :RIGHT, :INNER,
11
+ :AS, :ON,
12
+ :GROUP_BY, :ORDER_BY
13
+ )
11
14
 
15
+ HAS_VAR = /(\{\{)[^\}\>]+(\}\})/
16
+ SELECT_FROM_REG = /SELECT.+FROM.+/
17
+ COMMAS_OR_COLONS = /(\,|\:)+/
12
18
  ALL_UNDERSCORE = /\A[_]+\Z/
13
- COMBO_LEFT_RIGHT = [:left, :left, :right, :right]
14
- COMBO_OUT_IN = [:out, :in, :out, :in]
15
-
16
- Duplicate = Class.new RuntimeError
19
+ NOTHING = "".freeze
17
20
 
18
21
  class << self
19
- end # === class self ===
20
22
 
21
- attr_reader :digs, :WITHS, :data
22
- def initialize *args
23
- @WITH = nil
24
- @FRAGMENT = nil
25
- @SQL = nil
26
- @WITHS = []
23
+ def box_to_string arr
24
+ h = {:SELECT=>[], :FROM=>[], :GROUP_BY=>[], :ORDER_BY=>[]}
25
+ table_name_stack = []
26
+ arr.each { |raw|
27
+ name, args, blok = raw
28
+
29
+ case name
30
+ when :SELECT
31
+ h[:SELECT] << args.join(' AS ')
32
+
33
+ when :FROM, :LEFT, :RIGHT, :INNER
34
+ table_name_stack.push args.last
35
+ str = if name == :FROM
36
+ args.join(' ')
37
+ else
38
+ "#{name} JOIN #{args.join ' '}"
39
+ end
40
+
41
+ table_name = args.last
42
+
43
+ if blok
44
+ on = []
45
+ blok.each { |raw|
46
+ args = raw.dup
47
+ name = args.shift
48
+ blok = args.pop
49
+ case name
50
+ when :ON
51
+ on << args
52
+
53
+ when :SELECT
54
+ h[:SELECT] << "#{table_name}.#{args.join ' AS '}"
27
55
 
28
- @digs = []
29
- @data = H.new(:allow_update)
30
- .merge!(
31
- :name => nil,
32
- :raw => nil,
33
- :vars => H.new,
34
- :procs => H.new
35
- )
36
-
37
- args.each { |a|
56
+ else
57
+ fail ArgumentError, "Unknown name: #{name.inspect}"
58
+ end
59
+ }
38
60
 
39
- case a
61
+ ( str << "\nON " << on.join(' AND ') ) unless on.empty?
62
+ end
40
63
 
41
- when Symbol
42
- @data[:name] = a
64
+ str.gsub!(/\b([_]+)(?=\.)/) { |match|
65
+ t_name = table_name_stack[match.size * -1]
66
+ fail ArgumentError, "Name not found for: #{match}" if !t_name
67
+ t_name
68
+ }
43
69
 
44
- when I_Dig_Sql
45
- @digs << a
70
+ h[:FROM] << str
46
71
 
47
- when String
48
- @data.merge!(:raw=>a)
72
+ when :GROUP_BY, :ORDER_BY
73
+ h[name].concat args
49
74
 
50
- when Hash, H
51
- if a.has_key?(:raw)
52
- @data.merge! a
53
75
  else
54
- @data[:vars].merge! a
76
+ fail ArgumentError, "Unknown: #{name.inspect}"
55
77
  end
78
+ }
56
79
 
57
- else
58
- fail ArgumentError, "Unknown arg: #{a.inspect}"
59
-
60
- end # === case
80
+ <<-EOF
81
+ SELECT
82
+ #{h[:SELECT].empty? ? '*' : h[:SELECT].join(",\n")}
83
+ FROM
84
+ #{h[:FROM].join "\n"}
85
+ #{ h[:GROUP_BY].empty? ? '' : 'GROUP BY ' + h[:GROUP_BY].join(', ')}
86
+ #{ h[:ORDER_BY].empty? ? '' : 'ORDER BY ' + h[:ORDER_BY].join(', ')}
87
+ EOF
88
+ end # === def box_to_string
89
+
90
+ #
91
+ # Examples:
92
+ # string( "...", dig)
93
+ # string( dig ) { FROM ... }
94
+ #
95
+ def string *args, &blok
96
+ dig = args.pop
97
+ str = args.shift || box_to_string(Box.new &blok)
98
+ s = str.dup
99
+ withs = []
100
+
101
+ while s[HAS_VAR]
102
+ s.gsub!(/\{\{\s?([^\}]+)\s?\}\}/) do |match|
103
+ pieces = $1.split
104
+ name = pieces.shift
105
+ key = name.to_sym
106
+ args = pieces
107
+ withs << key
108
+ withs.concat(dig.sql(key)[:withs]) if dig.sqls.has_key?(key)
109
+ case args.size
110
+ when 0
111
+ name
112
+ else
113
+ "SELECT #{args.join ', '} FROM #{name}"
114
+ end # === case
115
+ end
116
+ end # === while s HAS_VAR
117
+
118
+ with = if withs.empty?
119
+ ""
120
+ else
121
+ maps = []
122
+ done = {}
123
+ withs.uniq!
124
+ withs.each { |name|
125
+ next if done[name]
126
+ fragment = dig.sql(name)[:base]
127
+ (fragment = fragment.gsub(/^/, " ")) if ENV['IS_DEV']
128
+ maps << "#{name} AS (\n#{fragment}\n )"
129
+ done[name] = true
130
+ } # === each withs
131
+
132
+ %^WITH\n #{maps.join ",\n "}\n\n^
133
+ end
134
+
135
+ [with + s, s, withs]
136
+ end # === def extract_withs
61
137
 
62
- }
138
+ end # === class self ===
63
139
 
140
+ attr_reader :vars, :sqls
141
+ def initialize
142
+ @stack = []
143
+ @sqls = H.new
144
+ @vars = H.new
64
145
  end # === def initialize
65
146
 
66
- %w{name raw vars}.each { |k|
67
- eval <<-EOF.strip, nil, __FILE__, __LINE__
68
- def #{k}
69
- @data[:#{k}]
70
- end
71
- EOF
72
- }
73
-
74
- def vars!
75
- vars = H.new.merge!(@data[:vars])
76
-
77
- @digs.reverse.inject(vars) { |memo, dig|
78
- if dig != self
79
- memo.merge! dig.vars
80
- end
81
- memo
82
- }
83
- end
84
-
85
- def has_key? name
86
- !!(search name)
87
- end
88
-
89
- def search name
90
- fail ArgumentError, "No name specified: #{name.inspect}" if !name
91
- return self if self.name == name
92
- found = false
93
- if @data[:procs].has_key?(name)
94
- return @data[:procs][name]
95
- end
96
-
97
- @digs.reverse.detect { |d|
98
- found = if d.name == name
99
- d
100
- elsif d.data[:procs].has_key?(name)
101
- d.data[:procs][name]
102
- else
103
- d.digs.detect { |deep|
104
- found = deep if deep.name == name
105
- found = deep.data[:procs][name] if deep.data[:procs].has_key?(name)
106
- found
107
- }
108
- end
109
- }
110
- found
111
- end
112
-
113
- def [] name
114
- found = search(name)
115
- fail ArgumentError, "SQL not found for: #{name.inspect}" unless found
116
- found
117
- end
118
-
119
- def []= name, val
120
- fail ArgumentError, "Name already taken: #{name.inspect}" if has_key?(name)
121
-
122
- case val
123
- when String
124
- @digs << I_Dig_Sql.new(self, name, val)
125
-
126
- when Hash, H
127
- case
128
- when val[:name] == :DEFAULT
129
- @digs << I_Dig_Sql.new(:DEFAULT, val)
130
- when val[:raw]
131
- @data.merge! val
132
- else
133
- @digs << I_Dig_Sql.new(self, name, val)
134
- end
135
-
136
- when I_Dig_Sql
137
- @digs << val
138
-
139
- when Proc
140
- @data[:procs][name] = val
147
+ def var name, *args
148
+ case args.size
141
149
 
142
- else
143
- fail ArgumentError, "Unknown class: #{name.inspect} -> #{val.class}"
150
+ when 0
151
+ @vars[name]
144
152
 
145
- end # === case
146
- end
153
+ when 1
154
+ @vars[name] = args.first
147
155
 
148
- def each
149
- digs = @digs.reverse
150
- digs.unshift self
151
- if block_given?
152
- digs.each { |d| yield d.name, d }
153
156
  else
154
- digs.each
155
- end
156
- end
157
-
158
-
159
- def << str
160
- (@data[:raw] ||= "") << str
161
- end
162
-
163
- def has_raw?
164
- !!(@data[:raw] && !@data[:raw].strip.empty?)
165
- end
166
-
167
- def to_pair
168
- [to_sql, vars!]
169
- end
170
-
171
- def to_meta *args
172
- compile *args
173
- end
174
-
175
- def to_sql *args
176
- compile(*args)[:SQL]
177
- end
178
-
179
- %w{ FRAGMENT SQL }.each { |k|
180
- eval <<-EOF.strip, nil, __FILE__, __LINE__ + 1
181
- def #{k}
182
- return @#{k} if @SQL
183
- to_meta[:#{k}]
184
- end
185
- EOF
186
- }
187
-
188
- private # ==========================================
189
-
190
- def prefix_raw sym
191
- "raw_#{sym.to_s.split('_').first}".to_sym
192
- end
193
-
194
- def prefix sym
195
- sym.to_s.split('_').first.to_sym
196
- end
197
-
198
- protected(
199
- def table_name one, two = nil
200
- if two
201
- k = two
202
- link_name = one
203
- else
204
- k = one
205
- link_name = nil
206
- end
207
-
208
- case
209
- when [:out_ftable, :in_ftable].include?(k)
210
- "#{real_table}_#{ meta[prefix(k)] }_#{meta[k]}"
211
-
212
- when link? && link_name && @data[link_name][:inner_join].include?(k)
213
- "#{self.name}_#{@data[link_name][:name]}_#{k}"
214
-
215
- else
216
- fail ArgumentError, "Unknown key for table name: #{k.inspect}"
217
- end
218
- end
219
- ) # === protected
220
-
221
- #
222
- # Examples:
223
- #
224
- # field :in, :owner_id
225
- # field :screen_name, :screen_name
226
- # field :in
227
- # field :raw_in
228
- #
229
- protected(
230
- def field *args
231
-
232
- case args
233
- when [:out], [:in]
234
- "#{name}.#{data[args.last][:name]}"
235
- when [:raw, :out], [:raw, :in]
236
- "#{name}.#{self[:DEFAULT].data[args.last]}"
237
- when [:owner_id], [:type_id]
238
- "#{name}.#{args.first}"
239
- else
240
- fail ArgumentError, "Unknown args: #{args.inspect}"
241
- end # === case
242
-
243
- end # === def field
244
- )
245
-
246
- protected def compile target = nil
247
- return(self[target].compile) if target && target != name
248
-
249
- if !@SQL
250
- compile_raw
251
- end
252
-
253
- {FRAGMENT: @FRAGMENT, SQL: @SQL, WITH: @WITH, VARS: vars!}
254
- end # === def to_sql
255
-
256
- def compile_raw
257
- @data[:raw].freeze
258
-
259
- s = @FRAGMENT = @data[:raw].dup
260
-
261
- while s[HAS_VAR]
262
- s.gsub!(/\{\{\s?([a-zA-Z0-9\_]+)\s?\}\}/) do |match|
263
- key = $1.to_sym
264
- @WITHS << key
265
- key
266
- end
267
-
268
- s.gsub!(/\<\<\s?([a-zA-Z0-9\_\-\ \*]+)\s?\>\>/) do |match|
269
- tokens = $1.split
270
-
271
- key = tokens.last.to_sym
157
+ fail ArgumentError, "Unknown args: #{args.inspect}"
272
158
 
273
- if has_key?(key)
159
+ end # === case
160
+ end # === def var
274
161
 
275
- tokens.pop
276
- target = self[key]
162
+ # Example:
163
+ # sql(:name)
164
+ # sql(:name, 'string')
165
+ # sql(:name) { FROM ... }
166
+ #
167
+ def sql name, *args, &blok
168
+ case
277
169
 
278
- if target.is_a?(Proc)
279
- target.call self, *tokens
280
- else
281
- field = tokens.empty? ? nil : tokens.join(' ')
282
-
283
- if field
284
- @WITHS << key
285
- tokens.pop
286
- "SELECT #{field} FROM #{key}"
287
- else
288
- target.to_sql
289
- end
290
- end
170
+ when args.size == 0 && !block_given?
171
+ @sqls[name]
291
172
 
292
- elsif has_key?(tokens.first.to_sym)
293
- self[tokens.shift.to_sym].call self, *tokens
173
+ when (args.size == 0 && block_given?) || args.size == 1
174
+ @sqls[name] = H.new
175
+ @sqls[name][:complete], @sqls[name][:base], @sqls[name][:withs] = I_Dig_Sql.string(*(args), self, &blok)
294
176
 
295
- else
296
- fail ArgumentError, "Not found: #{$1}"
297
-
298
- end # === if has_key?
299
- end
300
- end # === while s HAS_VAR
301
-
302
- @WITH = if @WITHS.empty?
303
- ""
304
- else
305
- %^WITH\n #{WITH()}\n\n^
306
- end
307
-
308
- @SQL = (@WITH + @FRAGMENT).strip
309
- end # === fragments_to_raw
310
-
311
- def WITH
312
- withs = @WITHS.dup
313
- maps = []
314
- done = {}
315
- while name = withs.shift
316
- next if done[name]
317
- if name == :DEFAULT || !self.has_key?(name)
318
- done[name] = true
319
- next
320
- end
321
-
322
- fragment = self[name].FRAGMENT
323
- fragment.gsub!(/^/, " ") if ENV['IS_DEV']
324
- maps << "#{name} AS (\n#{fragment}\n )"
325
- withs.concat self[name].WITHS
326
- done[name] = true
327
- end # === while name
328
-
329
- maps.join ",\n "
330
- end
331
-
332
- def WHERE
333
- wheres = @data[:WHERE].dup
334
-
335
- if link? && name != :block
336
- wheres << "#{field :type_id} = :#{name.to_s.upcase}_TYPE_ID"
337
-
338
- pattern = [ data[:out][:inner_join], data[:in][:inner_join] ]
339
- left, mid = data[:out][:inner_join]
340
- right, _ = *data[:in][:inner_join]
341
-
342
-
343
- case
344
- when [[:screen_name], [:screen_name]], [[:screen_name, :computer],[:screen_name]]
345
- # do nothing
346
- else
347
- fail "Programmer Error: Permissions not implemented for: #{pattern.inspect}"
348
- end # === case
349
-
350
- if left == :screen_name && right == :screen_name
351
- default = self[:DEFAULT]
352
- block = self[:block]
353
- blocked = block.table_name(:out, :screen_name)
354
- victim = block.table_name(:in, :screen_name)
355
- f_in = table_name :in, :screen_name
356
- f_out = table_name :out, :screen_name
357
-
358
- wheres << %^
359
- NOT EXISTS (
360
- SELECT 1
361
- FROM #{block.real_table} AS block
362
- WHERE
363
- (
364
- block.type_id = :BLOCK_SCREEN_TYPE_ID
365
- AND (
366
- (
367
- #{f_out}.owner_id = #{block.field :out}
368
- AND
369
- #{f_in}.owner_id = #{victim}.owner_id
370
- )
371
- OR
372
- (
373
- #{field :raw, :in} = #{block.field :out}
374
- AND
375
- #{blocked}.owner_id = #{victim}.owner_id
376
- )
377
- )
378
- )
379
- OR
380
- (
381
- block.type_id = :BLOCK_OWNER_TYPE_ID
382
- AND (
383
- (
384
- #{f_out}.owner_id = #{blocked}.owner_id
385
- AND
386
- #{f_in}.owner_id = #{victim}.owner_id
387
- )
388
- OR
389
- (
390
- #{f_in}.owner_id = #{blocked}.owner_id
391
- AND
392
- #{f_out}.owner_id = #{victim}.owner_id
393
- )
394
- ) -- AND
395
- )
396
- ) -- NOT EXISTS
397
- ^
398
- end # === if :screen_name, :screen_name
399
-
400
- if mid == :computer
401
- asql(wheres.last)
402
- fail "COMPUTER not ready"
403
- end
404
-
405
- end # === if link?
406
-
407
- if @data.has_key?(:OF)
408
- table = self[@data[:FROM].first]
409
- wheres << "#{table.field(:out)} = #{@data[:OF].first}"
410
- end
411
-
412
- if false && @data[:NOT_EXISTS]
413
- block = self[meta[:not_exists]]
414
- w = ""
415
- w << %^ NOT EXISTS (\n^
416
- w << %^ SELECT 1\n^
417
- w << %^ FROM #{meta[:not_exists]}\n^
418
- w << %^ WHERE\n^
419
-
420
- conds = []
421
- block[:where].each { |block_meta|
422
- type_id = block_meta.first
423
- c = ""
424
- c << %^ (\n^
425
- c << " #{field meta, block_meta[1][1], block_meta[1][2]} = #{field block, block_meta[2][1], block_meta[2][2]}\n"
426
- c << " AND\n"
427
- c << " #{meta[:not_exists]}.type_id = :#{type_id}_TYPE_ID\n"
428
- c << " AND\n"
429
- c << " #{field meta, block_meta[3][1], block_meta[3][2]} = #{field block, block_meta[4][1], block_meta[4][2]}\n"
430
- c << %^ )\n^
431
- conds << c
432
- }
177
+ else
178
+ fail ArgumentError, "Unknown args: #{args.inspect}"
433
179
 
434
- w << conds.join(" OR\n")
435
- sql[:WHERE] << w
436
- end
180
+ end # === case
181
+ end # === def sql
437
182
 
183
+ def pair name = :SQL, vars = {}
184
+ @vars.freeze unless @vars.frozen?
185
+ [sql(name)[:complete], @vars.merge(vars)]
186
+ end # === def to_sql
438
187
 
439
- return nil if wheres.empty?
440
- wheres.join " AND "
441
- end
188
+ end # === class I_Dig_Sql ===
442
189
 
443
- # === END: Rendering DSL ==========================================
444
190
 
445
- end # === class I_Dig_Sql ===
446
191
 
447
192
 
448
193
 
@@ -0,0 +1,10 @@
1
+
2
+ class I_Dig_Sql
3
+
4
+ def initialize str
5
+ @meta = {
6
+ :origin => str
7
+ }
8
+ end
9
+
10
+ end # === I_Dig_Sql
@@ -2,39 +2,36 @@
2
2
 
3
3
  describe :I_Dig_Sql do
4
4
 
5
+ it "runs" do
6
+ dig = I_Dig_Sql.new
7
+ dig.sql :heroes do
8
+ FROM(:tbl, :heroes) {
9
+ SELECT(:nickname, :name)
10
+ }
11
+ end
12
+
13
+ sql(dig.sql(:heroes)).should == sql("SELECT heroes.nickname AS name FROM tbl heroes")
14
+ end # === it
15
+
5
16
  it "runs the code from README.md" do
6
17
  readme = File.read(File.expand_path(File.dirname(__FILE__) + '/../README.md'))
7
- code = readme[/```ruby([^`]+)```/] && $1
18
+ code = (readme[/```ruby([^`]+)```/] && $1).split("\n").map { |l| l.sub('puts ', '') }.join("\n")
8
19
  line = 0
9
20
  readme.split("\n").detect { |l| line = line + 1; l['```ruby'] }
10
- result = eval(code, nil, 'README.md', line)
11
- sql(result).should == sql(%^
12
- WITH
13
- HEROES AS ( SELECT id FROM hero WHERE id = :PERSON_ID ),
14
- VILLIANS AS ( SELECT id FROM villian WHERE id = :PERSON_ID )
15
- SELECT * FROM people
16
- WHERE
17
- id IN ( SELECT id FROM hero WHERE id = :PERSON_ID AND status = :ALIVE)
18
- OR
19
- id IN (SELECT ID FROM HEROES AND status = :ALIVE)
20
- OR
21
- id IN ( SELECT * FROM HEROES )
22
- OR
23
- id IN ( SELECT patron_id FROM VILLIANS )
24
- OR
25
- id IN ( SELECT id FROM villian WHERE id = :PERSON_ID )
26
- ^)
21
+ should.not.raise {
22
+ eval(code, nil, 'README.md', line)
23
+ }
27
24
  end # === it
28
25
 
29
26
  it "adds WITH: {{MY_NAME}}" do
30
27
  sql = I_Dig_Sql.new
31
- sql[:MY_HERO] = "SELECT * FROM hero"
32
- sql[:MY_NAME] = "SELECT * FROM name"
33
- sql << %^
28
+ sql.sql :MY_HERO, "SELECT * FROM hero"
29
+ sql.sql :MY_NAME, "SELECT * FROM name"
30
+ sql.sql :SQL, %^
34
31
  {{MY_HERO}}
35
32
  {{MY_NAME}}
36
33
  ^
37
- sql(sql).should == sql(%^
34
+ sql(sql.sql :SQL).should == sql(%^
38
35
  WITH
39
36
  MY_HERO AS (
40
37
  SELECT * FROM hero
@@ -47,25 +44,16 @@ describe :I_Dig_Sql do
47
44
  ^)
48
45
  end
49
46
 
50
- it "replaces text with content: << MY_NAME >>" do
51
- sql = I_Dig_Sql.new
52
- sql[:MY_HERO] = "SELECT * FROM hero"
53
- sql << %^
54
- << MY_HERO >>
55
- ^
56
- sql(sql).should == "SELECT * FROM hero"
57
- end # === it
58
-
59
47
  %w{ * id }.each { |s|
60
- it "adds WITH: << #{s} MY_NAME >>" do
48
+ it "adds WITH: {{ MY_NAME #{s} }}" do
61
49
  sql = I_Dig_Sql.new
62
- sql[:MY_HERO] = "SELECT id, p_id FROM hero"
63
- sql[:MY_NAME] = "SELECT id, n_id FROM name"
64
- sql << %^
65
- << #{s} MY_NAME >>
66
- << #{s} MY_HERO >>
50
+ sql.sql :MY_HERO, "SELECT id, p_id FROM hero"
51
+ sql.sql :MY_NAME, "SELECT id, n_id FROM name"
52
+ sql.sql :SQL, %^
53
+ {{ MY_NAME #{s} }}
54
+ {{ MY_HERO #{s} }}
67
55
  ^
68
- sql(sql).should == sql(%^
56
+ sql(sql.sql :SQL).should == sql(%^
69
57
  WITH
70
58
  MY_NAME AS (
71
59
  SELECT id, n_id FROM name
@@ -77,8 +65,105 @@ describe :I_Dig_Sql do
77
65
  SELECT #{s} FROM MY_HERO
78
66
  ^)
79
67
  end # === it
80
- }
68
+
69
+ it "replaces text with content: {{ MY_NAME #{s} }}" do
70
+ sql = I_Dig_Sql.new
71
+ sql.sql :MY_HERO, "SELECT * FROM hero"
72
+ sql.sql :SQL, " {{ MY_HERO #{s} }} "
73
+ sql(sql.sql :SQL).should == "WITH MY_HERO AS ( SELECT * FROM hero ) SELECT #{s} FROM MY_HERO"
74
+ end # === it
75
+ } # === %w{}
76
+
77
+ it "renders nested replacements" do
78
+ i = I_Dig_Sql.new
79
+ i.sql :THIRD, "SELECT id FROM phantom"
80
+ i.sql :SECOND, " {{ THIRD }} "
81
+ i.sql :FIRST, " {{ SECOND }} "
82
+ i.sql :SQL, " {{ FIRST }} "
83
+ sql(i.sql :SQL).should == sql(
84
+ <<-EOF
85
+ WITH
86
+ FIRST AS ( SECOND ),
87
+ SECOND AS ( THIRD ),
88
+ THIRD AS ( SELECT id FROM phantom )
89
+ FIRST
90
+ EOF
91
+ )
92
+ end # === it
93
+
94
+ it "prints CTE definitions once, if used multiple times" do
95
+ i = I_Dig_Sql.new
96
+ i.sql :my_cte, "SELECT * FROM my"
97
+ i.sql :SQL, <<-EOF
98
+ {{my_cte}}
99
+ {{my_cte}}
100
+ EOF
101
+ sql(i.sql :SQL).should == sql(%^
102
+ WITH
103
+ my_cte AS (
104
+ SELECT * FROM my
105
+ )
106
+ my_cte
107
+ my_cte
108
+ ^)
109
+ end # === it
81
110
 
82
111
  end # === describe :I_Dig_Sql
83
112
 
84
113
 
114
+ describe ":pair" do
115
+
116
+ it "returns an array of: [string, hash]" do
117
+ s = "select * from THE_WORLD"
118
+ i = I_Dig_Sql.new
119
+ i.sql :SQL, s
120
+ i.pair(:SQL).should == [s, {}]
121
+ end # === it
122
+
123
+ it "merges the hash passed to it" do
124
+ s = "select * from THE_UNI"
125
+ i = I_Dig_Sql.new
126
+ i.sql :SQL, s
127
+ i.pair(:SQL, {:a=>:b}).should == [s, {:a=>:b}]
128
+ end # === it
129
+
130
+ it "does not save :vars passed to it" do
131
+ s = "select * from THE_UNI"
132
+ i = I_Dig_Sql.new
133
+ i.sql :SQL, s
134
+ i.pair(:SQL, {:a=>:b})
135
+ i.vars.should == {}
136
+ end # === it
137
+
138
+ end # === describe pair
139
+
140
+
141
+ describe ":box" do
142
+
143
+ it "turns a box into a String" do
144
+ sql = I_Dig_Sql.new
145
+ sql.sql :joins do
146
+
147
+ FROM(:n, :notes) {
148
+ SELECT(:long_name, :name)
149
+ }
150
+
151
+ LEFT(:f, :fails) {
152
+ ON('_.n = __.n')
153
+ SELECT(:long_name, :name)
154
+ }
155
+
156
+ end
157
+
158
+ sql(sql.sql :joins).should == sql(%^
159
+ SELECT
160
+ notes.long_name AS name,
161
+ fails.long_name AS name
162
+ FROM
163
+ n notes
164
+ LEFT JOIN f fails
165
+ ON fails.n = notes.n
166
+ ^)
167
+ end # === it
168
+
169
+ end # === describe ":box"
@@ -41,11 +41,20 @@ module Kernel
41
41
  end # === module Kernel
42
42
 
43
43
  def sql o
44
- if o.is_a? String
45
- return o.split.join(" ")
44
+ case o
45
+ when String
46
+ o.split.join(" ")
47
+
48
+ when I_Dig_Sql::H
49
+ sql o[:complete]
50
+
51
+ when Array
52
+ sql o.first
53
+
46
54
  else
47
55
  sql(o.to_sql)
48
- end
49
- end
56
+
57
+ end # === case
58
+ end # === def sql
50
59
 
51
60
 
metadata CHANGED
@@ -1,59 +1,59 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: i_dig_sql
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - da99
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-12 00:00:00.000000000 Z
11
+ date: 2015-05-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: bundler
14
+ name: boxomojo
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.5'
20
- type: :development
19
+ version: 1.0.1
20
+ type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.5'
26
+ version: 1.0.1
27
27
  - !ruby/object:Gem::Dependency
28
- name: bacon
28
+ name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.0'
33
+ version: '1.5'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.0'
40
+ version: '1.5'
41
41
  - !ruby/object:Gem::Dependency
42
- name: Bacon_Colored
42
+ name: bacon
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '1.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '1.0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: pry
56
+ name: Bacon_Colored
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">"
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: awesome_print
70
+ name: pry
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">"
@@ -81,7 +81,7 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: anbt-sql-formatter
84
+ name: awesome_print
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">"
@@ -127,7 +127,8 @@ description: "\n You probably want another gem: arel. Use that\n to generate S
127
127
  \ "
128
128
  email:
129
129
  - i-hate-spam-1234567@mailinator.com
130
- executables: []
130
+ executables:
131
+ - i_dig_sql
131
132
  extensions: []
132
133
  extra_rdoc_files: []
133
134
  files:
@@ -137,17 +138,12 @@ files:
137
138
  - README.md
138
139
  - Rakefile
139
140
  - VERSION
140
- - bin/test
141
+ - bin/i_dig_sql
141
142
  - i_dig_sql.gemspec
142
143
  - lib/i_dig_sql.rb
143
144
  - lib/i_dig_sql/H.rb
144
- - lib/i_dig_sql/String.rb
145
+ - lib/i_dig_sql/string.rb
145
146
  - specs/0000-I_Dig_Sql.rb
146
- - specs/0001-new.rb
147
- - specs/0010-vars.rb
148
- - specs/0011-to_pair.rb
149
- - specs/0020-to_sql.rb
150
- - specs/0030-funcs.rb
151
147
  - specs/helpers.rb
152
148
  homepage: https://github.com/da99/i_dig_sql
153
149
  licenses:
@@ -169,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
169
165
  version: '0'
170
166
  requirements: []
171
167
  rubyforge_project:
172
- rubygems_version: 2.4.5
168
+ rubygems_version: 2.4.6
173
169
  signing_key:
174
170
  specification_version: 4
175
171
  summary: Yet another way of generating Postgresql 9.2+ in Ruby.
data/bin/test DELETED
@@ -1,47 +0,0 @@
1
- #!/usr/bin/env bash
2
- # -*- bash -*-
3
- #
4
- #
5
- x="$1"
6
- set -u -e -o pipefail
7
-
8
- if [[ "$x" == "watch" ]]; then
9
- shift
10
-
11
- echo ""
12
- echo "=== Watching: $@"
13
-
14
- echo -e "\n=== Running test:"
15
- bin/test "$@" || echo ""
16
-
17
- inotifywait -q -m -e close_write,close --exclude .git/ -r . | while read CHANGE
18
- do
19
-
20
- dir=$(echo "$CHANGE" | cut -d' ' -f 1)
21
- op=$(echo "$CHANGE" | cut -d' ' -f 2)
22
- file=$(echo "$CHANGE" | cut -d' ' -f 3)
23
- path="${dir}$file"
24
-
25
- if [[ "$op" == *CLOSE_WRITE* && $file == *.rb* ]]; then
26
- echo -e "\n=== Running test:"
27
- bin/test "$@" || echo ""
28
- fi
29
-
30
- done # === do
31
-
32
- echo ""
33
- exit 0
34
- fi # === if watch
35
-
36
- files="$(echo specs/*-$x.rb)"
37
- if [[ -f "$files" ]]; then
38
- shift
39
- else
40
- files="$(echo specs/*-*.rb)"
41
- fi
42
-
43
-
44
-
45
- bundle exec bacon -rpry -ri_dig_sql specs/helpers.rb $files "$@"
46
-
47
-
@@ -1,8 +0,0 @@
1
-
2
- class String
3
-
4
- def i_dig_sql
5
- I_Dig_Sql.new self
6
- end
7
-
8
- end # === class String ===
@@ -1,20 +0,0 @@
1
-
2
-
3
- describe '.new' do
4
-
5
- it "merges sql values: .new(i_dig_sql)" do
6
- first = I_Dig_Sql.new
7
- first[:hero] = "SELECT * FROM hero"
8
- second = I_Dig_Sql.new(first)
9
- second[:name] = "SELECT * FROM name"
10
- second << %^
11
- << hero >>
12
- << name >>
13
- ^
14
- sql(second).should == sql(%^
15
- SELECT * FROM hero
16
- SELECT * FROM name
17
- ^)
18
- end # === it
19
-
20
- end # === describe '.new'
@@ -1,38 +0,0 @@
1
-
2
- describe :vars do
3
-
4
- it "combines vars from other digs" do
5
- one = I_Dig_Sql.new
6
- one.vars[:one] = 1
7
-
8
- two = I_Dig_Sql.new
9
- one.vars[:two] = 2
10
-
11
- three = I_Dig_Sql.new
12
- one.vars[:three] = 3
13
-
14
- dig = I_Dig_Sql.new one, two, three
15
- dig.vars[:four] = 4
16
-
17
- dig.vars!.should == {
18
- four: 4,
19
- three: 3,
20
- two: 2,
21
- one: 1
22
- }
23
- end # === it
24
-
25
- it "fails w/Duplicate if other digs have the same var name" do
26
- should.raise(ArgumentError) {
27
- one = I_Dig_Sql.new
28
- one.vars[:two] = 2
29
-
30
- i = I_Dig_Sql.new one
31
- i.vars[:two] = 3
32
- i.vars!
33
- }.message.should.match /Key already set: :two/
34
- end # === it
35
-
36
- end # === describe :vars
37
-
38
-
@@ -1,15 +0,0 @@
1
-
2
- describe :to_pair do
3
-
4
- it "returns an Array: [String, Hash]" do
5
- i = I_Dig_Sql.new <<-EOF
6
- SELECT * FROM new
7
- EOF
8
- i.vars[:one] = 2
9
- sql, vars = i.to_pair
10
- sql(sql).should == sql("SELECT * FROM new")
11
- vars.should == {one: 2}
12
- end # === it
13
-
14
- end # === describe :to_pair
15
-
@@ -1,31 +0,0 @@
1
-
2
-
3
- describe :to_sql do
4
-
5
- it "renders nested replacements" do
6
- i = I_Dig_Sql.new <<-EOF
7
- << FIRST >>
8
- EOF
9
- i[:FIRST] = " << SECOND >> "
10
- i[:SECOND] = " << THIRD >> "
11
- i[:THIRD] = "SELECT id FROM phantom"
12
- sql(i).should == "SELECT id FROM phantom"
13
- end # === it
14
-
15
- it "prints CTE definitions once, if used multiple times" do
16
- i = I_Dig_Sql.new <<-EOF
17
- {{my_cte}}
18
- {{my_cte}}
19
- EOF
20
- i[:my_cte] = "SELECT * FROM my"
21
- sql(i).should == sql(%^
22
- WITH
23
- my_cte AS (
24
- SELECT * FROM my
25
- )
26
- my_cte
27
- my_cte
28
- ^)
29
- end # === it
30
-
31
- end # === describe :to_sql
@@ -1,40 +0,0 @@
1
-
2
- describe :funcs do
3
-
4
- it "let's you use a lambda in a SQL fragment" do
5
- sql = I_Dig_Sql.new :q, <<-EOF
6
- SELECT name
7
- FROM screen_name
8
- WHERE
9
- << id >>
10
- EOF
11
-
12
- sql[:id] = lambda { |dig| "id = :id" }
13
-
14
- sql(sql).should == sql(%^
15
- SELECT name
16
- FROM screen_name
17
- WHERE
18
- id = :id
19
- ^)
20
- end # === it
21
-
22
- it "passes args to function" do
23
- sql = I_Dig_Sql.new :q, <<-EOF
24
- SELECT name
25
- FROM screen_name
26
- WHERE
27
- << names Hans Hoppe >>
28
- EOF
29
-
30
- sql[:names] = lambda { |dig, *args| "name IN [#{args.join ', '}]" }
31
-
32
- sql(sql).should == sql(%^
33
- SELECT name
34
- FROM screen_name
35
- WHERE
36
- name IN [Hans, Hoppe]
37
- ^)
38
- end # === it
39
-
40
- end # === describe :funcs