data_migrate 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +78 -0
- data/Rakefile +2 -0
- data/data_migrate.gemspec +23 -0
- data/lib/data_migrate.rb +2 -0
- data/lib/data_migrate/data_migrator.rb +15 -0
- data/lib/data_migrate/railtie.rb +9 -0
- data/lib/data_migrate/version.rb +3 -0
- data/lib/generators/data_migrate.rb +17 -0
- data/lib/generators/data_migrate/USAGE +8 -0
- data/lib/generators/data_migrate/install/install_generator.rb +24 -0
- data/lib/generators/data_migrate/install/templates/install_migration.rb +13 -0
- data/lib/generators/data_migration/data_migration_generator.rb +41 -0
- data/lib/generators/data_migration/templates/data_migration.rb +8 -0
- data/lib/generators/data_migration/templates/migration.rb +17 -0
- data/tasks/.gitkeep +0 -0
- data/tasks/databases.rake +344 -0
- metadata +86 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2011 Andrew J Vargo
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
Data Migrate
|
2
|
+
====
|
3
|
+
|
4
|
+
Run data migrations alongside schema migrations.
|
5
|
+
|
6
|
+
Data migrations are stored in db/data. They act like schema migrations, except they should be reserved for data migrations. For instance, if you realize you need to titleize all yours titles, this is the place to do it.
|
7
|
+
|
8
|
+
Data migrations can be created at the same time as schema migrations, or independently. Database (db:) tasks have been added and extended to run on data migrations only, or in conjunction with the schema migration. For instance, `rake db:migrate:with_data` will run both schema and data migrations in the proper order.
|
9
|
+
|
10
|
+
Note: If a data and schema migration share the same version number, schema gets precedence when migrating up. Data does down.
|
11
|
+
|
12
|
+
Rails 3 and Ruby 1.9
|
13
|
+
--------------------
|
14
|
+
|
15
|
+
Data Migrate is Rails 3.0.0 - 3.0.7, and Ruby 1.9 compatible
|
16
|
+
|
17
|
+
Installation
|
18
|
+
------------
|
19
|
+
After adding Data Migrate to your project,
|
20
|
+
|
21
|
+
rails g data_migrate:install
|
22
|
+
rake db:migrate
|
23
|
+
|
24
|
+
A table 'data_migrations' table will be generated.
|
25
|
+
|
26
|
+
Usage
|
27
|
+
-----
|
28
|
+
|
29
|
+
### Generating Migrations
|
30
|
+
|
31
|
+
You can generate a data migration as you would a schema migration:
|
32
|
+
|
33
|
+
rails g data_migration add_this_to_that
|
34
|
+
|
35
|
+
By default, the migration also generates a schema migration by the same name.
|
36
|
+
This allows you to do things like:
|
37
|
+
|
38
|
+
rails g data_migration add_this_to_that this:string
|
39
|
+
|
40
|
+
If you need a data only migration, either run it as such, with the skip-schema-migration flag:
|
41
|
+
|
42
|
+
rails g data_migration add_this_to_that --skip-schema-migration
|
43
|
+
|
44
|
+
|
45
|
+
### Rake Tasks
|
46
|
+
|
47
|
+
$> rake -T data
|
48
|
+
rake data:forward # Pushes the schema to the next version (specify steps w/ STEP=n).
|
49
|
+
rake data:migrate:down # Runs the "down" for a given migration VERSION.
|
50
|
+
rake data:migrate:redo # Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).
|
51
|
+
rake data:migrate:status # Display status of data migrations
|
52
|
+
rake data:migrate:up # Runs the "up" for a given migration VERSION.
|
53
|
+
rake data:rollback # Rolls the schema back to the previous version (specify steps w/ STEP=n).
|
54
|
+
rake data:version # Retrieves the current schema version number for data migrations
|
55
|
+
rake db:forward:with_data # Pushes the schema to the next version (specify steps w/ STEP=n).
|
56
|
+
rake db:migrate:data # Migrate the database through scripts in db/data/migrate.
|
57
|
+
rake db:migrate:down:with_data # Runs the "down" for a given migration VERSION.
|
58
|
+
rake db:migrate:redo:with_data # Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).
|
59
|
+
rake db:migrate:status:with_data # Display status of data and schema migrations
|
60
|
+
rake db:migrate:up:with_data # Runs the "up" for a given migration VERSION.
|
61
|
+
rake db:migrate:with_data # Migrate the database data and schema (options: VERSION=x, VERBOSE=false).
|
62
|
+
rake db:rollback:with_data # Rolls the schema back to the previous version (specify steps w/ STEP=n).
|
63
|
+
rake db:version:with_data # Retrieves the current schema version numbers for data and schema migrations `
|
64
|
+
|
65
|
+
Tasks work as they would with the 'vanilla' db version. The 'with_data' addition to the 'db' tasks will run the task in the context of both the data and schema migrations. That is, `rake db:rollback:with_data` will check to see if it was a schema or data migration invoked last, and do that. Tasks invoked in that space also have an additional line of output, indicating if the action is performed on data or schema.
|
66
|
+
|
67
|
+
With 'up' and 'down', you can specify the option 'BOTH', which defaults to false. Using true, will migrate both the data and schema (in the desired direction) if they both match the version provided. Again, going up, schema is given precedence. Down its data.
|
68
|
+
|
69
|
+
For more example, assume you have the 2 files:
|
70
|
+
db/migrate/20110419021211_add_x_to_y.rb
|
71
|
+
db/data/20110419021211_add_x_to_y.rb
|
72
|
+
|
73
|
+
Running `rake db:migrate:up:with_data VERSION=20110419021211` would execute the 'db/migrate' version.
|
74
|
+
Running `rake db:migrate:up:with_data VERSION=20110419021211` would execute the 'db/migrate' version, followed by the 'db/data' version.
|
75
|
+
|
76
|
+
Going down instead of up would be the opposite.
|
77
|
+
|
78
|
+
`rake db:migrate:status:with_data` provides and additional column to indicate which type of migration.
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "data_migrate/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "data_migrate"
|
7
|
+
s.version = DataMigrate::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Andrew J Vargo"]
|
10
|
+
s.email = ["ajvargo@computer.org"]
|
11
|
+
s.homepage = "http://ajvargo.com"
|
12
|
+
s.summary = %q{Rake tasks to migrate data alongside schema changes.}
|
13
|
+
s.description = %q{Rake tasks to migrate data alongside schema changes.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "data_migrate"
|
16
|
+
|
17
|
+
s.add_dependency('rails', '>= 3.0.0', '<= 3.0.7')
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
end
|
data/lib/data_migrate.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module DataMigrate
|
4
|
+
class DataMigrator < ActiveRecord::Migrator
|
5
|
+
class << self
|
6
|
+
def schema_migrations_table_name
|
7
|
+
ActiveRecord::Base.table_name_prefix + 'data_migrations' + ActiveRecord::Base.table_name_suffix
|
8
|
+
end
|
9
|
+
|
10
|
+
def migrations_path
|
11
|
+
'db/data'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rails/generators/base'
|
2
|
+
require 'rails/generators/named_base'
|
3
|
+
module DataMigrate
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < Rails::Generators::Base #:nodoc:
|
6
|
+
def self.source_root
|
7
|
+
@_data_migrate_source_root ||= File.expand_path(File.join(File.dirname(__FILE__), 'data_migrate', generator_name, 'templates'))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class DataMigrationGenerator < Rails::Generators::NamedBase #:nodoc:
|
12
|
+
def self.source_root
|
13
|
+
@_data_migrate_source_root ||= File.expand_path(File.join(File.dirname(__FILE__), generator_name, 'templates'))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'generators/data_migrate'
|
2
|
+
require 'rails/generators'
|
3
|
+
require 'rails/generators/migration'
|
4
|
+
|
5
|
+
module DataMigrate
|
6
|
+
module Generators
|
7
|
+
class InstallGenerator < Rails::Generators::Base
|
8
|
+
include Rails::Generators::Migration
|
9
|
+
|
10
|
+
def create_migration_file
|
11
|
+
migration_template "install_migration.rb", "db/migrate/create_data_migrations.rb"
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
def self.next_migration_number(dirname)
|
16
|
+
if ActiveRecord::Base.timestamped_migrations
|
17
|
+
Time.new.utc.strftime("%Y%m%d%H%M%S")
|
18
|
+
else
|
19
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateDataMigrations < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :data_migrations do |t|
|
4
|
+
t.string :version, :null => false
|
5
|
+
end
|
6
|
+
|
7
|
+
add_index :data_migrations, :version, :unique => true, :name => "<%= ActiveRecord::Base.table_name_prefix %>unique_data_migrations<%= ActiveRecord::Base.table_name_suffix %>"
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.down
|
11
|
+
drop_table :data_migrations
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'generators/data_migrate'
|
2
|
+
require 'rails/generators'
|
3
|
+
require 'rails/generators/migration'
|
4
|
+
|
5
|
+
module DataMigrate
|
6
|
+
module Generators
|
7
|
+
class DataMigrationGenerator < Rails::Generators::NamedBase
|
8
|
+
namespace "data_migration"
|
9
|
+
include Rails::Generators::Migration
|
10
|
+
|
11
|
+
argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
|
12
|
+
class_option :skip_schema_migration, :desc => 'Dont generate database migration file.', :type => :boolean
|
13
|
+
|
14
|
+
def create_data_migration
|
15
|
+
set_local_assigns!
|
16
|
+
unless options.skip_schema_migration?
|
17
|
+
migration_template "migration.rb", "db/migrate/#{file_name}.rb"
|
18
|
+
end
|
19
|
+
migration_template "data_migration.rb", "db/data/#{file_name}.rb"
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
attr_reader :migration_action
|
24
|
+
|
25
|
+
def self.next_migration_number(dirname)
|
26
|
+
if ActiveRecord::Base.timestamped_migrations
|
27
|
+
Time.new.utc.strftime("%Y%m%d%H%M%S")
|
28
|
+
else
|
29
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_local_assigns!
|
34
|
+
if file_name =~ /^(add|remove)_.*_(?:to|from)_(.*)/
|
35
|
+
@migration_action = $1
|
36
|
+
@table_name = $2.pluralize
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
<% attributes.each do |attribute| -%>
|
4
|
+
<%- if migration_action -%>
|
5
|
+
<%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end %>
|
6
|
+
<%- end -%>
|
7
|
+
<%- end -%>
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.down
|
11
|
+
<% attributes.reverse.each do |attribute| -%>
|
12
|
+
<%- if migration_action -%>
|
13
|
+
<%= migration_action == 'add' ? 'remove' : 'add' %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'remove' %>, :<%= attribute.type %><% end %>
|
14
|
+
<%- end -%>
|
15
|
+
<%- end -%>
|
16
|
+
end
|
17
|
+
end
|
data/tasks/.gitkeep
ADDED
File without changes
|
@@ -0,0 +1,344 @@
|
|
1
|
+
namespace :db do
|
2
|
+
namespace :migrate do
|
3
|
+
desc "Migrate the database data and schema (options: VERSION=x, VERBOSE=false)."
|
4
|
+
task :with_data => :environment do
|
5
|
+
config = connect_to_database
|
6
|
+
next unless config
|
7
|
+
|
8
|
+
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
|
9
|
+
target_version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
|
10
|
+
migrations = []
|
11
|
+
|
12
|
+
if target_version.nil?
|
13
|
+
migrations = pending_migrations.map{ |m| m.merge(:direction =>:up) }
|
14
|
+
else
|
15
|
+
current_schema_version = ActiveRecord::Migrator.current_version
|
16
|
+
schema_migrations = if target_version > current_schema_version
|
17
|
+
pending_schema_migrations.keep_if{ |m| m[:version] <= target_version }.map{ |m| m.merge(:direction =>:up) }
|
18
|
+
elsif target_version < current_schema_version
|
19
|
+
past_migrations.keep_if{ |m| m[:version] > target_version }.map{ |m| m.merge(:direction =>:down) }
|
20
|
+
else # ==
|
21
|
+
[]
|
22
|
+
end
|
23
|
+
|
24
|
+
current_data_version = ActiveRecord::Migrator.current_version
|
25
|
+
data_migrations = if target_version > current_data_version
|
26
|
+
pending_data_migrations.keep_if{ |m| m[:version] <= target_version }.map{ |m| m.merge(:direction =>:up) }
|
27
|
+
elsif target_version < current_data_version
|
28
|
+
past_migrations.keep_if{ |m| m[:version] > target_version }.map{ |m| m.merge(:direction =>:down) }
|
29
|
+
else # ==
|
30
|
+
[]
|
31
|
+
end
|
32
|
+
migrations = if schema_migrations.empty?
|
33
|
+
data_migrations
|
34
|
+
elsif data_migrations.empty?
|
35
|
+
schema_migrations
|
36
|
+
elsif target_version > current_data_version && target_version > current_schema_version
|
37
|
+
sort_migrations data_migrations, schema_migrations
|
38
|
+
elsif target_version < current_data_version && target_version < current_schema_version
|
39
|
+
sort_migrations(data_migrations, schema_migrations).reverse
|
40
|
+
elsif target_version > current_data_version && target_version < current_schema_version
|
41
|
+
schema_migrations + data_migrations
|
42
|
+
elsif target_version < current_data_version && target_version > current_schema_version
|
43
|
+
schema_migrations + data_migrations
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
migrations.each do |migration|
|
48
|
+
if migration[:kind] == :data
|
49
|
+
ActiveRecord::Migration.write("== %s %s" % ['Data', "=" * 71])
|
50
|
+
DataMigrate::DataMigrator.run(migration[:direction], "db/data/", migration[:version])
|
51
|
+
else
|
52
|
+
ActiveRecord::Migration.write("== %s %s" % ['Schema', "=" * 69])
|
53
|
+
ActiveRecord::Migrator.run(migration[:direction], "db/migrate/", migration[:version])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
|
58
|
+
end
|
59
|
+
|
60
|
+
namespace :redo do
|
61
|
+
desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).'
|
62
|
+
task :with_data => :environment do
|
63
|
+
if ENV["VERSION"]
|
64
|
+
Rake::Task["db:migrate:down:with_data"].invoke
|
65
|
+
Rake::Task["db:migrate:up:with_data"].invoke
|
66
|
+
else
|
67
|
+
Rake::Task["db:rollback:with_data"].invoke
|
68
|
+
Rake::Task["db:migrate:with_data"].invoke
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
namespace :up do
|
74
|
+
desc 'Runs the "up" for a given migration VERSION. (options both=false)'
|
75
|
+
task :with_data => :environment do
|
76
|
+
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
|
77
|
+
raise "VERSION is required" unless version
|
78
|
+
config = connect_to_database
|
79
|
+
run_both = ENV["BOTH"] == "true"
|
80
|
+
migrations = pending_migrations.keep_if{|m| m[:version] == version}
|
81
|
+
|
82
|
+
unless run_both || migrations.size < 2
|
83
|
+
migrations = migrations.slice(0,1)
|
84
|
+
end
|
85
|
+
|
86
|
+
migrations.each do |migration|
|
87
|
+
if migration[:kind] == :data
|
88
|
+
ActiveRecord::Migration.write("== %s %s" % ['Data', "=" * 71])
|
89
|
+
DataMigrate::DataMigrator.run(:up, "db/data/", migration[:version])
|
90
|
+
else
|
91
|
+
ActiveRecord::Migration.write("== %s %s" % ['Schema', "=" * 69])
|
92
|
+
ActiveRecord::Migrator.run(:up, "db/migrate/", migration[:version])
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
namespace :down do
|
99
|
+
desc 'Runs the "down" for a given migration VERSION. (option BOTH=false)'
|
100
|
+
task :with_data => :environment do
|
101
|
+
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
|
102
|
+
raise "VERSION is required" unless version
|
103
|
+
config = connect_to_database
|
104
|
+
run_both = ENV["BOTH"] == "true"
|
105
|
+
migrations = past_migrations.keep_if{|m| m[:version] == version}
|
106
|
+
|
107
|
+
unless run_both || migrations.size < 2
|
108
|
+
migrations = migrations.slice(0,1)
|
109
|
+
end
|
110
|
+
|
111
|
+
migrations.each do |migration|
|
112
|
+
if migration[:kind] == :data
|
113
|
+
ActiveRecord::Migration.write("== %s %s" % ['Data', "=" * 71])
|
114
|
+
DataMigrate::DataMigrator.run(:down, "db/data/", migration[:version])
|
115
|
+
else
|
116
|
+
ActiveRecord::Migration.write("== %s %s" % ['Schema', "=" * 69])
|
117
|
+
ActiveRecord::Migrator.run(:down, "db/migrate/", migration[:version])
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
namespace :status do
|
124
|
+
desc "Display status of data and schema migrations"
|
125
|
+
task :with_data => :environment do
|
126
|
+
config = connect_to_database
|
127
|
+
next unless config
|
128
|
+
|
129
|
+
db_list_data = ActiveRecord::Base.connection.select_values("SELECT version FROM #{DataMigrate::DataMigrator.schema_migrations_table_name}")
|
130
|
+
db_list_schema = ActiveRecord::Base.connection.select_values("SELECT version FROM #{ActiveRecord::Migrator.schema_migrations_table_name}")
|
131
|
+
file_list = []
|
132
|
+
|
133
|
+
Dir.foreach(File.join(Rails.root, 'db', 'data')) do |file|
|
134
|
+
# only files matching "20091231235959_some_name.rb" pattern
|
135
|
+
if match_data = /(\d{14})_(.+)\.rb/.match(file)
|
136
|
+
status = db_list_data.delete(match_data[1]) ? 'up' : 'down'
|
137
|
+
file_list << [status, match_data[1], match_data[2], 'data']
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
Dir.foreach(File.join(Rails.root, 'db', 'migrate')) do |file|
|
142
|
+
# only files matching "20091231235959_some_name.rb" pattern
|
143
|
+
if match_data = /(\d{14})_(.+)\.rb/.match(file)
|
144
|
+
status = db_list_schema.delete(match_data[1]) ? 'up' : 'down'
|
145
|
+
file_list << [status, match_data[1], match_data[2], 'schema']
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
file_list.sort!{|a,b| "#{a[1]}_#{a[3] == 'data' ? 1 : 0}" <=> "#{b[1]}_#{b[3] == 'data' ? 1 : 0}" }
|
150
|
+
|
151
|
+
# output
|
152
|
+
puts "\ndatabase: #{config['database']}\n\n"
|
153
|
+
puts "#{"Status".center(8)} #{"Type".center(8)} #{"Migration ID".ljust(14)} Migration Name"
|
154
|
+
puts "-" * 60
|
155
|
+
file_list.each do |file|
|
156
|
+
puts "#{file[0].center(8)} #{file[3].center(8)} #{file[1].ljust(14)} #{file[2].humanize}"
|
157
|
+
end
|
158
|
+
db_list_schema.each do |version|
|
159
|
+
puts "#{'up'.center(8)} #{version.ljust(14)} *** NO SCHEMA FILE ***"
|
160
|
+
end
|
161
|
+
db_list_data.each do |version|
|
162
|
+
puts "#{'up'.center(8)} #{version.ljust(14)} *** NO DATA FILE ***"
|
163
|
+
end
|
164
|
+
puts
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end # END OF MIGRATE NAME SPACE
|
168
|
+
|
169
|
+
namespace :rollback do
|
170
|
+
desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
|
171
|
+
task :with_data => :environment do
|
172
|
+
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
|
173
|
+
config = connect_to_database
|
174
|
+
next unless config
|
175
|
+
past_migrations[0..(step - 1)].each do | past_migration |
|
176
|
+
if past_migration[:kind] == :data
|
177
|
+
ActiveRecord::Migration.write("== %s %s" % ['Data', "=" * 71])
|
178
|
+
DataMigrate::DataMigrator.run(:down, "db/data/", past_migration[:version])
|
179
|
+
elsif past_migration[:kind] == :schema
|
180
|
+
ActiveRecord::Migration.write("== %s %s" % ['Schema', "=" * 69])
|
181
|
+
ActiveRecord::Migrator.run(:down, "db/migrate/", past_migration[:version])
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
namespace :forward do
|
188
|
+
desc 'Pushes the schema to the next version (specify steps w/ STEP=n).'
|
189
|
+
task :with_data => :environment do
|
190
|
+
# TODO: No worky for .forward
|
191
|
+
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
|
192
|
+
# DataMigrate::DataMigrator.forward('db/data/', step)
|
193
|
+
migrations = pending_migrations.reverse.pop(step).reverse
|
194
|
+
migrations.each do | pending_migration |
|
195
|
+
if pending_migration[:kind] == :data
|
196
|
+
ActiveRecord::Migration.write("== %s %s" % ['Data', "=" * 71])
|
197
|
+
DataMigrate::DataMigrator.run(:up, "db/data/", pending_migration[:version])
|
198
|
+
elsif pending_migration[:kind] == :schema
|
199
|
+
ActiveRecord::Migration.write("== %s %s" % ['Schema', "=" * 69])
|
200
|
+
ActiveRecord::Migrator.run(:up, "db/migrate/", pending_migration[:version])
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
namespace :version do
|
207
|
+
desc "Retrieves the current schema version numbers for data and schema migrations"
|
208
|
+
task :with_data => :environment do
|
209
|
+
puts "Current Schema version: #{ActiveRecord::Migrator.current_version}"
|
210
|
+
puts "Current Data version: #{DataMigrate::DataMigrator.current_version}"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
namespace :data do
|
216
|
+
task :migrate => :environment do
|
217
|
+
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
|
218
|
+
DataMigrate::DataMigrator.migrate("db/data/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
|
219
|
+
end
|
220
|
+
|
221
|
+
namespace :migrate do
|
222
|
+
desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).'
|
223
|
+
task :redo => :environment do
|
224
|
+
if ENV["VERSION"]
|
225
|
+
Rake::Task["data:migrate:down"].invoke
|
226
|
+
Rake::Task["data:migrate:up"].invoke
|
227
|
+
else
|
228
|
+
Rake::Task["data:rollback"].invoke
|
229
|
+
Rake::Task["data:migrate"].invoke
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
desc 'Runs the "up" for a given migration VERSION.'
|
234
|
+
task :up => :environment do
|
235
|
+
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
|
236
|
+
raise "VERSION is required" unless version
|
237
|
+
DataMigrate::DataMigrator.run(:up, "db/data/", version)
|
238
|
+
end
|
239
|
+
|
240
|
+
desc 'Runs the "down" for a given migration VERSION.'
|
241
|
+
task :down => :environment do
|
242
|
+
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
|
243
|
+
raise "VERSION is required" unless version
|
244
|
+
DataMigrate::DataMigrator.run(:down, "db/data/", version)
|
245
|
+
end
|
246
|
+
|
247
|
+
desc "Display status of data migrations"
|
248
|
+
task :status => :environment do
|
249
|
+
config = ActiveRecord::Base.configurations[Rails.env || 'development']
|
250
|
+
ActiveRecord::Base.establish_connection(config)
|
251
|
+
unless ActiveRecord::Base.connection.table_exists?(DataMigrate::DataMigrator.schema_migrations_table_name)
|
252
|
+
puts 'Data migrations table does not exist yet.'
|
253
|
+
next # means "return" for rake task
|
254
|
+
end
|
255
|
+
db_list = ActiveRecord::Base.connection.select_values("SELECT version FROM #{DataMigrate::DataMigrator.schema_migrations_table_name}")
|
256
|
+
file_list = []
|
257
|
+
Dir.foreach(File.join(Rails.root, 'db', 'data')) do |file|
|
258
|
+
# only files matching "20091231235959_some_name.rb" pattern
|
259
|
+
if match_data = /(\d{14})_(.+)\.rb/.match(file)
|
260
|
+
status = db_list.delete(match_data[1]) ? 'up' : 'down'
|
261
|
+
file_list << [status, match_data[1], match_data[2]]
|
262
|
+
end
|
263
|
+
end
|
264
|
+
# output
|
265
|
+
puts "\ndatabase: #{config['database']}\n\n"
|
266
|
+
puts "#{"Status".center(8)} #{"Migration ID".ljust(14)} Migration Name"
|
267
|
+
puts "-" * 50
|
268
|
+
file_list.each do |file|
|
269
|
+
puts "#{file[0].center(8)} #{file[1].ljust(14)} #{file[2].humanize}"
|
270
|
+
end
|
271
|
+
db_list.each do |version|
|
272
|
+
puts "#{'up'.center(8)} #{version.ljust(14)} *** NO FILE ***"
|
273
|
+
end
|
274
|
+
puts
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
|
279
|
+
task :rollback => :environment do
|
280
|
+
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
|
281
|
+
DataMigrate::DataMigrator.rollback('db/data/', step)
|
282
|
+
end
|
283
|
+
|
284
|
+
desc 'Pushes the schema to the next version (specify steps w/ STEP=n).'
|
285
|
+
task :forward => :environment do
|
286
|
+
# TODO: No worky for .forward
|
287
|
+
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
|
288
|
+
# DataMigrate::DataMigrator.forward('db/data/', step)
|
289
|
+
migrations = pending_data_migrations.reverse.pop(step).reverse
|
290
|
+
migrations.each do | pending_migration |
|
291
|
+
DataMigrate::DataMigrator.run(:up, "db/data/", pending_migration[:version])
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
desc "Retrieves the current schema version number for data migrations"
|
296
|
+
task :version => :environment do
|
297
|
+
puts "Current data version: #{DataMigrate::DataMigrator.current_version}"
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
def pending_migrations
|
302
|
+
sort_migrations pending_data_migrations, pending_schema_migrations
|
303
|
+
end
|
304
|
+
|
305
|
+
def pending_data_migrations
|
306
|
+
sort_migrations DataMigrate::DataMigrator.new(:up, 'db/data').pending_migrations.map{|m| { :version => m.version, :kind => :data }}
|
307
|
+
end
|
308
|
+
|
309
|
+
def pending_schema_migrations
|
310
|
+
sort_migrations ActiveRecord::Migrator.new(:up, 'db/migrate').pending_migrations.map{|m| { :version => m.version, :kind => :schema }}
|
311
|
+
end
|
312
|
+
|
313
|
+
def sort_migrations set_1, set_2=nil
|
314
|
+
migrations = set_1 + (set_2 || [])
|
315
|
+
migrations.sort{|a,b| sort_string(a) <=> sort_string(b)}
|
316
|
+
end
|
317
|
+
|
318
|
+
def sort_string migration
|
319
|
+
"#{migration[:version]}_#{migration[:kind] == :data ? 1 : 0}"
|
320
|
+
end
|
321
|
+
|
322
|
+
def connect_to_database
|
323
|
+
config = ActiveRecord::Base.configurations[Rails.env || 'development']
|
324
|
+
ActiveRecord::Base.establish_connection(config)
|
325
|
+
|
326
|
+
unless ActiveRecord::Base.connection.table_exists?(DataMigrate::DataMigrator.schema_migrations_table_name)
|
327
|
+
puts 'Data migrations table does not exist yet.'
|
328
|
+
config = nil
|
329
|
+
end
|
330
|
+
unless ActiveRecord::Base.connection.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
|
331
|
+
puts 'Schema migrations table does not exist yet.'
|
332
|
+
config = nil
|
333
|
+
end
|
334
|
+
config
|
335
|
+
end
|
336
|
+
|
337
|
+
def past_migrations sort=nil
|
338
|
+
sort = sort.downcase if sort
|
339
|
+
db_list_data = ActiveRecord::Base.connection.select_values("SELECT version FROM #{DataMigrate::DataMigrator.schema_migrations_table_name}").sort
|
340
|
+
db_list_schema = ActiveRecord::Base.connection.select_values("SELECT version FROM #{ActiveRecord::Migrator.schema_migrations_table_name}").sort
|
341
|
+
migrations = db_list_data.map{|d| {:version => d.to_i, :kind => :data }} + db_list_schema.map{|d| {:version => d.to_i, :kind => :schema }}
|
342
|
+
|
343
|
+
sort == 'asc' ? sort_migrations(migrations) : sort_migrations(migrations).reverse
|
344
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: data_migrate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 1.0.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Andrew J Vargo
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-05-02 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rails
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 3.0.0
|
24
|
+
- - <=
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.0.7
|
27
|
+
type: :runtime
|
28
|
+
version_requirements: *id001
|
29
|
+
description: Rake tasks to migrate data alongside schema changes.
|
30
|
+
email:
|
31
|
+
- ajvargo@computer.org
|
32
|
+
executables: []
|
33
|
+
|
34
|
+
extensions: []
|
35
|
+
|
36
|
+
extra_rdoc_files: []
|
37
|
+
|
38
|
+
files:
|
39
|
+
- .gitignore
|
40
|
+
- Gemfile
|
41
|
+
- LICENSE
|
42
|
+
- README.md
|
43
|
+
- Rakefile
|
44
|
+
- data_migrate.gemspec
|
45
|
+
- lib/data_migrate.rb
|
46
|
+
- lib/data_migrate/data_migrator.rb
|
47
|
+
- lib/data_migrate/railtie.rb
|
48
|
+
- lib/data_migrate/version.rb
|
49
|
+
- lib/generators/data_migrate.rb
|
50
|
+
- lib/generators/data_migrate/USAGE
|
51
|
+
- lib/generators/data_migrate/install/install_generator.rb
|
52
|
+
- lib/generators/data_migrate/install/templates/install_migration.rb
|
53
|
+
- lib/generators/data_migration/data_migration_generator.rb
|
54
|
+
- lib/generators/data_migration/templates/data_migration.rb
|
55
|
+
- lib/generators/data_migration/templates/migration.rb
|
56
|
+
- tasks/.gitkeep
|
57
|
+
- tasks/databases.rake
|
58
|
+
homepage: http://ajvargo.com
|
59
|
+
licenses: []
|
60
|
+
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: "0"
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: "0"
|
78
|
+
requirements: []
|
79
|
+
|
80
|
+
rubyforge_project: data_migrate
|
81
|
+
rubygems_version: 1.7.2
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: Rake tasks to migrate data alongside schema changes.
|
85
|
+
test_files: []
|
86
|
+
|