module ArchSpec::DSL::Context
The top-level DSL. Declare the project, its components, an architecture preset, and global rules.
root "." source "app/**/*.rb", "lib/**/*.rb" ignore "app/legacy/**/*.rb" todo "archspec_todo.yml" component :models, in: "app/models/**/*.rb" component :controllers, in: "app/controllers/**/*.rb" models.cannot_use :controllers
Declaring a component defines a reader for it, so models and controllers above return an ArchSpec::DSL::ComponentProxy you attach rules to.
Public Instance Methods
# File lib/archspec/dsl.rb, line 135 def architecture(name, **options) Architectures.apply(name, self, **options) end
Applies a bundled architecture preset, defining its components and rules together.
architecture :rails
architecture :hexagonal
architecture :modular_monolith, components: { ... }, allow: { ... }
preset is an alias. Use whichever word fits: architecture reads well for structural bundles like :rails, preset for convention packs like :ruby_conventions.
See ArchSpec::Architectures for every preset and its options.
# File lib/archspec/dsl.rb, line 109 def component(name, in: nil, except: nil, namespace: nil, constants: nil, descendants_of: nil) add_component( ComponentSpec.new( name, files: binding.local_variable_get(:in), except: except, namespace: namespace, constants: constants, descendants_of: descendants_of ) ) ComponentProxy.new(self, name) end
Declares a component: a named set of files, matched by glob, namespace, explicit constant, or semantic ancestry.
component :services, in: "app/services/**/*.rb" component :workflows, in: "app/models/**/*.rb", except: "app/models/legacy/**/*.rb" component :billing, namespace: "Billing" component :legacy, constants: %w[OldReport OldExport] component :records, descendants_of: "ApplicationRecord"
Returns an ArchSpec::DSL::ComponentProxy for attaching rules. The component is also available by name later in the file.
# File lib/archspec/dsl.rb, line 87 def each_directory(glob) base = absolute_root pairs = Dir.glob(File.join(base, glob)).select { |path| File.directory?(path) }.sort.map do |absolute| [File.basename(absolute), Pathname(absolute).relative_path_from(Pathname(base)).to_s] end return pairs unless block_given? pairs.each { |name, path| yield(name, path) } end
Yields each subdirectory matching a glob, so you can declare one component per engine or pack without hardcoding their names. Paths resolve against the Archspec.rb directory, not the working directory, so it does not matter where archspec is run from.
each_directory "engines/*" do |name, path| component name, in: "#{path}/**/*.rb" end
Yields the directory basename and its root-relative path. Returns the [name, path] pairs when called without a block.
# File lib/archspec/dsl.rb, line 72 def facts(path = 'archspec_facts') self.facts_path = path.to_s end
Loads versioned reference and generated-method facts from this directory. Producing Rails facts is an explicit runtime operation: archspec reflect. Normal checks only read the snapshots.
Source
# File lib/archspec/dsl.rb, line 54 def ignore(*patterns) add_ignore_patterns(patterns) end
Adds glob patterns for files to skip. Combines with the built-in ignores for .git, tmp, vendor, and node_modules.
Source
# File lib/archspec/dsl.rb, line 160 def method_missing(name, ...) return ComponentProxy.new(self, name) if component?(name) super end
# File lib/archspec/dsl.rb, line 148 def no_cycles(among: nil, because: nil) DSL.assert_known_components!(self, among, for_rule: 'no_cycles') if among add_rule(Rules.with_reason(Rules::NoCyclesRule.new(among: among), because)) end
Forbids dependency cycles between components. Pass among: to limit the check to a subset; omit it to check every declared component.
no_cycles no_cycles among: %i[billing catalog shared]
Rule id: dependencies.no_cycles.
# File lib/archspec/dsl.rb, line 166 def respond_to_missing?(name, include_private = false) component?(name) || super end
Source
# File lib/archspec/dsl.rb, line 39 def root(path = nil) return root_path unless path self.root_path = path.to_s end
Sets or reads the project root that file patterns resolve against. Defaults to the directory of the Archspec.rb file.
Source
# File lib/archspec/dsl.rb, line 156 def rule(rule, because: nil) add_rule(Rules.with_reason(rule, because)) end
Adds a custom rule object. A rule responds to id and evaluate(graph), returning ArchSpec::Diagnostic objects. Use this to extend ArchSpec with project-specific checks.
Source
# File lib/archspec/dsl.rb, line 48 def source(*patterns) add_source_patterns(patterns) end
Adds glob patterns for the files ArchSpec parses. Defaults cover app, lib, packs, and engines. Component patterns are always analyzed, so most projects never need this.
# File lib/archspec/dsl.rb, line 65 def todo(path = 'archspec_todo.yml') self.todo_path = path.to_s end
Points at a todo file of accepted violations. Diagnostics recorded there are subtracted from future runs, so you can adopt ArchSpec in an existing app without fixing everything first, then burn the list down.
todo "archspec_todo.yml"
Write or refresh it with archspec check --update-todo.