archunit 0.0.1 → 1.0.0
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 +4 -4
- data/API.md +290 -0
- data/CHANGELOG.md +45 -0
- data/README.md +389 -90
- data/assets/logo-rounded.png +0 -0
- data/lib/archunit/common/extraction/dependency_evidence.rb +44 -0
- data/lib/archunit/common/extraction/edge.rb +7 -2
- data/lib/archunit/common/extraction/import_kind.rb +2 -1
- data/lib/archunit/common/extraction/parse_diagnostic.rb +23 -0
- data/lib/archunit/common/fluentapi/check_options.rb +43 -2
- data/lib/archunit/common/fluentapi/checkable.rb +25 -1
- data/lib/archunit/common/logging/check_logger.rb +8 -0
- data/lib/archunit/common/logging/inspection.rb +59 -0
- data/lib/archunit/common/logging/logging_options.rb +5 -0
- data/lib/archunit/common/pattern.rb +18 -8
- data/lib/archunit/common/projection/cycles/johnson_cycles.rb +35 -19
- data/lib/archunit/common/projection/cycles/tarjan_scc.rb +30 -10
- data/lib/archunit/error/source_parse_error.rb +38 -0
- data/lib/archunit/extraction/arch_ignore.rb +91 -0
- data/lib/archunit/extraction/conservative_local_flow.rb +100 -0
- data/lib/archunit/extraction/constant_index.rb +110 -0
- data/lib/archunit/extraction/constant_visitor.rb +129 -0
- data/lib/archunit/extraction/discover_gemspec_load_paths.rb +96 -0
- data/lib/archunit/extraction/enumerate_source_files.rb +23 -8
- data/lib/archunit/extraction/extract_dependencies.rb +82 -14
- data/lib/archunit/extraction/extract_graph.rb +56 -12
- data/lib/archunit/extraction/extract_imports.rb +60 -124
- data/lib/archunit/extraction/extraction_profile.rb +105 -0
- data/lib/archunit/extraction/import_resolver.rb +56 -0
- data/lib/archunit/extraction/import_visitor.rb +104 -0
- data/lib/archunit/extraction/local_string_tracker.rb +80 -0
- data/lib/archunit/extraction/resolve_import.rb +5 -3
- data/lib/archunit/extraction/resolve_load_paths.rb +93 -0
- data/lib/archunit/extraction/resolved_import.rb +5 -2
- data/lib/archunit/extraction/validate_extraction_paths.rb +37 -0
- data/lib/archunit/files/extraction/extract_file_info.rb +14 -5
- data/lib/archunit/files/extraction/file_info.rb +1 -2
- data/lib/archunit/files/fluentapi/depend_on_external_module_condition.rb +2 -0
- data/lib/archunit/files/fluentapi/depend_on_external_module_condition_builder.rb +2 -0
- data/lib/archunit/files/fluentapi/depend_on_file_condition.rb +6 -0
- data/lib/archunit/files/fluentapi/depend_on_file_condition_builder.rb +6 -0
- data/lib/archunit/files/fluentapi/file_condition_builder.rb +12 -0
- data/lib/archunit/files/fluentapi/file_rule_support.rb +8 -1
- data/lib/archunit/files/fluentapi/files.rb +6 -0
- data/lib/archunit/files/fluentapi/match_pattern_file_condition_builder.rb +14 -0
- data/lib/archunit/files/fluentapi/positive_match_pattern_file_condition_builder.rb +2 -0
- data/lib/archunit/graph/fluentapi/graph.rb +5 -0
- data/lib/archunit/graph/fluentapi/project_graph_builder.rb +44 -0
- data/lib/archunit/graph/projection/aggregate_edges.rb +4 -2
- data/lib/archunit/graph/projection/graph_query_options.rb +1 -2
- data/lib/archunit/graph/projection/graph_report_edge.rb +12 -2
- data/lib/archunit/graph/rendering/json_renderer.rb +3 -1
- data/lib/archunit/layers/fluentapi/layer_definition_builder.rb +4 -0
- data/lib/archunit/layers/fluentapi/layer_dependency_rule_builder.rb +4 -0
- data/lib/archunit/layers/fluentapi/layered_architecture.rb +5 -0
- data/lib/archunit/layers/fluentapi/layers.rb +5 -0
- data/lib/archunit/metrics/extraction/extract_distance_info.rb +1 -2
- data/lib/archunit/metrics/extraction/extract_project_info.rb +6 -6
- data/lib/archunit/metrics/extraction/metric_info.rb +2 -4
- data/lib/archunit/metrics/extraction/source_metrics_visitor.rb +2 -4
- data/lib/archunit/metrics/fluentapi/custom_metric_builder.rb +2 -0
- data/lib/archunit/metrics/fluentapi/custom_metric_condition.rb +4 -1
- data/lib/archunit/metrics/fluentapi/logged_metric.rb +23 -0
- data/lib/archunit/metrics/fluentapi/metric_predicate_condition.rb +4 -1
- data/lib/archunit/metrics/fluentapi/metric_report_builder.rb +2 -0
- data/lib/archunit/metrics/fluentapi/metric_selection.rb +18 -2
- data/lib/archunit/metrics/fluentapi/metric_threshold_condition.rb +2 -1
- data/lib/archunit/metrics/fluentapi/metrics.rb +5 -0
- data/lib/archunit/metrics/fluentapi/metrics_builder.rb +15 -0
- data/lib/archunit/slices/fluentapi/diagram_slice_condition.rb +1 -0
- data/lib/archunit/slices/fluentapi/forbidden_slice_dependency_condition.rb +1 -0
- data/lib/archunit/slices/fluentapi/negative_slice_condition_builder.rb +2 -0
- data/lib/archunit/slices/fluentapi/positive_slice_condition_builder.rb +8 -0
- data/lib/archunit/slices/fluentapi/slice_scope_builder.rb +12 -0
- data/lib/archunit/slices/fluentapi/slices.rb +5 -0
- data/lib/archunit/testing/assert_passes.rb +4 -0
- data/lib/archunit/testing/result_factory.rb +16 -4
- data/lib/archunit/testing/rspec_adapter.rb +1 -2
- data/lib/archunit/testing.rb +7 -3
- data/lib/archunit/version.rb +1 -1
- data/lib/archunit.rb +9 -0
- metadata +20 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2ed496ce492119fbd9af981d5f3438d801fdcb27e4de3a620e90ec617ff00146
|
|
4
|
+
data.tar.gz: beacb18bfd3cf88bdd8907e58ba4e9f9eecce710893e77d934f0a5416531fc9f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8393fc4f6c4c7606a671015dc4a6a8e6258e0fe39c6750d2c518b309e00651d72222d92da438e7623dcdee5fce71d4c4a98bc019a1ff1df2228f515da2afc35d
|
|
7
|
+
data.tar.gz: 9690eea349c54b022ca11a92caa72f9342795082ffd520aafec76514f8fb11cbc6368d2e4606db4ac44bc4ff3f4c44ad1ff64fae5de6fd5672b5750175596dfb
|
data/API.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# ArchUnitRuby API Guide
|
|
2
|
+
|
|
3
|
+
This guide is the stable map of ArchUnitRuby's public fluent API. The
|
|
4
|
+
[class and method reference](class_list.html) is generated from the source, while this page explains
|
|
5
|
+
which entry point to choose and how the stages fit together.
|
|
6
|
+
|
|
7
|
+
All rule builders are immutable and lazy. Constructing a rule does not read the project; `check`,
|
|
8
|
+
`measure`, `snapshot`, and export methods are the terminals that perform work.
|
|
9
|
+
|
|
10
|
+
## 1.x compatibility contract
|
|
11
|
+
|
|
12
|
+
This guide defines the supported 1.0 public surface: documented entry points, fluent methods,
|
|
13
|
+
options, public data/error types, and report formats. Additive capabilities can arrive in minor
|
|
14
|
+
versions; incompatible public API changes require a major version and migration notes. Private
|
|
15
|
+
helpers and classes marked as implementation details are not part of that compatibility promise.
|
|
16
|
+
Correctness fixes can change detected violations; compatibility is not a promise to preserve missed
|
|
17
|
+
dependencies or misleading clean results.
|
|
18
|
+
|
|
19
|
+
Static extraction remains bounded and does not execute the target application's source or gemspecs.
|
|
20
|
+
Import-only is the default; expanded constant analysis is explicit opt-in. The documented unresolved
|
|
21
|
+
lookup cases, project source/exclusion policy, strict parser failures, and explicit cache-clearing
|
|
22
|
+
behavior remain important when upgrading. See the README's migration notes for changes from 0.0.1.
|
|
23
|
+
|
|
24
|
+
## Common rule shape
|
|
25
|
+
|
|
26
|
+
~~~ruby
|
|
27
|
+
rule = ArchUnit.project_files
|
|
28
|
+
.in_folder('app/api/**')
|
|
29
|
+
.should_not.depend_on_files
|
|
30
|
+
.in_folder('app/database/**')
|
|
31
|
+
|
|
32
|
+
violations = rule.check
|
|
33
|
+
~~~
|
|
34
|
+
|
|
35
|
+
| Stage | Public vocabulary | Result |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| Entry | `project_files`, `project_layers`, `project_slices`, `project_graph`, `metrics` | Immutable builder |
|
|
38
|
+
| Scope | `with_name`, `in_folder`, `in_path`, `in_file`, `for_classes_matching` | Narrower builder |
|
|
39
|
+
| Mood | `should`, `should_not` | Positive or negated predicate builder |
|
|
40
|
+
| Predicate | File, layer, slice, or metric policy | Checkable rule |
|
|
41
|
+
| Terminal | `check`, `measure`, `snapshot`, `to_*`, `export_as_*` | Data or an artifact |
|
|
42
|
+
|
|
43
|
+
String patterns are anchored globs. `*` stays inside one path segment, `**` crosses zero or more
|
|
44
|
+
segments, and `?` matches one non-separator character. A trailing folder glob such as
|
|
45
|
+
`app/api/**` includes the `app/api` folder itself and every descendant folder. Regular expressions
|
|
46
|
+
are accepted by all pattern selectors except the exact `in_file` selector.
|
|
47
|
+
|
|
48
|
+
Every pattern selector accepts `except:`. Pass a pattern, an array of patterns, or explicit selector
|
|
49
|
+
keys such as `{ in_folder: 'generated', with_name: '*_spec.rb' }`.
|
|
50
|
+
|
|
51
|
+
## File rules
|
|
52
|
+
|
|
53
|
+
Every completed rule implementing `ArchUnit::Checkable` supports `.because(reason)`. Append it
|
|
54
|
+
after all selectors and modifiers to get a distinct frozen rule of the same type, with a trimmed,
|
|
55
|
+
immutable `rationale` string. Non-string or blank reasons raise `ArgumentError`. The original rule,
|
|
56
|
+
violations, graph cache, and `check` options are unchanged. The shared result formatter adds
|
|
57
|
+
`Because: ...` exactly once to failed framework-neutral, RSpec, and Minitest assertions, including
|
|
58
|
+
failed negated RSpec expectations. Successful assertions and raw violation formatting omit it.
|
|
59
|
+
|
|
60
|
+
Start with `ArchUnit.project_files(project_locator = nil)`; `ArchUnit.files` is its short alias.
|
|
61
|
+
Without a locator, ArchUnitRuby searches upward for a `Gemfile` or gemspec.
|
|
62
|
+
|
|
63
|
+
Discovery skips symlinked source files and directories, including aliases inside the project root.
|
|
64
|
+
Direct `FileInfo` extraction validates canonical as well as lexical containment before reading.
|
|
65
|
+
The source walker reuses a single file-status result per entry for type and symlink decisions.
|
|
66
|
+
|
|
67
|
+
The detected root's `.archignore` is applied centrally to graphs and file-based metrics. It accepts
|
|
68
|
+
blank lines, full-line `#` comments, basename globs, root-relative path globs, leading `/` anchors,
|
|
69
|
+
and trailing `/` directory patterns. Backslashes normalize to `/`. Bare names match at any depth;
|
|
70
|
+
patterns containing a separator match from the project root. A directory pattern excludes its
|
|
71
|
+
descendants but not a similarly named regular file. Nested ignore files are not read.
|
|
72
|
+
|
|
73
|
+
Repository patterns combine with defaults or explicit `exclude_patterns:`. An explicit `[]` only
|
|
74
|
+
disables defaults, never repository patterns. The normalized effective pattern set participates in
|
|
75
|
+
graph cache identity; changing the file is noticed on the next check without `clear_cache`.
|
|
76
|
+
Changing source contents still requires explicit cache clearing. Reads are bounded to 1 MiB; UTF-8
|
|
77
|
+
BOMs are accepted and invalid bytes replaced. Symlinks (including project-local ones), non-regular
|
|
78
|
+
files, negation, NUL characters, and a bare `/` are rejected. No Ruby or gemspec code is evaluated.
|
|
79
|
+
|
|
80
|
+
| Scope or predicate | Purpose |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| `with_name(pattern)` | Select or require a filename pattern |
|
|
83
|
+
| `in_folder(pattern)` | Select or require a project-relative directory |
|
|
84
|
+
| `in_path(pattern)` | Select or require a complete project-relative path |
|
|
85
|
+
| `in_file(path)` | Select one exact project-relative file |
|
|
86
|
+
| `have_no_cycles` | Reject cycles among selected project files |
|
|
87
|
+
| `depend_on_files` | Allow or reject dependencies matching target selectors |
|
|
88
|
+
| `depend_on_external_modules` | Allow or reject standard-library or gem imports |
|
|
89
|
+
| `adhere_to(callable, message)` | Evaluate a custom predicate over immutable `FileInfo` values |
|
|
90
|
+
|
|
91
|
+
~~~ruby
|
|
92
|
+
service_scope = ArchUnit.project_files.in_folder('app/services/**')
|
|
93
|
+
|
|
94
|
+
rules = [
|
|
95
|
+
service_scope.should.have_name('*_service.rb'),
|
|
96
|
+
service_scope.should_not.depend_on_files.in_folder('app/controllers/**'),
|
|
97
|
+
service_scope.should_not.depend_on_external_modules.matching('net/http'),
|
|
98
|
+
service_scope.should.adhere_to(
|
|
99
|
+
->(file) { file.lines_of_code < 300 },
|
|
100
|
+
'services must stay below 300 non-blank lines'
|
|
101
|
+
)
|
|
102
|
+
]
|
|
103
|
+
~~~
|
|
104
|
+
|
|
105
|
+
Positive dependency rules are allowlists: every selected dependency must match at least one target.
|
|
106
|
+
Negated dependency rules are blocklists: matching dependencies become violations.
|
|
107
|
+
|
|
108
|
+
Cycle projection uses iterative strongly-connected-component and elementary-cycle traversals, so
|
|
109
|
+
long dependency chains do not consume Ruby's call stack. It still enumerates all elementary cycles;
|
|
110
|
+
dense graphs can have exponentially many results. Scope cycle rules to meaningful components.
|
|
111
|
+
|
|
112
|
+
## Layer rules
|
|
113
|
+
|
|
114
|
+
Use `ArchUnit.project_layers(project_locator = nil)` to name architectural layers and then declare
|
|
115
|
+
their allowed or forbidden relationships.
|
|
116
|
+
|
|
117
|
+
~~~ruby
|
|
118
|
+
rule = ArchUnit.project_layers
|
|
119
|
+
.layer('api').defined_by('app/api/**/*.rb')
|
|
120
|
+
.layer('services').defined_by_folder('app/services/**')
|
|
121
|
+
.layer('database').defined_by('app/database/**/*.rb')
|
|
122
|
+
.where_layer('api').may_only_depend_on_layers('services')
|
|
123
|
+
.where_layer('services').may_only_depend_on_layers('database')
|
|
124
|
+
.where_layer('database').may_only_depend_on_layers
|
|
125
|
+
~~~
|
|
126
|
+
|
|
127
|
+
`may_only_depend_on_layers` creates an allowlist. Calling it without targets seals the layer.
|
|
128
|
+
`may_not_depend_on_layers` creates a blocklist and requires at least one target. Dependencies inside
|
|
129
|
+
one layer are allowed; edges with an unassigned endpoint are ignored.
|
|
130
|
+
|
|
131
|
+
## Slices and diagrams
|
|
132
|
+
|
|
133
|
+
Use `ArchUnit.project_slices(project_locator = nil)` to capture one segment from project paths.
|
|
134
|
+
`(**)` is the glob capture; `defined_by_regex` uses the first regular-expression capture.
|
|
135
|
+
|
|
136
|
+
~~~ruby
|
|
137
|
+
slices = ArchUnit.project_slices.defined_by('lib/my_app/(**)/')
|
|
138
|
+
|
|
139
|
+
dependency_rule = slices.should_not.contain_dependency('api', 'database')
|
|
140
|
+
diagram_rule = slices.should
|
|
141
|
+
.ignoring_external_slices
|
|
142
|
+
.ignoring_orphan_slices
|
|
143
|
+
.adhere_to_diagram_in_file('docs/architecture.puml')
|
|
144
|
+
|
|
145
|
+
plantuml = slices.to_plantuml
|
|
146
|
+
slices.export_as_plantuml('reports/architecture.puml')
|
|
147
|
+
~~~
|
|
148
|
+
|
|
149
|
+
`adhere_to_diagram` accepts inline PlantUML. The supported subset includes components, directed
|
|
150
|
+
dependencies, comments, `@startuml`, and `@enduml`.
|
|
151
|
+
|
|
152
|
+
## Dependency graph reports
|
|
153
|
+
|
|
154
|
+
Use `ArchUnit.project_graph(project_locator = nil)` for queryable snapshots and reports.
|
|
155
|
+
|
|
156
|
+
~~~ruby
|
|
157
|
+
report = ArchUnit.project_graph
|
|
158
|
+
.include_external_dependencies
|
|
159
|
+
.focus_on('app/services/**', 2)
|
|
160
|
+
.collapse_to_folder_depth(2)
|
|
161
|
+
.titled('Service dependencies')
|
|
162
|
+
|
|
163
|
+
summary = report.summary
|
|
164
|
+
snapshot = report.snapshot
|
|
165
|
+
report.export_as_mermaid('reports/services.mmd')
|
|
166
|
+
report.export_as_html('reports/services.html')
|
|
167
|
+
~~~
|
|
168
|
+
|
|
169
|
+
Query modifiers are `focus_on(pattern, depth)`, `reachable_from(pattern)`, and
|
|
170
|
+
`dependents_of(pattern)`. Group nodes with `collapse_to_folder_depth` or
|
|
171
|
+
`collapse_by_pattern(pattern, replacement)`. Formats are DOT, Mermaid, D2, CSV, JSON, and
|
|
172
|
+
self-contained HTML; each provides both `to_<format>` and `export_as_<format>`.
|
|
173
|
+
|
|
174
|
+
## Metrics
|
|
175
|
+
|
|
176
|
+
Start with `ArchUnit.metrics(project_locator = nil)`. Select files with the standard file selectors
|
|
177
|
+
and classes with `for_classes_matching`.
|
|
178
|
+
|
|
179
|
+
~~~ruby
|
|
180
|
+
services = ArchUnit.metrics
|
|
181
|
+
.in_path('app/services/**/*.rb')
|
|
182
|
+
.for_classes_matching('*Service')
|
|
183
|
+
|
|
184
|
+
rules = [
|
|
185
|
+
services.count.method_count.should_be_below_or_equal(20),
|
|
186
|
+
services.lcom.lcom4.should_be(1),
|
|
187
|
+
services.distance.instability.should_be_below(0.8)
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
measurements = services.count.method_count.measure
|
|
191
|
+
services.count.export_as_html('reports/service-counts')
|
|
192
|
+
~~~
|
|
193
|
+
|
|
194
|
+
Count metrics cover methods, fields, lines, statements, imports, classes, and top-level functions.
|
|
195
|
+
Cohesion provides LCOM96a, LCOM96b, LCOM1-5, and LCOM*. Distance metrics include abstractness,
|
|
196
|
+
instability, main-sequence distance, normalized distance, and coupling factor.
|
|
197
|
+
|
|
198
|
+
Thresholds use exactly `should_be_below`, `should_be_above`, `should_be`,
|
|
199
|
+
`should_be_below_or_equal`, `should_be_above_or_equal`, and `should_satisfy`. Use `custom_metric`
|
|
200
|
+
for a calculation over `ClassInfo`; zone guards are `not_in_zone_of_pain` and
|
|
201
|
+
`not_in_zone_of_uselessness`.
|
|
202
|
+
|
|
203
|
+
## Checking and test frameworks
|
|
204
|
+
|
|
205
|
+
Every architecture rule includes `ArchUnit::Checkable` and exposes `check(options = nil)`. It returns
|
|
206
|
+
an array of structured `Violation` values; an architecture disagreement is not an exception until
|
|
207
|
+
an assertion adapter translates it at the test boundary.
|
|
208
|
+
|
|
209
|
+
~~~ruby
|
|
210
|
+
violations = rule.check
|
|
211
|
+
|
|
212
|
+
expect(rule).to pass # RSpec
|
|
213
|
+
assert_passes(rule) # Minitest
|
|
214
|
+
ArchUnit.assert_passes(rule) # Framework-neutral
|
|
215
|
+
~~~
|
|
216
|
+
|
|
217
|
+
Use `ArchUnit.format_violations` or `ArchUnit::ResultFactory` when integrating with another test
|
|
218
|
+
framework or command-line interface.
|
|
219
|
+
|
|
220
|
+
## Check options and logging
|
|
221
|
+
|
|
222
|
+
`ArchUnit::CheckOptions` keeps optional execution behavior out of the fluent sentence:
|
|
223
|
+
|
|
224
|
+
~~~ruby
|
|
225
|
+
logging = ArchUnit::LoggingOptions.new(
|
|
226
|
+
level: :debug,
|
|
227
|
+
output_directory: 'tmp/archunit-logs'
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
options = ArchUnit::CheckOptions.new(
|
|
231
|
+
allow_empty_tests: false,
|
|
232
|
+
clear_cache: false,
|
|
233
|
+
load_paths: ['components/billing/lib'],
|
|
234
|
+
dependency_analysis: :imports,
|
|
235
|
+
logging: logging
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
violations = rule.check(options)
|
|
239
|
+
~~~
|
|
240
|
+
|
|
241
|
+
Zero selected subjects produce `EmptyTestViolation` by default. Set `allow_empty_tests: true` only
|
|
242
|
+
when an empty scope is intentionally valid. Set `clear_cache: true` after modifying the analyzed
|
|
243
|
+
project during one process. `load_paths` adds project-contained source roots without evaluating
|
|
244
|
+
gemspecs or running application code.
|
|
245
|
+
|
|
246
|
+
`dependency_analysis` defaults to `:imports`. Imports accept literal arguments and preceding local
|
|
247
|
+
literal-string assignments in supported straight-line scopes. Aliases, mutation/escape, closures,
|
|
248
|
+
reflection, and unsupported control flow invalidate inferred values; runtime paths are not guessed.
|
|
249
|
+
|
|
250
|
+
For autoloaded projects, opt into known project constant references alongside imports:
|
|
251
|
+
|
|
252
|
+
~~~ruby
|
|
253
|
+
options = ArchUnit::CheckOptions.new(dependency_analysis: :imports_and_constants)
|
|
254
|
+
violations = rule.check(options)
|
|
255
|
+
json = ArchUnit.project_graph.with_check_options(options).to_json
|
|
256
|
+
values = ArchUnit.metrics.distance.instability.measure(options)
|
|
257
|
+
~~~
|
|
258
|
+
|
|
259
|
+
Expanded analysis resolves supported lexical, compact, and root-qualified references to their
|
|
260
|
+
definition files. Explicit superclass and mixin references also contribute edges. Missing or
|
|
261
|
+
ambiguous definitions, inherited-member lookup, and dynamic metaprogramming stay unresolved under
|
|
262
|
+
the bounded contract in the main guide. Namespaces that fail to stabilize within eight index passes
|
|
263
|
+
fall back to import-only results. Unknown third-party constants do not create architecture violations.
|
|
264
|
+
|
|
265
|
+
Declarations include ordinary `=` and conditional `||=`, with lexical/qualified/root-qualified
|
|
266
|
+
ownership and definition evidence. This does not predict runtime execution or load order. Multiple
|
|
267
|
+
definition files remain ambiguous; update-only compound and multiple-assignment forms are not
|
|
268
|
+
inferred as new declaration owners.
|
|
269
|
+
|
|
270
|
+
Expanded edges include `:constant_reference` in `import_kinds` and retain `DependencyEvidence` with
|
|
271
|
+
names and source lines in `edge.evidence` and JSON reports. An import and a constant use supporting
|
|
272
|
+
the same file pair merge into one edge. Import-only edge evidence stays empty for compatibility;
|
|
273
|
+
`ArchUnit::Extraction.extract_imports` retains import call locations in both modes. The analysis
|
|
274
|
+
mode participates in graph-cache identity, and `clear_cache: true` rebuilds the symbol index.
|
|
275
|
+
|
|
276
|
+
## Errors and support
|
|
277
|
+
|
|
278
|
+
Invalid API input raises `ArchUnit::UserError` or `ArgumentError`; library or environment failures
|
|
279
|
+
raise `ArchUnit::TechnicalError`. Rule failures remain violation data.
|
|
280
|
+
|
|
281
|
+
Malformed included Ruby raises `ArchUnit::SourceParseError`, a `TechnicalError`, during graph or
|
|
282
|
+
AST-metric extraction. No partial graph is returned or cached. `error.diagnostics` is an immutable
|
|
283
|
+
array of `ArchUnit::ParseDiagnostic` values with `path`, one-based `line`/`column`, and `message`;
|
|
284
|
+
each value supports `to_h`. RSpec/Minitest assertions propagate this technical failure instead of
|
|
285
|
+
reporting a pass or an ordinary architecture violation. Excluded files are not parsed. The profile's
|
|
286
|
+
`malformed_files` counter records a rejected file; existing cache-clearing rules still apply.
|
|
287
|
+
|
|
288
|
+
See the [main guide](index.html) for installation and complete examples, [SUPPORT.md](https://github.com/LukasNiessen/ArchUnitRuby/blob/main/SUPPORT.md)
|
|
289
|
+
for help, and [SECURITY.md](https://github.com/LukasNiessen/ArchUnitRuby/blob/main/SECURITY.md) for
|
|
290
|
+
responsible vulnerability reporting.
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.0 - 2026-10-26
|
|
4
|
+
|
|
5
|
+
First stable API baseline. The previously unreleased 0.0.2 development work is included here;
|
|
6
|
+
0.0.1 remains the preceding published version. See the README's migration notes before upgrading.
|
|
7
|
+
|
|
8
|
+
- Fail closed on malformed included Ruby in dependency graphs and AST metrics, with structured
|
|
9
|
+
source diagnostics and profile counters instead of silently treating incomplete analysis as clean.
|
|
10
|
+
|
|
11
|
+
- Add development-only locked dependency scanning with advisory refresh, fail-closed health checks,
|
|
12
|
+
Windows path normalization, and retained CI reports.
|
|
13
|
+
|
|
14
|
+
- Avoid stack overflows in strongly-connected-component detection, elementary-cycle traversal,
|
|
15
|
+
and blocked-vertex propagation on deep dependency graphs.
|
|
16
|
+
- Skip symlinked source entries and enforce canonical project boundaries for direct file metadata.
|
|
17
|
+
- Reuse source-entry file status instead of repeating separate directory and file probes.
|
|
18
|
+
- Validate version/changelog/lock consistency in CI and require prepared release metadata before
|
|
19
|
+
RubyGems publication.
|
|
20
|
+
- Enforce the unchanged coverage thresholds on runtime library source only, with a real-report
|
|
21
|
+
regression check that excludes loaded specs, benchmarks, and scripts from the denominator.
|
|
22
|
+
|
|
23
|
+
- Apply bounded project-root `.archignore` exclusions consistently to dependency graphs and metrics,
|
|
24
|
+
with cache invalidation, cross-platform glob handling, and configuration safety checks.
|
|
25
|
+
|
|
26
|
+
- Add immutable `.because(...)` explanations to every executable rule and shared assertion output.
|
|
27
|
+
|
|
28
|
+
- Add opt-in debug inspection of project roots, graphs, and selected files on cold and cached checks.
|
|
29
|
+
- Log passing metric values without repeating custom calculations.
|
|
30
|
+
- Document verbosity, CI log artifacts, and existing colored violation reports.
|
|
31
|
+
|
|
32
|
+
- Add opt-in project constant-reference dependencies with lexical lookup and conservative ambiguity handling.
|
|
33
|
+
- Index lexical, qualified, and root-qualified `||=` declarations in expanded analysis without
|
|
34
|
+
executing source or changing import-only mode.
|
|
35
|
+
- Infer import paths from safely known local literal strings without evaluating application code.
|
|
36
|
+
- Preserve dependency source evidence in expanded mode through graph merging and JSON reporting,
|
|
37
|
+
while keeping import-only edge values compatible with existing consumers.
|
|
38
|
+
- Support check options on metric measurements and add installed-gem extraction regression checks.
|
|
39
|
+
- Make trailing folder globs such as `app/api/**` include the named folder itself.
|
|
40
|
+
- Validate the README quickstart as a passing and failing end-to-end architecture test.
|
|
41
|
+
- Add a curated API guide, reliable Markdown rendering, responsive documentation navigation, and
|
|
42
|
+
documentation quality checks.
|
|
43
|
+
- Add the ArchUnitRuby family logo, social preview, and repository community health files.
|
|
44
|
+
- Discover adjacent `lib` directories in multi-gemspec repositories without evaluating gemspecs.
|
|
45
|
+
- Support validated, project-local custom load paths through `CheckOptions`.
|
|
46
|
+
- Add cold/warm extraction profiling, a reproducible benchmark, and resolution caching.
|
|
47
|
+
|
|
3
48
|
## 0.0.1 - 2026-08-11
|
|
4
49
|
|
|
5
50
|
Initial executable prototype.
|