migrer 0.0.1
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.
- data/.gitignore +20 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +103 -0
- data/Rakefile +1 -0
- data/db/migrate/20121229002105_create_migrer_table.rb +8 -0
- data/lib/data_migration.rb +26 -0
- data/lib/generators/data_migration/USAGE +11 -0
- data/lib/generators/data_migration/data_migration_generator.rb +16 -0
- data/lib/generators/data_migration/templates/data_migration.rb +8 -0
- data/lib/migrer/engine.rb +7 -0
- data/lib/migrer/version.rb +3 -0
- data/lib/migrer.rb +7 -0
- data/lib/tasks/migrer.rake +155 -0
- data/migrer.gemspec +23 -0
- data/script/rails +8 -0
- metadata +80 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Sathya Sekaran
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# Migrer
|
2
|
+
#### The polite data migration valet.
|
3
|
+
|
4
|
+
### What's with the name?
|
5
|
+
|
6
|
+
Migrer (me-gray) is French for migrate. My wife suggested the name, and it was much
|
7
|
+
less obnoxious than `morphin_time` or `flying_v`, say. - @sfsekaran
|
8
|
+
|
9
|
+
## What is it?
|
10
|
+
|
11
|
+
Migrer creates migration-like tasks for running application scripts. It is primarily useful for updating database
|
12
|
+
records or running one-time tasks against separate environments.
|
13
|
+
|
14
|
+
**What is the difference between this and ActiveRecord migrations?**
|
15
|
+
|
16
|
+
Migrations should always be stable and are primarily for altering the structure of a database. Migrer is intended for
|
17
|
+
data migrations, or manipulating the data within that structure. Such tasks can become error-prone since they depend on
|
18
|
+
the state of models in the codebase, and are best not included in ActiveRecord migrations.
|
19
|
+
|
20
|
+
**Why not just create a regular rake task or use a script runner?**
|
21
|
+
|
22
|
+
Although Migrer data migrations can be run multiple times, they are primarily suited for one-time tasks (and often for
|
23
|
+
tasks that are not intended to be run more than once). Migrer not only creates a structure for these one-time tasks,
|
24
|
+
but also keeps track of which data migrations have already been processed. This is especially useful when managing
|
25
|
+
such tasks among multiple environments.
|
26
|
+
|
27
|
+
## Installation
|
28
|
+
|
29
|
+
Add this line to your application's Gemfile:
|
30
|
+
|
31
|
+
gem 'migrer'
|
32
|
+
|
33
|
+
And then execute:
|
34
|
+
|
35
|
+
$ bundle
|
36
|
+
|
37
|
+
Or install it yourself as:
|
38
|
+
|
39
|
+
$ gem install migrer
|
40
|
+
|
41
|
+
And install database migrations like so:
|
42
|
+
|
43
|
+
$ bundle exec rake railties:install:migrations FROM=migrer
|
44
|
+
$ bundle exec rake db:migrate
|
45
|
+
|
46
|
+
## Usage
|
47
|
+
|
48
|
+
# 1) Create the data migration
|
49
|
+
|
50
|
+
Create a data migration:
|
51
|
+
|
52
|
+
$ bundle exec rails generate MyFirstDataMigration "optional description"
|
53
|
+
|
54
|
+
This will create the file: lib/tasks/data_migrations/<timestamp>_my_first_data_migration.rb
|
55
|
+
|
56
|
+
Open this file and replace "TODO" with your data migration code!
|
57
|
+
|
58
|
+
# 2) Run a task
|
59
|
+
|
60
|
+
Unless you have the RAILS_ENV environment variable already set, prepend this to all of the following commands (replace
|
61
|
+
<environment> with the correct Rails environment (development, staging, production, etc.):
|
62
|
+
|
63
|
+
RAILS_ENV=<environment>
|
64
|
+
|
65
|
+
All the following commands will ask for confirmation before executing.
|
66
|
+
|
67
|
+
**Run all unprocessed data migrations:**
|
68
|
+
|
69
|
+
bundle exec rake data:migrate
|
70
|
+
|
71
|
+
**Run a single data migration (<version> is the timestamp at the beginning of the data migration file, just like
|
72
|
+
ActiveRecord migrations):**
|
73
|
+
|
74
|
+
bundle exec rake data:migration VERSION=<version>
|
75
|
+
|
76
|
+
**Mark all data migrations as already processed:**
|
77
|
+
|
78
|
+
bundle exec rake data:mark_all
|
79
|
+
|
80
|
+
**Mark a single data migration as already processed:**
|
81
|
+
|
82
|
+
bundle exec rake data:mark VERSION=<version>
|
83
|
+
|
84
|
+
**Mark all data migrations as unprocessed (so they are included again when running data:migrate):**
|
85
|
+
|
86
|
+
bundle exec rake data:unmark_all
|
87
|
+
|
88
|
+
**Mark a single data migration as unprocessed:**
|
89
|
+
|
90
|
+
bundle exec rake data:unmark VERSION=<version>
|
91
|
+
|
92
|
+
## Contributing
|
93
|
+
|
94
|
+
1. Fork it
|
95
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
96
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
97
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
98
|
+
5. Create new Pull Request
|
99
|
+
|
100
|
+
## Contributors
|
101
|
+
|
102
|
+
* *Sathya Sekaran* ([sfsekaran](https://github.com/sfsekaran))
|
103
|
+
* *Michael Durnhofer* ([mdurn](https://github.com/mdurn))
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class DataMigration
|
2
|
+
def self.all
|
3
|
+
filenames = Dir.entries("#{Rails.root}/lib/tasks/data_migrations").select { |f| /^\d+.*\.rb$/ === f }
|
4
|
+
data_migrations = {}
|
5
|
+
|
6
|
+
filenames.each do |f|
|
7
|
+
match_data = /^(?<version>\d+)_(?<name>.+)\.rb$/.match(f)
|
8
|
+
|
9
|
+
record = ActiveRecord::Base.connection.execute(
|
10
|
+
"SELECT * FROM data_migration_versions WHERE version = #{match_data[:version]}").first
|
11
|
+
|
12
|
+
data_migrations.merge!(
|
13
|
+
match_data[:version] => {
|
14
|
+
basefilename: "#{match_data[:version]}_#{match_data[:name]}",
|
15
|
+
class_name: match_data[:name].classify,
|
16
|
+
filename: "#{match_data[:version]}_#{match_data[:name]}.rb",
|
17
|
+
name: match_data[:name],
|
18
|
+
processed: (record != nil),
|
19
|
+
created_at: record.try(:[], :created_at),
|
20
|
+
updated_at: record.try(:[], :updated_at)
|
21
|
+
})
|
22
|
+
end
|
23
|
+
|
24
|
+
data_migrations
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Description:
|
2
|
+
Data migration generator
|
3
|
+
|
4
|
+
Optional Arguments:
|
5
|
+
DESCRIPTION # a commented description in the created data migration file
|
6
|
+
|
7
|
+
Example:
|
8
|
+
rails generate data_migration DataMigrationName "Optional description"
|
9
|
+
|
10
|
+
This will create:
|
11
|
+
lib/tasks/data_migrations/<timestamp>_data_migration_name.rb
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class DataMigrationGenerator < Rails::Generators::NamedBase
|
2
|
+
require "rails/generators/active_record"
|
3
|
+
|
4
|
+
source_root File.expand_path('../templates', __FILE__)
|
5
|
+
argument :description, type: :string, default: nil, required: false
|
6
|
+
|
7
|
+
def generate_data_migration
|
8
|
+
template "data_migration.rb",
|
9
|
+
"lib/tasks/data_migrations/#{file_name}"
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
def file_name
|
14
|
+
"#{ActiveRecord::Generators::Base.next_migration_number('lib/tasks/data_migrations')}_#{name.underscore}.rb"
|
15
|
+
end
|
16
|
+
end
|
data/lib/migrer.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'data_migration'
|
2
|
+
|
3
|
+
namespace :data do
|
4
|
+
desc "Data migration tasks"
|
5
|
+
|
6
|
+
task migrate: :environment do
|
7
|
+
data_migrations = DataMigration.all
|
8
|
+
|
9
|
+
if (version = ENV['VERSION'])
|
10
|
+
data_migration = data_migrations[version]
|
11
|
+
|
12
|
+
if data_migration.present?
|
13
|
+
if data_migration[:processed]
|
14
|
+
puts "Data migration already processed. Do you want to run it anyway? (responses other than 'yes' will exit)"
|
15
|
+
else
|
16
|
+
puts "Starting data migration #{data_migration[:class_name]}. Do you wish to continue? (responses other than 'yes' will exit)"
|
17
|
+
end
|
18
|
+
|
19
|
+
prompt = $stdin.gets.chomp
|
20
|
+
|
21
|
+
if prompt == "yes"
|
22
|
+
puts "#{data_migration[:class_name]}: migrating"
|
23
|
+
t_start = Time.now
|
24
|
+
|
25
|
+
require "#{Rails.root}/lib/tasks/data_migrations/#{data_migration[:basefilename]}"
|
26
|
+
eval(data_migration[:class_name]).run
|
27
|
+
|
28
|
+
t_end = Time.now
|
29
|
+
|
30
|
+
unless data_migration[:processed]
|
31
|
+
ActiveRecord::Base.connection.execute(
|
32
|
+
"INSERT INTO data_migration_versions
|
33
|
+
VALUES (NULL, #{version}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)" )
|
34
|
+
end
|
35
|
+
|
36
|
+
puts "#{data_migration[:class_name]}: migrated (#{t_end - t_start}s)"
|
37
|
+
end
|
38
|
+
else
|
39
|
+
puts "No data migration found matching version: #{version}"
|
40
|
+
end
|
41
|
+
else
|
42
|
+
data_migrations.each do |k, v|
|
43
|
+
unless v[:processed]
|
44
|
+
puts "Starting data migration #{v[:class_name]}. Do you wish to continue? (responses other than 'yes' will exit)"
|
45
|
+
prompt = $stdin.gets.chomp
|
46
|
+
if prompt == "yes"
|
47
|
+
puts "#{v[:class_name]}: migrating"
|
48
|
+
t_start = Time.now
|
49
|
+
|
50
|
+
require "#{Rails.root}/lib/tasks/data_migrations/#{v[:basefilename]}"
|
51
|
+
eval(v[:class_name]).run
|
52
|
+
|
53
|
+
t_end = Time.now
|
54
|
+
|
55
|
+
ActiveRecord::Base.connection.execute(
|
56
|
+
"INSERT INTO data_migration_versions
|
57
|
+
VALUES (NULL, #{k}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)" )
|
58
|
+
|
59
|
+
puts "#{v[:class_name]}: migrated (#{t_end - t_start}s)"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
task mark: :environment do
|
67
|
+
data_migrations = DataMigration.all
|
68
|
+
|
69
|
+
if (version = ENV['VERSION'])
|
70
|
+
data_migration = data_migrations[version]
|
71
|
+
|
72
|
+
if data_migration.present?
|
73
|
+
if data_migration[:processed]
|
74
|
+
puts "Data migration already processed."
|
75
|
+
else
|
76
|
+
puts "Data migration #{data_migration[:class_name]} will be marked as processed. Continue? (responses other than 'yes' will exit)"
|
77
|
+
|
78
|
+
prompt = $stdin.gets.chomp
|
79
|
+
|
80
|
+
if prompt == "yes"
|
81
|
+
ActiveRecord::Base.connection.execute(
|
82
|
+
"INSERT INTO data_migration_versions
|
83
|
+
VALUES (NULL, #{version}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)" )
|
84
|
+
|
85
|
+
puts "#{data_migration[:class_name]}: marked as migrated"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
else
|
89
|
+
puts "No data migration found matching version: #{version}"
|
90
|
+
end
|
91
|
+
else
|
92
|
+
puts "VERSION must be supplied."
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
task mark_all: :environment do
|
97
|
+
unprocessed_data_migrations = DataMigration.all.select { |k, v| !v[:processed] }
|
98
|
+
|
99
|
+
puts "This will mark all data migrations as already processed. Continue? (responses other than 'yes' will exit)"
|
100
|
+
|
101
|
+
prompt = $stdin.gets.chomp
|
102
|
+
if prompt == "yes"
|
103
|
+
unprocessed_data_migrations.each do |k, v|
|
104
|
+
ActiveRecord::Base.connection.execute(
|
105
|
+
"INSERT INTO data_migration_versions
|
106
|
+
VALUES (NULL, #{k}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)" )
|
107
|
+
|
108
|
+
puts "#{v[:class_name]}: marked as migrated"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
task unmark: :environment do
|
114
|
+
data_migrations = DataMigration.all
|
115
|
+
|
116
|
+
if (version = ENV['VERSION'])
|
117
|
+
data_migration = data_migrations[version]
|
118
|
+
|
119
|
+
if data_migration.present?
|
120
|
+
if !data_migration[:processed]
|
121
|
+
puts "Data migration not yet processed."
|
122
|
+
else
|
123
|
+
puts "Data migration #{data_migration[:class_name]} will be unmarked as processed. Continue? (responses other than 'yes' will exit)"
|
124
|
+
|
125
|
+
prompt = $stdin.gets.chomp
|
126
|
+
|
127
|
+
if prompt == "yes"
|
128
|
+
ActiveRecord::Base.connection.execute(
|
129
|
+
"DELETE FROM data_migration_versions
|
130
|
+
WHERE version = #{version}" )
|
131
|
+
|
132
|
+
puts "#{data_migration[:class_name]}: unmarked as migrated"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
else
|
136
|
+
puts "No data migration found matching version: #{version}"
|
137
|
+
end
|
138
|
+
else
|
139
|
+
puts "VERSION must be supplied."
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
task unmark_all: :environment do
|
144
|
+
puts "All data migrations will be unmarked as processed. Continue? (responses other than 'yes' will exit)"
|
145
|
+
|
146
|
+
prompt = $stdin.gets.chomp
|
147
|
+
|
148
|
+
if prompt == "yes"
|
149
|
+
ActiveRecord::Base.connection.execute(
|
150
|
+
"DELETE FROM data_migration_versions" )
|
151
|
+
|
152
|
+
puts "Data migration records cleared"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
data/migrer.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'migrer/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "migrer"
|
8
|
+
gem.version = Migrer::VERSION
|
9
|
+
gem.authors = ["Sathya Sekaran", "Michael Durnhofer"]
|
10
|
+
gem.email = ["sfsekaran@gmail.com"]
|
11
|
+
gem.description = %q{The polite data migration valet.}
|
12
|
+
gem.summary = %q{The 'migrer' gem helps generate, execute, and keep track of data migrations.}
|
13
|
+
gem.homepage = "http://github.com/sfsekaran/migrer"
|
14
|
+
|
15
|
+
gem.rubyforge_project = "migrer"
|
16
|
+
|
17
|
+
gem.files = `git ls-files`.split($/)
|
18
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
19
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
20
|
+
gem.require_paths = ["lib", "script", "db"]
|
21
|
+
|
22
|
+
gem.add_runtime_dependency "activerecord", "~> 3.2.2"
|
23
|
+
end
|
data/script/rails
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
|
3
|
+
|
4
|
+
ENGINE_ROOT = File.expand_path('../..', __FILE__)
|
5
|
+
ENGINE_PATH = File.expand_path('../../lib/migrer/engine', __FILE__)
|
6
|
+
|
7
|
+
require 'rails/all'
|
8
|
+
require 'rails/engine/commands'
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: migrer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Sathya Sekaran
|
9
|
+
- Michael Durnhofer
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2013-01-12 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activerecord
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 3.2.2
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: 3.2.2
|
31
|
+
description: The polite data migration valet.
|
32
|
+
email:
|
33
|
+
- sfsekaran@gmail.com
|
34
|
+
executables: []
|
35
|
+
extensions: []
|
36
|
+
extra_rdoc_files: []
|
37
|
+
files:
|
38
|
+
- .gitignore
|
39
|
+
- Gemfile
|
40
|
+
- LICENSE.txt
|
41
|
+
- README.md
|
42
|
+
- Rakefile
|
43
|
+
- db/migrate/20121229002105_create_migrer_table.rb
|
44
|
+
- lib/data_migration.rb
|
45
|
+
- lib/generators/data_migration/USAGE
|
46
|
+
- lib/generators/data_migration/data_migration_generator.rb
|
47
|
+
- lib/generators/data_migration/templates/data_migration.rb
|
48
|
+
- lib/migrer.rb
|
49
|
+
- lib/migrer/engine.rb
|
50
|
+
- lib/migrer/version.rb
|
51
|
+
- lib/tasks/migrer.rake
|
52
|
+
- migrer.gemspec
|
53
|
+
- script/rails
|
54
|
+
homepage: http://github.com/sfsekaran/migrer
|
55
|
+
licenses: []
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
- script
|
61
|
+
- db
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ! '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ! '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubyforge_project: migrer
|
76
|
+
rubygems_version: 1.8.24
|
77
|
+
signing_key:
|
78
|
+
specification_version: 3
|
79
|
+
summary: The 'migrer' gem helps generate, execute, and keep track of data migrations.
|
80
|
+
test_files: []
|