evilution 0.22.4 → 0.22.6
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 +12 -0
- data/lib/evilution/integration/base.rb +55 -0
- data/lib/evilution/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f88a719ecd12ca2576f1254f3ef1771359f64345eea28e3537e0d9dae38e2c78
|
|
4
|
+
data.tar.gz: 01c65258b5a9d496a566437b74460ff77175d0dbea9988643d048df3b2e18171
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 136e6f94f55a9e312b777573bd73391d6ec7d05ac8c7455817341589edf018002836db6e550255ff16deb974e6a320a8e818e08661b7918bd2ac6e2f4e85aea1
|
|
7
|
+
data.tar.gz: 8e48faca42c452ba1d8ba1b8800deaf1eafc89d264997f34dc04840324296d2431fdc7e9ccb31f2cff5127862adeb818c15a10da5d060301bdcae0b0352d0cf4
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.22.6] - 2026-04-12
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Zeitwerk re-autoloads original file during mutation load** — Zeitwerk's `const_added` hook re-autoloaded the original source when a module constant was reopened from a temp-dir copy, re-setting `@_included_block` after `clear_concern_state` already removed it; now `pin_autoloaded_constants` resolves all module/class constants from the source via `Object.const_get` before loading, preventing the autoloader from re-triggering (#680)
|
|
8
|
+
|
|
9
|
+
## [0.22.5] - 2026-04-12
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **`ActiveSupport::Concern` modules: all mutations error with `MultipleIncludedBlocks`** — re-evaluating a mutated concern file triggered Rails' guard because `@_included_block` (and `@_prepended_block`) was already set from the original load with a different `source_location`; now `clear_concern_state` removes these instance variables from affected concern modules before `require`/`load`, matching both original file paths and temp-dir copies via subpath suffix so consecutive mutations of the same concern also succeed (#676, PR #678)
|
|
14
|
+
|
|
3
15
|
## [0.22.4] - 2026-04-12
|
|
4
16
|
|
|
5
17
|
### Fixed
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "fileutils"
|
|
4
|
+
require "prism"
|
|
4
5
|
require "tmpdir"
|
|
5
6
|
require_relative "../integration"
|
|
6
7
|
require_relative "../temp_dir_tracker"
|
|
@@ -87,6 +88,8 @@ class Evilution::Integration::Base
|
|
|
87
88
|
File.write(dest, mutation.mutated_source)
|
|
88
89
|
$LOAD_PATH.unshift(@temp_dir)
|
|
89
90
|
displace_loaded_feature(mutation.file_path)
|
|
91
|
+
pin_autoloaded_constants(mutation.original_source)
|
|
92
|
+
clear_concern_state(mutation.file_path)
|
|
90
93
|
require(subpath.delete_suffix(".rb"))
|
|
91
94
|
end
|
|
92
95
|
|
|
@@ -95,6 +98,8 @@ class Evilution::Integration::Base
|
|
|
95
98
|
dest = File.join(@temp_dir, absolute)
|
|
96
99
|
FileUtils.mkdir_p(File.dirname(dest))
|
|
97
100
|
File.write(dest, mutation.mutated_source)
|
|
101
|
+
pin_autoloaded_constants(mutation.original_source)
|
|
102
|
+
clear_concern_state(mutation.file_path)
|
|
98
103
|
load(dest)
|
|
99
104
|
end
|
|
100
105
|
|
|
@@ -110,6 +115,56 @@ class Evilution::Integration::Base
|
|
|
110
115
|
@temp_dir = nil
|
|
111
116
|
end
|
|
112
117
|
|
|
118
|
+
def pin_autoloaded_constants(source)
|
|
119
|
+
collect_constant_names(Prism.parse(source).value).each do |name|
|
|
120
|
+
Object.const_get(name) if Object.const_defined?(name, false)
|
|
121
|
+
rescue NameError # :nodoc:
|
|
122
|
+
nil
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def collect_constant_names(node, nesting = [])
|
|
127
|
+
names = []
|
|
128
|
+
case node
|
|
129
|
+
when Prism::ModuleNode, Prism::ClassNode
|
|
130
|
+
const = node.constant_path.full_name
|
|
131
|
+
qualified = nesting.any? && !const.include?("::") ? "#{nesting.join("::")}::#{const}" : const
|
|
132
|
+
names << qualified
|
|
133
|
+
names.concat(collect_constant_names(node.body, nesting + [const])) if node.body
|
|
134
|
+
when Prism::ProgramNode
|
|
135
|
+
names.concat(collect_constant_names(node.statements, nesting)) if node.statements
|
|
136
|
+
when Prism::StatementsNode
|
|
137
|
+
node.body.each { |child| names.concat(collect_constant_names(child, nesting)) }
|
|
138
|
+
end
|
|
139
|
+
names
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def clear_concern_state(file_path)
|
|
143
|
+
return unless defined?(ActiveSupport::Concern)
|
|
144
|
+
|
|
145
|
+
absolute = File.expand_path(file_path)
|
|
146
|
+
subpath = resolve_require_subpath(file_path)
|
|
147
|
+
|
|
148
|
+
ObjectSpace.each_object(Module) do |mod|
|
|
149
|
+
next unless mod.singleton_class.ancestors.include?(ActiveSupport::Concern)
|
|
150
|
+
|
|
151
|
+
%i[@_included_block @_prepended_block].each do |ivar|
|
|
152
|
+
next unless mod.instance_variable_defined?(ivar)
|
|
153
|
+
|
|
154
|
+
block = mod.instance_variable_get(ivar)
|
|
155
|
+
block_file = block.source_location&.first
|
|
156
|
+
next unless block_file
|
|
157
|
+
|
|
158
|
+
expanded = File.expand_path(block_file)
|
|
159
|
+
mod.remove_instance_variable(ivar) if source_matches?(expanded, absolute, subpath)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def source_matches?(block_path, absolute, subpath)
|
|
165
|
+
block_path == absolute || (subpath && block_path.end_with?("/#{subpath}"))
|
|
166
|
+
end
|
|
167
|
+
|
|
113
168
|
def resolve_require_subpath(file_path)
|
|
114
169
|
absolute = File.expand_path(file_path)
|
|
115
170
|
best_subpath = nil
|
data/lib/evilution/version.rb
CHANGED