migrer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .idea/
6
+ .yardoc
7
+ .rvmrc
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ _yardoc
11
+ coverage
12
+ doc/
13
+ lib/bundler/man
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test_app
18
+ test/tmp
19
+ test/version_tmp
20
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in migrer.gemspec
4
+ gemspec
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 (&lt;version&gt; 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,8 @@
1
+ class CreateMigrerTable < ActiveRecord::Migration
2
+ def change
3
+ create_table :data_migration_versions do |t|
4
+ t.string :version
5
+ t.timestamps
6
+ end
7
+ end
8
+ end
@@ -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
@@ -0,0 +1,8 @@
1
+ class <%= name %> < DataMigration
2
+
3
+ <%= "# #{description}" %>
4
+
5
+ def self.run
6
+ #TODO: data_migration code
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ module Migrer
2
+
3
+ class Engine < ::Rails::Engine
4
+ isolate_namespace Migrer
5
+ end
6
+
7
+ end
@@ -0,0 +1,3 @@
1
+ module Migrer
2
+ VERSION = "0.0.1"
3
+ end
data/lib/migrer.rb ADDED
@@ -0,0 +1,7 @@
1
+ require "migrer/version"
2
+
3
+ module Migrer
4
+ end
5
+
6
+ # Require our engine
7
+ require "migrer/engine"
@@ -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: []