· 8 years ago · Nov 23, 2017, 06:38 AM
1require 'pg'
2require 'uri'
3
4class Migrator
5
6 class Migration
7 def initialize(connection, path)
8 @connection, @path = connection, path
9 end
10
11 def execute
12 unless migration_run?
13 @connection.exec(sql)
14 record_migration
15 end
16 end
17
18 private
19
20 def migration_run?
21 count = 0
22
23 result = @connection.exec_params(
24 "SELECT COUNT(*) AS count FROM migrations WHERE version = $1",
25 [version]
26 )
27
28 count = result[0]["count"].to_i
29 result.clear()
30
31 count == 1
32 end
33
34 def sql
35 File.read(@path)
36 end
37
38 def record_migration
39 @connection.exec_params(
40 "INSERT INTO migrations (version) VALUES ($1)",
41 [version.to_s]
42 )
43 end
44
45 def version
46 matches = File.basename(@path).match(/\A(\d+)/)
47 matches[1].to_i
48 end
49 end
50
51 def initialize(database_url, migrations_path)
52 @database_url = database_url
53 @migrations_path = migrations_path
54 end
55
56 def migrate
57 create_migrations_table
58 migrations.map(&:execute)
59 end
60
61 private
62
63 def ignore
64 -> (r) { }
65 end
66
67 def connection
68 @connection ||= begin
69 conn = PG::Connection.open(connection_options)
70 conn.set_notice_receiver(&ignore)
71 conn
72 end
73 end
74
75 def uri
76 @uri ||= URI(@database_url)
77 end
78
79 def connection_options
80 {
81 host: uri.host,
82 user: uri.user,
83 password: uri.password,
84 port: uri.port || 5432,
85 dbname: uri.path[1..-1]
86
87 }
88 end
89
90 def files
91 Dir.glob(@migrations_path.expand_path.join("*.sql")).sort
92 end
93
94 def migrations
95 files.map {|f| Migration.new(connection, f) }
96 end
97
98 def create_migrations_table
99 connection.exec("
100 CREATE TABLE IF NOT EXISTS migrations (
101 version INTEGER NOT NULL UNIQUE
102 );
103 ")
104 end
105end