yiffspace-fixers 0.0.3 → 0.0.4
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/CHANGELOG.md +4 -0
- data/README.md +14 -0
- data/lib/yiffspace/fix_tracker.rb +18 -1
- data/lib/yiffspace/fixers/requires_fix.rb +40 -0
- data/lib/yiffspace/fixers/version.rb +1 -1
- data/lib/yiffspace/migration_sync.rb +24 -3
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 410c5b95f3bf8123d1cb102448230885bfd3f9017fc7b58e9b98a57c744c3e6b
|
|
4
|
+
data.tar.gz: e1676aa2ef8a186c1886ae40b02354acc6dc1ffe0cb77c272f623ff218c0f5cb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 40c9c084ed52eadd521474d6a08f0a96d1dc521ce8746af8d2180ff186a99df2fce72e424672b47b00e42f64bda790f83d5a1cc8202ef0b06646031bd1ce189f
|
|
7
|
+
data.tar.gz: 876bade619a962e9c7035bb8263f51c4adc977fe0cb2afb6db4625eed7528115d4f24ac785b0fe2eae736594575b808025a46e6baad5bc41daad85e2e41b97ca
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
## Unreleased
|
|
2
2
|
|
|
3
|
+
- Migrations can run fixes themselves with `run_fix(id_or_name)`, which runs and records any of
|
|
4
|
+
them not yet applied, and does nothing on rollback. `fixes:migrate_all` reads these calls out
|
|
5
|
+
of pending migrations and folds those fixes into the migration's step instead of scheduling
|
|
6
|
+
them separately. A fix run this way can `requires_migration!` the migration running it.
|
|
3
7
|
- Fixes run through `YiffSpace::FixTracker.run!` (so every `fixes:*` task) can call
|
|
4
8
|
`requires_migration!` unqualified, via `YiffSpace::Fixers::Script` - each fix is now loaded
|
|
5
9
|
wrapped in its own module including it, rather than straight into the top level, so it isn't
|
data/README.md
CHANGED
|
@@ -49,6 +49,20 @@ class RemoveLegacyWidgetColumn < ActiveRecord::Migration[8.1]
|
|
|
49
49
|
end
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
A migration can also run a fix itself with `run_fix`, inside its own transaction - skipped if the
|
|
53
|
+
fix is already applied, and when the migration is rolled back. `fixes:migrate_all` leaves those
|
|
54
|
+
fixes to the migration rather than running them on their own, and a fix run this way can
|
|
55
|
+
`requires_migration!` the migration running it:
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
class AddWidgetType < ActiveRecord::Migration[8.1]
|
|
59
|
+
def change
|
|
60
|
+
add_column(:widgets, :type, :string)
|
|
61
|
+
run_fix(12)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
```
|
|
65
|
+
|
|
52
66
|
See `YiffSpace::FixTracker`, `YiffSpace::FixerTemplate`, `YiffSpace::Fixers::RequiresFix`, and
|
|
53
67
|
`YiffSpace::MigrationSync` for the full API.
|
|
54
68
|
|
|
@@ -27,7 +27,7 @@ module YiffSpace
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def requires_migration!(version)
|
|
30
|
-
return if migration_applied?(version)
|
|
30
|
+
return if migration_applied?(version) || migration_running?(version)
|
|
31
31
|
|
|
32
32
|
raise(MigrationRequired, "migration #{version} must be applied before this fix can run")
|
|
33
33
|
end
|
|
@@ -36,6 +36,23 @@ module YiffSpace
|
|
|
36
36
|
migration_context.get_all_versions.include?(version.to_i)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
# A migration isn't recorded as applied until it finishes, so a fix it runs with
|
|
40
|
+
# YiffSpace::Fixers::RequiresFix#run_fix is let through requires_migration! for it via this.
|
|
41
|
+
def running_migration(version)
|
|
42
|
+
running_migrations << version.to_i
|
|
43
|
+
yield
|
|
44
|
+
ensure
|
|
45
|
+
running_migrations.pop
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def migration_running?(version)
|
|
49
|
+
running_migrations.include?(version.to_i)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def running_migrations
|
|
53
|
+
@running_migrations ||= []
|
|
54
|
+
end
|
|
55
|
+
|
|
39
56
|
# Statically reads a fix script's declared requires_migration! version, if any, without
|
|
40
57
|
# loading/running it - nil if the fix has no such call.
|
|
41
58
|
def required_migration_for(name)
|
|
@@ -18,10 +18,32 @@ module YiffSpace
|
|
|
18
18
|
# out of order), and read back ahead of time by YiffSpace::MigrationSync to order
|
|
19
19
|
# `fixes:migrate_all` correctly. Included into ActiveRecord::Migration by
|
|
20
20
|
# YiffSpace::Fixers::Engine.
|
|
21
|
+
#
|
|
22
|
+
# A migration can also run a fix itself, as part of migrating, with `run_fix`:
|
|
23
|
+
#
|
|
24
|
+
# def change
|
|
25
|
+
# add_column(:widgets, :type, :string)
|
|
26
|
+
# run_fix(3)
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# Each fix it covers is run and recorded the same as YiffSpace::FixTracker.run!, unless it's
|
|
30
|
+
# already applied, and inside the migration's transaction. Fixes can't be undone, so it does
|
|
31
|
+
# nothing when the migration is rolled back. A fix run this way can requires_migration! the
|
|
32
|
+
# migration running it.
|
|
21
33
|
module RequiresFix
|
|
22
34
|
# Raised when a migration runs before one of its required fixes has been applied.
|
|
23
35
|
class FixRequired < StandardError; end
|
|
24
36
|
|
|
37
|
+
# Same idea as YiffSpace::FixTracker::REQUIRES_MIGRATION_PATTERN - lets
|
|
38
|
+
# YiffSpace::MigrationSync read the fixes a migration runs out of its file, so
|
|
39
|
+
# `fixes:migrate_all` leaves them to the migration instead of running them on their own.
|
|
40
|
+
RUN_FIX_PATTERN = /^\s*run_fix\(\s*(?:(\d+)|["']([^"']+)["'])\s*\)/
|
|
41
|
+
|
|
42
|
+
# The id_or_name of every run_fix call in a migration file.
|
|
43
|
+
def self.fixes_run_by(path)
|
|
44
|
+
File.read(path).scan(RUN_FIX_PATTERN).map { |id, name| id ? id.to_i : name }
|
|
45
|
+
end
|
|
46
|
+
|
|
25
47
|
def self.included(base)
|
|
26
48
|
base.extend(ClassMethods)
|
|
27
49
|
base.prepend(InstanceMethods)
|
|
@@ -46,6 +68,24 @@ module YiffSpace
|
|
|
46
68
|
super
|
|
47
69
|
end
|
|
48
70
|
|
|
71
|
+
# id_or_name: same as requires_fix.
|
|
72
|
+
def run_fix(id_or_name)
|
|
73
|
+
return if reverting?
|
|
74
|
+
|
|
75
|
+
names = YiffSpace::FixTracker.resolve(id_or_name)
|
|
76
|
+
raise(ArgumentError, "#{self.class.name} runs fix #{id_or_name.inspect}, which does not exist") if names.empty?
|
|
77
|
+
|
|
78
|
+
names.each do |name|
|
|
79
|
+
if YiffSpace::FixTracker.applied?(name)
|
|
80
|
+
say("#{name} already applied, skipping")
|
|
81
|
+
else
|
|
82
|
+
say_with_time("run_fix(#{name.inspect})") do
|
|
83
|
+
YiffSpace::FixTracker.running_migration(version) { YiffSpace::FixTracker.run!(YiffSpace::FixTracker.fix_path(name)) }
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
49
89
|
private
|
|
50
90
|
|
|
51
91
|
def check_required_fixes!
|
|
@@ -4,8 +4,9 @@ module YiffSpace
|
|
|
4
4
|
# Computes and applies pending db/migrate/*.rb migrations and db/fixes/*.rb fixes together, in
|
|
5
5
|
# whichever order their cross requirements demand - a fix can require a migration
|
|
6
6
|
# (YiffSpace::FixTracker.requires_migration!) and a migration can require a fix
|
|
7
|
-
# (YiffSpace::Fixers::RequiresFix#requires_fix)
|
|
8
|
-
#
|
|
7
|
+
# (YiffSpace::Fixers::RequiresFix#requires_fix), or run it itself (RequiresFix#run_fix).
|
|
8
|
+
# Migrations stay ordered among themselves by version, fixes among themselves by
|
|
9
|
+
# YiffSpace::FixTracker.sort_key, and those cross
|
|
9
10
|
# requirements are woven in via a topological sort. #plan/#apply are used by `rake
|
|
10
11
|
# fixes:migrate_all` (see lib/tasks/fixes.rake).
|
|
11
12
|
module MigrationSync
|
|
@@ -24,15 +25,22 @@ module YiffSpace
|
|
|
24
25
|
def plan
|
|
25
26
|
migrations = pending_migrations
|
|
26
27
|
fixes = FixTracker.pending
|
|
28
|
+
run_by = fixes_run_by(migrations)
|
|
27
29
|
|
|
28
30
|
steps = {}
|
|
29
31
|
migrations.each { |m| steps[[:migration, m.version]] = Step.new(:migration, m, [:migration, m.version]) }
|
|
30
|
-
fixes.each { |f| steps[[:fix, f]] = Step.new(:fix, f, [:fix, f]) }
|
|
32
|
+
fixes.each { |f| steps[[:fix, f]] = Step.new(:fix, f, [:fix, f]) unless run_by.key?([:fix, f]) }
|
|
31
33
|
|
|
32
34
|
edges = Hash.new { |h, k| h[k] = [] }
|
|
33
35
|
indegree = steps.each_key.index_with(0)
|
|
34
36
|
|
|
35
37
|
link = lambda do |before, after|
|
|
38
|
+
# A fix a migration runs (see #fixes_run_by) has no step of its own, its requirements and
|
|
39
|
+
# ordering carry over to that migration instead. One requiring that same migration links
|
|
40
|
+
# it to itself, which needs nothing - FixTracker.running_migration covers it at runtime.
|
|
41
|
+
before = run_by.fetch(before, before)
|
|
42
|
+
after = run_by.fetch(after, after)
|
|
43
|
+
next if before == after
|
|
36
44
|
next unless steps.key?(before) && steps.key?(after)
|
|
37
45
|
|
|
38
46
|
edges[before] << after
|
|
@@ -70,6 +78,19 @@ module YiffSpace
|
|
|
70
78
|
end
|
|
71
79
|
end
|
|
72
80
|
|
|
81
|
+
# Maps [:fix, name] => [:migration, version] for every pending fix a pending migration runs
|
|
82
|
+
# itself with YiffSpace::Fixers::RequiresFix#run_fix.
|
|
83
|
+
def fixes_run_by(migrations)
|
|
84
|
+
migrations.each_with_object({}) do |m, run_by|
|
|
85
|
+
Fixers::RequiresFix.fixes_run_by(m.filename).each do |id_or_name|
|
|
86
|
+
names = FixTracker.resolve(id_or_name)
|
|
87
|
+
raise(UnresolvableOrder, "#{m.name} runs fix #{id_or_name.inspect}, which does not exist") if names.empty?
|
|
88
|
+
|
|
89
|
+
names.each { |name| run_by[[:fix, name]] ||= [:migration, m.version] }
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
73
94
|
# Kahn's algorithm - ready nodes are processed FIFO in `steps`' insertion order (migrations
|
|
74
95
|
# before fixes when both are ready at once), so requirements only ever reorder things when
|
|
75
96
|
# they actually have to.
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: yiffspace-fixers
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Donovan_DMC
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-10-
|
|
10
|
+
date: 2026-10-16 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: rails
|