Class: RuboCop::Cop::Seams::NoCrossEngineModelAccess

Inherits:
Base
  • Object
show all
Defined in:
lib/seams/cops/no_cross_engine_model_access.rb

Overview

Flags references to another engine's data classes from inside an engine. Engines should communicate via events or via explicitly-exposed concerns — never by reaching into another engine's data layer.

Configured per-engine via the OwnEngine, OtherEngines, and ExposedConcerns options inside the engine's own .rubocop.yml.

The cop deliberately ignores Rails framework constants that every engine exposes (Engine, VERSION, ApplicationController, ApplicationRecord, ApplicationJob, ApplicationMailer) and any class whose name ends in one of the configured suffixes (Controller, Job, Mailer, Helper, Component, Engine). Concerns (Billing::Billable, Billing::Concerns::Billable) are exempt when listed in ExposedConcerns.

<Engine>::Current is also exempt by design: every engine ships its own ActiveSupport::CurrentAttributes namespace (Auth::Current, Accounts::Current, Teams::Current, etc.) and these per-request state holders are intentionally readable from anywhere in the host. Treating them as boundary-violations would force every cross-engine read of per-request identity / account / team to go through a host-defined shim, which defeats the purpose of CurrentAttributes as a shared per-request bus. The exception is documented in doc/CURRENT_ATTRIBUTES.md.

Constant Summary collapse

MSG =
"Engine `%<own>s` must not access `%<const>s` directly. " \
"Use an event or a %<other>s-exposed concern instead."
DEFAULT_IGNORED_LEAF_NAMES =
%w[
  Engine
  VERSION
  ApplicationController
  ApplicationRecord
  ApplicationJob
  ApplicationMailer
  ApplicationHelper
  ApplicationCable
  Routes
  Current
].freeze
DEFAULT_IGNORED_LEAF_SUFFIXES =
%w[
  Controller
  Job
  Mailer
  Helper
  Component
  Channel
  Engine
].freeze

Instance Method Summary collapse

Instance Method Details

#on_const(node) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/seams/cops/no_cross_engine_model_access.rb', line 60

def on_const(node)
  return unless flaggable?(node)

  full_name = node.const_name.to_s
  top_level = full_name.split("::").first

  assert_own_engine_configured!

  add_offense(
    node,
    message: format(MSG, own: own_engine, const: full_name, other: top_level)
  )
end