migration_bundler 1.3.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.
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'