archspec 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b50ec4f86cfdf9e4f95b1cf4a28c4dec212d711781675dedd6648f13aed7c1f
4
- data.tar.gz: 9303aefb00b161004cc515b0d1b2ad03b3e60f396ef34b9dc86be4e7a1825650
3
+ metadata.gz: 07a83d8f3402826cb160272cea2739a3ad9542bc7d35fd6590544c39fcf7c597
4
+ data.tar.gz: 5d087f15b56575032c262a7f7e097270b4bad4bb09acd066003bfbbc338900eb
5
5
  SHA512:
6
- metadata.gz: 56bd81e5ee880404a80c7f29d498c58903bd8d087c5e8deb80af060d6d169c3b40e471eb09758500d6522516d83090b435b45ab243f780978539840b9548681e
7
- data.tar.gz: a3dc2d41b6b97edd3ddacb8c84632c0b8ae81471c9132ec02ddbf66ce68d8dd916d068aeb366a8bed99f996ed6f81cda8a3a48b502459b0d25afc525d81df95d
6
+ metadata.gz: c1ee035facb38cb68d9bde3b469f6fdd0e98bf434437254c3f9123cb7eb4ca9cfb2206ce0e4ad01fd25179054cc91e0c821de729026a08e69bb2cfb99092c084
7
+ data.tar.gz: de5d78b6d8e71e26d2a9cc67e6ad0854f532b876bd7e885536e867ddf27d5fb1a324b3bdb79e63f4d880c0194de036e94c2ae77d10219bedd19f6d84008e0d6b
data/README.md CHANGED
@@ -1,20 +1,24 @@
1
+ <div align="center">
2
+
1
3
  # ArchSpec
2
4
 
3
- Static analysis for your architecture. A linter for Ruby and Rails boundaries.
5
+ <strong>Executable architecture specifications for Ruby and Rails</strong>
6
+
7
+ <p>Declare your components and boundaries in one <code>Archspec.rb</code>, and every change gets checked in CI, whether a person or a coding agent wrote it. No AI involved, just Prism.</p>
8
+
9
+ Battle tested in [<picture><source media="(prefers-color-scheme: dark)" srcset="https://rubyllm.com/assets/images/logotype_dark.svg"><img src="https://rubyllm.com/assets/images/logotype.svg" alt="RubyLLM" height="30" align="absmiddle"></picture>](https://rubyllm.com) and at [<picture><source media="(prefers-color-scheme: dark)" srcset="https://chatwithwork.com/logotype-dark.svg"><img src="https://chatwithwork.com/logotype.svg" alt="Chat with Work" height="30" align="absmiddle"></picture>](https://chatwithwork.com)
4
10
 
5
- ArchSpec turns your application's architecture into executable checks. Declare
6
- your components, dependencies, and boundaries in one file, then check every
7
- change in CI, whether a person or a coding agent wrote it. It is plain static
8
- analysis: it reads Ruby source with Prism, never boots the app, and no AI is
9
- involved in checking your code.
11
+ [![Gem Version](https://badge.fury.io/rb/archspec.svg)](https://rubygems.org/gems/archspec)
12
+ [![Gem Downloads](https://img.shields.io/gem/dt/archspec)](https://rubygems.org/gems/archspec)
13
+ [![CI](https://github.com/crmne/archspec/actions/workflows/ci.yml/badge.svg)](https://github.com/crmne/archspec/actions/workflows/ci.yml)
10
14
 
11
- It maps conventional Rails files to constants and checks the structural rules
12
- you write down: components, layers, constant references, inheritance, mixins,
13
- named method calls, method protocols, cycles, and Rails boundaries.
15
+ <img src="https://archspecrb.dev/assets/images/archspec-check.png" alt="archspec check reporting an architecture violation with a code frame, the offending span underlined, and the evidence as a note" width="760">
14
16
 
15
- It does not try to infer the "true" design pattern of arbitrary Ruby code. You
16
- describe the architecture your team wants. ArchSpec checks whether the code still
17
- matches it.
17
+ </div>
18
+
19
+ ---
20
+
21
+ ArchSpec turns your architecture into executable checks: components, layers, constant references, inheritance, mixins, named method calls, method protocols, naming conventions, cycles, and Rails boundaries. It is plain static analysis: it reads Ruby source with Prism, never boots the app, and no AI is involved in checking your code. The full Discourse app, 1,899 files, checks in 2.5 seconds.
18
22
 
19
23
  ## Why ArchSpec?
20
24
 
@@ -32,6 +36,10 @@ to remember, and checks them on every change:
32
36
  - query objects do not call obvious write methods
33
37
  - generated code follows the same boundaries as hand-written code
34
38
 
39
+ ArchSpec does not try to infer the "true" design pattern of arbitrary Ruby code.
40
+ You describe the architecture your team wants. ArchSpec checks whether the code
41
+ still matches it.
42
+
35
43
  ## Show me the code
36
44
 
37
45
  Start with conventional Rails boundaries:
@@ -7,10 +7,15 @@ module ArchSpec
7
7
  def evaluate(definition, graph, todo: Todo.empty)
8
8
  diagnostics = parser_diagnostics(graph) + definition.rules.flat_map { |rule| rule.evaluate(graph) }
9
9
 
10
+ # Deduplicate before rejecting. One statement can raise several diagnostics
11
+ # that differ only in evidence, because `include Foo` is both an includes
12
+ # edge and a constant reference. Todo entries are matched on evidence, so
13
+ # rejecting first would drop the recorded diagnostic and report its
14
+ # surviving sibling as new.
10
15
  diagnostics
11
- .reject { |diagnostic| graph.suppressed?(diagnostic) || todo.include?(diagnostic) }
12
16
  .sort_by { |d| [d.location.path, d.location.line, d.rule, d.message, d.evidence] }
13
17
  .uniq { |d| [d.rule, d.message, d.location.path, d.location.line] }
18
+ .reject { |diagnostic| graph.suppressed?(diagnostic) || todo.include?(diagnostic) }
14
19
  end
15
20
 
16
21
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ArchSpec
4
- VERSION = '1.0.0'
4
+ VERSION = '1.0.1'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: archspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carmine Paolino
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-08-19 00:00:00.000000000 Z
11
+ date: 2026-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prism