migrations 1.0.0

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 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: []