Class: DevDoc::Test::Lints::DomainClassBaseChecker

Inherits:
Object
  • Object
show all
Defined in:
lib/dev_doc/test/lints/domain_class_base.rb

Overview

Runtime check: every CLASS defined by a file under app/models/ (excluding concerns/) must descend from one of the project's domain base classes. Runs with the app loaded, so real ancestry is checked — intermediate family bases and indirect inheritance resolve correctly, which is exactly what a per-file static cop cannot do.

Wrapped by the Minitest module DomainClassBase below — see that module for the rationale.

Constant Summary collapse

DEFAULT_BASE_CLASS_NAMES =

The three-way rule (best_practices/backend/en/03_model.md item 9): persisted -> ApplicationRecord; table-less but form-backed -> ApplicationModel; plain domain logic -> PlainModel.

%w[ApplicationRecord ApplicationModel PlainModel].freeze

Instance Method Summary collapse

Constructor Details

#initialize(project_root, base_class_names: DEFAULT_BASE_CLASS_NAMES, allowed_paths: []) ⇒ DomainClassBaseChecker

Returns a new instance of DomainClassBaseChecker.



20
21
22
23
24
# File 'lib/dev_doc/test/lints/domain_class_base.rb', line 20

def initialize(project_root, base_class_names: DEFAULT_BASE_CLASS_NAMES, allowed_paths: [])
  @project_root = Pathname(project_root)
  @base_class_names = base_class_names
  @allowed_paths = allowed_paths
end

Instance Method Details

#offendersObject

Returns an Array of offender descriptions, or [] when every model class descends from an allowed base. Modules are skipped: the three-way rule classifies CLASSES — namespaces, function-bag modules, and concerns carry no instance state to classify.



30
31
32
33
34
35
36
37
38
39
40
# File 'lib/dev_doc/test/lints/domain_class_base.rb', line 30

def offenders
  bases = @base_class_names.map(&:constantize)

  model_files.filter_map do |path|
    constant = constant_for(path)
    next if constant.nil? # module or namespace-only file
    next if bases.any? { |base| constant <= base }

    "  #{relative(path)}: #{constant.name} < #{constant.superclass.name}#{hint_for(constant)}"
  end
end