evilution 0.22.5 → 0.22.7
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 +73 -6
- data/lib/evilution/version.rb +1 -1
- 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: 953b824799c03617e8e41a67f759c5edbd122b63046f8916e19d215021de5871
|
|
4
|
+
data.tar.gz: 38e3498c7cc763e44a5cded169c035b1a48d02c14c95b9ff4ae0800dd63389cd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cceb0046285ad1efdd63914af840da66354006d97f1f9da0642dcf649e3628a342c6ed5f3c6776f3b76dee7eb41af833033a4c9c18992eaa6773bf1c5da8ca75
|
|
7
|
+
data.tar.gz: 6928979ecd999f213b87c3460179f4a09008894866e31d5e822f214ab521235e8463a389768e40bff065512fc1e55355d0ca249e739cb571210ccd429d94db82
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.22.7] - 2026-04-13
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Rails 8 models with `enum`: every mutation errors with `ArgumentError`** — re-running the class body via `load`/`require` retriggered Rails 8's `detect_enum_conflict!` because the enum's predicate methods already existed from the first load, so mutations scored 0% on any affected file; now `remove_defined_constants` drops constants defined by the mutated source (via `remove_const` on the parent namespace) before re-loading, so the class body runs on a fresh constant and DSLs with conflict detection (enum, and any future DSL with redefinition guards) see a clean slate (#683)
|
|
8
|
+
|
|
9
|
+
## [0.22.6] - 2026-04-12
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **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)
|
|
14
|
+
|
|
3
15
|
## [0.22.5] - 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,8 +88,11 @@ 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)
|
|
90
92
|
clear_concern_state(mutation.file_path)
|
|
91
|
-
|
|
93
|
+
with_redefinition_recovery(mutation.original_source) do
|
|
94
|
+
require(subpath.delete_suffix(".rb"))
|
|
95
|
+
end
|
|
92
96
|
end
|
|
93
97
|
|
|
94
98
|
def apply_via_load(mutation)
|
|
@@ -96,8 +100,24 @@ class Evilution::Integration::Base
|
|
|
96
100
|
dest = File.join(@temp_dir, absolute)
|
|
97
101
|
FileUtils.mkdir_p(File.dirname(dest))
|
|
98
102
|
File.write(dest, mutation.mutated_source)
|
|
103
|
+
pin_autoloaded_constants(mutation.original_source)
|
|
99
104
|
clear_concern_state(mutation.file_path)
|
|
100
|
-
|
|
105
|
+
with_redefinition_recovery(mutation.original_source) do
|
|
106
|
+
load(dest)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def with_redefinition_recovery(original_source)
|
|
111
|
+
yield
|
|
112
|
+
rescue ArgumentError => e
|
|
113
|
+
raise unless redefinition_conflict?(e)
|
|
114
|
+
|
|
115
|
+
remove_defined_constants(original_source)
|
|
116
|
+
yield
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def redefinition_conflict?(error)
|
|
120
|
+
error.message.include?("already defined")
|
|
101
121
|
end
|
|
102
122
|
|
|
103
123
|
def restore_original(_mutation)
|
|
@@ -112,6 +132,56 @@ class Evilution::Integration::Base
|
|
|
112
132
|
@temp_dir = nil
|
|
113
133
|
end
|
|
114
134
|
|
|
135
|
+
def pin_autoloaded_constants(source)
|
|
136
|
+
collect_constant_names(Prism.parse(source).value).each do |name|
|
|
137
|
+
Object.const_get(name) if Object.const_defined?(name, false)
|
|
138
|
+
rescue NameError # :nodoc:
|
|
139
|
+
nil
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def collect_constant_names(node, nesting = [])
|
|
144
|
+
names = []
|
|
145
|
+
case node
|
|
146
|
+
when Prism::ModuleNode, Prism::ClassNode
|
|
147
|
+
const = node.constant_path.full_name
|
|
148
|
+
qualified = nesting.any? && !const.include?("::") ? "#{nesting.join("::")}::#{const}" : const
|
|
149
|
+
names << qualified
|
|
150
|
+
names.concat(collect_constant_names(node.body, nesting + [const])) if node.body
|
|
151
|
+
when Prism::ProgramNode
|
|
152
|
+
names.concat(collect_constant_names(node.statements, nesting)) if node.statements
|
|
153
|
+
when Prism::StatementsNode
|
|
154
|
+
node.body.each { |child| names.concat(collect_constant_names(child, nesting)) }
|
|
155
|
+
end
|
|
156
|
+
names
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def remove_defined_constants(source)
|
|
160
|
+
collect_constant_names(Prism.parse(source).value).reverse_each do |name|
|
|
161
|
+
parent_name, _, local_name = name.rpartition("::")
|
|
162
|
+
parent = resolve_loaded_constant_parent(parent_name)
|
|
163
|
+
next unless parent
|
|
164
|
+
next unless parent.const_defined?(local_name, false)
|
|
165
|
+
next if parent.autoload?(local_name)
|
|
166
|
+
|
|
167
|
+
parent.send(:remove_const, local_name.to_sym)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def resolve_loaded_constant_parent(parent_name)
|
|
172
|
+
return Object if parent_name.empty?
|
|
173
|
+
|
|
174
|
+
parent_name.split("::").reduce(Object) do |mod, part|
|
|
175
|
+
return nil unless mod.const_defined?(part, false)
|
|
176
|
+
return nil if mod.autoload?(part)
|
|
177
|
+
|
|
178
|
+
resolved = mod.const_get(part, false)
|
|
179
|
+
return nil unless resolved.is_a?(Module)
|
|
180
|
+
|
|
181
|
+
resolved
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
115
185
|
def clear_concern_state(file_path)
|
|
116
186
|
return unless defined?(ActiveSupport::Concern)
|
|
117
187
|
|
|
@@ -135,10 +205,7 @@ class Evilution::Integration::Base
|
|
|
135
205
|
end
|
|
136
206
|
|
|
137
207
|
def source_matches?(block_path, absolute, subpath)
|
|
138
|
-
|
|
139
|
-
return true if subpath && block_path.end_with?("/#{subpath}")
|
|
140
|
-
|
|
141
|
-
false
|
|
208
|
+
block_path == absolute || (subpath && block_path.end_with?("/#{subpath}"))
|
|
142
209
|
end
|
|
143
210
|
|
|
144
211
|
def resolve_require_subpath(file_path)
|
data/lib/evilution/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: evilution
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.22.
|
|
4
|
+
version: 0.22.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Denis Kiselev
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
11
|
+
date: 2026-04-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: diff-lcs
|