nonschema_migrations 1.0.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.
- checksums.yaml +7 -0
- data/lib/active_record/data_migration.rb +52 -0
- data/lib/generators/data_migration_generator.rb +28 -0
- data/lib/generators/data_migrations/install_generator.rb +22 -0
- data/lib/generators/data_migrations/templates/create_data_migrations.rb +11 -0
- data/lib/nonschema_migrations/railtie.rb +9 -0
- data/lib/nonschema_migrations.rb +11 -0
- data/lib/nonschema_migrator.rb +161 -0
- data/lib/tasks/data.rb +34 -0
- metadata +66 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: da4a766ada05593da5379df6b6dc1fde7b18aae17138f07f458dab54784f019f
|
4
|
+
data.tar.gz: c7d572cd3a1b3a37b2d6aa795efeb7939731ccbd9e2d14bee4d89ef1f447d74a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 17fb2d645b46f83ddb25c069e8e2beeae6eb80d7821a0abe4f65386897644a075e54c88088d09f91cbd31f157eba63b547225891f93a9097d3bd63a42cfa8249
|
7
|
+
data.tar.gz: 83daa3c35a73bc96fbb26953e554a173f1079ea040b8737c6764b107efb7d2d8501dce9f337adf9cff4cc3a48bfcc7e7f787e37b9d0cc1da224234b8a76cdae1
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'active_record/scoping/default'
|
2
|
+
require 'active_record/scoping/named'
|
3
|
+
require 'active_record/base'
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
class DataMigration < ActiveRecord::Base
|
7
|
+
class << self
|
8
|
+
def primary_key
|
9
|
+
nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def table_name
|
13
|
+
"#{table_name_prefix}data_migrations#{table_name_suffix}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def index_name
|
17
|
+
"#{table_name_prefix}unique_data_migrations#{table_name_suffix}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def table_exists?
|
21
|
+
connection.table_exists?(table_name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def create_table(limit=nil)
|
25
|
+
unless table_exists?
|
26
|
+
version_options = {null: false}
|
27
|
+
version_options[:limit] = limit if limit
|
28
|
+
|
29
|
+
connection.create_table(table_name, id: false) do |t|
|
30
|
+
t.column :version, :string, version_options
|
31
|
+
end
|
32
|
+
connection.add_index table_name, :version, unique: true, name: index_name
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def drop_table
|
37
|
+
if table_exists?
|
38
|
+
connection.remove_index table_name, name: index_name
|
39
|
+
connection.drop_table(table_name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def normalize_migration_number(number)
|
44
|
+
"%.3d" % number.to_i
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def version
|
49
|
+
super.to_i
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
# this is the generator used to create a data migration
|
3
|
+
|
4
|
+
class DataMigrationGenerator < ActiveRecord::Generators::Base
|
5
|
+
self.source_root(File.join(self.superclass.base_root,'active_record/migration/templates'))
|
6
|
+
|
7
|
+
desc <<-DESC
|
8
|
+
Description:
|
9
|
+
Creates new nondestructive migration
|
10
|
+
DESC
|
11
|
+
|
12
|
+
def create_data_migration_file
|
13
|
+
migration_template "migration.rb", "db/data_migrate/#{file_name}.rb"
|
14
|
+
end
|
15
|
+
|
16
|
+
# 'migration.rb' now requires this var/method
|
17
|
+
def migration_action
|
18
|
+
'create'
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# this used in migration template which is inherited, we want this values to be an empty array.
|
24
|
+
def attributes
|
25
|
+
[]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
require 'rails/generators/active_record'
|
4
|
+
|
5
|
+
module DataMigrations
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
7
|
+
include Rails::Generators::Migration
|
8
|
+
source_root File.expand_path("../templates", __FILE__)
|
9
|
+
|
10
|
+
desc "Creates an initializer and copy files to your application."
|
11
|
+
class_option :orm, type: 'boolean'
|
12
|
+
|
13
|
+
def self.next_migration_number(path)
|
14
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
15
|
+
end
|
16
|
+
|
17
|
+
def copy_initializer
|
18
|
+
migration_template "create_data_migrations.rb",
|
19
|
+
"db/migrate/create_data_migrations.rb"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
|
2
|
+
MIGRATIONS_PATH = 'db/data_migrate'
|
3
|
+
|
4
|
+
require 'generators/data_migrations/install_generator.rb'
|
5
|
+
require 'generators/data_migration_generator.rb'
|
6
|
+
require 'active_record/data_migration.rb'
|
7
|
+
require 'nonschema_migrator.rb'
|
8
|
+
|
9
|
+
module NondestructiveMigrations
|
10
|
+
require "nonschema_migrations/railtie.rb" if defined?(Rails)
|
11
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
class NondestructiveMigrator < ActiveRecord::Migrator
|
5
|
+
# This class related to data migration.
|
6
|
+
# Used in rake tasks (rake data:[migrate|rollback|up|down])
|
7
|
+
|
8
|
+
if defined?(ActiveRecord::MigrationContext)
|
9
|
+
class SchemaMigration < ActiveRecord::SchemaMigration
|
10
|
+
def self.table_name
|
11
|
+
NondestructiveMigrator.schema_migrations_table_name
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class MigrationContext < ActiveRecord::MigrationContext
|
16
|
+
def initialize(migrations_paths)
|
17
|
+
super(migrations_paths)
|
18
|
+
@schema_migration = NondestructiveMigrator::SchemaMigration
|
19
|
+
end
|
20
|
+
|
21
|
+
def new_migrator(*args)
|
22
|
+
result = NondestructiveMigrator.new(*args)
|
23
|
+
result.migration_context = self
|
24
|
+
result
|
25
|
+
end
|
26
|
+
|
27
|
+
# these methods are copied from ActiveRecord::Migrator
|
28
|
+
# replaced:
|
29
|
+
# 1.) ActiveRecord::SchemaMigration with @schema_migration
|
30
|
+
# 2.) ActiveRecord::Migrator.new with new_migrator
|
31
|
+
|
32
|
+
def get_all_versions
|
33
|
+
if @schema_migration.table_exists?
|
34
|
+
@schema_migration.all_versions.map(&:to_i)
|
35
|
+
else
|
36
|
+
[]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def migrations_status
|
41
|
+
db_list = @schema_migration.normalized_versions
|
42
|
+
|
43
|
+
file_list = migration_files.map do |file|
|
44
|
+
version, name, scope = parse_migration_filename(file)
|
45
|
+
raise IllegalMigrationNameError.new(file) unless version
|
46
|
+
version = @schema_migration.normalize_migration_number(version)
|
47
|
+
status = db_list.delete(version) ? "up" : "down"
|
48
|
+
[status, version, (name + scope).humanize]
|
49
|
+
end.compact
|
50
|
+
|
51
|
+
db_list.map! do |version|
|
52
|
+
["up", version, "********** NO FILE **********"]
|
53
|
+
end
|
54
|
+
|
55
|
+
(db_list + file_list).sort_by { |_, version, _| version }
|
56
|
+
end
|
57
|
+
|
58
|
+
def move(direction, steps)
|
59
|
+
migrator = new_migrator(direction, migrations)
|
60
|
+
|
61
|
+
if current_version != 0 && !migrator.current_migration
|
62
|
+
raise UnknownMigrationVersionError.new(current_version)
|
63
|
+
end
|
64
|
+
|
65
|
+
start_index =
|
66
|
+
if current_version == 0
|
67
|
+
0
|
68
|
+
else
|
69
|
+
migrator.migrations.index(migrator.current_migration)
|
70
|
+
end
|
71
|
+
|
72
|
+
finish = migrator.migrations[start_index + steps]
|
73
|
+
version = finish ? finish.version : 0
|
74
|
+
send(direction, version)
|
75
|
+
end
|
76
|
+
|
77
|
+
def up(target_version = nil)
|
78
|
+
selected_migrations = if block_given?
|
79
|
+
migrations.select { |m| yield m }
|
80
|
+
else
|
81
|
+
migrations
|
82
|
+
end
|
83
|
+
|
84
|
+
new_migrator(:up, selected_migrations, target_version).migrate
|
85
|
+
end
|
86
|
+
|
87
|
+
def down(target_version = nil)
|
88
|
+
selected_migrations = if block_given?
|
89
|
+
migrations.select { |m| yield m }
|
90
|
+
else
|
91
|
+
migrations
|
92
|
+
end
|
93
|
+
|
94
|
+
new_migrator(:down, selected_migrations, target_version).migrate
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class << self
|
99
|
+
def context(path)
|
100
|
+
NondestructiveMigrator::MigrationContext.new(path)
|
101
|
+
end
|
102
|
+
|
103
|
+
def new_migrator(path, *args)
|
104
|
+
result = self.new(*args)
|
105
|
+
result.migration_context=context(path)
|
106
|
+
result
|
107
|
+
end
|
108
|
+
|
109
|
+
def migrate(path)
|
110
|
+
context(path).migrate()
|
111
|
+
end
|
112
|
+
|
113
|
+
def run(direction, path, target_version)
|
114
|
+
new_migrator(path, direction, context(path).migrations, target_version).run
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def migration_context=(context)
|
119
|
+
@migration_context = context
|
120
|
+
end
|
121
|
+
|
122
|
+
def load_migrated
|
123
|
+
@migrated_versions = Set.new(@migration_context.get_all_versions)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def record_version_state_after_migrating(version)
|
128
|
+
if down?
|
129
|
+
migrated.delete(version)
|
130
|
+
ActiveRecord::DataMigration.where(:version => version.to_s).delete_all
|
131
|
+
else
|
132
|
+
migrated << version
|
133
|
+
ActiveRecord::DataMigration.create!(:version => version.to_s)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
class <<self
|
139
|
+
def migrations_path
|
140
|
+
MIGRATIONS_PATH
|
141
|
+
end
|
142
|
+
|
143
|
+
def schema_migrations_table_name
|
144
|
+
'data_migrations'
|
145
|
+
end
|
146
|
+
|
147
|
+
def schema_migrations_table_name
|
148
|
+
ActiveRecord::DataMigration.table_name
|
149
|
+
end
|
150
|
+
|
151
|
+
def get_all_versions(connection = ActiveRecord::Base.connection)
|
152
|
+
if connection.table_exists?(schema_migrations_table_name)
|
153
|
+
ActiveRecord::DataMigration.all.map { |x| x.version.to_i }.sort
|
154
|
+
else
|
155
|
+
[]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
|
data/lib/tasks/data.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
namespace :data do
|
4
|
+
task :data_migration_dependencies => :environment do
|
5
|
+
require 'nonschema_migrations'
|
6
|
+
end
|
7
|
+
|
8
|
+
desc "run data migration (#{MIGRATIONS_PATH})"
|
9
|
+
task :migrate => :data_migration_dependencies do
|
10
|
+
NondestructiveMigrator.migrate(MIGRATIONS_PATH)
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "rollback data migration (#{MIGRATIONS_PATH})"
|
14
|
+
task :rollback => :data_migration_dependencies do
|
15
|
+
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
|
16
|
+
NondestructiveMigrator.rollback(MIGRATIONS_PATH,step)
|
17
|
+
end
|
18
|
+
|
19
|
+
namespace :migrate do
|
20
|
+
desc %Q{runs the "up" for a given _data_ migration VERSION}
|
21
|
+
task :up => :data_migration_dependencies do
|
22
|
+
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
|
23
|
+
raise "VERSION is required" unless version
|
24
|
+
NondestructiveMigrator.run(:up, MIGRATIONS_PATH, version)
|
25
|
+
end
|
26
|
+
|
27
|
+
desc %Q{runs the "down" for a given _data_ migration VERSION}
|
28
|
+
task :down => :data_migration_dependencies do
|
29
|
+
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
|
30
|
+
raise "VERSION is required" unless version
|
31
|
+
NondestructiveMigrator.run(:down, MIGRATIONS_PATH, version)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nonschema_migrations
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jason Fleetwood-Boldt
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-05-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.2'
|
27
|
+
description: Separate schema-only migrations from nonschema (data) migrations in your
|
28
|
+
Rails app
|
29
|
+
email: jason.fb@datatravels.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- lib/active_record/data_migration.rb
|
35
|
+
- lib/generators/data_migration_generator.rb
|
36
|
+
- lib/generators/data_migrations/install_generator.rb
|
37
|
+
- lib/generators/data_migrations/templates/create_data_migrations.rb
|
38
|
+
- lib/nonschema_migrations.rb
|
39
|
+
- lib/nonschema_migrations/railtie.rb
|
40
|
+
- lib/nonschema_migrator.rb
|
41
|
+
- lib/tasks/data.rb
|
42
|
+
homepage: https://github.com/jasonfb/nonschema_migrations
|
43
|
+
licenses:
|
44
|
+
- MIT
|
45
|
+
metadata: {}
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
requirements: []
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 2.7.9
|
63
|
+
signing_key:
|
64
|
+
specification_version: 4
|
65
|
+
summary: Nonschema(data-only) migrations for your Rails app
|
66
|
+
test_files: []
|