Module: ArchSpec

Defined in:
lib/archspec.rb,
lib/archspec/cli.rb,
lib/archspec/dsl.rb,
lib/archspec/todo.rb,
lib/archspec/error.rb,
lib/archspec/model.rb,
lib/archspec/version.rb,
lib/archspec/analyzer.rb,
lib/archspec/evaluator.rb,
lib/archspec/definition.rb,
lib/archspec/diagnostic.rb,
lib/archspec/value_object.rb,
lib/archspec/architectures.rb,
lib/archspec/component_spec.rb,
lib/archspec/formatters/json.rb,
lib/archspec/formatters/text.rb,
lib/archspec/source_location.rb,
lib/archspec/formatters/style.rb,
lib/archspec/rules/cycle_rule.rb,
lib/archspec/rules/naming_rules.rb,
lib/archspec/rules/privacy_rule.rb,
lib/archspec/rules/concern_rules.rb,
lib/archspec/rules/protocol_rules.rb,
lib/archspec/rules/component_rules.rb,
lib/archspec/formatters/explanation.rb,
lib/archspec/rules/dependency_rules.rb

Overview

ArchSpec turns your application's architecture into executable checks.

You describe components, dependencies, and boundaries in an Archspec.rb file written in the ArchSpec::DSL, then run archspec check to verify every change. ArchSpec reads Ruby source with Prism and never boots the app.

The DSL is the public API. An Archspec.rb file is evaluated directly:

architecture :rails

component :services, in: "app/services/**/*.rb"
services.cannot_call :render, :redirect_to, receiver: :none

See ArchSpec::DSL::Context for the top-level DSL and ArchSpec::DSL::ComponentProxy for per-component rules. See ArchSpec::Architectures for the bundled architecture presets.

You can also build a definition in plain Ruby with ArchSpec.define.

Defined Under Namespace

Modules: Analyzer, Architectures, CLI, DSL, Evaluator, Formatters, Rules, ValueObject Classes: Component, ComponentSpec, ConstantNode, Definition, Diagnostic, Error, Graph, SourceFile, Todo

Constant Summary collapse

ParseError =
ValueObject.define(:message, :location)
MethodDefinition =
ValueObject.define(:owner, :name, :scope, :location, :visibility)
Suppression =
ValueObject.define(:rule, :start_line, :end_line, :reason) do
  def matches?(diagnostic)
    (rule.nil? || rule == diagnostic.rule) &&
      diagnostic.location.line >= start_line &&
      diagnostic.location.line <= end_line
  end
end
Edge =
ValueObject.define(
  :type,
  :from_path,
  :from_constant,
  :to,
  :location,
  :confidence,
  :receiver,
  :lexical_nesting
) do
  VERBS = {
    references_constant: 'references',
    inherits_from: 'inherits from',
    includes: 'includes',
    prepends: 'prepends',
    extends: 'extends',
    calls_named_method: 'calls',
    instantiates_and_invokes: 'instantiates and invokes',
    requires: 'requires',
    requires_relative: 'requires',
    dynamic_feature: 'uses dynamic feature'
  }.freeze

  # The edge type as prose, for diagnostics and explain output.
  def verb
    VERBS.fetch(type, type.to_s.tr('_', ' '))
  end
end
VERSION =
'1.0.0.rc1'
SourceLocation =
ValueObject.define(:path, :line, :column, :end_line, :end_column) do
  def self.from_prism(path, location)
    new(path, location.start_line, location.start_column + 1, location.end_line, location.end_column + 1)
  end

  # A zero-width location for diagnostics that point at a file rather than a
  # span of code.
  def self.point(path, line, column)
    new(path, line, column, line, column)
  end

  def relative_path(root)
    Pathname(path).relative_path_from(Pathname(root)).to_s
  rescue ArgumentError
    path
  end
end

Class Method Summary collapse

Class Method Details

.define(name = nil, &block) ⇒ Object

Builds an architecture definition from a block of DSL calls.

ArchSpec.define do
component :models, in: "app/models/**/*.rb"
component :controllers, in: "app/controllers/**/*.rb"
models.cannot_use :controllers
end

An Archspec.rb file is not written with this wrapper. Its top level is already the DSL, so bare component and architecture calls work directly. Use define when constructing a definition from Ruby, such as in a test.

Returns the ArchSpec::Definition.



63
64
65
66
67
68
# File 'lib/archspec.rb', line 63

def define(name = nil, &block)
  definition = Definition.new(name)
  definition.extend(DSL::Context)
  definition.instance_eval(&block) if block
  definition
end