Class: Evilution::Integration::Loading::RedefinitionRecovery Private
- Inherits:
-
Object
- Object
- Evilution::Integration::Loading::RedefinitionRecovery
- Defined in:
- lib/evilution/integration/loading/redefinition_recovery.rb
Overview
This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.
Some DSLs (Rails 8 enum, define_method guards) raise ArgumentError on re-declaration. On such a conflict we strip constants declared in the source and retry the load once against a fresh namespace.
A second class of idempotency violation comes from gem-internal registries
(dry-monads register_mixin, Rails plugins, etc.) which raise when called
a second time in the same process. For these we swallow the error: the
class body executed up to the raise point — method defs preceding the
registry call are already in place — and any state change blocked by the
guard was intentional duplicate-prevention from the gem's side. Mutations
that target a def after such a class-body call would not be applied;
emit a one-shot warning so that mode is visible.
Constant Summary collapse
- IDEMPOTENCY_PATTERNS =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
[ "already registered", "already initialized", "already exists" ].freeze
Instance Method Summary collapse
- #call(source, &block) ⇒ Object private
-
#initialize(constant_names: Evilution::AST::ConstantNames.new) ⇒ RedefinitionRecovery
constructor
private
A new instance of RedefinitionRecovery.
Constructor Details
#initialize(constant_names: Evilution::AST::ConstantNames.new) ⇒ RedefinitionRecovery
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns a new instance of RedefinitionRecovery.
25 26 27 |
# File 'lib/evilution/integration/loading/redefinition_recovery.rb', line 25 def initialize(constant_names: Evilution::AST::ConstantNames.new) @constant_names = constant_names end |
Instance Method Details
#call(source, &block) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/evilution/integration/loading/redefinition_recovery.rb', line 29 def call(source, &block) block.call rescue ArgumentError => e if redefinition_conflict?(e) remove_defined_constants(source) block.call elsif idempotency_violation?(e) warn_once_for(e) nil else raise end rescue TypeError => e raise unless superclass_mismatch?(e) # `class X < Struct.new(...)` (or Data.define / Class.new in the same # position) returns a fresh anonymous parent class on every call, so the # recorded superclass of the existing X differs from the re-eval's. Ruby # raises TypeError. Simply swallowing would leave the *original* class # in place and silently report the mutation as survived — a false # negative. Instead, strip the constants this source declares and retry # exactly once, mirroring the ArgumentError 'already defined' path. If # the retry still mismatches (genuine inheritance conflict the mutation # cannot resolve), propagate so the mutation reports :error rather than # being silently miscounted. remove_defined_constants(source) block.call end |