kbam 0.3.3.alpha
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/kbam.rb +907 -0
- metadata +74 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 482f3bdcc44a2b0e1de4b64a7944bcab3bdac073
|
4
|
+
data.tar.gz: 1ac046a06875db3b938398624466528073fa8262
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e36a8e85103b1cfb292c61ef242b77a19a799ee6f898f54b2a97d30f5aef09c8e19401534948bcc041b727178f7946ceca1cddefa7c57a4a28434d60f322e43f
|
7
|
+
data.tar.gz: a03f5c696ace9c0dd038feab94d8020edcdf609ba5e858ac6d96356bc63010ed2a22ef1bfd8ee2e83a1f5a2cacd6c313dfd3bc1638b44a700de4b65ebb1aa6c6
|
data/lib/kbam.rb
ADDED
@@ -0,0 +1,907 @@
|
|
1
|
+
require 'mysql2' #the sql adapter
|
2
|
+
require 'colorize' #for error coloring ;)
|
3
|
+
#puts "test include extention"
|
4
|
+
#require 'kbam/extension.rb'
|
5
|
+
|
6
|
+
# string extension
|
7
|
+
class String
|
8
|
+
|
9
|
+
@sql_where_type = "and" # :and, :or
|
10
|
+
|
11
|
+
def sql_where_type
|
12
|
+
#puts "GET WHERE: #{@sql_where_type}"
|
13
|
+
@sql_where_type
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_sql_where_type(type)
|
17
|
+
@sql_where_type = type
|
18
|
+
#puts "SET WHERE: #{@sql_where_type}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Kbam
|
23
|
+
|
24
|
+
attr_reader :is_nested
|
25
|
+
attr_writer :is_nested
|
26
|
+
|
27
|
+
@@client = nil
|
28
|
+
@@sugar = false
|
29
|
+
|
30
|
+
# query_types
|
31
|
+
SELECT = 0
|
32
|
+
INSERT = 1
|
33
|
+
UPDATE = 2
|
34
|
+
|
35
|
+
#REVIEW/FIXME: remove instance connect?!
|
36
|
+
# create instance connection --> for multiple database connections
|
37
|
+
# maybe add settings --> allow separate client connections for each instance
|
38
|
+
# use?
|
39
|
+
# override default class connection if exists --> class fallback
|
40
|
+
def initialize(login_credentials = nil)
|
41
|
+
|
42
|
+
if login_credentials != nil
|
43
|
+
warning "avoid connecting to the database for each instance"
|
44
|
+
connect(login_credentials)
|
45
|
+
end
|
46
|
+
|
47
|
+
# query credentials
|
48
|
+
@selects = Array.new
|
49
|
+
@from = ""
|
50
|
+
@wheres = Array.new
|
51
|
+
@or_wheres = Array.new
|
52
|
+
@group = ""
|
53
|
+
@havings = Array.new
|
54
|
+
@orders = Array.new
|
55
|
+
@limit = 1000
|
56
|
+
@offset = 0
|
57
|
+
@query = "" #raw query
|
58
|
+
@as = "t"
|
59
|
+
@into = ""
|
60
|
+
@insert = {}
|
61
|
+
|
62
|
+
# meta data
|
63
|
+
@last_query = nil
|
64
|
+
@result = nil
|
65
|
+
@is_nested = false
|
66
|
+
@query_type = SELECT
|
67
|
+
#@count_query = nil
|
68
|
+
|
69
|
+
# offer block syntax
|
70
|
+
if block_given?
|
71
|
+
yield self
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
##############
|
77
|
+
# Pulbic API #
|
78
|
+
##############
|
79
|
+
|
80
|
+
#####################
|
81
|
+
### Class methods ###
|
82
|
+
|
83
|
+
# explicitly connect to database
|
84
|
+
def self.connect(login_credentials = nil)
|
85
|
+
if login_credentials != nil
|
86
|
+
if @@client == nil
|
87
|
+
@@client = Mysql2::Client.new(login_credentials)
|
88
|
+
else
|
89
|
+
warning "you are already connected!"
|
90
|
+
end
|
91
|
+
else
|
92
|
+
error "missing database credentials"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# check if has syntax sugar
|
97
|
+
def self.sugar?
|
98
|
+
return @@sugar
|
99
|
+
end
|
100
|
+
|
101
|
+
# sets sugar
|
102
|
+
# REVIEW: add .no_sugar! if possible
|
103
|
+
# or instance dependet sugar?
|
104
|
+
def self.sugar_please!
|
105
|
+
@@sugar = true
|
106
|
+
require 'kbam/sugar'
|
107
|
+
end
|
108
|
+
|
109
|
+
# escapes string
|
110
|
+
# uses native mysql2 client
|
111
|
+
def self.escape(string = nil)
|
112
|
+
Mysql2::Client.escape string.to_s
|
113
|
+
end
|
114
|
+
|
115
|
+
# REVIEW: right approach?
|
116
|
+
# needed when nesting queries --> class checking
|
117
|
+
# def self.name
|
118
|
+
# return "Kbam"
|
119
|
+
# end
|
120
|
+
|
121
|
+
# REVIEW: double check usage
|
122
|
+
def self.clear
|
123
|
+
@@wheres = Array.new
|
124
|
+
end
|
125
|
+
|
126
|
+
# FIXME: add symbole to string
|
127
|
+
# and decimal numbers
|
128
|
+
# escapes variable and adds
|
129
|
+
# backticks for strings
|
130
|
+
def self.sanatize(item = nil)
|
131
|
+
if item != nil
|
132
|
+
if item.is_a?(Integer)
|
133
|
+
item = item.to_i
|
134
|
+
else
|
135
|
+
item.to_s.strip!
|
136
|
+
if item =~ /\A\d+\Z/
|
137
|
+
item = item.to_i
|
138
|
+
else
|
139
|
+
item = "'#{Mysql2::Client.escape(item)}'"
|
140
|
+
#item = "'#{item}'"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
else
|
144
|
+
warning "can't sanatize nil class"
|
145
|
+
end
|
146
|
+
return item
|
147
|
+
end
|
148
|
+
|
149
|
+
# FIXME: add nil checking
|
150
|
+
def self.replace(string = nil, values = nil)
|
151
|
+
i = 0
|
152
|
+
last_value = nil
|
153
|
+
|
154
|
+
replaced_string = string.gsub(/\?/) do |match|
|
155
|
+
if i > 0
|
156
|
+
if values[i] != nil
|
157
|
+
last_value = replacement = self.class.sanatize values[i]
|
158
|
+
else
|
159
|
+
replacement = last_value
|
160
|
+
end
|
161
|
+
else
|
162
|
+
unless values[i] != nil
|
163
|
+
puts "missing argument!"
|
164
|
+
else
|
165
|
+
last_value = replacement = self.class.sanatize values[i]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
i += 1
|
170
|
+
replacement
|
171
|
+
end
|
172
|
+
|
173
|
+
return replaced_string
|
174
|
+
end
|
175
|
+
|
176
|
+
########################
|
177
|
+
### Instance methods ###
|
178
|
+
|
179
|
+
# DEPRECATED?
|
180
|
+
# explicitly connect to database
|
181
|
+
def connect(login_credentials)
|
182
|
+
self.connect(login_credentials)
|
183
|
+
end
|
184
|
+
|
185
|
+
# where API
|
186
|
+
def query(string, *value)
|
187
|
+
|
188
|
+
values = *value.to_a
|
189
|
+
|
190
|
+
query_statement = replace(string, values)
|
191
|
+
|
192
|
+
if query_statement != ""
|
193
|
+
@query = query_statement
|
194
|
+
end
|
195
|
+
|
196
|
+
return self
|
197
|
+
end
|
198
|
+
|
199
|
+
# REVIEW/DEPRECATED ?
|
200
|
+
# FIXME: query auto execute? --> get?
|
201
|
+
# FIXME: needs response/callback!
|
202
|
+
def execute()
|
203
|
+
log(@query, "raw sql")
|
204
|
+
|
205
|
+
@@client.query(@query)
|
206
|
+
end
|
207
|
+
|
208
|
+
# FIXME: check nil / empty
|
209
|
+
def select(*fields)
|
210
|
+
fields = *fields.to_a
|
211
|
+
|
212
|
+
fields.each do |field|
|
213
|
+
|
214
|
+
#remove preceeding and trailing whitespaces and comma
|
215
|
+
cleaned_select = field.to_s.gsub(/\s*\n\s*/, ' ').to_s.sub(/\A\s*,?\s*/, '').sub(/\s*,?\s*\Z/, '')#.gsub(/\s*,\s*/, ", ")
|
216
|
+
|
217
|
+
#back-quote fieldnames
|
218
|
+
# before NO '?<field>''
|
219
|
+
cleaned_select.gsub! /(\w+)(?=\s+AS\s+)/ do |match|
|
220
|
+
"#{field_sanatize($1)}" #before 'field' was '$1'
|
221
|
+
end
|
222
|
+
|
223
|
+
@selects.push(cleaned_select)
|
224
|
+
end
|
225
|
+
|
226
|
+
return self
|
227
|
+
end
|
228
|
+
|
229
|
+
# setter and getter AS
|
230
|
+
def as(table_name = nil)
|
231
|
+
if table_name != nil
|
232
|
+
@as = table_name
|
233
|
+
return self
|
234
|
+
else
|
235
|
+
return @as
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def insert(value_pair)
|
240
|
+
|
241
|
+
@query_type = INSERT
|
242
|
+
|
243
|
+
columns = ""
|
244
|
+
values = ""
|
245
|
+
column_count = 0
|
246
|
+
|
247
|
+
value_pair.each do |key, value|
|
248
|
+
|
249
|
+
if column_count != 0
|
250
|
+
values += ", "
|
251
|
+
columns += ", "
|
252
|
+
end
|
253
|
+
|
254
|
+
columns += "#{field_sanatize(key.to_s)}"
|
255
|
+
values += "#{self.class.sanatize(value)}"
|
256
|
+
|
257
|
+
column_count += 1
|
258
|
+
end
|
259
|
+
|
260
|
+
@insert = " (#{columns}) VALUES (#{values})"
|
261
|
+
|
262
|
+
return self
|
263
|
+
|
264
|
+
end
|
265
|
+
|
266
|
+
def update(value_pair)
|
267
|
+
|
268
|
+
@query_type = UPDATE
|
269
|
+
|
270
|
+
@update = "SET "
|
271
|
+
column_count = 0
|
272
|
+
|
273
|
+
value_pair.each do |key, value|
|
274
|
+
|
275
|
+
if column_count != 0
|
276
|
+
@update += ", "
|
277
|
+
end
|
278
|
+
|
279
|
+
@update += "#{field_sanatize(key.to_s)} = #{self.class.sanatize(value)}"
|
280
|
+
|
281
|
+
column_count += 1
|
282
|
+
|
283
|
+
end
|
284
|
+
|
285
|
+
return self
|
286
|
+
|
287
|
+
end
|
288
|
+
|
289
|
+
# used together with 'insert'
|
290
|
+
def into(table)
|
291
|
+
|
292
|
+
if @query_type === INSERT
|
293
|
+
@into = "INSERT INTO #{field_sanatize(table)} "
|
294
|
+
elsif @query_type === UPDATE
|
295
|
+
@into = "UPDATE #{field_sanatize(table)}"
|
296
|
+
end
|
297
|
+
|
298
|
+
return self
|
299
|
+
|
300
|
+
end
|
301
|
+
|
302
|
+
def run
|
303
|
+
|
304
|
+
if @query_type === INSERT
|
305
|
+
|
306
|
+
# execute insert query
|
307
|
+
@query = "#{@into} #{@insert}"
|
308
|
+
execute
|
309
|
+
|
310
|
+
# return the insert_id
|
311
|
+
@query = "SELECT LAST_INSERT_ID() AS insert_id;"
|
312
|
+
query_result = execute
|
313
|
+
|
314
|
+
insert_id = query_result.first["insert_id"]
|
315
|
+
return insert_id
|
316
|
+
|
317
|
+
elsif @query_type === UPDATE
|
318
|
+
|
319
|
+
# execute insert query
|
320
|
+
@query = "#{@into} #{@update} #{compose_where}"
|
321
|
+
execute
|
322
|
+
|
323
|
+
return true
|
324
|
+
|
325
|
+
end
|
326
|
+
|
327
|
+
end
|
328
|
+
|
329
|
+
def from(from_string = nil)
|
330
|
+
|
331
|
+
if from_string.class.name == "Kbam"
|
332
|
+
from_string.is_nested = true
|
333
|
+
@from = "(#{from_string.compose_query}\n )AS #{from_string.as}"
|
334
|
+
else
|
335
|
+
if from_string != nil && from_string.to_s.strip! != ""
|
336
|
+
@from = from_string
|
337
|
+
else
|
338
|
+
error "missing table"
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
return self
|
343
|
+
end
|
344
|
+
|
345
|
+
def group(group_string = nil)
|
346
|
+
if group_string != nil && group_string != ""
|
347
|
+
@group = group_string
|
348
|
+
else
|
349
|
+
warning "group method is empty"
|
350
|
+
end
|
351
|
+
|
352
|
+
return self
|
353
|
+
end
|
354
|
+
|
355
|
+
# add class alias
|
356
|
+
class << self
|
357
|
+
alias :esc :escape
|
358
|
+
end
|
359
|
+
|
360
|
+
# where API
|
361
|
+
def where(string, *value)
|
362
|
+
|
363
|
+
values = *value.to_a
|
364
|
+
|
365
|
+
#log string.class.name, "and log"
|
366
|
+
|
367
|
+
if string.class.name == "Kbam"
|
368
|
+
|
369
|
+
where_string = ""
|
370
|
+
|
371
|
+
i = 0
|
372
|
+
string.get_wheres.each do |w|
|
373
|
+
if i > 0
|
374
|
+
if w.sql_where_type == "or"
|
375
|
+
where_string += " OR "
|
376
|
+
else
|
377
|
+
where_string += " AND "
|
378
|
+
end
|
379
|
+
end
|
380
|
+
where_string += "#{w}\n "
|
381
|
+
i += 1
|
382
|
+
end
|
383
|
+
|
384
|
+
#puts "NESTED WHERE: #{where_string}"
|
385
|
+
|
386
|
+
if where_string =~ /\s+OR\s+/i
|
387
|
+
|
388
|
+
where_string = "(#{where_string})"
|
389
|
+
end
|
390
|
+
where_string.set_sql_where_type("and")
|
391
|
+
@wheres.push where_string
|
392
|
+
string.clear
|
393
|
+
|
394
|
+
else
|
395
|
+
#puts "WHERE public input: #{value}"
|
396
|
+
if string.respond_to?(:sql_prop) && (string.sql_prop != nil && string.sql_value != nil)
|
397
|
+
where_statement = "`#{self.class.escape string.to_s}` #{string.sql_prop} #{self.class.sanatize string.sql_value}"
|
398
|
+
elsif string !~ /\?/ && values.length == 1
|
399
|
+
where_statement = "`#{self.class.escape string.to_s}` = #{self.class.sanatize values[0]}"
|
400
|
+
|
401
|
+
else
|
402
|
+
where_statement = replace(string, values)
|
403
|
+
if string =~ /\s+or\s+/i
|
404
|
+
where_statement = "(#{where_statement})"
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
if where_statement != ""
|
409
|
+
where_statement.set_sql_where_type("and")
|
410
|
+
@wheres.push where_statement
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
#puts "WHERE after public input: #{where_statement}"
|
415
|
+
|
416
|
+
return self
|
417
|
+
|
418
|
+
end
|
419
|
+
|
420
|
+
alias_method :and, :where
|
421
|
+
|
422
|
+
# DEPRECATED
|
423
|
+
def get_wheres
|
424
|
+
@wheres
|
425
|
+
end
|
426
|
+
|
427
|
+
# REVIEW: right approach?
|
428
|
+
# def name
|
429
|
+
# return "Kbam"
|
430
|
+
# end
|
431
|
+
|
432
|
+
# What is this? remove?
|
433
|
+
def clear
|
434
|
+
@@wheres = Array.new
|
435
|
+
end
|
436
|
+
|
437
|
+
# where API
|
438
|
+
def or_where(string, *value)
|
439
|
+
|
440
|
+
values = *value.to_a
|
441
|
+
|
442
|
+
if string.class.name == "Kbam"
|
443
|
+
|
444
|
+
where_string = ""
|
445
|
+
|
446
|
+
i = 0
|
447
|
+
string.get_wheres.each do |w|
|
448
|
+
if i > 0
|
449
|
+
if w.sql_where_type == "or"
|
450
|
+
where_string += " OR "
|
451
|
+
else
|
452
|
+
where_string += " AND "
|
453
|
+
end
|
454
|
+
end
|
455
|
+
where_string += "#{w}\n "
|
456
|
+
i += 1
|
457
|
+
end
|
458
|
+
|
459
|
+
puts "NESTED WHERE: #{where_string}"
|
460
|
+
|
461
|
+
if where_string =~ /\s+AND\s+/i
|
462
|
+
|
463
|
+
where_string = "(#{where_string})"
|
464
|
+
end
|
465
|
+
where_string.set_sql_where_type("or")
|
466
|
+
@wheres.push where_string
|
467
|
+
string.clear
|
468
|
+
|
469
|
+
else
|
470
|
+
|
471
|
+
if string.sql_prop != nil && string.sql_value != nil
|
472
|
+
or_where_statement = "`#{self.class.escape string.to_s}` #{string.sql_prop} #{self.class.sanatize string.sql_value}"
|
473
|
+
elsif string !~ /\?/ && values.length == 1
|
474
|
+
or_where_statement = "`#{self.class.escape string.to_s}` = #{self.class.sanatize values[0]}"
|
475
|
+
else
|
476
|
+
or_where_statement = replace(string, values)
|
477
|
+
end
|
478
|
+
|
479
|
+
if string =~ /\s+AND\s+/i
|
480
|
+
|
481
|
+
or_where_statement = "(#{or_where_statement})"
|
482
|
+
end
|
483
|
+
|
484
|
+
if or_where_statement != ""
|
485
|
+
or_where_statement.set_sql_where_type("or")
|
486
|
+
@wheres.push or_where_statement
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
return self
|
491
|
+
end
|
492
|
+
|
493
|
+
alias_method :where_or, :or_where
|
494
|
+
alias_method :or, :or_where
|
495
|
+
|
496
|
+
# having API
|
497
|
+
def having(string, *value)
|
498
|
+
|
499
|
+
|
500
|
+
values = *value.to_a
|
501
|
+
|
502
|
+
having_statement = replace(string, values)
|
503
|
+
|
504
|
+
if string =~ /\s+or\s+/i
|
505
|
+
|
506
|
+
having_statement = "(#{having_statement})"
|
507
|
+
end
|
508
|
+
|
509
|
+
if having_statement != ""
|
510
|
+
@havings.push having_statement
|
511
|
+
end
|
512
|
+
|
513
|
+
return self
|
514
|
+
end
|
515
|
+
|
516
|
+
def order(field, direction = nil)
|
517
|
+
if direction == nil
|
518
|
+
direction = 'ASC'
|
519
|
+
end
|
520
|
+
|
521
|
+
@orders.push "#{field_sanatize(field)} #{sort_sanatize(direction)}"
|
522
|
+
|
523
|
+
return self
|
524
|
+
end
|
525
|
+
|
526
|
+
def limit(limit_int = nil)
|
527
|
+
|
528
|
+
if limit_int != nil
|
529
|
+
@limit = limit_int.to_i
|
530
|
+
else
|
531
|
+
@limit = nil
|
532
|
+
end
|
533
|
+
|
534
|
+
return self
|
535
|
+
end
|
536
|
+
|
537
|
+
def offset(offset_int = nil)
|
538
|
+
|
539
|
+
if offset_int != nil
|
540
|
+
@offset = offset_int.to_i
|
541
|
+
else
|
542
|
+
@offset = nil
|
543
|
+
end
|
544
|
+
|
545
|
+
return self
|
546
|
+
end
|
547
|
+
|
548
|
+
#REVIEW: using << instead of += --> faster
|
549
|
+
def to_json(result)
|
550
|
+
|
551
|
+
json = "{"
|
552
|
+
|
553
|
+
# loop through rows
|
554
|
+
i = 0
|
555
|
+
result.each do |row|
|
556
|
+
|
557
|
+
#add comma after each row
|
558
|
+
if i > 0 then json << ", " end
|
559
|
+
|
560
|
+
#puts row
|
561
|
+
|
562
|
+
# use id as json keys
|
563
|
+
json << "#{row["id"]}: {"
|
564
|
+
|
565
|
+
# remove id
|
566
|
+
row.delete("id")
|
567
|
+
|
568
|
+
#use rest of fields as json value
|
569
|
+
k = 0
|
570
|
+
row.each do |key, value|
|
571
|
+
if k > 0 then json << ", " end
|
572
|
+
json << "#{key}: '#{value}'"
|
573
|
+
k += 1
|
574
|
+
end
|
575
|
+
|
576
|
+
json << "}"
|
577
|
+
|
578
|
+
i += 1
|
579
|
+
end
|
580
|
+
|
581
|
+
json << "}"
|
582
|
+
|
583
|
+
return json
|
584
|
+
end
|
585
|
+
|
586
|
+
def get(format = "hash")
|
587
|
+
|
588
|
+
format = format.to_s
|
589
|
+
|
590
|
+
case format
|
591
|
+
when "json"
|
592
|
+
#puts "FETCHING JSON"
|
593
|
+
@result = to_json(@@client.query(compose_query))
|
594
|
+
|
595
|
+
when "array"
|
596
|
+
@result = @@client.query(compose_query, :as => :array)
|
597
|
+
else
|
598
|
+
@result = @@client.query(compose_query)
|
599
|
+
end
|
600
|
+
|
601
|
+
return @result
|
602
|
+
end
|
603
|
+
|
604
|
+
alias_method :fetch, :get
|
605
|
+
|
606
|
+
# if instance method
|
607
|
+
# then interferes with
|
608
|
+
# Array.each implementation!
|
609
|
+
def each
|
610
|
+
|
611
|
+
#puts "OBJECT TYPE: #{self.class}"
|
612
|
+
|
613
|
+
if self.class == Kbam
|
614
|
+
|
615
|
+
items = get
|
616
|
+
|
617
|
+
if items != nil
|
618
|
+
items.each do |item|
|
619
|
+
yield item
|
620
|
+
end
|
621
|
+
else
|
622
|
+
puts "Empty result"
|
623
|
+
end
|
624
|
+
|
625
|
+
else
|
626
|
+
puts "No Kbam object found!"
|
627
|
+
end
|
628
|
+
end
|
629
|
+
|
630
|
+
#FIXME:
|
631
|
+
def count
|
632
|
+
if @result != nil
|
633
|
+
return @result.count
|
634
|
+
else
|
635
|
+
warning "Can't count for empty result"
|
636
|
+
return nil
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
alias_method :length, :count
|
641
|
+
|
642
|
+
def total
|
643
|
+
if @result != nil
|
644
|
+
@@client.query("SELECT FOUND_ROWS() AS count").first["count"]
|
645
|
+
else
|
646
|
+
warning "Can't count total for empty result"
|
647
|
+
end
|
648
|
+
end
|
649
|
+
|
650
|
+
#FIXME: raw sql conflict and if not composed yet...
|
651
|
+
def sql
|
652
|
+
if @query == ""
|
653
|
+
return compose_query
|
654
|
+
else
|
655
|
+
return @query
|
656
|
+
end
|
657
|
+
end
|
658
|
+
|
659
|
+
alias_method :to_s, :sql
|
660
|
+
alias_method :to_str, :sql
|
661
|
+
alias_method :to_sql, :sql
|
662
|
+
|
663
|
+
##################
|
664
|
+
# Public helpers #
|
665
|
+
|
666
|
+
#FIXME: change to class method
|
667
|
+
# sanatize field
|
668
|
+
def field_sanatize(field)
|
669
|
+
field = field.to_s.strip
|
670
|
+
#matches word character and between 2 and 64 character length
|
671
|
+
if field =~ /\A[\w\.]{2,64}\Z/
|
672
|
+
|
673
|
+
field.sub!(/\./, "`.`")
|
674
|
+
|
675
|
+
return "`#{field}`"
|
676
|
+
else
|
677
|
+
error("invalid field name")
|
678
|
+
end
|
679
|
+
end
|
680
|
+
|
681
|
+
def Kbam.sanatize_field!(dirty_string)
|
682
|
+
dirty_string.replace(Kbam.sanatize_field(dirty_string))
|
683
|
+
|
684
|
+
nil
|
685
|
+
end
|
686
|
+
|
687
|
+
# for sanatizing fields, tables
|
688
|
+
def Kbam.sanatize_field(dirty_string)
|
689
|
+
unless dirty_string.respond_to? :to_s
|
690
|
+
raise ArgumentError,
|
691
|
+
"Cannot convert #{dirty_string.class} into a String"
|
692
|
+
end
|
693
|
+
|
694
|
+
'`' << dirty_string.to_s.gsub(/[^a-zA-Z0-9_]/, '') << '`'
|
695
|
+
end
|
696
|
+
|
697
|
+
#FIXME: change to class method
|
698
|
+
# senatize sort e.g. asc / desc
|
699
|
+
def sort_sanatize(sort)
|
700
|
+
|
701
|
+
sort = sort.to_s.strip.upcase
|
702
|
+
|
703
|
+
#if it matches DESC or DSC
|
704
|
+
if sort =~ /\ADE?SC\Z/
|
705
|
+
return 'DESC'
|
706
|
+
else
|
707
|
+
return 'ASC'
|
708
|
+
end
|
709
|
+
end
|
710
|
+
|
711
|
+
#####################
|
712
|
+
# PRIVATE FUNCTIONS #
|
713
|
+
#####################
|
714
|
+
|
715
|
+
###################
|
716
|
+
# Query composers #
|
717
|
+
|
718
|
+
def compose_select
|
719
|
+
select_string = "\nSELECT\n "
|
720
|
+
unless is_nested
|
721
|
+
select_string += "SQL_CALC_FOUND_ROWS "
|
722
|
+
end
|
723
|
+
|
724
|
+
unless @selects.empty?
|
725
|
+
select_string += "#{(@selects * ', ')}"
|
726
|
+
else
|
727
|
+
select_string += "*"
|
728
|
+
end
|
729
|
+
|
730
|
+
return select_string
|
731
|
+
end
|
732
|
+
|
733
|
+
def compose_from
|
734
|
+
unless @from == ""
|
735
|
+
"\nFROM\n #{@from}"
|
736
|
+
else
|
737
|
+
error('No table specifiyed')
|
738
|
+
end
|
739
|
+
end
|
740
|
+
|
741
|
+
def compose_group
|
742
|
+
unless @group == ""
|
743
|
+
"\nGROUP BY\n #{@group}"
|
744
|
+
else
|
745
|
+
""
|
746
|
+
end
|
747
|
+
end
|
748
|
+
|
749
|
+
def compose_where
|
750
|
+
unless @wheres.empty?
|
751
|
+
|
752
|
+
where_string = "\nWHERE\n "
|
753
|
+
|
754
|
+
i = 0
|
755
|
+
@wheres.each do |w|
|
756
|
+
if i > 0
|
757
|
+
if w.sql_where_type == "or"
|
758
|
+
where_string += "\n OR "
|
759
|
+
else
|
760
|
+
where_string += "\n AND "
|
761
|
+
end
|
762
|
+
end
|
763
|
+
where_string += "#{w}"
|
764
|
+
i += 1
|
765
|
+
end
|
766
|
+
|
767
|
+
return where_string
|
768
|
+
|
769
|
+
else
|
770
|
+
return ""
|
771
|
+
end
|
772
|
+
end
|
773
|
+
|
774
|
+
def compose_having
|
775
|
+
unless @havings.empty?
|
776
|
+
return "\nHAVING\n #{(@havings * ' AND ')}"
|
777
|
+
else
|
778
|
+
return ""
|
779
|
+
end
|
780
|
+
end
|
781
|
+
|
782
|
+
def compose_order
|
783
|
+
unless @orders.empty?
|
784
|
+
return "\nORDER BY\n #{(@orders * ', ')}"
|
785
|
+
else
|
786
|
+
return ""
|
787
|
+
end
|
788
|
+
end
|
789
|
+
|
790
|
+
def compose_limit
|
791
|
+
unless @limit == nil
|
792
|
+
return "\nLIMIT #{@limit}"
|
793
|
+
else
|
794
|
+
return "\nLIMIT 1000"
|
795
|
+
end
|
796
|
+
end
|
797
|
+
|
798
|
+
def compose_offset
|
799
|
+
unless @offset == nil || @offset == 0
|
800
|
+
return "\nOFFSET #{@offset}"
|
801
|
+
else
|
802
|
+
return ""
|
803
|
+
end
|
804
|
+
end
|
805
|
+
|
806
|
+
def compose_query
|
807
|
+
|
808
|
+
# join the query fragments
|
809
|
+
query_string = [
|
810
|
+
compose_select,
|
811
|
+
compose_from,
|
812
|
+
compose_where,
|
813
|
+
compose_group,
|
814
|
+
compose_having,
|
815
|
+
compose_order,
|
816
|
+
compose_limit,
|
817
|
+
compose_offset
|
818
|
+
] * ' '
|
819
|
+
|
820
|
+
unless is_nested
|
821
|
+
log(query_string, "query")
|
822
|
+
end
|
823
|
+
|
824
|
+
@last_query = query_string
|
825
|
+
|
826
|
+
return query_string
|
827
|
+
|
828
|
+
end
|
829
|
+
|
830
|
+
#####################
|
831
|
+
# Private functions #
|
832
|
+
|
833
|
+
# interrupts execution
|
834
|
+
# creates error output
|
835
|
+
def error(message)
|
836
|
+
error_message = " ERROR: #{message}! "
|
837
|
+
error_length = error_message.length
|
838
|
+
puts ("="*error_length).colorize( :color => :red, :background => :white )
|
839
|
+
puts error_message.colorize( :color => :red, :background => :white )
|
840
|
+
puts ("="*error_length).colorize( :color => :red, :background => :white )
|
841
|
+
|
842
|
+
Kernel::raise error_message
|
843
|
+
end
|
844
|
+
|
845
|
+
# creates warning
|
846
|
+
# continues execution
|
847
|
+
def warning(message)
|
848
|
+
warning_message = " #{caller[0]} WARNING: #{message}! "
|
849
|
+
warning_length = [warning_message.length, 80].min
|
850
|
+
puts ("="*warning_length).colorize( :color => :yellow, :background => :white )
|
851
|
+
puts warning_message.colorize( :color => :yellow, :background => :white )
|
852
|
+
puts ("="*warning_length).colorize( :color => :yellow, :background => :white )
|
853
|
+
end
|
854
|
+
|
855
|
+
# prints debug info / log
|
856
|
+
def log(message, title = nil)
|
857
|
+
if title == nil
|
858
|
+
title = "log"
|
859
|
+
end
|
860
|
+
color_width = 80
|
861
|
+
title_length = title.length
|
862
|
+
|
863
|
+
fill = color_width - title_length
|
864
|
+
|
865
|
+
log_message = "#{message}"
|
866
|
+
|
867
|
+
puts ("-"*color_width).colorize( :color => :black, :background => :white )
|
868
|
+
puts "#{title.upcase}:#{" " * (fill - 1)}".colorize( :color => :black, :background => :white )
|
869
|
+
puts ("-"*color_width).colorize( :color => :black, :background => :white )
|
870
|
+
puts log_message.colorize( :color => :black, :background => :white )
|
871
|
+
puts ("-"*color_width).colorize( :color => :black, :background => :white )
|
872
|
+
end
|
873
|
+
|
874
|
+
#takes only an array of values
|
875
|
+
def replace(string, values)
|
876
|
+
|
877
|
+
warning "DEPRECATED: use class method instead"
|
878
|
+
|
879
|
+
i = 0
|
880
|
+
last_value = nil
|
881
|
+
|
882
|
+
replaced_string = string.to_s.gsub(/\?/) do |match|
|
883
|
+
|
884
|
+
if i > 0
|
885
|
+
if values[i] != nil
|
886
|
+
last_value = replacement = self.class.sanatize values[i]
|
887
|
+
else
|
888
|
+
replacement = last_value
|
889
|
+
end
|
890
|
+
else
|
891
|
+
unless values[i] != nil
|
892
|
+
puts "missing argument!"
|
893
|
+
else
|
894
|
+
last_value = replacement = self.class.sanatize values[i]
|
895
|
+
end
|
896
|
+
end
|
897
|
+
|
898
|
+
i += 1
|
899
|
+
|
900
|
+
replacement
|
901
|
+
end
|
902
|
+
|
903
|
+
return replaced_string
|
904
|
+
|
905
|
+
end
|
906
|
+
|
907
|
+
end #END Kbam class
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kbam
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.3.alpha
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Leopold Burdyl
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-09-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mysql2
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.3.13
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.3.13
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: colorize
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.5.8
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.5.8
|
41
|
+
description: Simple gem that makes working with raw MySQL in Ruby efficient and fun!
|
42
|
+
It's basically a query string builder (not an ORM!) that takes care of sanatization
|
43
|
+
and sql chaining. UNSTABLE - only for testing!
|
44
|
+
email: nerd@whiteslash.eu
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- lib/kbam.rb
|
50
|
+
homepage: https://github.com/vilnius-leopold/kbam
|
51
|
+
licenses:
|
52
|
+
- MIT
|
53
|
+
metadata: {}
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options: []
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 2.0.0
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - '>'
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 1.3.1
|
68
|
+
requirements: []
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 2.0.7
|
71
|
+
signing_key:
|
72
|
+
specification_version: 4
|
73
|
+
summary: K'bam! UNSTABLE - only for testing!
|
74
|
+
test_files: []
|