Module: Shirobai::Inject
- Defined in:
- lib/shirobai/inject.rb
Class Method Summary collapse
-
.activate! ⇒ Object
Badge replacement rides on
Registry#clear_enrollment_queuebeing last-write-wins: whoever is enlisted later owns the badge. -
.align_autocorrect_incompatibilities!(extra_cops = []) ⇒ Object
Team#each_correctorskips a cop's corrector for the round when an earlier-merged cop'sautocorrect_incompatible_withincludes the cop's CLASS. -
.align_for(config) ⇒ Object
Per-config re-alignment for cops that load AFTER every shirobai require: rubocop resolves
plugins:/require:gems while loading the corpus config, and those gems (rubocop-capybara et al.) may list a replaced class or enlist new cops. -
.base_incompatible_list(cop) ⇒ Object
The cop's own
autocorrect_incompatible_with, bypassing modules prepended onto its singleton class (rubocop-performance'ssuper.push(...)pattern). - .claim_badges! ⇒ Object
-
.stock_counterpart(klass) ⇒ Object
The stock class whose badge
klasstook over, or nil when the stock constant does not exist (a shirobai-only cop). -
.wrapper_cops ⇒ Object
All wrapper classes defined so far, from the core departments and any loaded plugin gem's department (RSpec / Rails / Performance).
Class Method Details
.activate! ⇒ Object
Badge replacement rides on Registry#clear_enrollment_queue being
last-write-wins: whoever is enlisted later owns the badge. Up to
RuboCop 1.88 every stock cop was loaded (and enlisted) at
require "rubocop", so wrappers defined afterwards always won.
RuboCop 1.89 registers stock cops lazily: the registry maps each badge
to a constant-name String and the class only loads when the constant is
resolved (autoload). Loading enlists the stock class through
Base.inherited, so a replaced cop whose stock file loads AFTER the
wrapper — Registry#load_all_lazy_cops, a plugin gem referencing the
constant (rubocop-rails prepends onto Style/RedundantSelf), or our own
stock_counterpart — would steal the badge back on the next flush.
claim_badges! closes every such path at require time: resolve each
replaced cop's stock constant first (consuming the autoload, so the
stock class can never be enlisted again later), then re-enlist the
wrapper so the queue flushes with the wrapper last. Unreplaced cops
keep their lazy registration — shirobai must not force the whole
registry to load.
24 25 26 27 |
# File 'lib/shirobai/inject.rb', line 24 def self.activate! claim_badges! align_autocorrect_incompatibilities! end |
.align_autocorrect_incompatibilities!(extra_cops = []) ⇒ Object
Team#each_corrector skips a cop's corrector for the round when an
earlier-merged cop's autocorrect_incompatible_with includes the cop's
CLASS. Badge replacement breaks that identity check in both directions:
a wrapper copying stock's list still names the dismissed stock class,
and a stock cop that stays stock (Style/SymbolProc) names a class whose
registry slot a wrapper took over. Either way skips.include?(cop.class)
misses, the skip never fires, and the -a round applies corrections
stock would have dropped — the corrected trees then drift (fluentd
types.rb / log.rb: SpaceInsideBlockBraces lists BlockDelimiters).
Rewrite every active cop's list through the stock-to-shirobai map:
wrappers get stock's list (the drop-in truth) translated, and remaining
stock cops get their own list translated. Runs after all core wrapper
classes are defined (each auto-enlists via Base.inherited), again at
each shirobai plugin gem's require (their stock departments — e.g.
Rails/SafeNavigation listing Style::RedundantSelf — enlist between
aligner runs), and per config over the enabled set (third-party
plugins like rubocop-capybara load during config resolution, after
every shirobai require; see align_for and Dispatch.bundle_token).
The rewritten methods return a FRESH copy per call, like stock's
per-call array literals: rubocop-performance and rubocop-capybara
prepend singleton modules that do super.push(...), so a shared (or
frozen) array would accumulate duplicates across calls (or raise
FrozenError).
The wrapper pass runs unconditionally (the stock counterparts are
loaded by claim_badges!). The non-wrapper pass only covers
extra_cops — enumerating the whole registry would force every lazy
stock cop to load, so align_for passes the enabled set of the config
actually being run, which is exactly the set Team#each_corrector can
consult. Idempotent: an already-translated list maps to itself and is
skipped.
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/shirobai/inject.rb', line 85 def self.align_autocorrect_incompatibilities!(extra_cops = []) replacements = {} wrapper_cops.each do |cop| stock = stock_counterpart(cop) replacements[stock] = cop if stock end replacements.each do |stock, wrapper| list = stock.autocorrect_incompatible_with .map { |klass| replacements.fetch(klass, klass) }.freeze wrapper.define_singleton_method(:autocorrect_incompatible_with) { list.dup } end extra_cops.each do |cop| next if replacements.value?(cop) list = base_incompatible_list(cop) mapped = list.map { |klass| replacements.fetch(klass, klass) } next if mapped == list mapped.freeze cop.define_singleton_method(:autocorrect_incompatible_with) { mapped.dup } end end |
.align_for(config) ⇒ Object
Per-config re-alignment for cops that load AFTER every shirobai
require: rubocop resolves plugins: / require: gems while loading
the corpus config, and those gems (rubocop-capybara et al.) may list a
replaced class or enlist new cops. Called from Dispatch.bundle_token
on each new config, which happens after config resolution and before
any correction round. Memoized per (config identity, registry badge
count): a plugin load only ever ADDS badges, so a size change re-runs
the alignment mid-run. Registry#length only counts badges — it never
loads a lazy cop — and Registry#enabled loads exactly the cops the
run's team is built from.
132 133 134 135 136 137 138 139 140 |
# File 'lib/shirobai/inject.rb', line 132 def self.align_for(config) registry = RuboCop::Cop::Registry.global size = registry.length @aligned ||= {}.compare_by_identity return if @aligned[config] == size @aligned[config] = size align_autocorrect_incompatibilities!(registry.enabled(config)) end |
.base_incompatible_list(cop) ⇒ Object
The cop's own autocorrect_incompatible_with, bypassing modules
prepended onto its singleton class (rubocop-performance's
super.push(...) pattern). The rewritten method sits UNDER those
prepends, so snapshotting the full call would bake their pushes into
the base and the prepend would then add them again on every call.
114 115 116 117 118 119 120 |
# File 'lib/shirobai/inject.rb', line 114 def self.base_incompatible_list(cop) singleton = cop.singleton_class prepends = singleton.ancestors.take_while { |mod| !mod.equal?(singleton) } meth = cop.method(:autocorrect_incompatible_with) meth = meth.super_method while meth && prepends.include?(meth.owner) meth ? meth.call : [] end |
.claim_badges! ⇒ Object
29 30 31 32 33 34 35 36 |
# File 'lib/shirobai/inject.rb', line 29 def self.claim_badges! registry = RuboCop::Cop::Registry.global wrapper_cops.each do |wrapper| next unless stock_counterpart(wrapper) # consume the stock autoload registry.enlist(wrapper) end end |
.stock_counterpart(klass) ⇒ Object
The stock class whose badge klass took over, or nil when the stock
constant does not exist (a shirobai-only cop).
144 145 146 147 148 149 |
# File 'lib/shirobai/inject.rb', line 144 def self.stock_counterpart(klass) department, name = klass.cop_name.split("/") RuboCop::Cop.const_get(department, false).const_get(name, false) rescue NameError nil end |
.wrapper_cops ⇒ Object
All wrapper classes defined so far, from the core departments and any loaded plugin gem's department (RSpec / Rails / Performance).
40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/shirobai/inject.rb', line 40 def self.wrapper_cops Shirobai::Cop.constants(false).flat_map do |department| mod = Shirobai::Cop.const_get(department) next [] unless mod.is_a?(Module) && !mod.is_a?(Class) mod.constants(false).filter_map do |cop_name| klass = mod.const_get(cop_name) klass if klass.is_a?(Class) && klass < RuboCop::Cop::Base end end end |