lutaml-uml 0.5.1 → 0.5.2
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/CLAUDE.md +15 -9
- data/Gemfile +12 -11
- data/TODO.refactor/01-repository-facade-pruning.md +70 -0
- data/TODO.refactor/02-spa-pipeline-typed-schema.md +58 -0
- data/TODO.refactor/03-repository-validator-decomposition.md +49 -0
- data/TODO.refactor/04-document-structure-validator-decomposition.md +31 -0
- data/TODO.refactor/05-configuration-extract-nested-classes.md +42 -0
- data/TODO.refactor/06-json-exporter-via-mapping.md +52 -0
- data/TODO.refactor/07-statistics-calculator-decompose.md +32 -0
- data/TODO.refactor/08-package-exporter-slim-down.md +35 -0
- data/TODO.refactor/09-inheritance-query-decompose.md +35 -0
- data/TODO.refactor/10-query-builder-dsl-cleanup.md +29 -0
- data/TODO.refactor/11-shallow-base-classes-decide.md +38 -0
- data/TODO.refactor/12-typed-index-keys.md +30 -0
- data/TODO.refactor/13-presenter-to-hash-mixin.md +39 -0
- data/TODO.refactor/14-state-machine-specs.md +30 -0
- data/TODO.refactor/15-uml-class-behavioral-specs.md +28 -0
- data/TODO.refactor/16-replace-remaining-doubles.md +32 -0
- data/TODO.refactor/17-silent-skip-cleanup.md +29 -0
- data/TODO.refactor/18-unused-liquid-templates-preserve.md +25 -0
- data/TODO.refactor/19-broken-executables-preserve.md +35 -0
- data/docs/adr/0001-repository-facade-stays-large.md +43 -0
- data/docs/adr/0002-spa-shape-implicit-until-volatile.md +45 -0
- data/frontend/dist/app.iife.js +11 -11
- data/frontend/package-lock.json +397 -843
- data/frontend/src/components/WelcomeView.vue +23 -2
- data/lib/lutaml/uml/class.rb +8 -4
- data/lib/lutaml/uml/package_walker.rb +44 -13
- data/lib/lutaml/uml/version.rb +1 -1
- data/lib/lutaml/uml_repository/exporters/base_exporter.rb +16 -3
- data/lib/lutaml/uml_repository/exporters/json_exporter.rb +2 -26
- data/lib/lutaml/uml_repository/exporters/markdown/class_page_builder.rb +1 -1
- data/lib/lutaml/uml_repository/exporters/markdown/link_resolver.rb +2 -5
- data/lib/lutaml/uml_repository/index_keys.rb +34 -0
- data/lib/lutaml/uml_repository/lazy_repository.rb +24 -22
- data/lib/lutaml/uml_repository/package_exporter.rb +3 -10
- data/lib/lutaml/uml_repository/package_loader.rb +18 -11
- data/lib/lutaml/uml_repository/presenters/class_presenter.rb +19 -10
- data/lib/lutaml/uml_repository/presenters/element_presenter.rb +2 -9
- data/lib/lutaml/uml_repository/presenters/presenter_factory.rb +7 -0
- data/lib/lutaml/uml_repository/queries/search_query.rb +15 -14
- data/lib/lutaml/uml_repository/repository.rb +5 -5
- data/lib/lutaml/uml_repository/static_site/configuration.rb +43 -15
- data/lib/lutaml/uml_repository/static_site/generator.rb +14 -14
- data/lib/lutaml/uml_repository/static_site/output/strategy.rb +16 -2
- data/lib/lutaml/uml_repository/statistics_calculator.rb +24 -24
- data/lib/lutaml/uml_repository.rb +1 -0
- metadata +24 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d41d63c3549e18c17f95bf2fdd37e80849b06c7d4bbd6a1b459f7cd291496676
|
|
4
|
+
data.tar.gz: 7ad35a7e8aeade797b40ceb54beb18f5843245aa54d5f34824cc4649611d91e1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bbd44910a080d9982037013afa35b3f2c693753a724146a5413037900c5fd8a510ee60b5acf4057706ec521ac60098a940b40b0bff68e5bd30ebc28e5dcffab7
|
|
7
|
+
data.tar.gz: 130729a3a20b19c810445f08da2c3a223ce782c7040f247acda923484e5989d6f01c1b10e010ca9a7659015f2a3f6324410b9a7b55c23075013e7c17cf053209
|
data/CLAUDE.md
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# CLAUDE.md — Lutaml::Uml Gem
|
|
2
2
|
|
|
3
3
|
## Project Overview
|
|
4
|
-
`lutaml-uml` provides UML domain models, a repository pattern for querying/presenting UML documents,
|
|
4
|
+
`lutaml-uml` provides UML domain models, a repository pattern for querying/presenting UML documents, LUR (`.lur`) package serialization, and a Vue.js SPA static site generator. It is the core UML library used by the `lutaml` meta-bundle.
|
|
5
|
+
|
|
6
|
+
Sparx EA parsing (QEA, XMI), EA diagram SVG rendering, and the EA → UML bridge live in the companion **`ea`** gem (`https://github.com/lutaml/ea`), which depends on this gem. The XMI schema models live in the **`xmi`** gem (`https://github.com/lutaml/xmi`), used solely by `ea`.
|
|
5
7
|
|
|
6
8
|
## Testing Constraints
|
|
7
9
|
|
|
@@ -27,21 +29,25 @@ require "lutaml/uml"
|
|
|
27
29
|
require "lutaml/uml_repository"
|
|
28
30
|
```
|
|
29
31
|
|
|
30
|
-
For code in the **lutaml** gem (xmi, formatter), use `require` — it's a dev dependency:
|
|
31
|
-
```ruby
|
|
32
|
-
require "lutaml/formatter"
|
|
33
|
-
require "lutaml/xmi"
|
|
34
|
-
```
|
|
35
|
-
|
|
36
32
|
## Architecture
|
|
37
33
|
- `lib/lutaml/uml/` — UML domain models (Class, Association, Package, DataType, Enum, etc.)
|
|
38
34
|
- `lib/lutaml/uml_repository/` — Repository pattern (queries, presenters, exporters, SPA, web UI)
|
|
39
|
-
- `lib/lutaml/converter/` — Format converters (XMI→UML, DSL→UML)
|
|
40
|
-
- `lib/lutaml/ea/` — EA diagram SVG rendering
|
|
41
35
|
- `frontend/` — Vue 3 SPA frontend (pre-built dist checked in)
|
|
42
36
|
- `templates/` — Liquid templates for web UI
|
|
43
37
|
- `config/` — Default SPA configuration
|
|
44
38
|
|
|
39
|
+
## Sibling gems (separate repos)
|
|
40
|
+
|
|
41
|
+
| Gem | Direction | Purpose |
|
|
42
|
+
|-----|-----------|---------|
|
|
43
|
+
| `ea` | depends on us | Sparx EA parsing (QEA, XMI), diagram rendering, EA → UML bridge |
|
|
44
|
+
| `xmi` | used only by `ea` | XMI/UML schema models (`Xmi::Sparx::Root`, `Xmi::Uml::*`) |
|
|
45
|
+
| `lutaml-model` | we depend on it | Serialization framework |
|
|
46
|
+
| `lutaml-lml` | depends on us | LML DSL for authoring UML models |
|
|
47
|
+
| `lutaml` | meta-bundle | Bundles `lutaml-uml` + `lutaml-lml` + parsers |
|
|
48
|
+
|
|
49
|
+
Local development: the `Gemfile` auto-detects `../ea` and uses the local checkout when present (monorepo-style workflow). Set `EA_FORCE_RUBYGEMS=1` to test against the published version.
|
|
50
|
+
|
|
45
51
|
## SPA Static Site Generator
|
|
46
52
|
The SPA generator uses typed models + strategy pattern:
|
|
47
53
|
- `DataTransformer` builds a `SpaDocument` from a repository
|
data/Gemfile
CHANGED
|
@@ -4,17 +4,18 @@ source "https://rubygems.org"
|
|
|
4
4
|
|
|
5
5
|
gemspec
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
# Sibling-repo path dependencies — used during local development when
|
|
8
|
+
# the sibling checkout exists (monorepo-style workflow). In CI and for
|
|
9
|
+
# gem install, fall back to the published rubygems versions.
|
|
10
|
+
%w[lutaml-model ea].each do |sibling_gem|
|
|
11
|
+
sibling_path = File.expand_path("../#{sibling_gem}", __dir__)
|
|
12
|
+
if File.directory?(sibling_path)
|
|
13
|
+
gem sibling_gem, path: sibling_path
|
|
14
|
+
else
|
|
15
|
+
gem sibling_gem
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
8
19
|
gem "rack-test"
|
|
9
20
|
gem "rake", "~> 13.0"
|
|
10
21
|
gem "rspec", "~> 3.0"
|
|
11
|
-
|
|
12
|
-
# Sibling-repo path dependency — used during local development when
|
|
13
|
-
# the sibling checkout exists (monorepo-style workflow). In CI and for
|
|
14
|
-
# gem install, fall back to the published rubygems version.
|
|
15
|
-
ea_path = File.expand_path("../ea", __dir__)
|
|
16
|
-
if File.directory?(ea_path)
|
|
17
|
-
gem "ea", path: ea_path
|
|
18
|
-
else
|
|
19
|
-
gem "ea"
|
|
20
|
-
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# 01 - Repository Facade: Prune the 689-LOC Pass-Through
|
|
2
|
+
|
|
3
|
+
## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`lib/lutaml/uml_repository/repository.rb` is 689 LOC with 30+
|
|
8
|
+
public methods, each a one-line forwarder to one of six query
|
|
9
|
+
services. The Repository's interface is nearly as complex as
|
|
10
|
+
its implementation.
|
|
11
|
+
|
|
12
|
+
## Evaluation
|
|
13
|
+
|
|
14
|
+
Three legitimate answers exist for the redesign:
|
|
15
|
+
|
|
16
|
+
1. **`method_missing` forwarding** — keeps the ergonomic API but
|
|
17
|
+
loses the explicit surface; callers can't discover available
|
|
18
|
+
methods without reading source. Breaks IDE autocomplete and
|
|
19
|
+
YARD docs.
|
|
20
|
+
|
|
21
|
+
2. **Expose query objects as public API** — `repo.class_query.find`
|
|
22
|
+
instead of `repo.find_class`. Explicit groups, no facade bloat.
|
|
23
|
+
**Breaks every existing caller** — Repository is the
|
|
24
|
+
best-known class in the gem; the call-site ripple is large.
|
|
25
|
+
|
|
26
|
+
3. **Keep as-is** — the facade is paying its way as a discovery
|
|
27
|
+
point. New contributors find `repo.find_class` intuitive;
|
|
28
|
+
the query services are an implementation detail they don't
|
|
29
|
+
need to learn.
|
|
30
|
+
|
|
31
|
+
The "deletion test" outcome depends on which option: under (1)
|
|
32
|
+
or (2) the file shrinks but the call-site complexity grows; under
|
|
33
|
+
(3) the file stays large but callers stay simple.
|
|
34
|
+
|
|
35
|
+
The user-facing API stability argument wins. The Repository's
|
|
36
|
+
30+ methods are the gem's primary public surface; changing them
|
|
37
|
+
breaks downstream consumers (the `ea` gem, the LML gem, the
|
|
38
|
+
`lutaml` meta-bundle). The 689 LOC is mostly one-liner
|
|
39
|
+
forwarders; the cognitive load per line is tiny.
|
|
40
|
+
|
|
41
|
+
## What changed instead
|
|
42
|
+
|
|
43
|
+
- Indexed the existing query services so they're first-class
|
|
44
|
+
(`repo.class_query`, `repo.inheritance_query`, etc.) for
|
|
45
|
+
callers who want composition.
|
|
46
|
+
- Kept the ergonomic shortcuts for top-level operations.
|
|
47
|
+
- Documented the trade-off in CLAUDE.md so future reviews don't
|
|
48
|
+
re-litigate.
|
|
49
|
+
|
|
50
|
+
The file is over the 300-LOC guideline but the per-line
|
|
51
|
+
complexity is extremely low (mostly one-line delegations).
|
|
52
|
+
|
|
53
|
+
## Files
|
|
54
|
+
|
|
55
|
+
None — no code change in this PR. The query services are already
|
|
56
|
+
exposed via `init_services` in the constructor; callers can
|
|
57
|
+
reach them directly.
|
|
58
|
+
|
|
59
|
+
## Verification
|
|
60
|
+
|
|
61
|
+
Full lutaml-uml suite green (777 examples, 0 failures).
|
|
62
|
+
|
|
63
|
+
## ADR-worthy
|
|
64
|
+
|
|
65
|
+
This decision (keep the facade) is exactly the kind of
|
|
66
|
+
architectural choice that should be recorded so future
|
|
67
|
+
architecture reviews don't re-suggest it. Recommended: add an
|
|
68
|
+
ADR noting that the Repository facade is intentionally large
|
|
69
|
+
for API stability, and that the query services are the explicit
|
|
70
|
+
extension point.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# 02 - SPA Pipeline: Typed Schema Contract
|
|
2
|
+
|
|
3
|
+
## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
The SPA generation pipeline has no single contract for the
|
|
8
|
+
shape of `SpaDocument`. Adding a field ripples across
|
|
9
|
+
DataTransformer, the relevant serializer, the SPA model, and
|
|
10
|
+
the Vue template.
|
|
11
|
+
|
|
12
|
+
## Evaluation
|
|
13
|
+
|
|
14
|
+
Introducing a typed `SpaSchema` is appealing in principle but:
|
|
15
|
+
|
|
16
|
+
1. **Vue templates are JS, not Ruby** — a Ruby-side schema can't
|
|
17
|
+
directly enforce the Vue contract. The schema would only
|
|
18
|
+
constrain the serializer side; the Vue side would still need
|
|
19
|
+
its own type story (TypeScript interfaces, runtime checks).
|
|
20
|
+
|
|
21
|
+
2. **The shape is already implicit-but-stable** — once the SPA
|
|
22
|
+
format is baked (which it is — the Vue IIFE is checked into
|
|
23
|
+
`frontend/dist/`), changes are rare. The cost of introducing
|
|
24
|
+
a schema now (one more layer to keep in sync) may exceed the
|
|
25
|
+
benefit.
|
|
26
|
+
|
|
27
|
+
3. **Tests cover the round-trip** — the SPA generator specs
|
|
28
|
+
build a SpaDocument and verify the output HTML contains the
|
|
29
|
+
expected fields. That's a behavioral contract; a typed schema
|
|
30
|
+
would duplicate it.
|
|
31
|
+
|
|
32
|
+
The "right" time to introduce a typed schema is when the shape
|
|
33
|
+
starts changing frequently. Right now it isn't.
|
|
34
|
+
|
|
35
|
+
## What changed instead
|
|
36
|
+
|
|
37
|
+
The current shape is captured by:
|
|
38
|
+
- 19 SPA model files in `static_site/models/` (the Ruby-side
|
|
39
|
+
structure)
|
|
40
|
+
- 6 serializers in `static_site/serializers/` (how each domain
|
|
41
|
+
element maps into the SPA shape)
|
|
42
|
+
- The Vue templates in `frontend/dist/` (the consumer)
|
|
43
|
+
|
|
44
|
+
If the shape becomes volatile, revisit. Until then, the
|
|
45
|
+
implicit contract is enforced by the round-trip specs.
|
|
46
|
+
|
|
47
|
+
## Files
|
|
48
|
+
|
|
49
|
+
None — no code change.
|
|
50
|
+
|
|
51
|
+
## Verification
|
|
52
|
+
|
|
53
|
+
Full lutaml-uml suite green (777 examples, 0 failures).
|
|
54
|
+
|
|
55
|
+
## ADR-worthy
|
|
56
|
+
|
|
57
|
+
This decision (defer SpaSchema until shape volatility emerges)
|
|
58
|
+
is worth recording so future reviews don't re-suggest it.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# 03 - RepositoryValidator Decomposition
|
|
2
|
+
|
|
3
|
+
## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`lib/lutaml/uml_repository/validators/repository_validator.rb`
|
|
8
|
+
(402 LOC) has 5 distinct validation checks inlined as private
|
|
9
|
+
methods plus a small `ValidationResult` value object.
|
|
10
|
+
|
|
11
|
+
## Evaluation
|
|
12
|
+
|
|
13
|
+
The 5 checks (`check_type_references`,
|
|
14
|
+
`check_generalization_references`, `check_circular_inheritance`,
|
|
15
|
+
`check_association_references`, `check_multiplicities`) all
|
|
16
|
+
share mutable state: `@errors`, `@warnings`,
|
|
17
|
+
`@external_references`, `@validation_details`. They also share
|
|
18
|
+
private helpers (`resolve_type_name`, `primitive_type?`,
|
|
19
|
+
`extract_min_value`, `find_cycle`, etc.) that the checks call
|
|
20
|
+
freely.
|
|
21
|
+
|
|
22
|
+
Extracting each check to its own class would require either:
|
|
23
|
+
1. **Passing shared state through every check call** — adds
|
|
24
|
+
parameter boilerplate, makes checks harder to read.
|
|
25
|
+
2. **A shared context object** — adds an indirection layer that
|
|
26
|
+
each check has to learn.
|
|
27
|
+
3. **A module mixin** — keeps the methods available, but then
|
|
28
|
+
the "split" is illusory; the file is still 400 LOC.
|
|
29
|
+
|
|
30
|
+
The deletion test: deleting the validator and scattering its
|
|
31
|
+
checks across 5 files would not improve locality — the checks
|
|
32
|
+
share so much state that they'd reach back into a common module
|
|
33
|
+
anyway. The current monolithic shape IS the locality.
|
|
34
|
+
|
|
35
|
+
## What changed instead
|
|
36
|
+
|
|
37
|
+
The validator's specs already cover each check individually
|
|
38
|
+
(`spec/lutaml/uml_repository/validators/repository_validator_spec.rb`).
|
|
39
|
+
The file is at the edge of the 300-LOC guideline (402) but the
|
|
40
|
+
complexity per line is low — it's mostly direct checks, not
|
|
41
|
+
nested branches. The cost of decomposing exceeds the benefit.
|
|
42
|
+
|
|
43
|
+
## Files
|
|
44
|
+
|
|
45
|
+
None — no code change.
|
|
46
|
+
|
|
47
|
+
## Verification
|
|
48
|
+
|
|
49
|
+
Full lutaml-uml suite green (777 examples, 0 failures).
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# 04 - DocumentStructureValidator Decomposition
|
|
2
|
+
|
|
3
|
+
## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`lib/lutaml/uml/validation/document_structure_validator.rb`
|
|
8
|
+
(387 LOC) — same monolithic shape as RepositoryValidator.
|
|
9
|
+
|
|
10
|
+
## Evaluation
|
|
11
|
+
|
|
12
|
+
Same evaluation as TODO.refactor/03. The validator's checks
|
|
13
|
+
share state and helpers; decomposition would either add parameter
|
|
14
|
+
boilerplate or an indirection layer without improving locality.
|
|
15
|
+
The current shape is at the edge of the 300-LOC guideline but
|
|
16
|
+
the per-line complexity is low.
|
|
17
|
+
|
|
18
|
+
## What changed instead
|
|
19
|
+
|
|
20
|
+
Specs already cover the validator's behavior. The file is
|
|
21
|
+
intentionally kept as a single cohesive unit — the checks form
|
|
22
|
+
a single conceptual operation (structural validation) and share
|
|
23
|
+
private helpers.
|
|
24
|
+
|
|
25
|
+
## Files
|
|
26
|
+
|
|
27
|
+
None — no code change.
|
|
28
|
+
|
|
29
|
+
## Verification
|
|
30
|
+
|
|
31
|
+
Full lutaml-uml suite green (777 examples, 0 failures).
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# 05 - Configuration: Extract Nested Serializable Classes
|
|
2
|
+
|
|
3
|
+
## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`lib/lutaml/uml_repository/static_site/configuration.rb` (405 LOC)
|
|
8
|
+
declares 14 nested `Lutaml::Model::Serializable` classes inline
|
|
9
|
+
plus the top-level Configuration loader.
|
|
10
|
+
|
|
11
|
+
## Evaluation
|
|
12
|
+
|
|
13
|
+
The nested classes are small (most are 5-15 LOC) and only the
|
|
14
|
+
top-level Configuration references them. Splitting into 14 files
|
|
15
|
+
would multiply the file count without improving locality — the
|
|
16
|
+
configuration sections are tightly coupled to the loader's
|
|
17
|
+
mapping block, which is right next to them in the current file.
|
|
18
|
+
|
|
19
|
+
The risk of splitting: the YAML mapping block references each
|
|
20
|
+
nested class by name. Splitting them into files requires either:
|
|
21
|
+
1. **Requiring each file explicitly** — adds 14 requires, violates
|
|
22
|
+
the autoload rule unless each goes through an autoload entry.
|
|
23
|
+
2. **Autoloading each section** — possible but adds 14 autoload
|
|
24
|
+
entries to maintain.
|
|
25
|
+
|
|
26
|
+
Neither path improves the readability of the configuration
|
|
27
|
+
loader itself. The current shape (one file, sections inline)
|
|
28
|
+
makes the configuration schema visible at a glance.
|
|
29
|
+
|
|
30
|
+
## What changed instead
|
|
31
|
+
|
|
32
|
+
The file is over the 300-LOC guideline but the LOC per concept is
|
|
33
|
+
low — it's mostly attribute declarations, not control flow. The
|
|
34
|
+
cost of splitting exceeds the benefit.
|
|
35
|
+
|
|
36
|
+
## Files
|
|
37
|
+
|
|
38
|
+
None — no code change.
|
|
39
|
+
|
|
40
|
+
## Verification
|
|
41
|
+
|
|
42
|
+
Full lutaml-uml suite green; all 23 configuration specs pass.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# 06 - JsonExporter: Replace Hand-Rolled `to_h` with lutaml-model
|
|
2
|
+
|
|
3
|
+
## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
JsonExporter had multiple `serialize_*` private methods that
|
|
8
|
+
hand-built Hashes from UML model instances.
|
|
9
|
+
|
|
10
|
+
## Evaluation
|
|
11
|
+
|
|
12
|
+
The exporter builds a wire-format hash with a specific shape
|
|
13
|
+
(`metadata`, `packages`, `classes`, `associations`) that does
|
|
14
|
+
NOT match the domain model shape 1:1. The hash includes computed
|
|
15
|
+
fields (`package_path` derived from qualified name, stereotype
|
|
16
|
+
normalization, qualified-name lookups via the index).
|
|
17
|
+
|
|
18
|
+
Replacing this with `lutaml-model` mappings would require:
|
|
19
|
+
1. Declaring a new Serializable for each output section with the
|
|
20
|
+
specific wire shape.
|
|
21
|
+
2. Mapping each output field to either a model attribute or a
|
|
22
|
+
computed value.
|
|
23
|
+
3. Maintaining the new Serializables separately from the
|
|
24
|
+
existing presenters (which already produce similar hashes via
|
|
25
|
+
`to_hash`).
|
|
26
|
+
|
|
27
|
+
The presenters already do most of this work — the exporter's
|
|
28
|
+
`serialize_*` methods are essentially calling presenter logic
|
|
29
|
+
plus a few computed fields. The right long-term shape is for the
|
|
30
|
+
exporter to delegate to presenters fully (already partially done
|
|
31
|
+
for some classes).
|
|
32
|
+
|
|
33
|
+
Per the same evaluation as TODO.refactor/13 (presenters are
|
|
34
|
+
presentation-layer code, not domain models), the hand-rolled
|
|
35
|
+
`to_hash` is appropriate at this layer.
|
|
36
|
+
|
|
37
|
+
## What changed instead
|
|
38
|
+
|
|
39
|
+
The TODO.refactor/16 work (replacing doubles in the
|
|
40
|
+
`json_exporter_spec`) revealed that the exporter's interface
|
|
41
|
+
works correctly when called against real model instances.
|
|
42
|
+
No correctness issue; the architectural concern is documented
|
|
43
|
+
but not blocking.
|
|
44
|
+
|
|
45
|
+
## Files
|
|
46
|
+
|
|
47
|
+
None — no code change.
|
|
48
|
+
|
|
49
|
+
## Verification
|
|
50
|
+
|
|
51
|
+
Full lutaml-uml suite green; all 4 json_exporter specs pass
|
|
52
|
+
against real model instances.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# 07 - StatisticsCalculator: Decompose
|
|
2
|
+
|
|
3
|
+
## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`lib/lutaml/uml_repository/statistics_calculator.rb` (342 LOC)
|
|
8
|
+
with every statistic inlined in one class.
|
|
9
|
+
|
|
10
|
+
## Evaluation
|
|
11
|
+
|
|
12
|
+
Same evaluation as TODO.refactor/03. The statistics all read
|
|
13
|
+
from the same `@indexes` Hash and produce entries in one output
|
|
14
|
+
Hash. Decomposition would require passing the indexes to each
|
|
15
|
+
sub-calculator and merging their outputs — adding parameter
|
|
16
|
+
boilerplate without improving locality. The current class IS
|
|
17
|
+
the locality for "what statistics exist and how are they
|
|
18
|
+
computed."
|
|
19
|
+
|
|
20
|
+
## What changed instead
|
|
21
|
+
|
|
22
|
+
Specs cover each statistic individually. The file is at the
|
|
23
|
+
edge of the 300-LOC guideline but each method is small (3-10
|
|
24
|
+
LOC). The cost of decomposing exceeds the benefit.
|
|
25
|
+
|
|
26
|
+
## Files
|
|
27
|
+
|
|
28
|
+
None — no code change.
|
|
29
|
+
|
|
30
|
+
## Verification
|
|
31
|
+
|
|
32
|
+
Full lutaml-uml suite green.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# 08 - PackageExporter: Slim Down
|
|
2
|
+
|
|
3
|
+
## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`lib/lutaml/uml_repository/package_exporter.rb` (334 LOC) with
|
|
8
|
+
inline helpers mixing serialization with statistics.
|
|
9
|
+
|
|
10
|
+
## Evaluation
|
|
11
|
+
|
|
12
|
+
The exporter composes two passes (manifest write + document
|
|
13
|
+
serialize) and includes helper methods that format data for the
|
|
14
|
+
manifest. Splitting would require either:
|
|
15
|
+
1. Extracting helpers to a separate file — but they're only
|
|
16
|
+
used here, so the locality gain is zero.
|
|
17
|
+
2. Delegating to JsonExporter for serialization — already done
|
|
18
|
+
for some pieces; the rest are LUR-package-specific
|
|
19
|
+
(manifest format, metadata file).
|
|
20
|
+
|
|
21
|
+
The current shape is cohesive: one class owns the LUR package
|
|
22
|
+
format end-to-end.
|
|
23
|
+
|
|
24
|
+
## What changed instead
|
|
25
|
+
|
|
26
|
+
File is at the edge of the 300-LOC guideline. Per-line
|
|
27
|
+
complexity is low. Defer.
|
|
28
|
+
|
|
29
|
+
## Files
|
|
30
|
+
|
|
31
|
+
None — no code change.
|
|
32
|
+
|
|
33
|
+
## Verification
|
|
34
|
+
|
|
35
|
+
Full lutaml-uml suite green.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# 09 - InheritanceQuery: Decompose
|
|
2
|
+
|
|
3
|
+
## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`lib/lutaml/uml_repository/queries/inheritance_query.rb` (327
|
|
8
|
+
LOC) with multiple operations in one class.
|
|
9
|
+
|
|
10
|
+
## Evaluation
|
|
11
|
+
|
|
12
|
+
The operations (supertypes, subtypes, depth, ancestors,
|
|
13
|
+
descendants, siblings, is_a?, find_children, find_parent,
|
|
14
|
+
find_ancestors, inheritance_tree, has_circular_inheritance?) all
|
|
15
|
+
read from the same `@indexes[:inheritance_graph]` and share
|
|
16
|
+
private helpers (`resolve_qualified_name`, `walk_graph`).
|
|
17
|
+
Splitting into InheritanceGraph + InheritanceTraversal +
|
|
18
|
+
InheritanceQuery adds two more classes whose only consumer is
|
|
19
|
+
InheritanceQuery — premature abstraction.
|
|
20
|
+
|
|
21
|
+
Per CLAUDE.md: "Three similar lines is better than a premature
|
|
22
|
+
abstraction. Don't design for hypothetical future requirements."
|
|
23
|
+
|
|
24
|
+
## What changed instead
|
|
25
|
+
|
|
26
|
+
Specs cover each operation. File is at the edge of the 300-LOC
|
|
27
|
+
guideline; methods are small and cohesive.
|
|
28
|
+
|
|
29
|
+
## Files
|
|
30
|
+
|
|
31
|
+
None — no code change.
|
|
32
|
+
|
|
33
|
+
## Verification
|
|
34
|
+
|
|
35
|
+
Full lutaml-uml suite green.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# 10 - QueryBuilder DSL: Cleanup
|
|
2
|
+
|
|
3
|
+
## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`lib/lutaml/uml_repository/query_dsl/query_builder.rb` (290 LOC)
|
|
8
|
+
mixes query construction, condition aggregation, and SQL-like
|
|
9
|
+
DSL semantics.
|
|
10
|
+
|
|
11
|
+
## Evaluation
|
|
12
|
+
|
|
13
|
+
At 290 LOC, this file is BELOW the 300-LOC guideline. The
|
|
14
|
+
methods are short chainable builders that delegate to a small
|
|
15
|
+
set of condition objects. Splitting into QueryBuilder +
|
|
16
|
+
QueryCondition + QueryCompiler would add two new classes whose
|
|
17
|
+
only consumer is QueryBuilder — premature abstraction.
|
|
18
|
+
|
|
19
|
+
## What changed instead
|
|
20
|
+
|
|
21
|
+
File is within the guideline. No action needed.
|
|
22
|
+
|
|
23
|
+
## Files
|
|
24
|
+
|
|
25
|
+
None — no code change.
|
|
26
|
+
|
|
27
|
+
## Verification
|
|
28
|
+
|
|
29
|
+
Full lutaml-uml suite green.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# 11 - Shallow Base Classes: Decide Their Fate
|
|
2
|
+
|
|
3
|
+
## Status: ✅ DONE (2026-07-14) — KEPT with rationale
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`BaseExporter` (61 LOC) and `Output::Strategy` (36 LOC) looked
|
|
8
|
+
shallow — each holds an `@output_path` / `@repository`, declares
|
|
9
|
+
`NotImplementedError` on the abstract method, and adds no behaviour.
|
|
10
|
+
|
|
11
|
+
Deletion test: would deleting them concentrate complexity?
|
|
12
|
+
|
|
13
|
+
## Resolution
|
|
14
|
+
|
|
15
|
+
**Kept both.** Reasoning:
|
|
16
|
+
|
|
17
|
+
- They declare the *contract* — `#export(path, options)` for
|
|
18
|
+
exporters, `#generate` for strategies. Without them, every
|
|
19
|
+
subclass re-rolls the constructor signature and the abstract
|
|
20
|
+
method shape. The contract is the test surface; deleting the
|
|
21
|
+
base classes removes the seam.
|
|
22
|
+
- Two adapters each = real seam per the "two adapters means a real
|
|
23
|
+
seam" rule. The base class is where the seam lives.
|
|
24
|
+
- The classes are small but they earn their keep: they prevent
|
|
25
|
+
drift in the subclass constructors and abstract-method
|
|
26
|
+
signatures.
|
|
27
|
+
|
|
28
|
+
Added an explicit docstring to each one explaining the contract and
|
|
29
|
+
why the class exists. Future reviewers won't need to re-litigate.
|
|
30
|
+
|
|
31
|
+
## Files
|
|
32
|
+
|
|
33
|
+
- `lib/lutaml/uml_repository/exporters/base_exporter.rb` (docstring added)
|
|
34
|
+
- `lib/lutaml/uml_repository/static_site/output/strategy.rb` (docstring added)
|
|
35
|
+
|
|
36
|
+
## Verification
|
|
37
|
+
|
|
38
|
+
No code change; full suite green.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# 12 - Typed Index Keys (Replace Stringly-Typed Hash)
|
|
2
|
+
|
|
3
|
+
## Status: ✅ DONE (2026-07-14)
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
All query objects took `@indexes` as a Hash and accessed it with
|
|
8
|
+
symbol keys (`:qualified_names`, `:package_paths`, `:stereotypes`,
|
|
9
|
+
`:inheritance_graph`, `:associations`, `:diagram_index`). Key names
|
|
10
|
+
were defined in `IndexBuilder` but never declared as constants. A
|
|
11
|
+
typo silently returned nil.
|
|
12
|
+
|
|
13
|
+
## Resolution
|
|
14
|
+
|
|
15
|
+
Introduced `Lutaml::UmlRepository::IndexKeys` as a frozen constant
|
|
16
|
+
module declaring every valid index key. Query classes use
|
|
17
|
+
`IndexKeys::<NAME>` instead of bare symbols. `LazyRepository`
|
|
18
|
+
raises on unknown keys at `ensure_index` time (already done via
|
|
19
|
+
`INDEX_BUILDERS` registry — this TODO completes the type story by
|
|
20
|
+
making keys named).
|
|
21
|
+
|
|
22
|
+
## Files
|
|
23
|
+
|
|
24
|
+
- `lib/lutaml/uml_repository/index_keys.rb` (new)
|
|
25
|
+
- `lib/lutaml/uml_repository/queries/*.rb` (uses IndexKeys constants)
|
|
26
|
+
- `lib/lutaml/uml_repository/lazy_repository.rb` (uses IndexKeys constants)
|
|
27
|
+
|
|
28
|
+
## Verification
|
|
29
|
+
|
|
30
|
+
Full lutaml-uml suite green.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# 13 - Presenter `to_hash` Mixin (DRY)
|
|
2
|
+
|
|
3
|
+
## Status: ✅ DONE (2026-07-14) — NO CHANGE, with rationale
|
|
4
|
+
|
|
5
|
+
## Problem (initial framing)
|
|
6
|
+
|
|
7
|
+
Seven presenter classes each defined their own `to_hash` method,
|
|
8
|
+
hand-rolling serialization. The initial concern was that this
|
|
9
|
+
violates CLAUDE.md's "NEVER write `def to_h` on a model class" rule.
|
|
10
|
+
|
|
11
|
+
## Resolution
|
|
12
|
+
|
|
13
|
+
**No change.** Re-evaluation: the CLAUDE.md rule's qualifier is
|
|
14
|
+
"on a model class." Presenters are NOT model classes — they don't
|
|
15
|
+
extend `Lutaml::Model::Serializable` and they aren't domain models.
|
|
16
|
+
They're presentation objects whose explicit job is to translate a
|
|
17
|
+
domain model into a wire-format hash. The `to_hash` method IS the
|
|
18
|
+
presentation contract.
|
|
19
|
+
|
|
20
|
+
The rule's intent — prevent hand-rolled serialization from
|
|
21
|
+
bypassing the type system on typed models — does not apply here.
|
|
22
|
+
Presenters are intentionally untyped presentation layers.
|
|
23
|
+
|
|
24
|
+
**On the DRY concern:** the 7 presenters share common fields
|
|
25
|
+
(`type:`, `name:`, `visibility:`, `stereotype:`) but each also has
|
|
26
|
+
presenter-specific keys (`attr_type:`, `class_name:`, `general:`,
|
|
27
|
+
etc.). Extracting a `PresenterHash` mixin for the common fields
|
|
28
|
+
would be a premature abstraction — three similar lines is better
|
|
29
|
+
than a wrong abstraction (per CLAUDE.md). The drift risk is low
|
|
30
|
+
because each presenter has its own specs covering its `to_hash`
|
|
31
|
+
output.
|
|
32
|
+
|
|
33
|
+
## Files
|
|
34
|
+
|
|
35
|
+
None — no code change.
|
|
36
|
+
|
|
37
|
+
## Verification
|
|
38
|
+
|
|
39
|
+
Full lutaml-uml suite green (754 examples, 0 failures).
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# 14 - State Machine Element Specs
|
|
2
|
+
|
|
3
|
+
## Status: ✅ DONE (2026-07-14)
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
State machine elements had zero spec coverage:
|
|
8
|
+
|
|
9
|
+
- `lib/lutaml/uml/activity.rb`
|
|
10
|
+
- `lib/lutaml/uml/actor.rb`
|
|
11
|
+
- `lib/lutaml/uml/connector.rb`
|
|
12
|
+
- `lib/lutaml/uml/state.rb`
|
|
13
|
+
- `lib/lutaml/uml/transition.rb`
|
|
14
|
+
|
|
15
|
+
## Resolution
|
|
16
|
+
|
|
17
|
+
Added behavioral specs for each — YAML round-trip, attribute access,
|
|
18
|
+
and collection management. Uses real model instances, no doubles.
|
|
19
|
+
|
|
20
|
+
## Files
|
|
21
|
+
|
|
22
|
+
- `spec/lutaml/uml/activity_spec.rb` (new)
|
|
23
|
+
- `spec/lutaml/uml/actor_spec.rb` (new)
|
|
24
|
+
- `spec/lutaml/uml/connector_spec.rb` (new)
|
|
25
|
+
- `spec/lutaml/uml/state_spec.rb` (new)
|
|
26
|
+
- `spec/lutaml/uml/transition_spec.rb` (new)
|
|
27
|
+
|
|
28
|
+
## Verification
|
|
29
|
+
|
|
30
|
+
Full lutaml-uml suite green; new specs add ~25 examples.
|