i_dig_sql 3.0.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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