archspec 1.0.0 → 1.1.0.rc1

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: 3d1149a18e2ffb69c03d15e95ca539d3c42c1fda406376069313b9e07365a1b1
4
+ data.tar.gz: 290b05eb65ad246ebd1d81a025abe013a3150d5afc7e0bb6cb834dae3f64a429
5
5
  SHA512:
6
- metadata.gz: 56bd81e5ee880404a80c7f29d498c58903bd8d087c5e8deb80af060d6d169c3b40e471eb09758500d6522516d83090b435b45ab243f780978539840b9548681e
7
- data.tar.gz: a3dc2d41b6b97edd3ddacb8c84632c0b8ae81471c9132ec02ddbf66ce68d8dd916d068aeb366a8bed99f996ed6f81cda8a3a48b502459b0d25afc525d81df95d
6
+ metadata.gz: 2e02c22290a4616b60edb2a5fd97ba991237e6da3b2fe495a5fde7802ac520fa432c821a9eb895c98333ce1acb170aff476ffa016c1de924199a9b47463d3994
7
+ data.tar.gz: 99d07ea4daff657b1823a81beff200f2139a3646c1ef7ad1bb3f041f13a9a03d7bd39a8b5b4b0176bda00ca13506043a691a1c93f65f54eed33a17c129e0f6af
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 static analysis.</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)
10
+
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)
4
14
 
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.
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">
10
16
 
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.
17
+ </div>
14
18
 
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.
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: Rubydex builds the semantic index, Prism supplies a small syntax overlay, the app never boots, and no AI is involved in checking your code.
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:
@@ -92,10 +100,13 @@ Write local rules in plain Ruby:
92
100
  component :controllers, in: "app/controllers/**/*.rb"
93
101
  component :models, in: "app/models/**/*.rb"
94
102
  component :services, in: "app/services/**/*.rb"
103
+ component :records, descendants_of: "ApplicationRecord"
95
104
 
96
105
  controllers.can_only_use :models, :services
97
106
  models.cannot_use :controllers
98
- services.cannot_call :render, :redirect_to, :params, :session
107
+ services.cannot_call :render, :redirect_to, :params, :session,
108
+ because: "services do not own the HTTP response"
109
+ models.cannot_call :find_by_sql, receiver: "ActiveRecord::Base"
99
110
  services.cannot_instantiate_and_invoke
100
111
  ```
101
112
 
@@ -116,7 +127,7 @@ architecture :cqrs,
116
127
  - **Layers:** dependency direction and cycles
117
128
  - **Rails:** controller APIs kept out of models and services
118
129
  - **Architectures:** Rails, vanilla Rails, layered, hexagonal, clean, modular monolith, CQRS, event-driven, and Ruby conventions bundles
119
- - **Protocols:** required methods such as `resolve`, `perform`, or project-specific interfaces
130
+ - **Protocols:** instance/class APIs and callable signatures such as `call(amount, actor:)`
120
131
  - **Naming:** conventions on a component's public API, such as banning `get_`/`set_` or pairing `with_x` with `without_x`
121
132
  - **Objects:** rules against one-shot `Something.new(...).whatever` command objects
122
133
  - **Empty components:** directories that must stay empty, like `app/services` in vanilla Rails
@@ -128,6 +139,8 @@ ArchSpec does not check Zeitwerk constant names. Zeitwerk does that itself. Add
128
139
 
129
140
  ## Installation
130
141
 
142
+ ArchSpec requires Ruby 3.2 or newer.
143
+
131
144
  Add ArchSpec to your Gemfile:
132
145
 
133
146
  ```ruby
@@ -164,8 +177,9 @@ bundle exec archspec check --update-todo
164
177
  bundle exec archspec explain app/models/user.rb
165
178
  ```
166
179
 
167
- `explain` shows why a file or constant belongs to a component and which outgoing
168
- facts ArchSpec found:
180
+ `explain` shows why a file or constant belongs to a component, its resolved
181
+ ancestry, outgoing facts, incoming dependencies, and anything the analysis
182
+ could not prove:
169
183
 
170
184
  ```text
171
185
  app/models/user.rb