Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 11 additions & 19 deletions TODO.sts-refactor/00-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
|---|------|----------|----------|--------------|
| 01 | `01-mathml-delegation.md` | Anti-Pattern Fix | DONE | — |
| 02 | `02-type-resolution.md` | Anti-Pattern Fix | DONE | — |
| 03 | `03-namespace-coupling.md` | Architecture | IN PROGRESS (63→6 refs) | 01, 02 |
| 03 | `03-namespace-coupling.md` | Architecture | DONE (63→0 refs) | 01, 02 |
| 04 | `04-register-versioning.md` | Architecture | HIGH | 01, 02 |
| 05 | `05-missing-elements.md` | Feature Gap | MOSTLY DONE | 04 |
| 06 | `06-missing-attributes.md` | Feature Gap | DONE | 04 |
Expand Down Expand Up @@ -166,9 +166,9 @@ grep -r "method_missing|respond_to_missing|Object.const_get|\.send" lib/
- ~~Expand StringName usage (in contrib, element-citation, related-article — NISO STS 1.2)~~ → DONE: added to name-alternatives; already in person-group and mixed-citation

### Architectural Items (High Effort)
- `03-namespace-coupling.md` — IN PROGRESS: 63→6 IsoSts→NisoSts references
remaining (issue #40). ISOSTS.xsd governs content models and all non-`@id`
attributes; the `@id` follows the 86948b9 convention.
- `03-namespace-coupling.md` — DONE: 63→0 IsoSts→NisoSts references
(issue #40). ISOSTS.xsd governs content models and all non-`@id` attributes;
the `@id` follows the 86948b9 convention.
- `04-register-versioning.md` — Version the models via lutaml-model Registers
- `11-duplicate-models.md` — 44 overlapping element resolution (depends on 03)

Expand All @@ -184,22 +184,14 @@ the **NISO** XSD and applied the result to IsoSts.
name. (`meta-date` was NOT lossy — it already maps `type`.)
- **Not a bug — the conventional `@id`.** IsoSts models carry an `@id` that
ISOSTS does not itself define; this is the deliberate 86948b9 convention
(NISO-XSD-verified), not dead surface.
(NISO-XSD-verified), not dead surface. All 220 registered IsoSts models now
expose and map it.
- **Remaining follow-up (separate change).** 7 spurious attributes on 5 models
(model carries an attr ISOSTS does not define: `graphic` `type`, `body`/`back`
`content-type`, `content-language`/`language` extras), and the 6 classes left
without `@id` in 86948b9 (`monospace`, `sc`, `strike`, `underline`, `uri`,
`standard_ref`).
`content-type`, `content-language`/`language` extras).

## Next Action
Two independent tracks:
1. Finish `03` — the 6 remaining refs split across five recursive roots
(`ElementCitation`, `PersonGroup`, `Collab`, `Source`, `TermDisplay`) and
five child-bearing roots. `DispQuote` and `BoxedText` themselves are now
IsoSts classes. The first 15 high-reuse dependency leaves are modelled,
reducing the unresolved closures to 78–84 not-yet-modelled ISOSTS elements
before existing IsoSts boundaries. The remaining work still needs an
architecture decision for the recursive content core.
2. The remaining schema-conformance follow-up above — the 7 spurious attributes
on 5 models and the 6 classes without `@id`. (The dropped-attribute data loss
is now fixed.)
`03` is complete: the 90-model closure removes every IsoSts→NisoSts reference.
Continue with the remaining schema-conformance follow-up above: the 7 spurious
attributes on 5 models. (The dropped-attribute data loss and mandatory `@id`
surface are now fixed.)
79 changes: 40 additions & 39 deletions TODO.sts-refactor/03-namespace-coupling.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
**Priority**: HIGH
**Category**: Architecture
**Estimated Effort**: High
**Status**: Partially done — 63 → 6 references (GitHub issue #40)
**Status**: Complete — 63 → 0 references (GitHub issue #40)

## Problem

Expand All @@ -18,7 +18,8 @@ The `IsoSts::Attrib` addition then reduced it from 13 to 11
(Array#attrib, TableWrapFoot#attrib) and filled the omission in DispQuote /
BoxedText from PR #48. The License/TermHead/CustomMetaGroup closure then
reduced it from 11 to 6 (Permissions#license, DefList#term_head, and
IsoMeta/RegMeta/NatMeta#custom_meta_group).
IsoMeta/RegMeta/NatMeta#custom_meta_group). The full recursive closure then
removed the final 6 references.

## Content models from ISOSTS.xsd; `@id` from the 86948b9 convention

Expand Down Expand Up @@ -46,7 +47,7 @@ Non-`@id` attribute lists must be **generated** from the XSD, never hand-read:
`version` on `tex-math` and `specific-use` on `pub-id` sit after long
`xs:enumeration` blocks and are invisible to a truncated read.

## Done in issue #40 — 47 refs removed, 46 classes added
## Done in issue #40 — 63 refs removed, 136 classes added

- **14 `xs:string` elements** (`originator`, `doc-type`, `doc-number`,
`part-number`, `version`, `suppl-type`, `suppl-number`, `suppl-version`,
Expand Down Expand Up @@ -78,25 +79,25 @@ Non-`@id` attribute lists must be **generated** from the XSD, never hand-read:
- **`DispQuote` and `BoxedText` added** — modelled from ISOSTS.xsd (not copied
from NisoSts, which disagrees: `NisoSts::DispQuote` lacks `xml_lang` and
`title`; `NisoSts::BoxedText` carries `form_type`/`is_form` that ISOSTS does
not define). Children limited to existing IsoSts types; omitted children
(`speech`, `statement`, `verse-group`, `address`, `alternatives`,
`array`, `chem-struct-wrap`, `fig-group`, `media`, `supplementary-material`,
`table-wrap`, `table-wrap-group`, `disp-formula-group`, `mml:math`,
`related-article`, `related-object`, `glossary`) are tracked below.
not define). Their first pass was limited to existing IsoSts child types.
`BoxedText#sts_object_id` (not `:object_id`) follows the `NisoSts::Graphic`
convention to avoid clashing with `Object#object_id`. 3 refs.
- **`Attrib` added** — mixed-content class modelled from ISOSTS.xsd with the
25 inline-element children that have IsoSts types today. Fills the
25 inline-element children that had IsoSts types at the time. It fills the
`attrib` gap left in `DispQuote` and `BoxedText` from the prior bullet.
2 refs (`Array#attrib`, `TableWrapFoot#attrib`). Omitted inline children
(`inline-supplementary-material`, `related-article`, `related-object`,
`element-citation`, `overline`, `roman`, `sans-serif`, `alternatives`,
`private-char`, `chem-struct`, `mml:math`, `target`, `tbx:entailedTerm`)
tracked below.
2 refs (`Array#attrib`, `TableWrapFoot#attrib`).
- **License / TermHead / CustomMetaGroup closure added** — `License`,
`LicenseP`, `TermHead`, `CustomMetaGroup`, `CustomMeta`, `MetaName`, and
`MetaValue` modelled from ISOSTS.xsd. 5 refs (`Permissions#license`,
`DefList#term_head`, `IsoMeta/RegMeta/NatMeta#custom_meta_group`).
- **90-model full closure completed** — 80 models are new and 10 earlier
bounded models are expanded to their complete content models. All direct
and transitive ISOSTS elements needed by `disp-quote`, `boxed-text`,
`person-group`, `collab`, `source`, `article-title`, and the bounded
metadata roots now close through IsoSts, TBX, and MathML types. Every model
uses the exact ISOSTS attributes and child cardinalities plus the mandatory
project `@id` surface; none uses `required: true`. This removes the final
6 refs from `MixedCitation`, `Ref`, and `TermSec`.

### Why classes and not plain `:string`

Expand All @@ -107,26 +108,26 @@ round-trip. For a scalar, `render_empty: :empty` recovers it; for a collection
to `[]`, destroying the information at parse time before any render option
applies. Content-only classes round-trip every case.

## Remaining6 refs

**5 refs to 5 recursive roots**: `ElementCitation`, `PersonGroup`, `Collab`,
`Source`, `TermDisplay`. Each reaches the same 78–79-element
mutually-recursive core before existing IsoSts boundaries (`sec` → `p` →
`disp-quote` → `p`). `DispQuote`, `BoxedText`, `Attrib`, `License`,
`TermHead`, and `CustomMetaGroup` are now IsoSts classes and no longer in
this list. The remaining 6 are the recursive roots — see
`TODO.roadmap/04-issue-40-recursive-roots.md` for the strategy.

**8 refs to 5 child-bearing roots**, now measured after the first dependency
layer: `attrib` (:1082) reaches 78 not-yet-modelled ISOSTS elements before
existing IsoSts boundaries, `term-head` (:4308) reaches 79, `article-title`
reaches 78, `license` → `license-p` (:51) reaches 84, and
`custom-meta-group` → `custom-meta` reaches 82. They converge on the same
recursive content core as the deep roots, so none is another bounded model
batch. `publisher` was the only exception: its four-element closure
(`publisher`, `publisher-name`, `publisher-loc`, `email`) is complete.
The Niso `Attrib` class is only 17 lines because it omits all 37 ISOSTS child
types; copying it would silently drop valid ISO content rather than decouple it.
## Complete0 refs

The final 6 references were `Ref#element_citation`, four `MixedCitation`
children (`person_group`, `collab`, `source`, and `article_title`), and
`TermSec#term_display`. Their complete recursive and bounded closures are now
modelled in IsoSts.

The earlier four bounded roots had a combined 12-element closure before
existing IsoSts boundaries:

- `TermHead` — 1 ref in `DefList`; only `term-head` was needed.
- `CustomMetaGroup` — 3 refs in `IsoMeta`, `NatMeta`, and `RegMeta`; its
4-element closure is `custom-meta-group`, `custom-meta`, `meta-name`, and
`meta-value`.
- `License` — 1 ref in `Permissions`; its 6-element closure is `license`,
`license-p`, `award-id`, `funding-source`, `open-access`, and `price`.
- `TermDisplay` — 1 ref in `TermSec`; only `term-display` was needed.

All four closures are now modelled in IsoSts and all six host references point
to their IsoSts types.

## Rejected: three-tier `Sts::Base` hierarchy

Expand All @@ -140,7 +141,7 @@ That draft also targeted `lib/sts/iso_sts/content_groups/highlight_elements.rb`
and "194+ references" to emphasis types. That file was deleted as dead code; the
real work was metadata types, not highlight elements.

## Naming trap (for the deferred work)
## Naming trap

A mechanical `s/NisoSts::/IsoSts::/` is wrong. Nine elements already have
different class names per namespace, so a blind rename creates `IsoSts::Section`
Expand All @@ -156,13 +157,13 @@ Remap child references by **XML element name**, not class name.
## Scope boundary

Independence from NisoSts is not independence in general: `lib/sts/iso_sts/`
still references 6 `TbxIsoTml` types directly, and much of the deferred closure
reaches `TbxIsoTml`/MathML. Those are shared namespaces, outside this ADR.
still references 6 `TbxIsoTml` types directly, and the completed closure reaches
`TbxIsoTml`/MathML. Those are shared namespaces, outside this ADR.

## Verification

1. `grep -rho "Sts::NisoSts::" lib/sts/iso_sts/ | wc -l` → `16`
1. `grep -rho "Sts::NisoSts::" lib/sts/iso_sts/ | wc -l` → `0`
2. Every *non-`@id`* attribute on an IsoSts model traces to a line in
`ISOSTS.xsd`; `@id` follows the 86948b9 convention
3. Autoload registry 1:1 with the directory (131/131)
3. Autoload registry 1:1 with the directory (220/220)
4. `bundle exec rspec` green; `bundle exec rubocop` clean
103 changes: 94 additions & 9 deletions lib/sts/iso_sts.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,6 @@ module IsoSts
autoload :Label, "#{__dir__}/iso_sts/label"
autoload :Title, "#{__dir__}/iso_sts/title"
autoload :Bold, "#{__dir__}/iso_sts/bold"
autoload :BoxedText, "#{__dir__}/iso_sts/boxed_text"
autoload :DispQuote, "#{__dir__}/iso_sts/disp_quote"
autoload :Italic, "#{__dir__}/iso_sts/italic"
autoload :Monospace, "#{__dir__}/iso_sts/monospace"
autoload :NamedContent, "#{__dir__}/iso_sts/named_content"
Expand All @@ -101,26 +99,113 @@ module IsoSts
# Shared model foundations
autoload :Abbrev, "#{__dir__}/iso_sts/abbrev"
autoload :Annotation, "#{__dir__}/iso_sts/annotation"
autoload :Attrib, "#{__dir__}/iso_sts/attrib"
autoload :Country, "#{__dir__}/iso_sts/country"
autoload :CustomMeta, "#{__dir__}/iso_sts/custom_meta"
autoload :CustomMetaGroup, "#{__dir__}/iso_sts/custom_meta_group"
autoload :Day, "#{__dir__}/iso_sts/day"
autoload :Etal, "#{__dir__}/iso_sts/etal"
autoload :Fax, "#{__dir__}/iso_sts/fax"
autoload :InlineGraphic, "#{__dir__}/iso_sts/inline_graphic"
autoload :Institution, "#{__dir__}/iso_sts/institution"
autoload :License, "#{__dir__}/iso_sts/license"
autoload :LicenseP, "#{__dir__}/iso_sts/license_p"
autoload :MetaName, "#{__dir__}/iso_sts/meta_name"
autoload :MetaValue, "#{__dir__}/iso_sts/meta_value"
autoload :MilestoneEnd, "#{__dir__}/iso_sts/milestone_end"
autoload :MilestoneStart, "#{__dir__}/iso_sts/milestone_start"
autoload :Month, "#{__dir__}/iso_sts/month"
autoload :Num, "#{__dir__}/iso_sts/num"
autoload :ObjectId, "#{__dir__}/iso_sts/object_id"
autoload :Phone, "#{__dir__}/iso_sts/phone"
autoload :Season, "#{__dir__}/iso_sts/season"

# Recursive content closure
autoload :AddrLine, "#{__dir__}/iso_sts/addr_line"
autoload :Address, "#{__dir__}/iso_sts/address"
autoload :Aff, "#{__dir__}/iso_sts/aff"
autoload :AffAlternatives, "#{__dir__}/iso_sts/aff_alternatives"
autoload :Alternatives, "#{__dir__}/iso_sts/alternatives"
autoload :Anonymous, "#{__dir__}/iso_sts/anonymous"
autoload :ArticleTitle, "#{__dir__}/iso_sts/article_title"
autoload :Attrib, "#{__dir__}/iso_sts/attrib"
autoload :AuthorComment, "#{__dir__}/iso_sts/author_comment"
autoload :Bio, "#{__dir__}/iso_sts/bio"
autoload :BoxedText, "#{__dir__}/iso_sts/boxed_text"
autoload :ChapterTitle, "#{__dir__}/iso_sts/chapter_title"
autoload :ChemStruct, "#{__dir__}/iso_sts/chem_struct"
autoload :ChemStructWrap, "#{__dir__}/iso_sts/chem_struct_wrap"
autoload :Collab, "#{__dir__}/iso_sts/collab"
autoload :Comment, "#{__dir__}/iso_sts/comment"
autoload :ConfDate, "#{__dir__}/iso_sts/conf_date"
autoload :ConfLoc, "#{__dir__}/iso_sts/conf_loc"
autoload :ConfName, "#{__dir__}/iso_sts/conf_name"
autoload :ConfSponsor, "#{__dir__}/iso_sts/conf_sponsor"
autoload :Contrib, "#{__dir__}/iso_sts/contrib"
autoload :ContribGroup, "#{__dir__}/iso_sts/contrib_group"
autoload :Date, "#{__dir__}/iso_sts/date"
autoload :DateInCitation, "#{__dir__}/iso_sts/date_in_citation"
autoload :Degrees, "#{__dir__}/iso_sts/degrees"
autoload :DispFormulaGroup, "#{__dir__}/iso_sts/disp_formula_group"
autoload :DispQuote, "#{__dir__}/iso_sts/disp_quote"
autoload :ElementCitation, "#{__dir__}/iso_sts/element_citation"
autoload :ElocationId, "#{__dir__}/iso_sts/elocation_id"
autoload :FigGroup, "#{__dir__}/iso_sts/fig_group"
autoload :GivenNames, "#{__dir__}/iso_sts/given_names"
autoload :Glossary, "#{__dir__}/iso_sts/glossary"
autoload :GlyphData, "#{__dir__}/iso_sts/glyph_data"
autoload :GlyphRef, "#{__dir__}/iso_sts/glyph_ref"
autoload :Gov, "#{__dir__}/iso_sts/gov"
autoload :InlineSupplementaryMaterial,
"#{__dir__}/iso_sts/inline_supplementary_material"
autoload :Isbn, "#{__dir__}/iso_sts/isbn"
autoload :Issn, "#{__dir__}/iso_sts/issn"
autoload :IssueId, "#{__dir__}/iso_sts/issue_id"
autoload :IssuePart, "#{__dir__}/iso_sts/issue_part"
autoload :IssueTitle, "#{__dir__}/iso_sts/issue_title"
autoload :JournalId, "#{__dir__}/iso_sts/journal_id"
autoload :Media, "#{__dir__}/iso_sts/media"
autoload :Name, "#{__dir__}/iso_sts/name"
autoload :NameAlternatives, "#{__dir__}/iso_sts/name_alternatives"
autoload :OnBehalfOf, "#{__dir__}/iso_sts/on_behalf_of"
autoload :Overline, "#{__dir__}/iso_sts/overline"
autoload :PartTitle, "#{__dir__}/iso_sts/part_title"
autoload :Patent, "#{__dir__}/iso_sts/patent"
autoload :PersonGroup, "#{__dir__}/iso_sts/person_group"
autoload :Prefix, "#{__dir__}/iso_sts/prefix"
autoload :PrivateChar, "#{__dir__}/iso_sts/private_char"
autoload :RelatedArticle, "#{__dir__}/iso_sts/related_article"
autoload :RelatedObject, "#{__dir__}/iso_sts/related_object"
autoload :Role, "#{__dir__}/iso_sts/role"
autoload :Roman, "#{__dir__}/iso_sts/roman"
autoload :SansSerif, "#{__dir__}/iso_sts/sans_serif"
autoload :Series, "#{__dir__}/iso_sts/series"
autoload :Size, "#{__dir__}/iso_sts/size"
autoload :Source, "#{__dir__}/iso_sts/source"
autoload :Speaker, "#{__dir__}/iso_sts/speaker"
autoload :Speech, "#{__dir__}/iso_sts/speech"
autoload :Statement, "#{__dir__}/iso_sts/statement"
autoload :StringName, "#{__dir__}/iso_sts/string_name"
autoload :Subtitle, "#{__dir__}/iso_sts/subtitle"
autoload :Suffix, "#{__dir__}/iso_sts/suffix"
autoload :Supplement, "#{__dir__}/iso_sts/supplement"
autoload :SupplementaryMaterial, "#{__dir__}/iso_sts/supplementary_material"
autoload :Surname, "#{__dir__}/iso_sts/surname"
autoload :TableWrapGroup, "#{__dir__}/iso_sts/table_wrap_group"
autoload :Target, "#{__dir__}/iso_sts/target"
autoload :TextualForm, "#{__dir__}/iso_sts/textual_form"
autoload :TransSource, "#{__dir__}/iso_sts/trans_source"
autoload :TransTitle, "#{__dir__}/iso_sts/trans_title"
autoload :VerseGroup, "#{__dir__}/iso_sts/verse_group"
autoload :VerseLine, "#{__dir__}/iso_sts/verse_line"
autoload :VolumeId, "#{__dir__}/iso_sts/volume_id"
autoload :VolumeSeries, "#{__dir__}/iso_sts/volume_series"

# Remaining namespace closure
autoload :AwardId, "#{__dir__}/iso_sts/award_id"
autoload :CustomMeta, "#{__dir__}/iso_sts/custom_meta"
autoload :CustomMetaGroup, "#{__dir__}/iso_sts/custom_meta_group"
autoload :FundingSource, "#{__dir__}/iso_sts/funding_source"
autoload :License, "#{__dir__}/iso_sts/license"
autoload :LicenseP, "#{__dir__}/iso_sts/license_p"
autoload :MetaName, "#{__dir__}/iso_sts/meta_name"
autoload :MetaValue, "#{__dir__}/iso_sts/meta_value"
autoload :OpenAccess, "#{__dir__}/iso_sts/open_access"
autoload :Price, "#{__dir__}/iso_sts/price"
autoload :TermDisplay, "#{__dir__}/iso_sts/term_display"
autoload :TermHead, "#{__dir__}/iso_sts/term_head"

# List elements
Expand Down
Loading
Loading