db-migrate 0.0.1

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.
@@ -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