db-evolve 0.1.4
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/db-evolve.rb +9 -0
- data/lib/tasks/db.rb +482 -0
- data/lib/tasks/db_mock.rb +24 -0
- data/lib/tasks/sql_color.rb +32 -0
- metadata +48 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a7f3f14f0e03a5b37bc20363d266cfc1b4b01d14
|
4
|
+
data.tar.gz: b26ebd66e911fad82cfda7a7d0b808e64b5d58e2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 158eef8c638f24938426a879f4ee721a8734b33debd8cbb653a8f3c279dff368455afcb4130b1d4a8d8ace3beecfae514ff4f772f3d4ce84acf29219ede63c6c
|
7
|
+
data.tar.gz: 251428b0ca100a1c8eaa1d97ecb4cfdc78c7fb64d775c815f72997fa2706b92cbcf8f9ce3ae0f5b0b8e01ff6979fcf5c026cc073e3905ce6031a08fa37a10821
|
data/lib/db-evolve.rb
ADDED
data/lib/tasks/db.rb
ADDED
@@ -0,0 +1,482 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'active_record'
|
3
|
+
require 'active_support/all'
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
class Migration
|
7
|
+
def iloaded()
|
8
|
+
end
|
9
|
+
end
|
10
|
+
class Schema
|
11
|
+
def self.define(x)
|
12
|
+
raise "\nTo use rails-db-evolve, please edit your schema.db file and change:\n\n ActiveRecord::Schema.define(...) do\n\nto:\n\n DB::Schema.define do\n\nAnd re-run this task.\n\n"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
namespace :db do
|
19
|
+
|
20
|
+
desc "Diff your database against your schema.rb and offer SQL to bring your database up to date."
|
21
|
+
task :evolve, [:arg1,:arg2] => :environment do |t, args|
|
22
|
+
|
23
|
+
argv = [ args[:arg1], args[:arg2] ]
|
24
|
+
noop = argv.include? "noop"
|
25
|
+
nowait = argv.include? "nowait"
|
26
|
+
yes = argv.include? "yes"
|
27
|
+
|
28
|
+
# confirm our shim is in place before we load schema.rb
|
29
|
+
# lest we accidentally drop and reload their database!
|
30
|
+
ActiveRecord::Schema.iloaded
|
31
|
+
|
32
|
+
do_evolve(noop, yes, nowait)
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
# mock db:schema:load db:test:load so "rake spec" works
|
37
|
+
namespace :test do
|
38
|
+
task :load do
|
39
|
+
# do nothing
|
40
|
+
end
|
41
|
+
end
|
42
|
+
namespace :schema do
|
43
|
+
task :load do
|
44
|
+
do_evolve(false, true, true)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def do_evolve(noop, yes, nowait)
|
52
|
+
existing_tables, existing_indexes = load_existing_tables()
|
53
|
+
|
54
|
+
require_relative 'db_mock'
|
55
|
+
|
56
|
+
require Rails.root + 'db/schema'
|
57
|
+
|
58
|
+
adds, deletes, renames = calc_table_changes(existing_tables.keys, $schema_tables.keys, $akas_tables)
|
59
|
+
|
60
|
+
to_run = []
|
61
|
+
|
62
|
+
to_run += sql_adds(adds)
|
63
|
+
to_run += sql_renames(renames)
|
64
|
+
|
65
|
+
rename_cols_by_table = {}
|
66
|
+
|
67
|
+
existing_tables.each do |etn, ecols|
|
68
|
+
next if deletes.include? etn
|
69
|
+
ntn = renames[etn] || etn
|
70
|
+
commands, rename_cols = calc_column_changes(ntn, existing_tables[etn], $schema_tables[ntn].columns)
|
71
|
+
to_run += commands
|
72
|
+
rename_cols_by_table[ntn] = rename_cols
|
73
|
+
end
|
74
|
+
|
75
|
+
to_run += calc_index_changes(existing_indexes, $schema_indexes, renames, rename_cols_by_table)
|
76
|
+
|
77
|
+
to_run += sql_drops(deletes)
|
78
|
+
|
79
|
+
# prompt and execute
|
80
|
+
|
81
|
+
if to_run.empty?
|
82
|
+
if !noop
|
83
|
+
puts "\nYour database is up to date!"
|
84
|
+
puts
|
85
|
+
end
|
86
|
+
else
|
87
|
+
to_run.unshift("\nBEGIN TRANSACTION")
|
88
|
+
to_run.append("\nCOMMIT")
|
89
|
+
|
90
|
+
require_relative 'sql_color'
|
91
|
+
to_run.each do |sql|
|
92
|
+
puts SQLColor.colorize(sql)
|
93
|
+
end
|
94
|
+
puts
|
95
|
+
|
96
|
+
if noop
|
97
|
+
return
|
98
|
+
end
|
99
|
+
|
100
|
+
config = ActiveRecord::Base.connection_config
|
101
|
+
puts "Connecting to database:"
|
102
|
+
config.each do |k,v|
|
103
|
+
next if k==:password
|
104
|
+
puts "\t#{k} => #{v}"
|
105
|
+
end
|
106
|
+
|
107
|
+
if !yes
|
108
|
+
print "Run this SQL? (type yes or no) "
|
109
|
+
end
|
110
|
+
if yes || STDIN.gets.strip=='yes'
|
111
|
+
require 'pg'
|
112
|
+
config = ActiveRecord::Base.connection_config
|
113
|
+
config.delete(:adapter)
|
114
|
+
config[:dbname] = config.delete(:database)
|
115
|
+
config[:user] = config.delete(:username)
|
116
|
+
if !nowait
|
117
|
+
print "\nExecuting in "
|
118
|
+
[3,2,1].each do |c|
|
119
|
+
print "#{c}..."
|
120
|
+
sleep(1)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
puts
|
124
|
+
conn = PG::Connection.open(config)
|
125
|
+
to_run.each do |sql|
|
126
|
+
puts SQLColor.colorize(sql)
|
127
|
+
conn.exec(sql)
|
128
|
+
end
|
129
|
+
puts "\n--==[ COMPLETED ]==--"
|
130
|
+
else
|
131
|
+
puts "\n--==[ ABORTED ]==--"
|
132
|
+
end
|
133
|
+
puts
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
def calc_index_changes(existing_indexes, schema_indexes, table_renames, rename_cols_by_table)
|
141
|
+
# rename_cols_by_table is by the new table name
|
142
|
+
existing_indexes = Set.new existing_indexes
|
143
|
+
schema_indexes = Set.new schema_indexes
|
144
|
+
|
145
|
+
add_indexes = schema_indexes - existing_indexes
|
146
|
+
delete_indexes = existing_indexes - schema_indexes
|
147
|
+
|
148
|
+
$tmp_to_run = []
|
149
|
+
|
150
|
+
connection = ActiveRecord::Base.connection
|
151
|
+
|
152
|
+
add_indexes.each do |index|
|
153
|
+
table = index.delete(:table)
|
154
|
+
columns = index.delete(:columns)
|
155
|
+
connection.add_index table, columns, index
|
156
|
+
end
|
157
|
+
|
158
|
+
to_run = $tmp_to_run
|
159
|
+
|
160
|
+
delete_indexes.each do |index|
|
161
|
+
$tmp_to_run = []
|
162
|
+
table = index.delete(:table)
|
163
|
+
name = index[:name]
|
164
|
+
connection.remove_index table, :name => name
|
165
|
+
to_run.append($tmp_to_run[0].sub('DROP INDEX', 'DROP INDEX IF EXISTS'))
|
166
|
+
end
|
167
|
+
|
168
|
+
if !to_run.empty?
|
169
|
+
to_run.unshift("\n-- update indexes")
|
170
|
+
end
|
171
|
+
|
172
|
+
return to_run
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
|
177
|
+
IgnoreTables = Set.new ["schema_migrations"]
|
178
|
+
|
179
|
+
def load_existing_tables()
|
180
|
+
existing_tables = {}
|
181
|
+
existing_indexes = []
|
182
|
+
connection = ActiveRecord::Base.connection
|
183
|
+
connection.tables.sort.each do |tbl|
|
184
|
+
next if IgnoreTables.include? tbl
|
185
|
+
columns = connection.columns(tbl)
|
186
|
+
existing_tables[tbl] = columns
|
187
|
+
connection.indexes(tbl).each do |i|
|
188
|
+
index = {:table => i.table, :name => i.name, :columns => i.columns, :unique => i.unique}
|
189
|
+
existing_indexes.append(index)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
return existing_tables, existing_indexes
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
|
197
|
+
|
198
|
+
class Table
|
199
|
+
attr_accessor :name, :opts, :id, :columns
|
200
|
+
|
201
|
+
def initialize()
|
202
|
+
@columns = []
|
203
|
+
end
|
204
|
+
|
205
|
+
def method_missing(method_sym, *arguments, &block)
|
206
|
+
c = Column.new
|
207
|
+
c.type = method_sym.to_s
|
208
|
+
c.name = arguments[0]
|
209
|
+
c.opts = arguments[1]
|
210
|
+
if c.opts
|
211
|
+
c.default = c.opts["default"]
|
212
|
+
c.default = c.opts["null"]
|
213
|
+
aka = c.opts[:aka]
|
214
|
+
if !aka.nil?
|
215
|
+
if aka.respond_to?('each')
|
216
|
+
c.akas = aka
|
217
|
+
else
|
218
|
+
c.akas = [aka]
|
219
|
+
end
|
220
|
+
end
|
221
|
+
else
|
222
|
+
c.opts = {}
|
223
|
+
end
|
224
|
+
@columns.append c
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
228
|
+
|
229
|
+
class Column
|
230
|
+
attr_accessor :name, :type, :opts, :null, :default, :akas
|
231
|
+
end
|
232
|
+
|
233
|
+
$schema_tables = {}
|
234
|
+
$akas_tables = Hash.new { |h, k| h[k] = Set.new }
|
235
|
+
|
236
|
+
def create_table(name, opts={})
|
237
|
+
tbl = Table.new
|
238
|
+
tbl.name = name
|
239
|
+
tbl.opts = opts
|
240
|
+
if opts
|
241
|
+
if opts.has_key? 'id'
|
242
|
+
tbl.id = opts['id']
|
243
|
+
else
|
244
|
+
tbl.id = true
|
245
|
+
end
|
246
|
+
end
|
247
|
+
if tbl.id
|
248
|
+
c = Column.new
|
249
|
+
c.type = "integer"
|
250
|
+
c.name = "id"
|
251
|
+
c.opts = { :null=>false }
|
252
|
+
tbl.columns.append c
|
253
|
+
end
|
254
|
+
yield tbl
|
255
|
+
$schema_tables[name] = tbl
|
256
|
+
aka = tbl.opts[:aka]
|
257
|
+
if !aka.nil?
|
258
|
+
if aka.respond_to?('each')
|
259
|
+
$akas_tables[tbl.name].merge(aka)
|
260
|
+
else
|
261
|
+
$akas_tables[tbl.name].add(aka)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
$schema_indexes = []
|
267
|
+
|
268
|
+
def add_index(table, columns, opts)
|
269
|
+
opts[:table] = table
|
270
|
+
opts[:columns] = columns
|
271
|
+
if !opts.has_key? :unique
|
272
|
+
opts[:unique] = false
|
273
|
+
end
|
274
|
+
$schema_indexes.append(opts)
|
275
|
+
end
|
276
|
+
|
277
|
+
module DB
|
278
|
+
module Evolve
|
279
|
+
class Schema
|
280
|
+
def self.define()
|
281
|
+
yield
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def calc_table_changes(existing_tables, schema_tables, akas_tables)
|
288
|
+
existing_tables = Set.new existing_tables
|
289
|
+
schema_tables = Set.new schema_tables
|
290
|
+
adds = schema_tables - existing_tables
|
291
|
+
deletes = existing_tables - schema_tables
|
292
|
+
renames = {}
|
293
|
+
adds.each do |newt|
|
294
|
+
akas = Set.new akas_tables[newt]
|
295
|
+
possibles = akas & deletes
|
296
|
+
if possibles.size > 1
|
297
|
+
raise "Too many possible table matches (#{possibles}) for #{newt}. Please trim your akas."
|
298
|
+
end
|
299
|
+
if possibles.size == 1
|
300
|
+
oldt = possibles.to_a()[0]
|
301
|
+
renames[oldt] = newt
|
302
|
+
adds.delete(newt)
|
303
|
+
deletes.delete(oldt)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
return adds, deletes, renames
|
307
|
+
end
|
308
|
+
|
309
|
+
def escape_table(k)
|
310
|
+
return PG::Connection.quote_ident k
|
311
|
+
end
|
312
|
+
|
313
|
+
def gen_pg_adapter()
|
314
|
+
$tmp_to_run = []
|
315
|
+
a = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.allocate
|
316
|
+
ActiveRecord::ConnectionAdapters::AbstractAdapter.instance_method(:initialize).bind(a).call ActiveRecord::Base.connection
|
317
|
+
return a
|
318
|
+
end
|
319
|
+
|
320
|
+
def sql_renames(renames)
|
321
|
+
to_run = []
|
322
|
+
pg_a = gen_pg_adapter()
|
323
|
+
renames.each do |k,v|
|
324
|
+
pg_a.rename_table(k, v)
|
325
|
+
to_run += $tmp_to_run
|
326
|
+
end
|
327
|
+
if !to_run.empty?
|
328
|
+
to_run.unshift("\n-- rename tables")
|
329
|
+
end
|
330
|
+
return to_run
|
331
|
+
end
|
332
|
+
|
333
|
+
def sql_drops(tables)
|
334
|
+
to_run = []
|
335
|
+
tables.each do |tbl|
|
336
|
+
sql = "DROP TABLE #{escape_table(tbl)}"
|
337
|
+
to_run.append sql
|
338
|
+
end
|
339
|
+
if !to_run.empty?
|
340
|
+
to_run.unshift("\n-- remove tables")
|
341
|
+
end
|
342
|
+
return to_run
|
343
|
+
end
|
344
|
+
|
345
|
+
def sql_adds(tables)
|
346
|
+
a = gen_pg_adapter()
|
347
|
+
tables.each do |tn|
|
348
|
+
tbl = $schema_tables[tn]
|
349
|
+
a.create_table tbl.name, :force => true do |t|
|
350
|
+
tbl.columns.each do |c|
|
351
|
+
t.send(c.type.to_sym, *[c.name, c.opts])
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
if !$tmp_to_run.empty?
|
356
|
+
$tmp_to_run.unshift("\n-- add tables")
|
357
|
+
end
|
358
|
+
return $tmp_to_run
|
359
|
+
end
|
360
|
+
|
361
|
+
def can_convert(type1, type2)
|
362
|
+
if type1==type2
|
363
|
+
return true
|
364
|
+
end
|
365
|
+
if type1=='integer' and type2=='decimal'
|
366
|
+
return true
|
367
|
+
end
|
368
|
+
return false
|
369
|
+
end
|
370
|
+
|
371
|
+
# taken from comments in ActiveRecord::ConnectionAdapters::TableDefinition
|
372
|
+
NATIVE_DATABASE_PRECISION = {
|
373
|
+
:numeric => 19,
|
374
|
+
:decimal => 19, #38,
|
375
|
+
}
|
376
|
+
NATIVE_DATABASE_SCALE = {
|
377
|
+
}
|
378
|
+
|
379
|
+
def calc_column_changes(tbl, existing_cols, schema_cols)
|
380
|
+
|
381
|
+
existing_cols_by_name = Hash[existing_cols.collect { |c| [c.name, c] }]
|
382
|
+
schema_cols_by_name = Hash[schema_cols.collect { |c| [c.name, c] }]
|
383
|
+
existing_col_names = Set.new existing_cols_by_name.keys
|
384
|
+
schema_col_names = Set.new schema_cols_by_name.keys
|
385
|
+
new_cols = schema_col_names - existing_col_names
|
386
|
+
delete_cols = existing_col_names - schema_col_names
|
387
|
+
rename_cols = {}
|
388
|
+
|
389
|
+
new_cols.each do |cn|
|
390
|
+
sc = schema_cols_by_name[cn]
|
391
|
+
if sc.akas
|
392
|
+
sc.akas.each do |aka|
|
393
|
+
if delete_cols.include? aka
|
394
|
+
ec = existing_cols_by_name[aka]
|
395
|
+
if can_convert(sc.type.to_s, ec.type.to_s)
|
396
|
+
rename_cols[ec.name] = sc.name
|
397
|
+
new_cols.delete cn
|
398
|
+
delete_cols.delete aka
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
to_run = []
|
406
|
+
|
407
|
+
pg_a = gen_pg_adapter()
|
408
|
+
|
409
|
+
if new_cols.size > 0
|
410
|
+
# puts "tbl: #{tbl} new_cols: #{new_cols}"
|
411
|
+
new_cols.each do |cn|
|
412
|
+
sc = schema_cols_by_name[cn]
|
413
|
+
pg_a.add_column(tbl, cn, sc.type.to_sym, sc.opts)
|
414
|
+
end
|
415
|
+
to_run += $tmp_to_run
|
416
|
+
end
|
417
|
+
|
418
|
+
$tmp_to_run = []
|
419
|
+
rename_cols.each do |ecn, scn|
|
420
|
+
pg_a.rename_column(tbl, ecn, scn)
|
421
|
+
end
|
422
|
+
to_run += $tmp_to_run
|
423
|
+
delete_cols.each do |cn|
|
424
|
+
to_run.append("ALTER TABLE #{escape_table(tbl)} DROP COLUMN #{escape_table(cn)}")
|
425
|
+
end
|
426
|
+
|
427
|
+
same_names = existing_col_names - delete_cols
|
428
|
+
same_names.each do |ecn|
|
429
|
+
$tmp_to_run = []
|
430
|
+
ec = existing_cols_by_name[ecn]
|
431
|
+
if rename_cols.include? ecn
|
432
|
+
sc = schema_cols_by_name[rename_cols[ecn]]
|
433
|
+
else
|
434
|
+
sc = schema_cols_by_name[ecn]
|
435
|
+
end
|
436
|
+
type_changed = sc.type.to_s != ec.type.to_s
|
437
|
+
# numeric and decimal are equiv in postges, and the db always returns numeric
|
438
|
+
if type_changed and sc.type.to_s=="decimal" and ec.type.to_s=="numeric"
|
439
|
+
type_changed = false
|
440
|
+
end
|
441
|
+
# ruby turns decimal(x,0) into integer when reading meta-data
|
442
|
+
if type_changed and sc.type.to_s=="decimal" and ec.type.to_s=="integer" and sc.opts[:scale]==0
|
443
|
+
type_changed = false
|
444
|
+
end
|
445
|
+
sc_limit = sc.opts.has_key?(:limit) ? sc.opts[:limit] : ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[sc.type.to_sym][:limit]
|
446
|
+
limit_changed = (sc.type=="string" and sc_limit!=ec.limit) # numeric types in postgres report the precision as the limit - ignore non string types for now
|
447
|
+
sc_precision = sc.opts.has_key?(:precision) ? sc.opts[:precision] : NATIVE_DATABASE_PRECISION[sc.type]
|
448
|
+
precision_changed = (sc.type=="decimal" and sc_precision!=ec.precision) # by type_to_sql in schema_statements.rb, precision is only used on decimal types
|
449
|
+
sc_scale = sc.opts.has_key?(:scale) ? sc.opts[:scale] : NATIVE_DATABASE_SCALE[sc.type]
|
450
|
+
scale_changed = (sc.type=="decimal" and sc_scale!=ec.scale)
|
451
|
+
if type_changed or limit_changed or precision_changed or scale_changed
|
452
|
+
pg_a.change_column(tbl, sc.name, sc.type.to_sym, sc.opts)
|
453
|
+
end
|
454
|
+
if ec.default != sc.opts[:default]
|
455
|
+
pg_a.change_column_default(tbl, sc.name, sc.opts[:default])
|
456
|
+
end
|
457
|
+
sc_null = sc.opts.has_key?(:null) ? sc.opts[:null] : true
|
458
|
+
if ec.null != sc_null
|
459
|
+
if !sc_null and !sc.opts.has_key?(:default)
|
460
|
+
raise "\nERROR: In order to set #{tbl}.#{sc.name} as NOT NULL you need to add a :default value.\n\n"
|
461
|
+
end
|
462
|
+
pg_a.change_column_null(tbl, sc.name, sc_null, sc.opts[:default])
|
463
|
+
end
|
464
|
+
to_run += $tmp_to_run
|
465
|
+
end
|
466
|
+
|
467
|
+
if !to_run.empty?
|
468
|
+
to_run.unshift("\n-- column changes for table #{tbl}")
|
469
|
+
end
|
470
|
+
|
471
|
+
return to_run, rename_cols
|
472
|
+
end
|
473
|
+
|
474
|
+
|
475
|
+
|
476
|
+
$tmp_to_run = []
|
477
|
+
|
478
|
+
|
479
|
+
|
480
|
+
|
481
|
+
|
482
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'pg'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
class PostgreSQLAdapter
|
6
|
+
def execute(sql, name = nil)
|
7
|
+
$tmp_to_run.append sql
|
8
|
+
end
|
9
|
+
def table_exists?(name)
|
10
|
+
false
|
11
|
+
end
|
12
|
+
def clear_cache!
|
13
|
+
end
|
14
|
+
def quote_string s
|
15
|
+
# hack to prevent double-quoting when setting default
|
16
|
+
return s
|
17
|
+
end
|
18
|
+
def escape(s)
|
19
|
+
return PG::Connection.quote_ident s
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
class SQLColor
|
4
|
+
|
5
|
+
BLACK = 30
|
6
|
+
RED = 31
|
7
|
+
GREEN = 32
|
8
|
+
YELLOW = 33
|
9
|
+
BLUE = 34
|
10
|
+
MAGENTA = 35
|
11
|
+
CYAN = 36
|
12
|
+
WHITE = 37
|
13
|
+
|
14
|
+
def self.colorize(sql)
|
15
|
+
if sql.strip.start_with?('--')
|
16
|
+
return apply(CYAN, sql)
|
17
|
+
end
|
18
|
+
sql = sql.gsub(/(CREATE|ALTER|TABLE|COLUMN|ADD|TYPE|BEGIN|TRANSACTION|COMMIT| ON |INDEX|UPDATE|SET|WHERE)/){|s|apply(GREEN, s)}
|
19
|
+
sql = sql.gsub(/(DROP)/){|s|apply(RED, s)}
|
20
|
+
sql = sql.gsub(/("[^"]*")/){|s|apply(WHITE, s, bold=true)}
|
21
|
+
return sql
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.apply(color_code, text, bold=false)
|
25
|
+
bold = bold ? ";1" : ""
|
26
|
+
"\e[#{color_code}#{bold}m#{text}\e[0m"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
|
metadata
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: db-evolve
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Derek Anderson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-06 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A diff/patch between your schema.rb and what's in your database.
|
14
|
+
email: public@kered.org
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/db-evolve.rb
|
20
|
+
- lib/tasks/db.rb
|
21
|
+
- lib/tasks/db_mock.rb
|
22
|
+
- lib/tasks/sql_color.rb
|
23
|
+
homepage: https://github.com/keredson/ruby-db-evolve
|
24
|
+
licenses:
|
25
|
+
- GPLv2
|
26
|
+
metadata: {}
|
27
|
+
post_install_message:
|
28
|
+
rdoc_options: []
|
29
|
+
require_paths:
|
30
|
+
- lib
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
requirements: []
|
42
|
+
rubyforge_project:
|
43
|
+
rubygems_version: 2.2.2
|
44
|
+
signing_key:
|
45
|
+
specification_version: 4
|
46
|
+
summary: Schema Evolution for Ruby
|
47
|
+
test_files: []
|
48
|
+
has_rdoc:
|