migration_bundler 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.bundle/config +2 -0
  3. data/.gitignore +3 -0
  4. data/.ruby-version +1 -0
  5. data/CHANGELOG.md +49 -0
  6. data/Gemfile +4 -0
  7. data/Gemfile.lock +122 -0
  8. data/Guardfile +8 -0
  9. data/LICENSE +202 -0
  10. data/README.md +219 -0
  11. data/Rakefile +16 -0
  12. data/bin/mb +6 -0
  13. data/lib/migration_bundler/actions.rb +38 -0
  14. data/lib/migration_bundler/cli.rb +355 -0
  15. data/lib/migration_bundler/databases/abstract_database.rb +54 -0
  16. data/lib/migration_bundler/databases/cassandra_database.rb +88 -0
  17. data/lib/migration_bundler/databases/sqlite_database.rb +116 -0
  18. data/lib/migration_bundler/migrations.rb +52 -0
  19. data/lib/migration_bundler/project.rb +90 -0
  20. data/lib/migration_bundler/targets/base.rb +85 -0
  21. data/lib/migration_bundler/targets/cassandra/cassandra_target.rb +73 -0
  22. data/lib/migration_bundler/targets/cassandra/create_schema_migrations.cql.erb +16 -0
  23. data/lib/migration_bundler/targets/cassandra/migration.cql.erb +0 -0
  24. data/lib/migration_bundler/targets/cocoapods/cocoapods_target.rb +43 -0
  25. data/lib/migration_bundler/targets/cocoapods/podspec.erb +12 -0
  26. data/lib/migration_bundler/targets/maven/maven_target.rb +62 -0
  27. data/lib/migration_bundler/targets/maven/project/.gitignore +6 -0
  28. data/lib/migration_bundler/targets/maven/project/MonkeyButler.iml +19 -0
  29. data/lib/migration_bundler/targets/maven/project/build.gradle +54 -0
  30. data/lib/migration_bundler/targets/sqlite/create_migration_bundler_tables.sql.erb +15 -0
  31. data/lib/migration_bundler/targets/sqlite/migration.sql.erb +11 -0
  32. data/lib/migration_bundler/targets/sqlite/sqlite_target.rb +92 -0
  33. data/lib/migration_bundler/templates/Gemfile.erb +4 -0
  34. data/lib/migration_bundler/templates/gitignore.erb +1 -0
  35. data/lib/migration_bundler/util.rb +71 -0
  36. data/lib/migration_bundler/version.rb +3 -0
  37. data/lib/migration_bundler.rb +1 -0
  38. data/migration_bundler.gemspec +33 -0
  39. data/spec/cli_spec.rb +700 -0
  40. data/spec/databases/cassandra_database_spec.rb +260 -0
  41. data/spec/databases/sqlite_database_spec.rb +198 -0
  42. data/spec/migrations_spec.rb +4 -0
  43. data/spec/project_spec.rb +128 -0
  44. data/spec/sandbox/cassandra/.gitignore +2 -0
  45. data/spec/sandbox/cassandra/.migration_bundler.yml +9 -0
  46. data/spec/sandbox/cassandra/migrations/20140523123443021_create_sandbox.cql.sql +14 -0
  47. data/spec/sandbox/cassandra/sandbox.cql +0 -0
  48. data/spec/sandbox/sqlite/.gitignore +2 -0
  49. data/spec/sandbox/sqlite/.migration_bundler.yml +9 -0
  50. data/spec/sandbox/sqlite/migrations/20140523123443021_create_sandbox.sql +14 -0
  51. data/spec/sandbox/sqlite/sandbox.sql +0 -0
  52. data/spec/spec_helper.rb +103 -0
  53. data/spec/targets/cassandra_target_spec.rb +191 -0
  54. data/spec/targets/cocoapods_target_spec.rb +197 -0
  55. data/spec/targets/maven_target_spec.rb +156 -0
  56. data/spec/targets/sqlite_target_spec.rb +103 -0
  57. data/spec/util_spec.rb +13 -0
  58. metadata +260 -0
