sts 0.6.6 → 0.6.7
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/TODO.roadmap/00-overview.md +80 -0
- data/TODO.roadmap/01-lutaml-model-namespace-scope-uri-matching.md +111 -0
- data/TODO.roadmap/02-lutaml-model-element-builder-dispatch.md +115 -0
- data/TODO.roadmap/03-issue-40-child-bearing-roots.md +57 -0
- data/TODO.roadmap/04-issue-40-recursive-roots.md +105 -0
- data/TODO.roadmap/05-content-mathml-unification.md +75 -0
- data/TODO.roadmap/06-mathml-namespace-deduplication.md +59 -0
- data/TODO.roadmap/07-tbxisotml-misclassification-audit.md +82 -0
- data/TODO.roadmap/08-register-versioning.md +95 -0
- data/TODO.roadmap/09-test-coverage-gaps.md +78 -0
- data/TODO.roadmap/10-iso-sts-common-attributes-module.md +115 -0
- data/TODO.roadmap/11-docs-and-readme-refresh.md +52 -0
- data/TODO.sts-refactor/00-overview.md +3 -3
- data/TODO.sts-refactor/03-namespace-coupling.md +13 -5
- data/lib/sts/iso_sts/custom_meta.rb +37 -0
- data/lib/sts/iso_sts/custom_meta_group.rb +16 -0
- data/lib/sts/iso_sts/def_list.rb +1 -1
- data/lib/sts/iso_sts/iso_meta.rb +1 -1
- data/lib/sts/iso_sts/license.rb +35 -0
- data/lib/sts/iso_sts/license_p.rb +96 -0
- data/lib/sts/iso_sts/meta_name.rb +14 -0
- data/lib/sts/iso_sts/meta_value.rb +41 -0
- data/lib/sts/iso_sts/nat_meta.rb +1 -1
- data/lib/sts/iso_sts/permissions.rb +1 -1
- data/lib/sts/iso_sts/reg_meta.rb +1 -1
- data/lib/sts/iso_sts/term_head.rb +74 -0
- data/lib/sts/iso_sts.rb +7 -0
- data/lib/sts/version.rb +1 -1
- metadata +21 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 53e7ba614487ea4d658f8292c403c6811255a85affd65ef9f98976ccc32aa426
|
|
4
|
+
data.tar.gz: a750e10d49eddc0664746e85114c7db000638b0a3e3addc4b352d179bb21684b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a03cfc08c64cec197ee4a7a88a9ec41f46b3e94a80ea916092444be3419ec8c5aff06af3888094937be86cd7bbd2ed97b1663a688c16ba46650c0c61eed3cd60
|
|
7
|
+
data.tar.gz: 9ff6274690f18e27d2528497ffa5f4d0b5f3304e2636d276af5503b17318d6c86a090f3eeb583c0e889d26104878d8df9baed1b0d81ea227ed8b8be44e30573c
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# sts-ruby + lutaml-model Roadmap
|
|
2
|
+
|
|
3
|
+
**Date**: 2026-07-27
|
|
4
|
+
**Replaces**: `TODO.sts-refactor/00-overview.md` (now mostly historical — that
|
|
5
|
+
series landed through PRs #31, #40 work, #42, #44–#50). This roadmap covers
|
|
6
|
+
all remaining work across both repos.
|
|
7
|
+
|
|
8
|
+
## Status at start (2026-07-27)
|
|
9
|
+
|
|
10
|
+
- **sts gem**: 0.6.6 on RubyGems
|
|
11
|
+
- **Issue #40**: 11 IsoSts→NisoSts refs remaining (5 recursive roots +
|
|
12
|
+
4 child-bearing roots + 2 already-enumerated ArticleTitle / ElementCitation
|
|
13
|
+
co-roots)
|
|
14
|
+
- **Open PRs/Issues**: #40 (umbrella)
|
|
15
|
+
- **Anti-pattern enforcement**: 9 per-file checks in
|
|
16
|
+
`spec/anti_patterns_spec.rb`; covers all global rules
|
|
17
|
+
- **Tests**: 2563 examples, 0 failures
|
|
18
|
+
|
|
19
|
+
## Tracks
|
|
20
|
+
|
|
21
|
+
Work is grouped into four MECE tracks. Each track is independent unless
|
|
22
|
+
noted.
|
|
23
|
+
|
|
24
|
+
| Track | Subject | Items |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| A | lutaml-model upstream fixes (file BUGREPORTs, propose patches) | 01, 02 |
|
|
27
|
+
| B | Issue #40 namespace coupling completion | 03, 04 |
|
|
28
|
+
| C | Architectural improvements (DRY, versioning, audit) | 05, 06, 07, 08 |
|
|
29
|
+
| D | Quality, specs, and coverage | 09, 10, 11 |
|
|
30
|
+
|
|
31
|
+
## Priority order
|
|
32
|
+
|
|
33
|
+
The priority is "highest leverage per unit of risk" first:
|
|
34
|
+
|
|
35
|
+
1. **B.03** (child-bearing roots): model `IsoSts::License`, `IsoSts::TermHead`,
|
|
36
|
+
`IsoSts::CustomMetaGroup` — closes 5 of 11 issue #40 refs in one PR. Low
|
|
37
|
+
risk, schema-correct, no recursion.
|
|
38
|
+
2. **A.01, A.02** (lutaml-model BUGREPORTs): pure documentation, zero risk.
|
|
39
|
+
Unblocks C.06 (MathmlNamespace deduplication).
|
|
40
|
+
3. **C.07** (TbxIsoTml misclassification audit): documents a deferred audit
|
|
41
|
+
(Xref, TableWrap, etc.) — pattern is already proven by Fn/FnGroup and
|
|
42
|
+
Math removals.
|
|
43
|
+
4. **C.05** (Content MathML unification): 148 parallel classes in
|
|
44
|
+
`lib/sts/niso_sts/mml_content/` — same DRY argument as Presentation
|
|
45
|
+
MathML unification (commit 9e977a5).
|
|
46
|
+
5. **B.04** (recursive roots): the heavy lift. Each root pulls a ~78-element
|
|
47
|
+
mutually-recursive core. Requires per-element planning. Most architecturally
|
|
48
|
+
significant.
|
|
49
|
+
6. **C.08** (register versioning): lets ISOSTS v1.1, NISO STS 1.0, NISO STS 1.2
|
|
50
|
+
coexist without class-name clashes.
|
|
51
|
+
7. **C.06** (MathmlNamespace deduplication): blocked on A.01.
|
|
52
|
+
8. **D.09, D.10, D.11** (quality, coverage, docs): ongoing.
|
|
53
|
+
|
|
54
|
+
## Architectural principles (enforced on all new work)
|
|
55
|
+
|
|
56
|
+
- **MECE**: each model class lives in exactly one namespace; no parallel
|
|
57
|
+
hierarchies across namespaces.
|
|
58
|
+
- **Model-driven**: every XML element reference is a typed
|
|
59
|
+
`attribute :foo, ::Sts::<Namespace>::Foo`. No runtime string lookup.
|
|
60
|
+
- **Open/Closed**: adding a new STS host namespace = adding a new Foo class
|
|
61
|
+
there, not editing existing namespaces.
|
|
62
|
+
- **DRY**: one class per (namespace, XML element) pair. Shared attribute
|
|
63
|
+
patterns within a namespace use Ruby modules (not cross-namespace base
|
|
64
|
+
classes — those are forbidden by the 2026-05-07 ADR).
|
|
65
|
+
- **No anti-patterns**: no `send`, no `instance_variable_set/get`, no
|
|
66
|
+
`respond_to?` type-checks, no `require_relative` in lib/ (autoload only),
|
|
67
|
+
no `require` with internal library paths. Enforced by
|
|
68
|
+
`spec/anti_patterns_spec.rb`.
|
|
69
|
+
- **Autoload convention**: top-level `lib/sts.rb` autoloads namespaces;
|
|
70
|
+
`lib/sts/<namespace>.rb` autoloads classes. Alphabetical within category
|
|
71
|
+
groups.
|
|
72
|
+
|
|
73
|
+
## Memory cross-references
|
|
74
|
+
|
|
75
|
+
- [[architecture-namespace-independence]] — IsoSts/NisoSts never share base types
|
|
76
|
+
- [[duplicate-fn-class-attribute-mismatch]] — the trap A.02 prevents
|
|
77
|
+
- [[isosts-uses-mathml2-not-mathml3]] — V2/V3 host binding pattern
|
|
78
|
+
- [[mml-gem-integration-failure]] — MathmlNamespace duplication context
|
|
79
|
+
- [[release-workflow-two-phase]] — release flow
|
|
80
|
+
- [[ci-rubocop-stricter-than-local]] — substring-check convention
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# A.01: lutaml-model `namespace_scope` URI-based matching
|
|
2
|
+
|
|
3
|
+
**Status**: BUGREPORT drafted; sts-ruby fix blocked on upstream.
|
|
4
|
+
**Repo**: `lutaml/lutaml-model`
|
|
5
|
+
**Estimated effort**: small (lutaml-model: ~20 lines + spec; sts-ruby: ~5 lines once landed)
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
`namespace_scope` in `Lutaml::Model::Serializable.xml do … end` currently
|
|
10
|
+
matches namespace classes by **identity** (`==`), not by URI. Two classes
|
|
11
|
+
that declare the same URI/prefix are treated as different namespaces, so a
|
|
12
|
+
parent that lists one cannot satisfy a child that declares the other.
|
|
13
|
+
|
|
14
|
+
## Real-world symptom in sts-ruby
|
|
15
|
+
|
|
16
|
+
`Sts::Namespaces::MathmlNamespace` (lib/sts/namespaces.rb) and
|
|
17
|
+
`Mml::Namespace` (the mml gem's top-level namespace class) both encode:
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
uri "http://www.w3.org/1998/Math/MathML"
|
|
21
|
+
prefix_default "mml"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
They are different classes (different `object_id`s). When `Mml::V3::Math` is
|
|
25
|
+
nested inside `Sts::NisoSts::Standard`, the parent's `namespace_scope` lists
|
|
26
|
+
`MathmlNamespace`. The child declares `Mml::Namespace`. Identity comparison
|
|
27
|
+
fails, the `mml:` prefix is dropped on serialization.
|
|
28
|
+
|
|
29
|
+
The current workaround — listing both classes in `namespace_scope`:
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
namespace_scope [
|
|
33
|
+
::Sts::Namespaces::MathmlNamespace,
|
|
34
|
+
::Mml::Namespace,
|
|
35
|
+
::Sts::Namespaces::TbxNamespace,
|
|
36
|
+
::Lutaml::Xml::W3c::XlinkNamespace,
|
|
37
|
+
]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
— is a DRY violation. It documents the lutaml-model limitation rather than
|
|
41
|
+
fixing it. See `lib/sts/iso_sts/standard.rb:18-19` and
|
|
42
|
+
`lib/sts/niso_sts/standard.rb:20-21`.
|
|
43
|
+
|
|
44
|
+
## Proposed lutaml-model fix
|
|
45
|
+
|
|
46
|
+
In `Lutaml::Xml::NamespaceScope` (or wherever the membership check lives),
|
|
47
|
+
compare by URI instead of (or in addition to) class identity. The public
|
|
48
|
+
API doesn't change; only the matching semantics do.
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
# pseudocode
|
|
52
|
+
def includes?(namespace_class)
|
|
53
|
+
namespaces.any? do |declared|
|
|
54
|
+
declared.uri == namespace_class.uri
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Files
|
|
60
|
+
|
|
61
|
+
- `BUGREPORT.namespace-scope-uri-matching.md` in
|
|
62
|
+
`~/src/lutaml/lutaml-model/` (this track files it)
|
|
63
|
+
- lutaml-model spec: `spec/lutaml/xml/namespace_scope_spec.rb` — assert that
|
|
64
|
+
two classes with the same URI are equivalent for `namespace_scope`
|
|
65
|
+
- sts-ruby: remove the duplicated `MathmlNamespace` entry from
|
|
66
|
+
`IsoSts::Standard` and `NisoSts::Standard` once the upstream fix lands
|
|
67
|
+
|
|
68
|
+
## Verification
|
|
69
|
+
|
|
70
|
+
- Reproduction case (currently failing in 0.8.18):
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
class A < Lutaml::Model::Serializable
|
|
74
|
+
xml do
|
|
75
|
+
namespace SomeNs # uri "urn:test", prefix "t"
|
|
76
|
+
element "a"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
class B < Lutaml::Model::Serializable
|
|
80
|
+
xml do
|
|
81
|
+
namespace OtherNs # also uri "urn:test", prefix "t" — different class
|
|
82
|
+
element "b"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
class Parent < Lutaml::Model::Serializable
|
|
86
|
+
attribute :child, B
|
|
87
|
+
xml do
|
|
88
|
+
namespace_scope [SomeNs]
|
|
89
|
+
map_element "child", to: :child
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
# Currently: child's prefix is dropped on serialize.
|
|
93
|
+
# After fix: child's prefix is preserved (URIs match).
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Why not fix in sts-ruby today
|
|
97
|
+
|
|
98
|
+
Cannot: the duplication is the only way to make `Mml::V3::Math` and
|
|
99
|
+
`Mml::V2::Math` serialize with the `mml:` prefix inside STS Standard
|
|
100
|
+
elements. Removing `MathmlNamespace` without the upstream fix would
|
|
101
|
+
silently drop the prefix on every IsoSts/NisoSts document with MathML
|
|
102
|
+
content.
|
|
103
|
+
|
|
104
|
+
Track C.06 (MathmlNamespace deduplication) is the sts-ruby cleanup; it
|
|
105
|
+
blocks on this track.
|
|
106
|
+
|
|
107
|
+
## How to apply
|
|
108
|
+
|
|
109
|
+
1. File BUGREPORT in `~/src/lutaml/lutaml-model/BUGREPORT.namespace-scope-uri-matching.md`.
|
|
110
|
+
2. Optionally open PR upstream with the fix sketch above.
|
|
111
|
+
3. After upstream lands and sts-ruby bumps its lutaml-model dep, do C.06.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# A.02: lutaml-model `ElementBuilder` dispatch on `value.class`
|
|
2
|
+
|
|
3
|
+
**Status**: BUGREPORT drafted; analysis complete. Deferred per downstream
|
|
4
|
+
maintainer feedback (2026-07-25) — see "Why deferred" below.
|
|
5
|
+
**Repo**: `lutaml/lutaml-model`
|
|
6
|
+
**Estimated effort**: small (lutaml-model: ~10 lines + spec)
|
|
7
|
+
|
|
8
|
+
## Problem
|
|
9
|
+
|
|
10
|
+
`Lutaml::Xml::TransformationSupport::ElementBuilder#create_nested_model_element`
|
|
11
|
+
(lib/lutaml/xml/transformation/element_builder.rb:131) reuses the rule's
|
|
12
|
+
cached `child_transformation` for serialization regardless of the value's
|
|
13
|
+
actual class. When a caller assigns a `Lutaml::Model::Serializable` whose
|
|
14
|
+
class differs from the declared `attribute_type`, the wrong transformation
|
|
15
|
+
is applied silently:
|
|
16
|
+
|
|
17
|
+
- The declared type's `compiled_rules` are iterated
|
|
18
|
+
- `extract_rule_value` (rule_applier.rb:252) calls
|
|
19
|
+
`model_instance.public_send(rule.attribute_name)` on the value
|
|
20
|
+
- If the value's class doesn't have that attribute name (e.g., declared type
|
|
21
|
+
uses `:paragraph`, value's class uses `:p`), it raises `NoMethodError`
|
|
22
|
+
|
|
23
|
+
The cache itself is correct: `TransformationRegistry#transformation_key`
|
|
24
|
+
(lib/lutaml/model/transformation_registry.rb:218) includes `object_id`, so
|
|
25
|
+
keys are unique per class. The bug is in the dispatch logic, not the cache.
|
|
26
|
+
|
|
27
|
+
## Real-world trigger
|
|
28
|
+
|
|
29
|
+
The original sts-ruby report (PR #47): `Sts::TbxIsoTml::Fn` (Ruby attr
|
|
30
|
+
`:p`) was nested inside `Sts::NisoSts::Back` (whose `:fn_group` is typed
|
|
31
|
+
`NisoSts::FnGroup`, whose `:fn` is typed `NisoSts::Fn` with attr
|
|
32
|
+
`:paragraph`). lutaml-model applied the cached
|
|
33
|
+
`NisoSts::Fn`-transformation to the `TbxIsoTml::Fn` value, calling
|
|
34
|
+
`fn.public_send(:paragraph)` and raising.
|
|
35
|
+
|
|
36
|
+
## Why deferred
|
|
37
|
+
|
|
38
|
+
Downstream metanorma-iso maintainer feedback: "introduces a new contract
|
|
39
|
+
that may break other downstream consumers who've been quietly relying on
|
|
40
|
+
the current lenient behavior. Worth doing eventually, but urgency drops
|
|
41
|
+
once #1 lands — without the duplicate classes there's no easy way to
|
|
42
|
+
trigger the trap."
|
|
43
|
+
|
|
44
|
+
After PR #47 (delete TbxIsoTml::Fn/FnGroup), the trap cannot fire through
|
|
45
|
+
sts-ruby's own classes. The fix is still correct and worth doing, just not
|
|
46
|
+
urgent.
|
|
47
|
+
|
|
48
|
+
## Proposed lutaml-model fix
|
|
49
|
+
|
|
50
|
+
In `create_nested_model_element`, dispatch on `value.class` whenever value
|
|
51
|
+
is a `Serializable` that differs from the declared `attribute_type`:
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
dispatch_on_value_class = union || is_polymorphic ||
|
|
55
|
+
(value.is_a?(Lutaml::Model::Serialize) &&
|
|
56
|
+
value.class != rule.attribute_type)
|
|
57
|
+
|
|
58
|
+
child_transformation = if dispatch_on_value_class
|
|
59
|
+
value.class.transformation_for(:xml, register)
|
|
60
|
+
else
|
|
61
|
+
rule.child_transformation ||
|
|
62
|
+
rule.attribute_type.transformation_for(:xml, register)
|
|
63
|
+
end
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
This subsumes the existing `is_polymorphic_subtype` branch (subtypes already
|
|
67
|
+
have `value.class != rule.attribute_type`). The fast path
|
|
68
|
+
(`value.class == rule.attribute_type`) preserves the cached
|
|
69
|
+
`rule.child_transformation` lookup, so no performance regression for the
|
|
70
|
+
common case.
|
|
71
|
+
|
|
72
|
+
## Files
|
|
73
|
+
|
|
74
|
+
- `BUGREPORT.element-builder-dispatch-on-value-class.md` in
|
|
75
|
+
`~/src/lutaml/lutaml-model/`
|
|
76
|
+
- lutaml-model spec: `spec/lutaml/model/cross_class_dispatch_spec.rb` — three
|
|
77
|
+
examples minimum (unrelated Serializable dispatch, round-trip preserved,
|
|
78
|
+
declared-type fast path unchanged)
|
|
79
|
+
|
|
80
|
+
## Verification
|
|
81
|
+
|
|
82
|
+
- Reproduction case (currently raises in 0.8.18):
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
class ParentClass < Lutaml::Model::Serializable; end
|
|
86
|
+
class OtherSerializable < Lutaml::Model::Serializable
|
|
87
|
+
attribute :name, :string
|
|
88
|
+
xml do
|
|
89
|
+
element "other"
|
|
90
|
+
map_element "name", to: :name
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
class Holder < Lutaml::Model::Serializable
|
|
94
|
+
attribute :child, ParentClass
|
|
95
|
+
xml do
|
|
96
|
+
map_element "child", to: :child
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
h = Holder.new
|
|
101
|
+
h.child = OtherSerializable.new(name: "x")
|
|
102
|
+
h.to_xml # currently: NoMethodError on ParentClass's :name
|
|
103
|
+
# after fix: serialises using OtherSerializable's mapping
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## How to apply
|
|
107
|
+
|
|
108
|
+
1. File BUGREPORT in `~/src/lutaml/lutaml-model/`.
|
|
109
|
+
2. Pair with the existing polymorphic specs (`spec/lutaml/model/polymorphic_spec.rb`)
|
|
110
|
+
to ensure no regression on subtype dispatch.
|
|
111
|
+
3. Open upstream PR with the fix sketch above.
|
|
112
|
+
|
|
113
|
+
Revisit when: (a) a second cross-class case emerges anywhere in the
|
|
114
|
+
metanorma fleet, or (b) the next lutaml-model major release (good time to
|
|
115
|
+
tighten contracts).
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# B.03: Issue #40 — model child-bearing IsoSts roots
|
|
2
|
+
|
|
3
|
+
**Status**: in flight (License, TermHead, CustomMetaGroup all completable in one PR).
|
|
4
|
+
**Blocks**: nothing.
|
|
5
|
+
**Closes**: 5 of 11 IsoSts→NisoSts refs (issue #40).
|
|
6
|
+
|
|
7
|
+
## Remaining child-bearing refs
|
|
8
|
+
|
|
9
|
+
| Class | Referenced from | ISOSTS schema complexity |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| `IsoSts::License` | `IsoSts::Permissions#license` | simple (3 attrs + `<license-p>` children) |
|
|
12
|
+
| `IsoSts::TermHead` | `IsoSts::DefList#term_head` | simple (mixed content) |
|
|
13
|
+
| `IsoSts::CustomMetaGroup` | `IsoSts::{IsoMeta,RegMeta,NatMeta}#custom_meta_group` (×3) | simple (`<custom-meta>` children) |
|
|
14
|
+
|
|
15
|
+
## Plan
|
|
16
|
+
|
|
17
|
+
1. **IsoSts::License** — model from ISOSTS.xsd (`<license>` element):
|
|
18
|
+
- 8 attrs: license_type, specific_use, xml_lang, xlink_* (6)
|
|
19
|
+
- children: `license_p` (unbounded) — needs `IsoSts::LicenseP` too
|
|
20
|
+
- `license_p` itself is mixed-content with the full block-element set;
|
|
21
|
+
for first pass, model `license_p` with content + common inline children
|
|
22
|
+
(similar to IsoSts::Attrib)
|
|
23
|
+
2. **IsoSts::LicenseP** — needed as License child
|
|
24
|
+
3. **IsoSts::TermHead** — model from ISOSTS.xsd (`<term-head>`):
|
|
25
|
+
- mixed content
|
|
26
|
+
- inline-element children (similar to IsoSts::Attrib pattern)
|
|
27
|
+
4. **IsoSts::CustomMetaGroup** — model from ISOSTS.xsd:
|
|
28
|
+
- No attributes
|
|
29
|
+
- Single child type: `<custom-meta>` (unbounded)
|
|
30
|
+
5. **IsoSts::CustomMeta** — needed as CustomMetaGroup child
|
|
31
|
+
- 9 attrs: id, specific_use, xml_lang, xlink_*
|
|
32
|
+
- children: `<meta-name>`, `<meta-value>`
|
|
33
|
+
6. **IsoSts::MetaName** — `xs:string` element
|
|
34
|
+
7. **IsoSts::MetaValue** — mixed content
|
|
35
|
+
|
|
36
|
+
## Specs (per established pattern)
|
|
37
|
+
|
|
38
|
+
- Attribute-set assertions for each new class
|
|
39
|
+
- Child-class type assertions
|
|
40
|
+
- Round-trip test for at least one realistic XML fragment
|
|
41
|
+
- No-NisoSts guard on each new file
|
|
42
|
+
- Repoint type assertions (e.g., `IsoSts::Permissions#license is IsoSts::License`)
|
|
43
|
+
|
|
44
|
+
## Verification
|
|
45
|
+
|
|
46
|
+
- `grep -rn "NisoSts::" lib/sts/iso_sts/` count drops from 11 → 6
|
|
47
|
+
- Full test suite green
|
|
48
|
+
- Rubocop clean (anti-patterns spec enforces all rules)
|
|
49
|
+
|
|
50
|
+
## How to apply
|
|
51
|
+
|
|
52
|
+
- One PR (e.g., `feat/isosts-license-termhead-custommetagroup`) covering all
|
|
53
|
+
7 new classes + the 5 repoints.
|
|
54
|
+
- Same pattern as PR #48 (DispQuote/BoxedText) and PR #50 (Attrib): model
|
|
55
|
+
from ISOSTS.xsd, omit not-yet-modelled IsoSts children, track omissions
|
|
56
|
+
in the spec comment.
|
|
57
|
+
- Update `TODO.sts-refactor/03-namespace-coupling.md` count.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# B.04: Issue #40 — break the recursive roots
|
|
2
|
+
|
|
3
|
+
**Status**: planning. Heavy lift; needs per-element design.
|
|
4
|
+
**Blocks**: closing issue #40.
|
|
5
|
+
**Closes**: 6 of 11 IsoSts→NisoSts refs (5 recursive roots + ArticleTitle).
|
|
6
|
+
|
|
7
|
+
## Remaining recursive refs
|
|
8
|
+
|
|
9
|
+
| Class | Referenced from | Approx. closure size |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| `IsoSts::ElementCitation` | `IsoSts::Ref#element_citation` | ~78 elements |
|
|
12
|
+
| `IsoSts::PersonGroup` | `IsoSts::MixedCitation#person_group` | ~50 elements |
|
|
13
|
+
| `IsoSts::Collab` | `IsoSts::MixedCitation#collab` | ~40 elements |
|
|
14
|
+
| `IsoSts::Source` | `IsoSts::MixedCitation#source` | ~25 elements |
|
|
15
|
+
| `IsoSts::ArticleTitle` | `IsoSts::MixedCitation#article_title` | ~25 elements |
|
|
16
|
+
| `IsoSts::TermDisplay` | `IsoSts::TermSec#term_display` | ~30 elements |
|
|
17
|
+
|
|
18
|
+
Each root reaches the same ~78-element mutually-recursive core
|
|
19
|
+
(`sec` → `p` → `disp-quote` → `p`, etc.). The recursion resolves once
|
|
20
|
+
every element in the cycle has an IsoSts class.
|
|
21
|
+
|
|
22
|
+
## Architecture decision needed
|
|
23
|
+
|
|
24
|
+
Two strategies, mutually exclusive:
|
|
25
|
+
|
|
26
|
+
### Strategy A: bottom-up leaf modelling
|
|
27
|
+
|
|
28
|
+
Model every leaf ISOSTS element (no children) first, then bottom-up
|
|
29
|
+
through the dependency graph. Eventually every recursive cycle has all
|
|
30
|
+
its members as IsoSts classes.
|
|
31
|
+
|
|
32
|
+
- **Pro**: each PR is small and verifiable
|
|
33
|
+
- **Pro**: every modelled leaf reduces the closure of the recursive roots
|
|
34
|
+
- **Con**: many PRs (could be 50+); requires tracking the dependency DAG
|
|
35
|
+
in a checklist
|
|
36
|
+
|
|
37
|
+
### Strategy B: top-down with `raw` mapping
|
|
38
|
+
|
|
39
|
+
Use lutaml-model's `raw` mapping type to capture the recursive core as
|
|
40
|
+
opaque XML strings, then incrementally model the surface elements.
|
|
41
|
+
|
|
42
|
+
- **Pro**: closes the recursive refs in fewer PRs
|
|
43
|
+
- **Con**: `raw` mapping doesn't expose the recursive content as Ruby
|
|
44
|
+
objects — callers can't introspect or modify the captured XML
|
|
45
|
+
- **Con**: violates the model-driven principle for the deferred parts
|
|
46
|
+
|
|
47
|
+
### Recommendation: Strategy A
|
|
48
|
+
|
|
49
|
+
Track B.03 (child-bearing roots) closes 5 refs with no recursion.
|
|
50
|
+
Strategy A is the right continuation: model the next layer of leaves
|
|
51
|
+
(elements with no children, or with only already-modelled IsoSts children)
|
|
52
|
+
until the recursive roots' closures collapse.
|
|
53
|
+
|
|
54
|
+
## Tracking the DAG
|
|
55
|
+
|
|
56
|
+
A `TODO.roadmap/04-dag.md` (this file's sibling) should list every
|
|
57
|
+
not-yet-modelled ISOSTS element with:
|
|
58
|
+
|
|
59
|
+
- Element name
|
|
60
|
+
- Whether it has children (and if so, which)
|
|
61
|
+
- Whether all its children are already IsoSts classes
|
|
62
|
+
- Priority (high if it's referenced by a still-NisoSts ref)
|
|
63
|
+
|
|
64
|
+
This is mechanical work but tedious. A scripted generator from ISOSTS.xsd
|
|
65
|
+
could populate it; the existing `lib/sts/niso_sts/mml_content/` directory
|
|
66
|
+
suggests the project has tolerated generated-model code before.
|
|
67
|
+
|
|
68
|
+
## Per-root breakdown
|
|
69
|
+
|
|
70
|
+
### `ElementCitation` (~78 elements)
|
|
71
|
+
|
|
72
|
+
The biggest. The ISOSTS schema for `<element-citation>` is enormous (37+
|
|
73
|
+
child elements, many recursive). Schema-correct modelling requires:
|
|
74
|
+
- `IsoSts::ElementCitation` itself
|
|
75
|
+
- Many `<name>`, `<date>`, `<page>` style leaves — most already exist as
|
|
76
|
+
NisoSts classes; need IsoSts equivalents per namespace independence
|
|
77
|
+
- `<person-group>` (also a recursive root — see below)
|
|
78
|
+
|
|
79
|
+
Likely 10+ PRs to close this root alone.
|
|
80
|
+
|
|
81
|
+
### `PersonGroup`, `Collab`, `Source`, `ArticleTitle` (~25-50 elements each)
|
|
82
|
+
|
|
83
|
+
All referenced from `<mixed-citation>`. Each pulls in citation-relevant
|
|
84
|
+
inline content. PersonGroup is itself a minor recursive root (contains
|
|
85
|
+
`<name>` which contains its own structure).
|
|
86
|
+
|
|
87
|
+
### `TermDisplay` (~30 elements)
|
|
88
|
+
|
|
89
|
+
Referenced from `<term-sec>`. Pulls in TBX-flavored terminology elements
|
|
90
|
+
plus the regular block-element core.
|
|
91
|
+
|
|
92
|
+
## Verification
|
|
93
|
+
|
|
94
|
+
- After each leaf is modelled: `bundle exec rspec` green
|
|
95
|
+
- After each root is closed: `grep -rn "NisoSts::<Root>"` returns nothing
|
|
96
|
+
- After all 6 roots closed: issue #40 down to ~0 refs (only ArticleTitle
|
|
97
|
+
co-root count check)
|
|
98
|
+
|
|
99
|
+
## How to apply
|
|
100
|
+
|
|
101
|
+
- Do B.03 first (closes 5 refs cheaply)
|
|
102
|
+
- Then start B.04 with the smallest root (Source or ArticleTitle)
|
|
103
|
+
- Track DAG progress in `TODO.roadmap/04-dag.md` (sibling file)
|
|
104
|
+
- Each leaf modelled = one PR (same pattern as PRs #48, #50)
|
|
105
|
+
- Cross-reference each PR in `TODO.sts-refactor/03-namespace-coupling.md`
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# C.05: Content MathML unification on the mml gem
|
|
2
|
+
|
|
3
|
+
**Status**: planning. Medium-large effort (148 classes to migrate).
|
|
4
|
+
**Depends on**: nothing (independent of A.01, A.02).
|
|
5
|
+
**Memory**: `mml-gem-integration-failure` (Presentation MathML is already
|
|
6
|
+
unified; Content MathML is not).
|
|
7
|
+
|
|
8
|
+
## Problem
|
|
9
|
+
|
|
10
|
+
`lib/sts/niso_sts/mml_content/` ships 148 Content MathML classes (Apply,
|
|
11
|
+
Bind, Ci, Cn, Plus, Eq, Lambda, etc.) as a parallel in-gem implementation.
|
|
12
|
+
The mml gem has its own Content MathML set under `Mml::V2::*` and
|
|
13
|
+
`Mml::V3::*` (same pattern as the Presentation MathML it already provides,
|
|
14
|
+
which we unified in commit 9e977a5).
|
|
15
|
+
|
|
16
|
+
The duplication means:
|
|
17
|
+
- Two implementations of every Content MathML element
|
|
18
|
+
- Attribute sets can drift between them (no schema sync)
|
|
19
|
+
- sts-ruby users get sts-specific behavior, not mml-gem-correct behavior
|
|
20
|
+
- Bug fixes have to land in both repos
|
|
21
|
+
|
|
22
|
+
## Plan
|
|
23
|
+
|
|
24
|
+
Same pattern as commit 9e977a5 (which deleted `TbxIsoTml::{Math, Mrow, Mi,
|
|
25
|
+
…}` and replaced with `Mml::V2::Math` / `Mml::V3::Math`):
|
|
26
|
+
|
|
27
|
+
1. **Inventory** — `ls lib/sts/niso_sts/mml_content/` to enumerate the 148
|
|
28
|
+
classes (already autoloaded in `lib/sts/niso_sts/mml_content.rb`).
|
|
29
|
+
2. **Verify mml gem coverage** — for each sts class, confirm the mml gem
|
|
30
|
+
has an equivalent (under `Mml::V2::*` and/or `Mml::V3::*`).
|
|
31
|
+
3. **Repoint** — every `Sts::NisoSts::MmlContent::*` reference becomes
|
|
32
|
+
`Mml::V2::*` (for ISOSTS-hosted documents) or `Mml::V3::*` (for NISO STS).
|
|
33
|
+
Follow the host-binding pattern established in PR #45.
|
|
34
|
+
4. **Delete** — once all references are repointed, delete
|
|
35
|
+
`lib/sts/niso_sts/mml_content/` entirely (and its autoload block).
|
|
36
|
+
5. **Spec** — round-trip tests for documents with Content MathML must
|
|
37
|
+
continue to pass.
|
|
38
|
+
|
|
39
|
+
## Risks
|
|
40
|
+
|
|
41
|
+
- **Coverage gap**: if the mml gem lacks a class for some sts Content
|
|
42
|
+
element, the migration is blocked. Mitigation: contribute the missing
|
|
43
|
+
class to the mml gem first.
|
|
44
|
+
- **Attribute divergence**: sts Content classes may have attributes the
|
|
45
|
+
mml gem lacks (or vice versa). Schema authority is `mathml3.xsd` (NISO
|
|
46
|
+
STS host) or `ncbi-mathml2/content/*.xsd` (ISOSTS host).
|
|
47
|
+
- **MathML version**: same V2/V3 split as Presentation MathML. sts Content
|
|
48
|
+
classes are version-agnostic; the mml gem's are not. Migration must
|
|
49
|
+
preserve the host-binding pattern.
|
|
50
|
+
|
|
51
|
+
## Files affected
|
|
52
|
+
|
|
53
|
+
- `lib/sts/niso_sts/mml_content.rb` (the autoload block; will be deleted)
|
|
54
|
+
- `lib/sts/niso_sts/mml_content/*.rb` (~30 files, 148 classes total via
|
|
55
|
+
grouping files like `operators.rb`, `relations.rb`, `containers.rb`)
|
|
56
|
+
- Any `NisoSts::*` class that references `MmlContent::*` (audit with
|
|
57
|
+
`grep -rn "MmlContent::" lib/`)
|
|
58
|
+
- `lib/sts/namespaces.rb` — `MathmlNamespace` (relevant if A.01 hasn't
|
|
59
|
+
landed; the cleanup in C.06 may run before this)
|
|
60
|
+
|
|
61
|
+
## How to apply
|
|
62
|
+
|
|
63
|
+
- Read [[isosts-uses-mathml2-not-mathml3]] for the host-binding pattern
|
|
64
|
+
- Read [[mml-gem-integration-failure]] for why the analogous Presentation
|
|
65
|
+
MathML unification took two attempts
|
|
66
|
+
- Open as a series of PRs (one per mml_content subdirectory) rather than
|
|
67
|
+
one giant PR; each PR closes one grouping file
|
|
68
|
+
- Final PR deletes the directory and the autoload block
|
|
69
|
+
|
|
70
|
+
## Verification
|
|
71
|
+
|
|
72
|
+
- `bundle exec rspec spec/round_trip/reference_docs_spec.rb` — all
|
|
73
|
+
reference docs with Content MathML still round-trip
|
|
74
|
+
- `grep -rn "MmlContent::" lib/` returns nothing after the final PR
|
|
75
|
+
- Rubocop clean
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# C.06: MathmlNamespace deduplication
|
|
2
|
+
|
|
3
|
+
**Status**: blocked on A.01 (lutaml-model namespace_scope URI matching).
|
|
4
|
+
**Estimated effort**: trivial (delete one class, two `namespace_scope`
|
|
5
|
+
entries).
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
`Sts::Namespaces::MathmlNamespace` (lib/sts/namespaces.rb:5-8) and
|
|
10
|
+
`Mml::Namespace` (mml gem's top-level namespace class) both encode:
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
uri "http://www.w3.org/1998/Math/MathML"
|
|
14
|
+
prefix_default "mml"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Two classes, same URI/prefix. The current `namespace_scope` workaround on
|
|
18
|
+
`IsoSts::Standard` and `NisoSts::Standard` lists both:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
namespace_scope [
|
|
22
|
+
::Sts::Namespaces::MathmlNamespace,
|
|
23
|
+
::Mml::Namespace,
|
|
24
|
+
::Sts::Namespaces::TbxNamespace,
|
|
25
|
+
::Lutaml::Xml::W3c::XlinkNamespace,
|
|
26
|
+
]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This is the documented smell noted in memory `mml-gem-integration-failure`
|
|
30
|
+
("namespace_scope duplication is a known smell"). It exists because
|
|
31
|
+
lutaml-model's `namespace_scope` matches by class identity, not by URI.
|
|
32
|
+
|
|
33
|
+
## Plan
|
|
34
|
+
|
|
35
|
+
Once A.01 lands upstream and sts-ruby has bumped its lutaml-model dep:
|
|
36
|
+
|
|
37
|
+
1. Delete `Sts::Namespaces::MathmlNamespace` from `lib/sts/namespaces.rb`.
|
|
38
|
+
2. Replace both `namespace_scope` entries with `::Mml::Namespace` only.
|
|
39
|
+
3. Audit any other references to `MathmlNamespace` (likely only
|
|
40
|
+
`lib/sts/niso_sts/mml_content.rb:10` — but that whole module is
|
|
41
|
+
being deleted by C.05).
|
|
42
|
+
|
|
43
|
+
## Verification
|
|
44
|
+
|
|
45
|
+
- `grep -rn "MathmlNamespace" lib/` returns nothing
|
|
46
|
+
- TBX round-trip fixtures still preserve `<mml:math>` prefix
|
|
47
|
+
- NISO STS fixtures still preserve `<mml:math>` prefix
|
|
48
|
+
- IsoSts fixtures (V2) still preserve `<mml:math>` prefix
|
|
49
|
+
|
|
50
|
+
## How to apply
|
|
51
|
+
|
|
52
|
+
- Coordinate with A.01 — file the BUGREPORT first, then either propose the
|
|
53
|
+
upstream fix or wait for it
|
|
54
|
+
- When the upstream fix is in a released lutaml-model version, bump
|
|
55
|
+
sts.gemspec and ship this cleanup as a follow-up PR
|
|
56
|
+
|
|
57
|
+
See also: TODO.sts-refactor/03-namespace-coupling.md "MathML wrapper"
|
|
58
|
+
discussion (now historical — the wrapper itself was deleted in 9e977a5,
|
|
59
|
+
but the namespace duplication remains).
|