migrations 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 45ef69a67e679944490603eaa27ea10f1581e248
4
+ data.tar.gz: 4a7c63fe40651c10b01e2c4e5c978f6477b83084
5
+ SHA512:
6
+ metadata.gz: dad6e96c85c347d66b3ed58f62a144acc0be6437f89962ecf7ca218bf0ac1d24d407ae5c6e9f51219b1a001c8f6601f1508d3fc6a585018445b3c57312c0ab42
7
+ data.tar.gz: 8e7e2b8c33af112c4fa7902a193701c67dac37fc136b6ed08b0349ba5502606e18dd3d1b19b3798e9437feca1c37fa1f194dbd16d34d284a2e26f07ab4d44779
data/bin/migrate ADDED
@@ -0,0 +1,319 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'migrations'
4
+
5
+ class MigrationCommand
6
+ def initialize(args)
7
+ if args.empty?
8
+ usage
9
+ end
10
+
11
+ @prompt = true
12
+
13
+ if respond_to? args[0]
14
+ method = args.shift
15
+
16
+ if args[args.length-1] == '-y'
17
+ @prompt = false
18
+ args.pop
19
+ end
20
+
21
+ if args.empty?
22
+ send method
23
+ else
24
+ send method, *args
25
+ end
26
+ else
27
+ usage
28
+ end
29
+ end
30
+
31
+ def usage
32
+ puts "\nMigrations for ruby by m4rkw - https://github.com/m4rkw/migration\n\n"
33
+ puts "Usage:\n\n"
34
+
35
+ format(
36
+ 'migrate list' => 'list all migrations',
37
+ 'migrate up [-y]' => 'run all pending migrations',
38
+ 'migrate up <n> [-y]' => 'run <n> pending migrations',
39
+ 'migrate down [-y]' => 'revert previous migration',
40
+ 'migrate down <n> [-y]' => 'revert previous <n> migrations',
41
+ 'migrate down all [-y]' => 'revert all previous migrations',
42
+ 'migrate to <version> [-y]' => 'migrate up or down to a specific version',
43
+ 'migrate add <name> [-y]' => 'generate a new migration class',
44
+ 'migrate set <version || "none"> [-y]' => 'set the migrated version without running any migrations',
45
+ 'migrate apply <version> [-y]' => 'apply a specific migration without updating the version',
46
+ 'migrate revert <version> [-y]' => 'revert a specific migration without updating the version'
47
+ )
48
+ puts "\n"
49
+ exit
50
+ end
51
+
52
+ def format(keys)
53
+ width = (keys.keys.max do |a,b| a.length <=> b.length end).length + 2
54
+
55
+ keys.each do |key, value|
56
+ puts "#{key.ljust(width,' ')}: #{value}"
57
+ end
58
+ end
59
+
60
+ def format_table(list)
61
+ widths = {}
62
+ total = 0
63
+
64
+ list[0].keys.each do |key|
65
+ widths[key] = key.length
66
+ list.each do |item|
67
+ if table_value(item[key]).length > widths[key]
68
+ widths[key] = table_value(item[key]).length
69
+ end
70
+ end
71
+ total += widths[key]
72
+ end
73
+
74
+ table_spacer(widths)
75
+
76
+ list[0].keys.each do |key|
77
+ print "| #{key.to_s.rjust(widths[key],' ')} "
78
+ end
79
+
80
+ puts "|"
81
+
82
+ table_spacer(widths)
83
+
84
+ list.each do |item|
85
+ item.each do |key,value|
86
+ print "| #{table_value(value).rjust(widths[key],' ')} "
87
+ end
88
+ puts "|"
89
+ end
90
+
91
+ table_spacer(widths)
92
+ end
93
+
94
+ def table_spacer(widths)
95
+ widths.each do |key, width|
96
+ print "+-" + ("-" * width) + "-"
97
+ end
98
+ puts "+"
99
+ end
100
+
101
+ def table_value(value)
102
+ if !!value == value
103
+ return value ? 'yes' : 'no'
104
+ end
105
+ if value.is_a?(Time)
106
+ return value.strftime('%Y-%m-%d %H:%M:%S')
107
+ end
108
+
109
+ value
110
+ end
111
+
112
+ def list
113
+ m = Migrations.new
114
+
115
+ list = m.list
116
+
117
+ if list.empty?
118
+ puts "\nNo migrations found.\n\n"
119
+ else
120
+ puts "\n"
121
+ format_table(list)
122
+ puts "\n"
123
+ end
124
+ end
125
+
126
+ def prompt_continue
127
+ print "\nContinue? [y/N] "
128
+ input = STDIN.gets.strip
129
+
130
+ if input[0].downcase != 'y'
131
+ puts "Aborting.\n"
132
+ exit
133
+ end
134
+ end
135
+
136
+ def list_migrations(title, versions)
137
+ puts "\n#{title}\n\n"
138
+
139
+ versions.each do |version|
140
+ puts version
141
+ end
142
+ end
143
+
144
+ def up(n=nil)
145
+ m = Migrations.new
146
+
147
+ pending = m.pending(n)
148
+
149
+ if pending.empty?
150
+ puts "\nNo migrations pending.\n\n"
151
+ else
152
+ if @prompt
153
+ list_migrations "Migrations pending:",pending
154
+ prompt_continue
155
+ end
156
+
157
+ puts "\n"
158
+ m.up(n)
159
+ puts "\n"
160
+ end
161
+ end
162
+
163
+ def down(n=nil)
164
+ if n.nil?
165
+ n = 1
166
+ elsif n == 'all'
167
+ n = nil
168
+ end
169
+
170
+ m = Migrations.new
171
+
172
+ applied = m.applied(n)
173
+
174
+ if applied.empty?
175
+ puts "\nNo migrations applied.\n\n"
176
+ else
177
+ if @prompt
178
+ list_migrations "Migrations to roll back:",applied
179
+ prompt_continue
180
+ end
181
+
182
+ puts "\n"
183
+ m.down(n)
184
+ puts "\n"
185
+ end
186
+ end
187
+
188
+ def add(name)
189
+ name = name.gsub(/[^a-zA-Z0-9_]+/,'')
190
+
191
+ m = Migrations.new
192
+
193
+ puts "\n"
194
+ m.generate(name)
195
+ puts "\n"
196
+ end
197
+
198
+ def to(version)
199
+ m = Migrations.new
200
+
201
+ pending = m.pending
202
+
203
+ if pending.include? version
204
+ to_apply = []
205
+
206
+ pending.each do |pending_version|
207
+ to_apply.push pending_version
208
+
209
+ if pending_version == version
210
+ break
211
+ end
212
+ end
213
+
214
+ if to_apply.empty?
215
+ puts "\nNothing to do, we are already at that version.\n\n"
216
+ return
217
+ end
218
+
219
+ if @prompt
220
+ list_migrations "Migrations to apply:", to_apply
221
+ prompt_continue
222
+ end
223
+
224
+ puts "\n"
225
+ to_apply.each do |version|
226
+ m.apply version
227
+ end
228
+ puts "\n"
229
+ return
230
+
231
+ else
232
+ applied = m.applied
233
+
234
+ if applied.include? version
235
+ if applied[0] == version
236
+ puts "\nNothing to do, we are already at that version.\n\n"
237
+ return
238
+ end
239
+ to_revert = []
240
+ applied.each do |applied_version|
241
+ if applied_version == version
242
+ break
243
+ end
244
+ to_revert.push applied_version
245
+ end
246
+
247
+ if @prompt
248
+ list_migrations "Migrations to roll back:", to_revert
249
+ prompt_continue
250
+ end
251
+
252
+ puts "\n"
253
+ to_revert.each do |version|
254
+ m.revert version
255
+ end
256
+ puts "\n"
257
+ return
258
+ end
259
+ end
260
+
261
+ puts "\nVersion not found.\n\n"
262
+ end
263
+
264
+ def set(version)
265
+ m = Migrations.new
266
+
267
+ pending = m.pending
268
+
269
+ if pending.include? version
270
+ pending.each do |pending_version|
271
+ m.insert pending_version
272
+
273
+ if pending_version == version
274
+ break
275
+ end
276
+ end
277
+
278
+ puts "\nVersion has been set to: #{version}\n\n"
279
+ return
280
+ else
281
+ applied = m.applied
282
+
283
+ if applied.first == version
284
+ puts "\nNothing to do, we are already at version: #{version}\n\n"
285
+ return
286
+ end
287
+
288
+ if (applied.include?(version) || version == "none")
289
+ applied.each do |applied_version|
290
+ if applied_version == version
291
+ break
292
+ end
293
+ m.remove applied_version
294
+ end
295
+
296
+ if version == "none"
297
+ puts "\nVersion has been set to (none)\n\n"
298
+ else
299
+ puts "\nVersion has been set to: #{version}\n\n"
300
+ end
301
+ return
302
+ end
303
+ end
304
+
305
+ puts "\nVersion #{version} was not found.\n\n"
306
+ end
307
+
308
+ def apply(version)
309
+ m = Migrations.new
310
+ m.apply(version,false)
311
+ end
312
+
313
+ def revert(version)
314
+ m = Migrations.new
315
+ m.revert(version,false)
316
+ end
317
+ end
318
+
319
+ MigrationCommand.new(ARGV)
@@ -0,0 +1,6 @@
1
+
2
+ class Migration
3
+ def initialize(db)
4
+ @db = db
5
+ end
6
+ end
data/lib/migrations.rb ADDED
@@ -0,0 +1,207 @@
1
+
2
+ require 'fileutils'
3
+ require 'sequel'
4
+
5
+ class Migrations
6
+ attr_accessor :path
7
+ attr_accessor :verbose
8
+
9
+ def initialize(db=nil)
10
+ config_file = "config/config.rb"
11
+
12
+ if db
13
+ @db = db
14
+ elsif File.exist? config_file
15
+ config = {}
16
+ instance_eval(File.read(config_file)).each do |key, value|
17
+ config[key] = value
18
+ end
19
+
20
+ @db = Sequel.connect("#{config[:db][:type]}://#{config[:db][:user]}:#{config[:db][:pass]}@#{config[:db][:host]}/#{config[:db][:name]}")
21
+ else
22
+ raise "#{config_file} was not found and no database handle was passed to us."
23
+ end
24
+
25
+ @path = 'migrations'
26
+ @verbose = true
27
+ end
28
+
29
+ def get_migrations
30
+ ensure_migration_table
31
+
32
+ filesystem = []
33
+ database = {}
34
+ all = []
35
+
36
+ Dir.glob(@path + '/*.rb').map do |migration|
37
+ class_name = migration.match(/(M[0-9]{8}_[0-9]{6}_[a-zA-Z0-9_]+)\.rb\z/)[1]
38
+
39
+ filesystem.push class_name
40
+ all.push class_name
41
+ end
42
+
43
+ @db[:migration].each do |migration|
44
+ database[migration[:version]] = migration[:applied_at]
45
+ if !all.include? migration[:version]
46
+ all.push migration
47
+ end
48
+ end
49
+
50
+ [filesystem,database,all]
51
+ end
52
+
53
+ def list
54
+ filesystem, database, all = get_migrations
55
+
56
+ list = []
57
+
58
+ all.sort.each do |migration|
59
+ list.push({
60
+ :version => migration,
61
+ :applied => database[migration] ? true : false,
62
+ :applied_at => database[migration] ? database[migration] : ''
63
+ })
64
+ end
65
+
66
+ list
67
+ end
68
+
69
+ def pending(n=nil)
70
+ filesystem, database, all = get_migrations
71
+
72
+ pending = []
73
+
74
+ filesystem.sort.each do |migration|
75
+ if !database.keys.include?(migration) and (n.nil? or pending.length < n)
76
+ pending.push migration
77
+ end
78
+ end
79
+
80
+ pending
81
+ end
82
+
83
+ def applied(n=nil)
84
+ filesystem, database, all = get_migrations
85
+
86
+ applied = []
87
+
88
+ database.keys.sort.reverse.each do |migration|
89
+ if (n.nil? or applied.length < n)
90
+ applied.push migration
91
+ end
92
+ end
93
+
94
+ applied
95
+ end
96
+
97
+ def generate(name)
98
+ if !File.exist? @path
99
+ FileUtils.mkdir_p @path
100
+ end
101
+
102
+ t = Time.now
103
+
104
+ while 1
105
+ t = Time.at(t.to_i + 1)
106
+ class_name = 'M' + t.strftime('%Y%m%d_%H%M%S') + '_' + name.gsub(/[^a-zA-Z0-9_]/i, '')
107
+ migration = "#{@path}/#{class_name}.rb"
108
+
109
+ if !File.exist? migration
110
+ break
111
+ end
112
+ end
113
+
114
+ File.open(migration,'w') do |f|
115
+ f.write("
116
+ require 'migrations/migration'
117
+
118
+ class #{class_name} < Migration
119
+ def up
120
+ end
121
+
122
+ def down
123
+ end
124
+ end
125
+ ")
126
+ @verbose and puts "Create new migration: #{migration}"
127
+
128
+ true
129
+ end
130
+ end
131
+
132
+ def ensure_migration_table
133
+ begin
134
+ table = @db[:migration].map(:version)
135
+ rescue Sequel::DatabaseError => e
136
+ if e.message.match /Table .*? doesn\'t exist/
137
+ @db.create_table :migration do
138
+ primary_key :id
139
+ String :version, :unique => true, :null => false
140
+ DateTime :applied_at
141
+ end
142
+ else
143
+ raise Sequel::DatabaseError e
144
+ end
145
+ end
146
+ end
147
+
148
+ def up(n=nil)
149
+ ensure_migration_table
150
+
151
+ pending(n).each do |version|
152
+ apply(version)
153
+ end
154
+ end
155
+
156
+ def down(n=nil)
157
+ ensure_migration_table
158
+
159
+ applied(n).each do |version|
160
+ revert(version)
161
+ end
162
+ end
163
+
164
+ def apply(version, update_table=true)
165
+ migration = @path + '/' + version + '.rb'
166
+
167
+ load migration
168
+
169
+ m = Object::const_get(version).new(@db)
170
+
171
+ @verbose and print "Applying: #{version} ... "
172
+
173
+ m.up
174
+
175
+ @verbose and puts "ok"
176
+
177
+ update_table and insert(version)
178
+ end
179
+
180
+ def insert(version)
181
+ @db[:migration].insert(:version => version, :applied_at => Time.now)
182
+ end
183
+
184
+ def revert(version, update_table=true)
185
+ migration = @path + '/' + version + '.rb'
186
+
187
+ if !File.exist? migration
188
+ raise "Migration file #{migration} not found."
189
+ end
190
+
191
+ load migration
192
+
193
+ m = Object::const_get(version).new(@db)
194
+
195
+ @verbose and print "Rolling back: #{version} ... "
196
+
197
+ m.down
198
+
199
+ @verbose and puts "ok"
200
+
201
+ update_table and remove(version)
202
+ end
203
+
204
+ def remove(version)
205
+ @db[:migration].where('version = ?',version).delete
206
+ end
207
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: migrations
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - m4rkw
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sequel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.25'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.25'
27
+ description: Ruby database migration library
28
+ email: m@rkw.io
29
+ executables:
30
+ - migrate
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - bin/migrate
35
+ - lib/migrations.rb
36
+ - lib/migrations/migration.rb
37
+ homepage: https://github.com/m4rkw/migrations
38
+ licenses:
39
+ - MIT
40
+ metadata: {}
41
+ post_install_message:
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ requirements: []
56
+ rubyforge_project:
57
+ rubygems_version: 2.4.5
58
+ signing_key:
59
+ specification_version: 4
60
+ summary: Migrations
61
+ test_files: []