db-migrate 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,25 @@
1
+ module Migrate
2
+ module Lang
3
+ class Javascript < Lang
4
+ def initialize
5
+ @ext = "js"
6
+ end
7
+
8
+ def create_migration(dir)
9
+ File.open("#{dir}/up.#{@ext}", "w") do |f|
10
+ f.puts "// Here goes JS for migration forward\n"
11
+ end
12
+
13
+ File.open("#{dir}/down.#{@ext}", "w") do |f|
14
+ f.puts "// Here goes JS for migration backward\n"
15
+ end
16
+ end
17
+
18
+ def exec_migration(dir, is_up)
19
+ script = "#{dir}/#{is_up ? "up" : "down"}.#{@ext}"
20
+ Log.info("Executing #{script}...")
21
+ `node #{script}`
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ module Migrate
2
+ module Lang
3
+ class Lang
4
+ attr_reader :ext
5
+
6
+ def create_migration(dir)
7
+ raise "Implementation for creating new migration not found."
8
+ end
9
+
10
+ def exec_migration(dir, is_up)
11
+ raise "Implementation for executing migration not found."
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ module Migrate
2
+ module Lang
3
+ class Python < Lang
4
+ def initialize
5
+ @ext = "py"
6
+ end
7
+
8
+ def create_migration(dir)
9
+ File.open("#{dir}/up.#{@ext}", "w") do |f|
10
+ f.puts "# Here goes Python code for migration forward\n"
11
+ end
12
+
13
+ File.open("#{dir}/down.#{@ext}", "w") do |f|
14
+ f.puts "# Here goes Python code for migration backward\n"
15
+ end
16
+ end
17
+
18
+ def exec_migration(dir, is_up)
19
+ script = "#{dir}/#{is_up ? "up" : "down"}.#{@ext}"
20
+ Log.info("Executing #{script}...")
21
+ `python #{script}`
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module Migrate
2
+ module Lang
3
+ class Ruby < Lang
4
+ def initialize
5
+ @ext = "rb"
6
+ end
7
+
8
+ def create_migration(dir)
9
+ File.open("#{dir}/up.#{@ext}", "w") do |f|
10
+ f.puts "# Here goes Ruby code for migration forward\n"
11
+ end
12
+
13
+ File.open("#{dir}/down.#{@ext}", "w") do |f|
14
+ f.puts "# Here goes Ruby code for migration backward\n"
15
+ end
16
+ end
17
+
18
+ def exec_migration(dir, is_up)
19
+ script = "#{dir}/#{is_up ? "up" : "down"}.#{@ext}"
20
+ Log.info("Executing #{script}...")
21
+ `ruby #{script}`
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ module Migrate
2
+ module Lang
3
+ class Sql < Lang
4
+ def initialize(db)
5
+ @db = db
6
+ @ext = "sql"
7
+ end
8
+
9
+ def create_migration(dir)
10
+ File.open("#{dir}/up.#{@ext}", "w") do |f|
11
+ f.puts "-- Here goes SQL for migration forward\n"
12
+ end
13
+
14
+ File.open("#{dir}/down.#{@ext}", "w") do |f|
15
+ f.puts "-- Here goes SQL for migration backward\n"
16
+ end
17
+ end
18
+
19
+ def exec_migration(dir, is_up)
20
+ script = "#{dir}/#{is_up ? "up" : "down"}.#{@ext}"
21
+ Log.info("Executing #{script}...")
22
+ @db.exec_sql(File.read(script))
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,39 @@
1
+ require "colorize"
2
+
3
+ module Migrate
4
+ class Log
5
+ @@debug = true
6
+
7
+ def self.verbose(verbose)
8
+ @@debug = verbose
9
+ end
10
+
11
+ def self.info(msg)
12
+ return if not @@debug
13
+ puts ("[INFO] " + msg).green
14
+ end
15
+
16
+ def self.warn(msg)
17
+ return if not @@debug
18
+ puts ("[WARN] " + msg).yellow
19
+ end
20
+
21
+ def self.error(msg, e=nil)
22
+ return if not @@debug
23
+ puts ("[ERRPR] " + msg + (e != nil ? " #{e.message}" : "")).red
24
+
25
+ if e != nil
26
+ puts e.backtrace
27
+ end
28
+ end
29
+
30
+ def self.success(msg)
31
+ return if not @@debug
32
+ puts ("[SUCCESS] " + msg).blue
33
+ end
34
+
35
+ def self.version(msg)
36
+ puts (" [VERSION] #{msg} ").colorize(:color => :white, :background => :blue)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,144 @@
1
+ module Migrate
2
+ class Migrator
3
+ def initialize(config)
4
+ @config = config
5
+ @db = config.get_db
6
+ @lang = config.get_lang
7
+
8
+ if @db == nil
9
+ throw "Database connection not found."
10
+ exit
11
+ end
12
+
13
+ if @lang == nil
14
+ throw "Language not found."
15
+ end
16
+ end
17
+
18
+ def init
19
+ @db.tx do
20
+ @db.create_tables
21
+ end
22
+ end
23
+
24
+ def migration_dir(migration)
25
+ date = DateTime.parse(migration["created_date"].to_s)
26
+ "#{@config.root}/v#{migration["version"]}-#{date.strftime("%Y-%m-%d")}"
27
+ end
28
+
29
+ def new(desc)
30
+ @db.tx do
31
+ Log.info("Creating new migration...")
32
+
33
+ migration = @db.new_migration(desc)
34
+ migration_dir = self.migration_dir(migration)
35
+ Dir.mkdir migration_dir
36
+ @lang.create_migration(migration_dir)
37
+
38
+ Log.success("Migration for version #{migration["version"]} created.")
39
+ migration_dir
40
+ end
41
+ rescue Exception => e
42
+ Log.error("Error while creating new migration.", e)
43
+ exit
44
+ end
45
+
46
+ # will execute single migration by running up or down script
47
+ def exec_migration(migration, is_up)
48
+ migration_dir = self.migration_dir(migration)
49
+ result = @lang.exec_migration(migration_dir, is_up)
50
+ if @lang.ext != "sql"
51
+ puts result
52
+ end
53
+
54
+ Log.info("Updating current version number...")
55
+ version = migration["version"]
56
+ is_up ? @db.log_up(version) : @db.log_down(version)
57
+ end
58
+
59
+ # will execute range of migrations
60
+ def exec_migrations(is_up=true)
61
+ Log.info("Executing migrations...")
62
+ migrations = yield @db.current_version
63
+
64
+ if migrations.count == 0
65
+ Log.warn("Migrations not found")
66
+ return
67
+ end
68
+
69
+ migrations.each do |migration|
70
+ self.exec_migration(migration, is_up)
71
+ end
72
+ Log.success("Migrations executed. Current version: #{@db.current_version}")
73
+ end
74
+
75
+ def up(to_version=nil)
76
+ @db.tx do
77
+ self.exec_migrations do |last_version|
78
+ new_version = @db.next_version
79
+ if to_version == nil
80
+ to_version = new_version
81
+ end
82
+
83
+ @db.migrations_range(new_version, to_version, true)
84
+ end
85
+ end
86
+ end
87
+
88
+ def down(to_version=nil)
89
+ @db.tx do
90
+ self.exec_migrations(false) do |current_version|
91
+ if current_version == 0
92
+ raise VersionNotFound
93
+ end
94
+
95
+ if to_version == nil
96
+ to_version = current_version
97
+ else
98
+ to_version = to_version.to_i + 1
99
+ end
100
+
101
+ @db.migrations_range(to_version, current_version, false)
102
+ end
103
+ end
104
+ end
105
+
106
+ def current_version
107
+ @db.tx do
108
+ return @db.current_version
109
+ end
110
+ end
111
+
112
+ def delete(version)
113
+ @db.tx do
114
+ Log.info("Removing migration data...")
115
+
116
+ if @db.current_version.to_i == version
117
+ return Log.error("Cannot remove current version.")
118
+ end
119
+
120
+ dir = self.migration_dir(@db.get_migration(version))
121
+ @db.delete version
122
+
123
+ if Dir.exist? dir
124
+ File.delete "#{dir}/up.sql"
125
+ File.delete "#{dir}/down.sql"
126
+ Dir.rmdir dir
127
+ end
128
+
129
+ Log.success("Migration data removed.")
130
+ end
131
+ end
132
+
133
+ def list(select, limit)
134
+ @db.tx do
135
+ migrations = @db.list_migrations(select, limit)
136
+ if not migrations.any?
137
+ return Log.info("Migrations not found")
138
+ end
139
+
140
+ @db.print(migrations, "Migrations")
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,7 @@
1
+ module Migrate
2
+ module Storage
3
+ require_relative "./storage/db"
4
+ require_relative "./storage/postgres"
5
+ require_relative "./storage/mysql"
6
+ end
7
+ end
@@ -0,0 +1,150 @@
1
+ require 'terminal-table'
2
+
3
+ module Migrate
4
+ module Storage
5
+ class DB
6
+ attr_reader :config
7
+
8
+ def initialize(config)
9
+ @config = config
10
+ end
11
+
12
+ def type
13
+ @config.storage
14
+ end
15
+
16
+ def new_migration(description="")
17
+ self.exec_sql <<-eos
18
+ INSERT INTO #{@config.version_info} (description, created_date)
19
+ VALUES('#{description}', now())
20
+ eos
21
+
22
+ res = self.exec_sql <<-eos
23
+ SELECT * FROM #{@config.version_info} ORDER BY version DESC LIMIT 1
24
+ eos
25
+ res[0]
26
+ end
27
+
28
+ def list_migrations(selects, limit)
29
+ self.exec_sql <<-eos
30
+ SELECT #{(selects == nil ? "*" : selects)} FROM #{@config.version_info}
31
+ ORDER BY last_up, version
32
+ #{limit != nil ? "LIMIT #{limit}" : ""}
33
+ eos
34
+ end
35
+
36
+ def migrations_range(from, to, is_up)
37
+ self.exec_sql <<-eos
38
+ SELECT * FROM #{@config.version_info}
39
+ WHERE version >= #{from} AND version <= #{to}
40
+ ORDER BY version #{!is_up ? "DESC" : ""}
41
+ eos
42
+ end
43
+
44
+ def extract_version(results)
45
+ if results && results.count > 0
46
+ results[0]["version"]
47
+ else
48
+ raise VersionNotFound
49
+ end
50
+ end
51
+
52
+ def lowest_version
53
+ self.extract_version self.exec_sql <<-eos
54
+ SELECT version FROM #{@config.version_info}
55
+ ORDER BY version
56
+ LIMIT 1
57
+ eos
58
+ end
59
+
60
+ def next_version
61
+ self.extract_version self.exec_sql <<-eos
62
+ SELECT version FROM #{@config.version_info}
63
+ WHERE version > (SELECT version FROM #{@config.version_number} LIMIT 1)
64
+ ORDER BY version
65
+ LIMIT 1
66
+ eos
67
+ end
68
+
69
+ def current_version
70
+ self.extract_version self.exec_sql <<-eos
71
+ SELECT * FROM #{config.version_number}
72
+ LIMIT 1
73
+ eos
74
+ end
75
+
76
+ def prev_version
77
+ self.extract_version self.exec_sql <<-eos
78
+ SELECT version FROM #{@config.version_info}
79
+ WHERE version < (SELECT version FROM #{@config.version_number} LIMIT 1)
80
+ ORDER BY version DESC
81
+ LIMIT 1
82
+ eos
83
+ end
84
+
85
+ def log_up(version)
86
+ self.exec_sql "UPDATE #{@config.version_info} SET last_up=now() WHERE version=#{version}"
87
+ self.exec_sql "UPDATE #{@config.version_number} SET version=#{version}"
88
+ end
89
+
90
+ def log_down(version)
91
+ self.exec_sql "UPDATE #{@config.version_info} SET last_down=now() WHERE version=#{version}"
92
+
93
+ lowest_version = self.lowest_version
94
+ version_to_save = lowest_version.to_i < version.to_i ? self.prev_version().to_i : 0
95
+ self.exec_sql "UPDATE #{@config.version_number} SET version=#{version_to_save}"
96
+ end
97
+
98
+ def get_migration(version)
99
+ res = self.exec_sql "SELECT * FROM #{@config.version_info} WHERE version=#{version}"
100
+ if res && res.count > 0
101
+ res[0]
102
+ else
103
+ raise VersionNotFound
104
+ end
105
+ end
106
+
107
+ def delete(version)
108
+ self.exec_sql "DELETE FROM #{@config.version_info} WHERE version=#{version}"
109
+ end
110
+
111
+ def print(results, title="")
112
+ rows = []
113
+ headings = results[0].keys
114
+
115
+ results.each do |result|
116
+ row = []
117
+ result.each do |column, value|
118
+ if column == "description"
119
+ if value.length > 70
120
+ value = value.scan(/.{1,70}/).join("\n")
121
+ end
122
+ end
123
+
124
+ row << value
125
+ end
126
+ rows << row
127
+ end
128
+
129
+ table = Terminal::Table.new :headings => headings, :rows => rows
130
+ table.title = title
131
+ puts table
132
+ end
133
+
134
+ # Will create database model used by tool
135
+ def create_tables
136
+ raise "Implementation for creating tables not found"
137
+ end
138
+
139
+ # Executes SQL
140
+ def exec_sql(sql)
141
+ raise "Implementation for executing SQL script not found"
142
+ end
143
+
144
+ # Creates new transaction. Should accept block.
145
+ def tx
146
+ raise "Implementation for starting new transaction not found"
147
+ end
148
+ end
149
+ end
150
+ end