dekiru-data_migration 0.2.0 → 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 +4 -4
- data/README.md +78 -1
- data/lib/dekiru/data_migration/migration.rb +81 -0
- data/lib/dekiru/data_migration/version.rb +1 -1
- data/lib/dekiru/data_migration.rb +1 -0
- data/lib/generators/maintenance_script/maintenance_script_generator.rb +4 -1
- data/lib/generators/maintenance_script/templates/maintenance_script.rb.erb +16 -2
- data/sig/dekiru/data_migration/migration.rbs +23 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c5428a8cf5e2a02ff63e8b5a7d3dd2703cf471f60a7bf1dfdd289418a93f1bca
|
4
|
+
data.tar.gz: d0b1afb0f2d5bad6112849c8f046e90c309a19eddde473bde1cf179e8e2a395a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 30e87b497f402579cac1d212f0cc9e12ba4bf8bcc84166de33dbbf9619f9da734c5d468d115801db0b326cffc7dfe09e4ca21b1b7e2c818ecf4848e18bf7c894
|
7
|
+
data.tar.gz: f5a5b31881fe7485d18ffea31950d70b4a5a05a7f5defd44197ec87b78c454a8db707be376825fc28a010c9f95d0c9584beb50f14965cc508fa211602d4bd226
|
data/README.md
CHANGED
@@ -50,6 +50,60 @@ Dekiru::DataMigration::Operator.execute('Grant admin privileges to users') do
|
|
50
50
|
end
|
51
51
|
```
|
52
52
|
|
53
|
+
## Data Migration Class (Recommended)
|
54
|
+
|
55
|
+
You can also define migration logic as a class, which makes testing easier:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
# scripts/20230118_demo_migration.rb
|
59
|
+
class DemoMigration < Dekiru::DataMigration::Migration
|
60
|
+
def migration_targets
|
61
|
+
User.where("email LIKE '%sonicgarden%'").where(admin: false)
|
62
|
+
end
|
63
|
+
|
64
|
+
def migrate_record(user)
|
65
|
+
user.update!(admin: true)
|
66
|
+
end
|
67
|
+
|
68
|
+
def migrate
|
69
|
+
super
|
70
|
+
log "Updated user count: #{User.where(admin: true).count}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
DemoMigration.run
|
75
|
+
```
|
76
|
+
|
77
|
+
### Testing Migration Classes
|
78
|
+
|
79
|
+
The class-based approach makes it easy to write unit tests:
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
# spec/migrations/demo_migration_spec.rb
|
83
|
+
RSpec.describe DemoMigration do
|
84
|
+
let(:migration) { described_class.new }
|
85
|
+
|
86
|
+
describe '#migration_targets' do
|
87
|
+
it 'returns correct migration targets' do
|
88
|
+
create_list(:user, 3, email: 'test@sonicgarden.jp', admin: false)
|
89
|
+
create_list(:user, 2, email: 'other@example.com', admin: false)
|
90
|
+
|
91
|
+
targets = migration.migration_targets
|
92
|
+
expect(targets.count).to eq(3)
|
93
|
+
expect(targets.all? { |u| u.email.include?('sonicgarden') }).to be true
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '#migrate_record' do
|
98
|
+
it 'updates user to admin' do
|
99
|
+
user = create(:user, admin: false)
|
100
|
+
expect { migration.migrate_record(user) }
|
101
|
+
.to change { user.reload.admin }.from(false).to(true)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
53
107
|
Execution result:
|
54
108
|
```
|
55
109
|
$ bin/rails r scripts/demo.rb
|
@@ -98,7 +152,7 @@ Are you sure to commit? (yes/no) > yes
|
|
98
152
|
|
99
153
|
## Generating Maintenance Scripts
|
100
154
|
|
101
|
-
You can generate maintenance scripts that use `Dekiru::DataMigration::
|
155
|
+
You can generate maintenance scripts that use `Dekiru::DataMigration::Migration` with the generator. The filename will be prefixed with the execution date.
|
102
156
|
|
103
157
|
```bash
|
104
158
|
$ bin/rails g maintenance_script demo_migration
|
@@ -109,6 +163,29 @@ Generated file example:
|
|
109
163
|
# scripts/20230118_demo_migration.rb
|
110
164
|
# frozen_string_literal: true
|
111
165
|
|
166
|
+
class DemoMigration < Dekiru::DataMigration::Migration
|
167
|
+
def migration_targets
|
168
|
+
# 移行対象を返すActiveRecord::Relationを定義
|
169
|
+
# 例: User.where(some_condition: true)
|
170
|
+
raise NotImplementedError, 'migration_targets method must be implemented'
|
171
|
+
end
|
172
|
+
|
173
|
+
def migrate_record(record)
|
174
|
+
# 個別レコードの更新処理を定義
|
175
|
+
# 例: record.update!(some_attribute: new_value)
|
176
|
+
raise NotImplementedError, 'migrate_record method must be implemented'
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
DemoMigration.run
|
181
|
+
```
|
182
|
+
|
183
|
+
### Legacy Block-based Approach
|
184
|
+
|
185
|
+
For backward compatibility, you can still use the block-based approach:
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
# scripts/legacy_demo.rb
|
112
189
|
Dekiru::DataMigration::Operator.execute('demo_migration') do
|
113
190
|
# write here
|
114
191
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dekiru
|
4
|
+
module DataMigration
|
5
|
+
# Base class for data migration with testable method separation
|
6
|
+
class Migration
|
7
|
+
def self.run(options = {})
|
8
|
+
migration = new
|
9
|
+
title = migration.title
|
10
|
+
|
11
|
+
Operator.execute(title, options) do
|
12
|
+
migration.instance_variable_set(:@operator_context, self)
|
13
|
+
migration.migrate
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def title
|
18
|
+
self.class.name.demodulize.underscore.humanize
|
19
|
+
end
|
20
|
+
|
21
|
+
def migrate
|
22
|
+
targets = migration_targets
|
23
|
+
|
24
|
+
log "Target count: #{targets.count}"
|
25
|
+
confirm?
|
26
|
+
|
27
|
+
find_each_with_progress(targets) do |record|
|
28
|
+
migrate_record(record)
|
29
|
+
end
|
30
|
+
|
31
|
+
log "Migration completed"
|
32
|
+
end
|
33
|
+
|
34
|
+
def migration_targets
|
35
|
+
raise NotImplementedError, "#{self.class}#migration_targets must be implemented"
|
36
|
+
end
|
37
|
+
|
38
|
+
def migrate_record(record)
|
39
|
+
raise NotImplementedError, "#{self.class}#migrate_record must be implemented"
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def confirm?
|
45
|
+
if @operator_context
|
46
|
+
@operator_context.send(:confirm?)
|
47
|
+
else
|
48
|
+
# Default behavior during test (no confirmation)
|
49
|
+
puts "Confirmation skipped in test mode"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def log(message)
|
54
|
+
if @operator_context
|
55
|
+
@operator_context.send(:log, message)
|
56
|
+
else
|
57
|
+
# Default behavior during test
|
58
|
+
puts message
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_each_with_progress(scope, options = {}, &block)
|
63
|
+
if @operator_context
|
64
|
+
@operator_context.send(:find_each_with_progress, scope, options, &block)
|
65
|
+
else
|
66
|
+
# Default behavior during test (no progress bar)
|
67
|
+
scope.find_each(&block)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def each_with_progress(enum, options = {}, &block)
|
72
|
+
if @operator_context
|
73
|
+
@operator_context.send(:each_with_progress, enum, options, &block)
|
74
|
+
else
|
75
|
+
# Default behavior during test (no progress bar)
|
76
|
+
enum.each(&block)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -14,8 +14,11 @@ class MaintenanceScriptGenerator < Rails::Generators::NamedBase
|
|
14
14
|
source_root File.expand_path("templates", __dir__)
|
15
15
|
|
16
16
|
def copy_maintenance_script_file
|
17
|
+
@filename_date = filename_date
|
18
|
+
@class_name = "#{name.classify}#{@filename_date}"
|
19
|
+
|
17
20
|
template "maintenance_script.rb.erb",
|
18
|
-
"#{Dekiru::DataMigration.configuration.maintenance_script_directory}/#{filename_date}_#{file_name}.rb"
|
21
|
+
"#{Dekiru::DataMigration.configuration.maintenance_script_directory}/#{@filename_date}_#{file_name}.rb"
|
19
22
|
end
|
20
23
|
|
21
24
|
private
|
@@ -2,6 +2,20 @@
|
|
2
2
|
|
3
3
|
using Dekiru::DataMigration::DangerousMethodGuard
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
class <%= @class_name %> < Dekiru::DataMigration::Migration
|
6
|
+
def title
|
7
|
+
'<%= @name %>'
|
8
|
+
end
|
9
|
+
|
10
|
+
def migration_targets
|
11
|
+
# Define ActiveRecord::Relation that returns the target migration
|
12
|
+
# User.where(some_condition: true)
|
13
|
+
end
|
14
|
+
|
15
|
+
def migrate_record(record)
|
16
|
+
# Define the update process for individual records
|
17
|
+
# user.update!(some_attribute: new_value)
|
18
|
+
end
|
7
19
|
end
|
20
|
+
|
21
|
+
<%= @class_name %>.run if __FILE__ == $PROGRAM_NAME
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Dekiru
|
2
|
+
module DataMigration
|
3
|
+
class Migration
|
4
|
+
def self.run: (?Hash[Symbol, untyped] options) -> bool
|
5
|
+
|
6
|
+
def title: () -> String
|
7
|
+
|
8
|
+
def migrate: () -> void
|
9
|
+
|
10
|
+
def migration_targets: () -> untyped
|
11
|
+
|
12
|
+
def migrate_record: (untyped record) -> void
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def log: (String message) -> void
|
17
|
+
|
18
|
+
def find_each_with_progress: (untyped scope, ?Hash[Symbol, untyped] options) { (untyped) -> void } -> void
|
19
|
+
|
20
|
+
def each_with_progress: [T] (Enumerable[T] enum, ?Hash[Symbol, untyped] options) { (T) -> void } -> void
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dekiru-data_migration
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- SonicGarden
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-06-
|
10
|
+
date: 2025-06-30 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: rails
|
@@ -54,6 +54,7 @@ files:
|
|
54
54
|
- Rakefile
|
55
55
|
- lib/dekiru/data_migration.rb
|
56
56
|
- lib/dekiru/data_migration/dangerous_method_guard.rb
|
57
|
+
- lib/dekiru/data_migration/migration.rb
|
57
58
|
- lib/dekiru/data_migration/operator.rb
|
58
59
|
- lib/dekiru/data_migration/transaction_provider.rb
|
59
60
|
- lib/dekiru/data_migration/version.rb
|
@@ -62,6 +63,7 @@ files:
|
|
62
63
|
- lib/generators/maintenance_script/maintenance_script_generator.rb
|
63
64
|
- lib/generators/maintenance_script/templates/maintenance_script.rb.erb
|
64
65
|
- sig/dekiru/data_migration.rbs
|
66
|
+
- sig/dekiru/data_migration/migration.rbs
|
65
67
|
- sig/dekiru/data_migration/operator.rbs
|
66
68
|
homepage: https://github.com/SonicGarden/dekiru-data_migration
|
67
69
|
licenses:
|