@@ -0,0 +1,90 @@
1
+ require 'yaml'
2
+ require 'uri'
3
+
4
+ module MigrationBundler
5
+ class Project
6
+ class << self
7
+ def load(path = Dir.pwd)
8
+ @project ||= proc do
9
+ project_path = File.join(path, '.migration_bundler.yml')
10
+ raise "fatal: Not a migration_bundler repository: no .migration_bundler.yml" unless File.exists?(project_path)
11
+ options = YAML.load(File.read(project_path))
12
+ new(options)
13
+ end.call
14
+ end
15
+
16
+ def set(options)
17
+ @project = new(options)
18
+ end
19
+
20
+ def clear
21
+ @project = nil
22
+ end
23
+ end
24
+
25
+ attr_accessor :name, :config, :database_url, :targets
26
+
27
+ def initialize(options = {})
28
+ options.each { |k,v| send("#{k}=", v) }
29
+ end
30
+
31
+ def database_url=(database_url)
32
+ @database_url = database_url ? URI(database_url) : nil
33
+ end
34
+
35
+ def database
36
+ database_url.scheme || 'sqlite'
37
+ end
38
+
39
+ def schema_path
40
+ "#{name}" + database_class.migration_ext
41
+ end
42
+
43
+ def migrations_path
44
+ "migrations"
45
+ end
46
+
47
+ def git_url
48
+ `git config remote.origin.url`.chomp
49
+ end
50
+
51
+ def git_latest_tag
52
+ git_tag_for_version(nil)
53
+ end
54
+
55
+ def git_current_branch
56
+ `git symbolic-ref --short HEAD`.chomp
57
+ end
58
+
59
+ def git_tag_for_version(version)
60
+ pattern = version && "#{version}*"
61
+ tag = `git tag -l --sort=-v:refname #{pattern} | head -n 1`.chomp
62
+ tag.empty? ? nil : tag
63
+ end
64
+
65
+ def git_user_email
66
+ `git config user.email`.chomp
67
+ end
68
+
69
+ def git_user_name
70
+ `git config user.name`.chomp
71
+ end
72
+
73
+ def save!(path)
74
+ project_path = File.join(path, '.migration_bundler.yml')
75
+ File.open(project_path, 'w') { |f| f << YAML.dump(self.to_hash) }
76
+ end
77
+
78
+ def database_class
79
+ MigrationBundler::Util.database_named(database)
80
+ end
81
+
82
+ def database_target_class
83
+ MigrationBundler::Util.target_classes_named(database)[0]
84
+ end
85
+
86
+ def to_hash
87
+ { "name" => name, "config" => config, "database_url" => database_url.to_s, "targets" => targets }
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,85 @@
1
+ module MigrationBundler
2
+ module Targets
3
+ class Base < Thor
4
+ include Thor::Actions
5
+ include MigrationBundler::Actions
6
+ add_runtime_options!
7
+
8
+ class << self
9
+ def source_root
10
+ File.join File.dirname(__FILE__), name
11
+ end
12
+
13
+ def name
14
+ "#{self}".split('::').last.gsub(/Target$/, '').downcase
15
+ end
16
+
17
+ def register_with_cli(cli)
18
+ # Allows targets a chance to configure the CLI
19
+ # This is an ideal place to register any options, tweak description, etc.
20
+ end
21
+ end
22
+
23
+ attr_reader :project, :database, :migrations
24
+
25
+ # Target Command
26
+
27
+ desc "init", "Initializes the target."
28
+ def init
29
+ # Default implementation does nothing
30
+ end
31
+
32
+ desc "new NAME", "Create a new migration"
33
+ def new(path)
34
+ # Default implementation does nothing
35
+ end
36
+
37
+ desc "generate", "Generates a platform specific package."
38
+ def generate
39
+ # Default implementation does nothing
40
+ end
41
+
42
+ desc "push", "Pushes a built package."
43
+ def push
44
+ # Default implementation does nothing
45
+ end
46
+
47
+ desc "push", "Validates the environment."
48
+ def validate
49
+ # Default implementation does nothing
50
+ end
51
+
52
+ desc "dump", "Dumps the schema"
53
+ def dump
54
+ # Default implementation does nothing
55
+ end
56
+
57
+ desc "load", "Loads the schema"
58
+ def load
59
+ # Default implementation does nothing
60
+ end
61
+
62
+ desc "drop", "Drops all tables and records"
63
+ def drop
64
+ # Default implementation does nothing
65
+ end
66
+
67
+ protected
68
+ def project
69
+ @project ||= MigrationBundler::Project.load(destination_root)
70
+ end
71
+
72
+ def database
73
+ @database ||= project.database_class.new(database_url)
74
+ end
75
+
76
+ def database_url
77
+ (options[:database] && URI(options[:database])) || project.database_url
78
+ end
79
+
80
+ def migrations
81
+ @migrations ||= MigrationBundler::Migrations.new(project.migrations_path, database)
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,73 @@
1
+ require 'migration_bundler/databases/cassandra_database'
2
+
3
+ module MigrationBundler
4
+ module Targets
5
+ class CassandraTarget < Base
6
+ def init
7
+ migration_path = "migrations/" + MigrationBundler::Util.migration_named('create_' + options[:name]) + '.cql'
8
+ template('create_schema_migrations.cql.erb', migration_path)
9
+ git_add migration_path
10
+ end
11
+
12
+ def new(name)
13
+ migration_path = "migrations/" + MigrationBundler::Util.migration_named(name) + '.cql'
14
+ template('migration.cql.erb', migration_path)
15
+ git_add migration_path
16
+ end
17
+
18
+ def dump
19
+ database_url = (options[:database] && URI(options[:database])) || project.database_url
20
+ @database = MigrationBundler::Databases::CassandraDatabase.new(database_url)
21
+ fail Error, "Cannot dump database: the database at '#{database_url}' does not have a `schema_migrations` table." unless database.migrations_table?
22
+ say "Dumping schema from database '#{database_url}'"
23
+
24
+ say "Dumping keyspaces '#{keyspaces.join(', ')}'..."
25
+ describe_statements = keyspaces.map { |keyspace| "describe keyspace #{keyspace};" }
26
+ run "cqlsh -e '#{describe_statements.join(' ')}' #{database_url.host} > #{project.schema_path}"
27
+
28
+ File.open(project.schema_path, 'a') do |f|
29
+ f.puts "USE #{keyspace};"
30
+ project.config['db.dump_tables'].each do |table_name|
31
+ say "Dumping rows from '#{table_name}'..."
32
+ with_padding do
33
+ row_statements = database.dump_rows(table_name)
34
+ f.puts row_statements.join("\n")
35
+ say "wrote #{row_statements.size} rows.", :green
36
+ end
37
+ end
38
+ say
39
+ end
40
+
41
+ say "Dump complete. Schema written to #{project.schema_path}."
42
+ end
43
+
44
+ def load
45
+ project = MigrationBundler::Project.load
46
+ unless File.size?(project.schema_path)
47
+ raise Error, "Cannot load database: empty schema found at #{project.schema_path}. Maybe you need to `mb migrate`?"
48
+ end
49
+
50
+ @database = MigrationBundler::Databases::CassandraDatabase.new(database_url)
51
+ drop
52
+ run "cqlsh #{database_url.host} -f #{project.schema_path}"
53
+
54
+ say "Loaded schema at version #{database.current_version}"
55
+ end
56
+
57
+ def drop
58
+ say_status :drop, database_url, :yellow
59
+ database.drop(keyspaces)
60
+ end
61
+
62
+ private
63
+ def keyspace
64
+ database_url.path[1..-1]
65
+ end
66
+
67
+ def keyspaces
68
+ keyspaces = (project.config['cassandra.keyspaces'] || []).dup
69
+ keyspaces.unshift(keyspace)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,16 @@
1
+ /* Create Monkey Butler Tables */
2
+
3
+ -- Maintains list of applied migrations
4
+ USE KEYSPACE <%= keyspace -%>;
5
+ CREATE TABLE schema_migrations(
6
+ version INTEGER UNIQUE NOT NULL
7
+ );
8
+
9
+ /* Create application tables */
10
+
11
+ /*
12
+ CREATE TABLE table_name (
13
+ id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
14
+ ...
15
+ );
16
+ */
@@ -0,0 +1,43 @@
1
+ require 'migration_bundler/targets/base'
2
+
3
+ module MigrationBundler
4
+ module Targets
5
+ class CocoapodsTarget < MigrationBundler::Targets::Base
6
+ def init
7
+ unless project.config['cocoapods.repo']
8
+ project.config['cocoapods.repo'] = ask("What is the name of your Cocoapods specs repo? ")
9
+ end
10
+ if options['bundler']
11
+ append_to_file 'Gemfile', "gem 'cocoapods'\n"
12
+ end
13
+ end
14
+
15
+ def generate
16
+ invoke :validate
17
+ template('podspec.erb', podspec_name, force: true)
18
+ end
19
+
20
+ def validate
21
+ fail Error, "Invalid configuration: cocoapods.repo is not configured." unless cocoapods_repo
22
+ end
23
+
24
+ def push
25
+ invoke :validate
26
+ run "pod repo push #{options['quiet'] && '--silent '}--allow-warnings #{cocoapods_repo} #{podspec_name}"
27
+ end
28
+
29
+ private
30
+ def cocoapods_repo
31
+ project.config['cocoapods.repo']
32
+ end
33
+
34
+ def podspec_name
35
+ "#{project.name}.podspec"
36
+ end
37
+
38
+ def pod_version
39
+ unique_tag_for_version(migrations.latest_version)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,12 @@
1
+ Pod::Spec.new do |s|
2
+ s.name = "<%= project.name %>"
3
+ s.version = "<%= pod_version %>"
4
+ s.summary = "Packages the database schema and migrations for <%= project.name -%>"
5
+ s.homepage = "<%= project.config['homepage'] || "http://github.com/layerhq" %>"
6
+ s.author = { "<%= project.git_user_name -%>" => "<%= project.git_user_email -%>" }
7
+ s.source = { :git => "<%= project.git_url -%>", :tag => "<%= pod_version -%>" }
8
+ s.license = 'Commercial'
9
+ s.requires_arc = true
10
+
11
+ s.resource_bundles = { '<%= project.name -%>' => ['migrations/*', '<%= project.schema_path -%>'] }
12
+ end
@@ -0,0 +1,62 @@
1
+ require 'migration_bundler/targets/base'
2
+
3
+ module MigrationBundler
4
+ module Targets
5
+ class MavenTarget < MigrationBundler::Targets::Base
6
+ def init
7
+ unless project.config['maven.url']
8
+ project.config['maven.url'] = ask("What is the URL of your Java Maven repo? ")
9
+ end
10
+ unless project.config['maven.username']
11
+ project.config['maven.username'] = ask("What is the username for your Java Maven repo? ")
12
+ end
13
+ unless project.config['maven.password']
14
+ project.config['maven.password'] = ask("What is the password for your Java Maven repo? ")
15
+ end
16
+ end
17
+
18
+ def generate
19
+ invoke :validate
20
+ remove_file "project"
21
+ empty_directory "project"
22
+ empty_directory "project/src"
23
+ empty_directory "project/src/main"
24
+ empty_directory "project/src/main/resources"
25
+ empty_directory "project/src/main/resources/schema"
26
+
27
+ copy_file "project/build.gradle", "project/build.gradle"
28
+ FileUtils.cp_r project.schema_path, "project/src/main/resources/schema/schema.sql"
29
+ FileUtils.cp_r project.migrations_path, "project/src/main/resources"
30
+
31
+ version = unique_tag_for_version(migrations.latest_version)
32
+ run "cd project && gradle#{options['quiet'] && ' -q '} -Pversion=#{version} clean jar"
33
+ end
34
+
35
+ def validate
36
+ fail Error, "Invalid configuration: maven.repo is not configured." unless maven_url
37
+ fail Error, "Invalid configuration: maven.username is not configured." unless maven_username
38
+ fail Error, "Invalid configuration: maven.password is not configured." unless maven_password
39
+ end
40
+
41
+ def push
42
+ invoke :validate
43
+ version = project.git_latest_tag
44
+ run "cd project && gradle#{options['quiet'] && ' -q'} -Pversion=#{version} -Purl=#{maven_url} -Pusername=#{maven_username} -Ppassword=#{maven_password} publish"
45
+ end
46
+
47
+ private
48
+
49
+ def maven_url
50
+ project.config['maven.url']
51
+ end
52
+
53
+ def maven_username
54
+ project.config['maven.username']
55
+ end
56
+
57
+ def maven_password
58
+ project.config['maven.password']
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,6 @@
1
+ .gradle
2
+ /local.properties
3
+ /.idea/workspace.xml
4
+ .DS_Store
5
+ /build
6
+ /classes
@@ -0,0 +1,19 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.1.1" type="JAVA_MODULE" version="4">
3
+ <component name="NewModuleRootManager" inherit-compiler-output="false">
4
+ <output url="file://$MODULE_DIR$/build/classes/main" />
5
+ <output-test url="file://$MODULE_DIR$/build/classes/test" />
6
+ <exclude-output />
7
+ <content url="file://$MODULE_DIR$">
8
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
9
+ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
10
+ <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
11
+ <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
12
+ <excludeFolder url="file://$MODULE_DIR$/.gradle" />
13
+ <excludeFolder url="file://$MODULE_DIR$/build" />
14
+ </content>
15
+ <orderEntry type="inheritedJdk" />
16
+ <orderEntry type="sourceFolder" forTests="false" />
17
+ </component>
18
+ </module>
19
+
@@ -0,0 +1,54 @@
1
+ apply plugin: 'java'
2
+ apply plugin: 'maven'
3
+ apply plugin: 'maven-publish'
4
+
5
+ /**
6
+ * Specify:
7
+ * -Pversion=[VERSION]
8
+ * -Purl=[MAVEN URL TO PUBLISH TO] e.g. "http://nexus.dev.layer.com:8081/nexus/content/repositories/releases" or "${System.env.HOME}/.m2/repository"
9
+ * -Pusername=[USERNAME FOR MAVEN PUBLISH]
10
+ * -Ppassword=[PASSWORD FOR MAVEN PUBLISH]
11
+ */
12
+
13
+ ext {
14
+ mavenUrl = rootProject.hasProperty("url") ? rootProject.property("url") : "${System.env.HOME}/.m2/repository"
15
+ mavenUsername = rootProject.hasProperty("username") ? rootProject.property("username") : null;
16
+ mavenPassword = rootProject.hasProperty("password") ? rootProject.property("password") : null;
17
+ }
18
+
19
+ compileJava {
20
+ sourceCompatibility = 1.6
21
+ targetCompatibility = 1.6
22
+ }
23
+
24
+ repositories {
25
+ mavenCentral()
26
+ }
27
+
28
+ /*************************************************
29
+ * Uploading
30
+ ************************************************/
31
+ task Jar(type: Jar) {
32
+ jar.archiveName = "MigrationBundler-" + version + ".jar"
33
+ }
34
+
35
+ publishing {
36
+ publications {
37
+ mavenJava(MavenPublication) {
38
+ groupId "com.layer"
39
+ artifactId "MigrationBundler"
40
+ from components.java
41
+ }
42
+ }
43
+ repositories {
44
+ maven {
45
+ credentials {
46
+ username = mavenUsername;
47
+ password = mavenPassword;
48
+ }
49
+ url = mavenUrl;
50
+ }
51
+ }
52
+ }
53
+
54
+
@@ -0,0 +1,15 @@
1
+ /* Create Monkey Butler Tables */
2
+
3
+ -- Maintains list of applied migrations
4
+ CREATE TABLE schema_migrations(
5
+ version INTEGER UNIQUE NOT NULL
6
+ );
7
+
8
+ /* Create application tables */
9
+
10
+ /*
11
+ CREATE TABLE table_name (
12
+ id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
13
+ ...
14
+ );
15
+ */
@@ -0,0 +1,11 @@
1
+ -- Create or alter tables
2
+
3
+ /*
4
+ CREATE TABLE table_name (
5
+ id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT
6
+ );
7
+
8
+ ALTER TABLE table_name (
9
+ ...
10
+ )
11
+ */
@@ -0,0 +1,92 @@
1
+ require 'migration_bundler/databases/sqlite_database'
2
+
3
+ module MigrationBundler
4
+ module Targets
5
+ class SqliteTarget < Base
6
+ # TODO: Need a way to do this for self elegantly...
7
+ def self.register_with_cli(cli)
8
+ cli.method_option :database, type: :string, aliases: '-d', desc: "Set target DATABASE", for: :dump
9
+ cli.method_option :database, type: :string, aliases: '-d', desc: "Set target DATABASE", for: :load
10
+ end
11
+
12
+ def init
13
+ migration_name = MigrationBundler::Util.migration_named('create_' + options['name'])
14
+ template('create_migration_bundler_tables.sql.erb', "migrations/#{migration_name}.sql")
15
+ git_add "migrations/#{migration_name}.sql"
16
+ create_file(database_path)
17
+ append_to_file '.gitignore', database_path
18
+ end
19
+
20
+ no_commands do
21
+ # NOTE: Used instead of database.url to avoid creation of the database in pretend mode
22
+ def database_path
23
+ database_url.path || database_url.opaque
24
+ end
25
+ end
26
+
27
+ def new(name)
28
+ migration_ext = project.database_class.migration_ext
29
+ migration_name = MigrationBundler::Util.migration_named(name) + migration_ext
30
+ template('migration.sql.erb', "migrations/#{migration_name}")
31
+ git_add "migrations/#{migration_name}"
32
+ end
33
+
34
+ def dump
35
+ database_url = (options[:database] && URI(options[:database])) || project.database_url
36
+ database_path = database_url.path || database_url.opaque
37
+ fail Error, "Cannot dump database: no file at path '#{database_path}'." unless File.exists?(database_path)
38
+
39
+ @database = MigrationBundler::Databases::SqliteDatabase.new(database_url)
40
+ fail Error, "Cannot dump database: the database at path '#{database_path}' does not have a `schema_migrations` table." unless database.migrations_table?
41
+ say "Dumping schema from database '#{database_path}'"
42
+
43
+ File.truncate(project.schema_path, 0)
44
+
45
+ types = { table: 'tables', index: 'indexes', trigger: 'triggers', view: 'views'}
46
+ types.each do |type, name|
47
+ say "Dumping #{name}..."
48
+ with_padding do
49
+ database.dump_to_schema(type, project.schema_path) do |name|
50
+ say "wrote #{type}: #{name}", :green
51
+ end
52
+ end
53
+ say
54
+ end
55
+
56
+ File.open(project.schema_path, 'a') do |f|
57
+ project.config['db.dump_tables'].each do |table_name|
58
+ say "Dumping rows from '#{table_name}'..."
59
+ with_padding do
60
+ row_statements = database.dump_rows(table_name)
61
+ f.puts row_statements.join("\n")
62
+ say "wrote #{row_statements.size} rows.", :green
63
+ end
64
+ end
65
+ say
66
+ end
67
+
68
+ say "Dump complete. Schema written to #{project.schema_path}."
69
+ end
70
+
71
+ def load
72
+ project = MigrationBundler::Project.load
73
+ unless File.size?(project.schema_path)
74
+ raise Error, "Cannot load database: empty schema found at #{project.schema_path}. Maybe you need to `mb migrate`?"
75
+ end
76
+
77
+ drop
78
+ command = "sqlite3 #{database.path} < #{project.schema_path}"
79
+ say_status :executing, command
80
+ stdout_str, stderr_str, status = Open3.capture3(command)
81
+ fail Error, "Failed loading schema: #{stderr_str}" unless stderr_str.empty?
82
+
83
+ say "Loaded schema at version #{database.current_version}"
84
+ end
85
+
86
+ def drop
87
+ say_status :truncate, database.path, :yellow
88
+ database.drop
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+ ruby '2.0.0'
3
+
4
+ gem 'migration_bundler'
@@ -0,0 +1 @@
1
+ .DS_Store
@@ -0,0 +1,71 @@
1
+ module MigrationBundler
2
+ class Util
3
+ class << self
4
+ def migration_timestamp
5
+ Time.now.strftime('%Y%m%d%H%M%S%3N').to_i
6
+ end
7
+
8
+ def migration_named(name, timestamp = migration_timestamp)
9
+ migration_name = [timestamp, Thor::Util.snake_case(name)].join('_')
10
+ end
11
+
12
+ def strip_leading_whitespace(string)
13
+ string.gsub(/^\s+/, '')
14
+ end
15
+
16
+ def migration_version_from_path(path)
17
+ path.match(/(\d{17})_/)[1].to_i
18
+ end
19
+
20
+ def migration_versions_from_paths(paths)
21
+ paths.map { |path| migration_version_from_path(path) }
22
+ end
23
+
24
+ def migrations_by_version(paths)
25
+ paths.inject({}) do |hash, path|
26
+ version = migration_version_from_path(path)
27
+ hash[version] = path
28
+ hash
29
+ end
30
+ end
31
+
32
+ def camelize(term, uppercase_first_letter = true)
33
+ string = term.to_s
34
+ string = string.sub(/^[a-z\d]*/) { $&.capitalize }
35
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
36
+ string.gsub!('/', '::')
37
+ string
38
+ end
39
+
40
+ def database_named(name)
41
+ raise ArgumentError, "Database name cannot be nil." if name.nil?
42
+ require "migration_bundler/databases/#{name}_database"
43
+ klass_name = "MigrationBundler::Databases::#{Util.camelize(name)}Database"
44
+ Object.const_get(klass_name)
45
+ end
46
+
47
+ def target_classes_named(*names)
48
+ raise ArgumentError, "Database name cannot be nil." if names.nil?
49
+ names.flatten.map do |name|
50
+ require "migration_bundler/targets/#{name}/#{name}_target"
51
+ klass_name = "MigrationBundler::Targets::#{Util.camelize(name)}Target"
52
+ Object.const_get(klass_name).tap do |klass|
53
+ yield klass if block_given?
54
+ end
55
+ end
56
+ end
57
+
58
+ def unique_tag_for_version(version)
59
+ revision = nil
60
+ tag = nil
61
+ begin
62
+ tag = [version, revision].compact.join('.')
63
+ existing_tag = `git tag -l #{tag}`
64
+ break if existing_tag == ""
65
+ revision = revision.to_i + 1
66
+ end while true
67
+ tag
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,3 @@
1
+ module MigrationBundler
2
+ VERSION = "1.3.0"
3
+ end
@@ -0,0 +1 @@
1
+ require 'migration_bundler/cli'