diff --git a/TODO.sts-refactor/00-overview.md b/TODO.sts-refactor/00-overview.md index 1cbe21b..3e82413 100644 --- a/TODO.sts-refactor/00-overview.md +++ b/TODO.sts-refactor/00-overview.md @@ -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 | 01, 02 | +| 03 | `03-namespace-coupling.md` | Architecture | IN PROGRESS (63→20 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 | @@ -140,7 +140,9 @@ grep -r "method_missing|respond_to_missing|Object.const_get|\.send" lib/ ``` ### IsoSts Namespace Independence (2026-05-07) -- ARCHITECTURE DECISION: IsoSts and NisoSts must remain fully independent +- ARCHITECTURE DECISION: IsoSts must be independent **of NisoSts**. This is not + independence in general — `lib/sts/iso_sts/` still references `TbxIsoTml` and + MathML types directly, and those shared namespaces are outside this ADR. - ISOSTS is frozen legacy; NISO STS evolves — combining them violates OCP - Created 11 new IsoSts-specific types: Monospace, Sc, Strike, Underline, Uri, NamedContent, StandardRef, MetaDate, ContentLanguage (with autoloads) @@ -164,9 +166,27 @@ 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: 157→63 IsoSts→NisoSts cross-references remaining +- `03-namespace-coupling.md` — IN PROGRESS: 63→20 IsoSts→NisoSts references + remaining (issue #40). Source of truth is `ISOSTS.xsd`, NOT the NisoSts + models — they disagree on nearly every element. - `04-register-versioning.md` — Version the models via lutaml-model Registers - `11-duplicate-models.md` — 44 overlapping element resolution (depends on 03) +### Known bugs — schema conformance (separate from 03) +The 2026-05-07 "@id added to all models (XSD-verified)" pass verified against +the **NISO** XSD and applied the result to IsoSts. ISOSTS disagrees: +- **16 IsoSts models silently drop ISOSTS attributes that exist** — real data + loss. `sub`/`sup` lose `arrange`+`specific-use`; `ext-link` loses 5 xlink + attrs; `mixed-citation` loses 6; `graphic`, `copyright-*`, `edition`, + `title`, `label`, `uri`, `named-content`, `underline`, `meta-date`, `body` + lose others. +- **25 IsoSts models carry an `@id` ISOSTS does not define** — harmless (never + populated on parse, never emitted on serialise), but dead surface. + ## Next Action -Proceed with `03-namespace-coupling.md` — audit and resolve 157+ IsoSts→NisoSts cross-references. +Two independent tracks: +1. Finish `03` — the 20 remaining refs split into 11 deep-root refs (each + reaching a ~152-class recursive core) and 9 child-bearing refs whose closure + is unmeasured. Both need sizing before planning. +2. File and fix the 16 lossy models above — real data loss, unrelated to + decoupling. diff --git a/TODO.sts-refactor/03-namespace-coupling.md b/TODO.sts-refactor/03-namespace-coupling.md index 6f347ac..06124ab 100644 --- a/TODO.sts-refactor/03-namespace-coupling.md +++ b/TODO.sts-refactor/03-namespace-coupling.md @@ -3,156 +3,117 @@ **Priority**: HIGH **Category**: Architecture **Estimated Effort**: High -**Files Affected**: `lib/sts/iso_sts/content_groups/highlight_elements.rb` (194+ references) +**Status**: Partially done — 63 → 20 references (GitHub issue #40) ## Problem -`lib/sts/iso_sts/content_groups/highlight_elements.rb` contains 194+ references to `Sts::NisoSts::*` types: - -```ruby -# Examples from highlight_elements.rb -[:sub, "Sts::NisoSts::Sub", "sub"] -[:sup, "Sts::NisoSts::Sup", "sup"] -[:italic, "Sts::NisoSts::Italic", "italic"] -[:bold, "Sts::NisoSts::Bold", "bold"] -# ... 190 more -``` - -### Why This Is Wrong - -1. **Violation of namespace isolation**: IsoSts should not depend on NisoSts implementation -2. **Shared-namespace ambiguity**: These elements appear in both ISOSTS and NISO STS but with different attribute sets -3. **Tight coupling**: Changes to NisoSts can break IsoSts -4. **Schema divergence ignored**: `

` in ISOSTS has different attributes than `

` in NISO STS Extended - -### Root Cause - -The schemas ARE different: -- ISOSTS `

`: `@content-type, @id, @specific-use, @xml:lang` -- NISO STS Extended `

`: `@content-type, @id, @originator, @specific-use, @style-type, @xml:base, @xml:lang` - -## Solution: Three-Tier Architecture - -### Tier 1: Shared Base Types - -Create shared types in `Sts::Base` that both namespaces inherit: - -```ruby -# lib/sts/base/text/emphasis.rb -module Sts - module Base - module Text - class Emphasis < Lutaml::Model::Serializable - # Shared attributes/methods - attribute :id, :string - attribute :base_fontsize, :string - attribute :baseline_shift, :string - attribute :color, :string - - xml do - map_attribute :id, to: :id - # ... - end - end - - class Bold < Emphasis - xml { element "bold" } - end - - class Italic < Emphasis - xml { element "italic" } - end - - class Sub < Emphasis - xml { element "sub" } - end - - class Sup < Emphasis - xml { element "sup" } - end - end - end -end -``` - -### Tier 2: IsoSts-Specific Extensions - -```ruby -# lib/sts/iso_sts/text/emphasis.rb -module Sts - module IsoSts - module Text - class Bold < ::Sts::Base::Text::Bold - # Add IsoSts-specific attributes - attribute :content_type, :string - - xml do - # Override/add IsoSts-specific mapping - map_attribute "content-type", to: :content_type - end - end - end - end -end -``` - -### Tier 3: NisoSts-Specific Extensions - -```ruby -# lib/sts/niso_sts/text/emphasis.rb -module Sts - module NisoSts - module Text - class Bold < ::Sts::Base::Text::Bold - # Add NisoSts-specific attributes - attribute :originator, :string - attribute :style_type, :string - - xml do - map_attribute "originator", to: :originator - map_attribute "style-type", to: :style_type - end - end - end - end -end -``` - -## Implementation Strategy - -1. **Phase 1**: Identify all shared element types (bold, italic, sub, sup, p, etc.) -2. **Phase 2**: Create base types with MINIMAL shared attributes -3. **Phase 3**: Refactor IsoSts to extend bases -4. **Phase 4**: Refactor NisoSts to extend bases -5. **Phase 5**: Delete `Sts::NisoSts::*` references from IsoSts content groups - -## Files to Refactor - -Primary targets: -- `lib/sts/iso_sts/content_groups/highlight_elements.rb` -- `lib/sts/iso_sts/content_groups/emphasis_elements.rb` (if exists) -- `lib/sts/niso_sts/content_groups/highlight_elements.rb` +`lib/sts/iso_sts/` references `Sts::NisoSts::*` types directly. IsoSts must be +independent of NisoSts (ADR 2026-05-07): ISOSTS is frozen legacy, NISO STS +evolves, so coupling them violates OCP. + +PR #31 reduced this from 157 to 63 references. Issue #40 reduced it further, +from 63 to 20. + +## Source of truth: ISOSTS.xsd, NOT the NisoSts models + +The obvious approach — duplicate each NisoSts class into IsoSts — produces +**schema-incorrect models**. The NisoSts models disagree with +`reference-docs/isosts-v1/xsd/ISOSTS.xsd` on nearly every element: + +| Element | ISOSTS says | NisoSts model | +|---|---|---| +| `year` (:948) | `content-type`, `specific-use`, `xml:lang` — no `@id` | `@id` only | +| `doc-type` (:6378) | `type="xs:string"` — no attributes at all | `@id` + content | +| `ics` (:6373) | `type="xs:string"` | `@id` + `ics-desc` child | +| `fpage` | `content-type`, `seq`, `specific-use`, `xml:lang`; no children | `@id` + bold/italic | +| `license` (:51) | `license-type`, `specific-use`, `xml:lang` | `@id`, `xlink:href` | +| `ruby` | **not an ISOSTS element** | exists (NISO only) | + +`feature_doc.xml` declares ``, and +the ISOSTS DTD agrees with ISOSTS.xsd (both derive `bold`, `sub` etc. from the +JATS 0.4 modules). ISOSTS.xsd is a faithful conversion and is the authority. + +**Round-tripping does not prove correctness.** A model that invents or drops an +attribute still parses and serialises symmetrically, so the suite stays green +while the model is wrong. Assert the exact attribute set per element instead. + +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 — 43 refs removed, 27 classes added + +- **14 `xs:string` elements** (`originator`, `doc-type`, `doc-number`, + `part-number`, `version`, `suppl-type`, `suppl-number`, `suppl-version`, + `urn`, `sdo`, `proj-id`, `release-version`, `ics`, `secretariat`) — modelled + as content-only IsoSts classes with no attributes. 23 refs. +- **3 `permissions` refs** repointed to the existing `IsoSts::Permissions`. +- **`ruby` deleted** from `StyledContent` — ISOSTS defines no such element. + Behaviour change: `` in `` no longer round-trips. +- **13 element classes** modelled from ISOSTS.xsd: `Year`, `PubDate`, + `ReleaseVersionId`, `IsProof`, `AltText`, `LongDesc`, `TexMath`, `PubId`, + `Volume`, `Issue`, `Fpage`, `Lpage`, `PageRange`. 16 refs. +- **`WiNumber` added** — ISOSTS gives `wi-number` an `@id`; it was typed as a + plain string, silently discarding that `@id`. + +### Why classes and not plain `:string` + +`xs:string` elements look like they need no class. They do: an empty element +(``, which `feature_doc.xml` contains) does not survive a `:string` +round-trip. For a scalar, `render_empty: :empty` recovers it; for a collection +(`secretariat`, `ics` — both `maxOccurs="unbounded"`) an empty element parses +to `[]`, destroying the information at parse time before any render option +applies. Content-only classes round-trip every case. + +## Remaining — 20 refs + +**11 refs to 9 deep roots**: `MetadataStd`, `ElementCitation`, `PersonGroup`, +`Collab`, `Source`, `DispQuote`, `EditingInstruction`, `TermDisplay`, +`BoxedText`. Each reaches the same ~152-class mutually-recursive core +(`Section` → `Paragraph` → `DispQuote` → `Paragraph`). `DispQuote` has 3 direct +children but pulls all 152. No incremental path — needs its own decision. + +**9 refs whose ISOSTS content models have real children**, closure not measured: +`attrib` (:1082), `term-head` (:4308), `article-title`, `license` → `license-p` +(:51), `publisher` → `publisher-loc`, `custom-meta-group` → `custom-meta`. +`attrib` and `term-head` are **not** trivial leaves in ISOSTS despite what the +NisoSts models suggest; `license-p` reaches `array`/`alternatives`, so these may +not be cheap. Measure before planning. + +## Rejected: three-tier `Sts::Base` hierarchy + +An earlier draft of this document proposed shared base types in `Sts::Base` that +both namespaces inherit. This contradicts the 2026-05-07 ADR, and the schemas +genuinely diverge — `IsoSts::Fig` has `title`/`alternatives` that +`NisoSts::Figure` lacks; `IsoSts::Ref` has `nlm-citation`/`citation-alternatives` +that `NisoSts::Reference` lacks. A shared base would fight the schemas. + +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) + +A mechanical `s/NisoSts::/IsoSts::/` is wrong. Nine elements already have +different class names per namespace, so a blind rename creates `IsoSts::Section` +alongside the existing `IsoSts::Sec` — two classes for one `sec` element: + +`Section`/`Sec`, `Figure`/`Fig`, `Reference`/`Ref`, `ReferenceList`/`RefList`, +`SectionArray`/`Array`, `StdCrossReference`/`StandardCrossReference`, +`DisplayFormula`/`DispFormula`, `TermSection`/`TermSec`, +`ReferenceStandard`/`Std`. + +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. ## Verification -1. No `Sts::NisoSts` references in `lib/sts/iso_sts/` -2. All round-trip tests pass -3. Schema validation against both ISOSTS DTD and NISO STS XSD passes - -## Dependencies - -- `01-mathml-delegation.md` — uses similar patterns -- `02-type-resolution.md` — must be clean first - -## TODO Checklist - -- [ ] Audit all IsoSts → NisoSts references -- [ ] Categorize by shared vs. truly namespace-specific -- [ ] Design base type hierarchy -- [ ] Create `lib/sts/base/` directory structure -- [ ] Implement base types for shared elements -- [ ] Refactor IsoSts to use base types -- [ ] Refactor NisoSts to use base types -- [ ] Remove cross-namespace references -- [ ] Verify no circular dependencies -- [ ] Run full test suite +1. `grep -rho "Sts::NisoSts::" lib/sts/iso_sts/ | wc -l` → `20` +2. Every attribute on an IsoSts model traces to a line in `ISOSTS.xsd` +3. Autoload registry 1:1 with the directory (112/112) +4. `bundle exec rspec` green; `bundle exec rubocop` clean diff --git a/lib/sts/iso_sts.rb b/lib/sts/iso_sts.rb index cc7eb63..20a4c4a 100644 --- a/lib/sts/iso_sts.rb +++ b/lib/sts/iso_sts.rb @@ -38,6 +38,33 @@ module IsoSts autoload :MetaDate, "#{__dir__}/iso_sts/meta_date" autoload :ContentLanguage, "#{__dir__}/iso_sts/content_language" autoload :StandardRef, "#{__dir__}/iso_sts/standard_ref" + autoload :WiNumber, "#{__dir__}/iso_sts/wi_number" + autoload :Originator, "#{__dir__}/iso_sts/originator" + autoload :DocType, "#{__dir__}/iso_sts/doc_type" + autoload :DocNumber, "#{__dir__}/iso_sts/doc_number" + autoload :PartNumber, "#{__dir__}/iso_sts/part_number" + autoload :Version, "#{__dir__}/iso_sts/version" + autoload :SupplType, "#{__dir__}/iso_sts/suppl_type" + autoload :SupplNumber, "#{__dir__}/iso_sts/suppl_number" + autoload :SupplVersion, "#{__dir__}/iso_sts/suppl_version" + autoload :Urn, "#{__dir__}/iso_sts/urn" + autoload :Sdo, "#{__dir__}/iso_sts/sdo" + autoload :ProjId, "#{__dir__}/iso_sts/proj_id" + autoload :ReleaseVersion, "#{__dir__}/iso_sts/release_version" + autoload :Ics, "#{__dir__}/iso_sts/ics" + autoload :Year, "#{__dir__}/iso_sts/year" + autoload :PubDate, "#{__dir__}/iso_sts/pub_date" + autoload :ReleaseVersionId, "#{__dir__}/iso_sts/release_version_id" + autoload :IsProof, "#{__dir__}/iso_sts/is_proof" + autoload :AltText, "#{__dir__}/iso_sts/alt_text" + autoload :LongDesc, "#{__dir__}/iso_sts/long_desc" + autoload :TexMath, "#{__dir__}/iso_sts/tex_math" + autoload :PubId, "#{__dir__}/iso_sts/pub_id" + autoload :Volume, "#{__dir__}/iso_sts/volume" + autoload :Issue, "#{__dir__}/iso_sts/issue" + autoload :Fpage, "#{__dir__}/iso_sts/fpage" + autoload :Lpage, "#{__dir__}/iso_sts/lpage" + autoload :PageRange, "#{__dir__}/iso_sts/page_range" # Section elements autoload :Sec, "#{__dir__}/iso_sts/sec" diff --git a/lib/sts/iso_sts/alt_text.rb b/lib/sts/iso_sts/alt_text.rb new file mode 100644 index 0000000..6e0e339 --- /dev/null +++ b/lib/sts/iso_sts/alt_text.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + class AltText < Lutaml::Model::Serializable + attribute :content, :string + attribute :id, :string + attribute :content_type, :string + attribute :specific_use, :string + attribute :xml_lang, :string + + xml do + element "alt-text" + + map_content to: :content + map_attribute "id", to: :id + map_attribute "content-type", to: :content_type + map_attribute "specific-use", to: :specific_use + map_attribute "xml:lang", to: :xml_lang + end + end + end +end diff --git a/lib/sts/iso_sts/app.rb b/lib/sts/iso_sts/app.rb index 5fc77d3..1d331b2 100644 --- a/lib/sts/iso_sts/app.rb +++ b/lib/sts/iso_sts/app.rb @@ -15,7 +15,7 @@ class App < Lutaml::Model::Serializable attribute :term_sec, ::Sts::IsoSts::TermSec, collection: true attribute :fn_group, ::Sts::IsoSts::FnGroup, collection: true attribute :ref_list, ::Sts::IsoSts::RefList, collection: true - attribute :permissions, ::Sts::NisoSts::Permissions + attribute :permissions, ::Sts::IsoSts::Permissions attribute :non_normative_note, ::Sts::IsoSts::NonNormativeNote, collection: true attribute :non_normative_example, ::Sts::IsoSts::NonNormativeExample, diff --git a/lib/sts/iso_sts/array.rb b/lib/sts/iso_sts/array.rb index 9da2e28..2394ab4 100644 --- a/lib/sts/iso_sts/array.rb +++ b/lib/sts/iso_sts/array.rb @@ -12,7 +12,7 @@ class Array < Lutaml::Model::Serializable attribute :label, ::Sts::IsoSts::Label attribute :table, ::Sts::IsoSts::Table attribute :attrib, ::Sts::NisoSts::Attrib, collection: true - attribute :permissions, ::Sts::NisoSts::Permissions, collection: true + attribute :permissions, ::Sts::IsoSts::Permissions, collection: true xml do element "array" diff --git a/lib/sts/iso_sts/disp_formula.rb b/lib/sts/iso_sts/disp_formula.rb index 0f9dea8..6643b62 100644 --- a/lib/sts/iso_sts/disp_formula.rb +++ b/lib/sts/iso_sts/disp_formula.rb @@ -17,7 +17,7 @@ class DispFormula < Lutaml::Model::Serializable attribute :styled_content, ::Sts::IsoSts::StyledContent attribute :preformat, ::Sts::IsoSts::Preformat attribute :graphic, ::Sts::IsoSts::Graphic, collection: true - attribute :tex_math, ::Sts::NisoSts::TexMath + attribute :tex_math, ::Sts::IsoSts::TexMath xml do element "disp-formula" diff --git a/lib/sts/iso_sts/doc_number.rb b/lib/sts/iso_sts/doc_number.rb new file mode 100644 index 0000000..9237d6b --- /dev/null +++ b/lib/sts/iso_sts/doc_number.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + # ISOSTS declares as type="xs:string" -- no attributes. + class DocNumber < Lutaml::Model::Serializable + attribute :content, :string + + xml do + element "doc-number" + + map_content to: :content + end + end + end +end diff --git a/lib/sts/iso_sts/doc_type.rb b/lib/sts/iso_sts/doc_type.rb new file mode 100644 index 0000000..a729ab0 --- /dev/null +++ b/lib/sts/iso_sts/doc_type.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + # ISOSTS declares as type="xs:string" -- no attributes. + class DocType < Lutaml::Model::Serializable + attribute :content, :string + + xml do + element "doc-type" + + map_content to: :content + end + end + end +end diff --git a/lib/sts/iso_sts/document_identification.rb b/lib/sts/iso_sts/document_identification.rb index 2e1f3df..b030b1c 100644 --- a/lib/sts/iso_sts/document_identification.rb +++ b/lib/sts/iso_sts/document_identification.rb @@ -4,11 +4,11 @@ module Sts module IsoSts class DocumentIdentification < Lutaml::Model::Serializable attribute :id, :string - attribute :sdo, ::Sts::NisoSts::Sdo - attribute :proj_id, ::Sts::NisoSts::ProjId + attribute :sdo, ::Sts::IsoSts::Sdo + attribute :proj_id, ::Sts::IsoSts::ProjId attribute :language, ::Sts::IsoSts::Language - attribute :release_version, ::Sts::NisoSts::ReleaseVersion - attribute :urn, ::Sts::NisoSts::Urn + attribute :release_version, ::Sts::IsoSts::ReleaseVersion + attribute :urn, ::Sts::IsoSts::Urn xml do element "doc-ident" diff --git a/lib/sts/iso_sts/fpage.rb b/lib/sts/iso_sts/fpage.rb new file mode 100644 index 0000000..de76153 --- /dev/null +++ b/lib/sts/iso_sts/fpage.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + class Fpage < Lutaml::Model::Serializable + attribute :content, :string + attribute :content_type, :string + attribute :seq, :string + attribute :specific_use, :string + attribute :xml_lang, :string + + xml do + element "fpage" + + map_content to: :content + map_attribute "content-type", to: :content_type + map_attribute "seq", to: :seq + map_attribute "specific-use", to: :specific_use + map_attribute "xml:lang", to: :xml_lang + end + end + end +end diff --git a/lib/sts/iso_sts/graphic.rb b/lib/sts/iso_sts/graphic.rb index 142f464..bb24037 100644 --- a/lib/sts/iso_sts/graphic.rb +++ b/lib/sts/iso_sts/graphic.rb @@ -16,8 +16,8 @@ class Graphic < Lutaml::Model::Serializable attribute :graphic_type, :string attribute :label, ::Sts::IsoSts::Label attribute :caption, ::Sts::IsoSts::Caption - attribute :alt_text, ::Sts::NisoSts::AltText - attribute :long_desc, ::Sts::NisoSts::LongDesc + attribute :alt_text, ::Sts::IsoSts::AltText + attribute :long_desc, ::Sts::IsoSts::LongDesc xml do element "graphic" diff --git a/lib/sts/iso_sts/ics.rb b/lib/sts/iso_sts/ics.rb new file mode 100644 index 0000000..acaa863 --- /dev/null +++ b/lib/sts/iso_sts/ics.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + # ISOSTS declares as type="xs:string" -- no attributes. + class Ics < Lutaml::Model::Serializable + attribute :content, :string + + xml do + element "ics" + + map_content to: :content + end + end + end +end diff --git a/lib/sts/iso_sts/is_proof.rb b/lib/sts/iso_sts/is_proof.rb new file mode 100644 index 0000000..2835330 --- /dev/null +++ b/lib/sts/iso_sts/is_proof.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + # ISOSTS declares as an empty complexType: its presence is the + # whole signal -- no attributes, no content. + class IsProof < Lutaml::Model::Serializable + xml do + element "is-proof" + end + end + end +end diff --git a/lib/sts/iso_sts/iso_meta.rb b/lib/sts/iso_sts/iso_meta.rb index 19e71e8..43dc1cd 100644 --- a/lib/sts/iso_sts/iso_meta.rb +++ b/lib/sts/iso_sts/iso_meta.rb @@ -14,7 +14,7 @@ class IsoMeta < Lutaml::Model::Serializable attribute :release_date, ::Sts::IsoSts::ReleaseDate, collection: true attribute :comm_ref, ::Sts::IsoSts::CommRef attribute :secretariat, ::Sts::IsoSts::Secretariat, collection: true - attribute :ics, ::Sts::NisoSts::Ics, collection: true + attribute :ics, ::Sts::IsoSts::Ics, collection: true attribute :page_count, ::Sts::IsoSts::PageCount attribute :permissions, ::Sts::IsoSts::Permissions, collection: true attribute :std_xref, ::Sts::IsoSts::StandardCrossReference, @@ -22,8 +22,8 @@ class IsoMeta < Lutaml::Model::Serializable attribute :custom_meta_group, ::Sts::NisoSts::CustomMetaGroup, collection: true attribute :meta_date, ::Sts::IsoSts::MetaDate, collection: true - attribute :pub_date, ::Sts::NisoSts::PubDate - attribute :is_proof, ::Sts::NisoSts::IsProof + attribute :pub_date, ::Sts::IsoSts::PubDate + attribute :is_proof, ::Sts::IsoSts::IsProof xml do element "iso-meta" diff --git a/lib/sts/iso_sts/issue.rb b/lib/sts/iso_sts/issue.rb new file mode 100644 index 0000000..be0b74e --- /dev/null +++ b/lib/sts/iso_sts/issue.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + class Issue < Lutaml::Model::Serializable + attribute :content, :string + attribute :content_type, :string + attribute :seq, :string + attribute :specific_use, :string + attribute :xml_lang, :string + + xml do + element "issue" + + map_content to: :content + map_attribute "content-type", to: :content_type + map_attribute "seq", to: :seq + map_attribute "specific-use", to: :specific_use + map_attribute "xml:lang", to: :xml_lang + end + end + end +end diff --git a/lib/sts/iso_sts/long_desc.rb b/lib/sts/iso_sts/long_desc.rb new file mode 100644 index 0000000..a093b81 --- /dev/null +++ b/lib/sts/iso_sts/long_desc.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + class LongDesc < Lutaml::Model::Serializable + attribute :content, :string + attribute :id, :string + attribute :content_type, :string + attribute :specific_use, :string + attribute :xml_lang, :string + + xml do + element "long-desc" + + map_content to: :content + map_attribute "id", to: :id + map_attribute "content-type", to: :content_type + map_attribute "specific-use", to: :specific_use + map_attribute "xml:lang", to: :xml_lang + end + end + end +end diff --git a/lib/sts/iso_sts/lpage.rb b/lib/sts/iso_sts/lpage.rb new file mode 100644 index 0000000..870f153 --- /dev/null +++ b/lib/sts/iso_sts/lpage.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + class Lpage < Lutaml::Model::Serializable + attribute :content, :string + attribute :content_type, :string + attribute :specific_use, :string + attribute :xml_lang, :string + + xml do + element "lpage" + + map_content to: :content + map_attribute "content-type", to: :content_type + map_attribute "specific-use", to: :specific_use + map_attribute "xml:lang", to: :xml_lang + end + end + end +end diff --git a/lib/sts/iso_sts/mixed_citation.rb b/lib/sts/iso_sts/mixed_citation.rb index b6e12ab..74f67aa 100644 --- a/lib/sts/iso_sts/mixed_citation.rb +++ b/lib/sts/iso_sts/mixed_citation.rb @@ -24,16 +24,16 @@ class MixedCitation < Lutaml::Model::Serializable attribute :break, ::Sts::IsoSts::Break, collection: true attribute :person_group, ::Sts::NisoSts::PersonGroup, collection: true attribute :collab, ::Sts::NisoSts::Collab, collection: true - attribute :year, ::Sts::NisoSts::Year, collection: true + attribute :year, ::Sts::IsoSts::Year, collection: true attribute :source, ::Sts::NisoSts::Source attribute :article_title, ::Sts::NisoSts::ArticleTitle - attribute :volume, ::Sts::NisoSts::Volume - attribute :issue, ::Sts::NisoSts::Issue - attribute :fpage, ::Sts::NisoSts::Fpage - attribute :lpage, ::Sts::NisoSts::Lpage - attribute :page_range, ::Sts::NisoSts::PageRange + attribute :volume, ::Sts::IsoSts::Volume + attribute :issue, ::Sts::IsoSts::Issue + attribute :fpage, ::Sts::IsoSts::Fpage + attribute :lpage, ::Sts::IsoSts::Lpage + attribute :page_range, ::Sts::IsoSts::PageRange attribute :publisher, ::Sts::NisoSts::Publisher - attribute :pub_id, ::Sts::NisoSts::PubId, collection: true + attribute :pub_id, ::Sts::IsoSts::PubId, collection: true xml do element "mixed-citation" diff --git a/lib/sts/iso_sts/nat_meta.rb b/lib/sts/iso_sts/nat_meta.rb index 2295d1b..cb362e7 100644 --- a/lib/sts/iso_sts/nat_meta.rb +++ b/lib/sts/iso_sts/nat_meta.rb @@ -14,8 +14,8 @@ class NatMeta < Lutaml::Model::Serializable attribute :doc_ref, :string attribute :release_date, ::Sts::IsoSts::ReleaseDate attribute :comm_ref, :string - attribute :secretariat, ::Sts::NisoSts::Secretariat, collection: true - attribute :ics, ::Sts::NisoSts::Ics, collection: true + attribute :secretariat, ::Sts::IsoSts::Secretariat, collection: true + attribute :ics, ::Sts::IsoSts::Ics, collection: true attribute :page_count, ::Sts::IsoSts::PageCount attribute :permissions, ::Sts::IsoSts::Permissions, collection: true attribute :std_xref, ::Sts::IsoSts::StandardCrossReference, @@ -23,7 +23,7 @@ class NatMeta < Lutaml::Model::Serializable attribute :custom_meta_group, ::Sts::NisoSts::CustomMetaGroup, collection: true attribute :meta_date, ::Sts::IsoSts::MetaDate, collection: true - attribute :pub_date, ::Sts::NisoSts::PubDate + attribute :pub_date, ::Sts::IsoSts::PubDate xml do element "nat-meta" diff --git a/lib/sts/iso_sts/originator.rb b/lib/sts/iso_sts/originator.rb new file mode 100644 index 0000000..21d32c9 --- /dev/null +++ b/lib/sts/iso_sts/originator.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + # ISOSTS declares as type="xs:string" -- no attributes. + class Originator < Lutaml::Model::Serializable + attribute :content, :string + + xml do + element "originator" + + map_content to: :content + end + end + end +end diff --git a/lib/sts/iso_sts/page_range.rb b/lib/sts/iso_sts/page_range.rb new file mode 100644 index 0000000..e02d62e --- /dev/null +++ b/lib/sts/iso_sts/page_range.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + class PageRange < Lutaml::Model::Serializable + attribute :content, :string + attribute :content_type, :string + attribute :specific_use, :string + attribute :xml_lang, :string + + xml do + element "page-range" + + map_content to: :content + map_attribute "content-type", to: :content_type + map_attribute "specific-use", to: :specific_use + map_attribute "xml:lang", to: :xml_lang + end + end + end +end diff --git a/lib/sts/iso_sts/part_number.rb b/lib/sts/iso_sts/part_number.rb new file mode 100644 index 0000000..d86ee78 --- /dev/null +++ b/lib/sts/iso_sts/part_number.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + # ISOSTS declares as type="xs:string" -- no attributes. + class PartNumber < Lutaml::Model::Serializable + attribute :content, :string + + xml do + element "part-number" + + map_content to: :content + end + end + end +end diff --git a/lib/sts/iso_sts/proj_id.rb b/lib/sts/iso_sts/proj_id.rb new file mode 100644 index 0000000..a34ab85 --- /dev/null +++ b/lib/sts/iso_sts/proj_id.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + # ISOSTS declares as type="xs:string" -- no attributes. + class ProjId < Lutaml::Model::Serializable + attribute :content, :string + + xml do + element "proj-id" + + map_content to: :content + end + end + end +end diff --git a/lib/sts/iso_sts/pub_date.rb b/lib/sts/iso_sts/pub_date.rb new file mode 100644 index 0000000..8a1c429 --- /dev/null +++ b/lib/sts/iso_sts/pub_date.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + class PubDate < Lutaml::Model::Serializable + attribute :content, :string + attribute :pub_type, :string + + xml do + element "pub-date" + + map_content to: :content + map_attribute "pub-type", to: :pub_type + end + end + end +end diff --git a/lib/sts/iso_sts/pub_id.rb b/lib/sts/iso_sts/pub_id.rb new file mode 100644 index 0000000..e12f808 --- /dev/null +++ b/lib/sts/iso_sts/pub_id.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + class PubId < Lutaml::Model::Serializable + attribute :content, :string + attribute :pub_id_type, :string + attribute :specific_use, :string + + xml do + element "pub-id" + + map_content to: :content + map_attribute "pub-id-type", to: :pub_id_type + map_attribute "specific-use", to: :specific_use + end + end + end +end diff --git a/lib/sts/iso_sts/reg_meta.rb b/lib/sts/iso_sts/reg_meta.rb index 6ed9935..caff0f7 100644 --- a/lib/sts/iso_sts/reg_meta.rb +++ b/lib/sts/iso_sts/reg_meta.rb @@ -14,8 +14,8 @@ class RegMeta < Lutaml::Model::Serializable attribute :doc_ref, :string attribute :release_date, ::Sts::IsoSts::ReleaseDate attribute :comm_ref, :string - attribute :secretariat, ::Sts::NisoSts::Secretariat, collection: true - attribute :ics, ::Sts::NisoSts::Ics, collection: true + attribute :secretariat, ::Sts::IsoSts::Secretariat, collection: true + attribute :ics, ::Sts::IsoSts::Ics, collection: true attribute :page_count, ::Sts::IsoSts::PageCount attribute :permissions, ::Sts::IsoSts::Permissions, collection: true attribute :std_xref, ::Sts::IsoSts::StandardCrossReference, @@ -23,9 +23,9 @@ class RegMeta < Lutaml::Model::Serializable attribute :custom_meta_group, ::Sts::NisoSts::CustomMetaGroup, collection: true attribute :meta_date, ::Sts::IsoSts::MetaDate, collection: true - attribute :pub_date, ::Sts::NisoSts::PubDate - attribute :wi_number, :string - attribute :release_version_id, ::Sts::NisoSts::ReleaseVersionId + attribute :pub_date, ::Sts::IsoSts::PubDate + attribute :wi_number, ::Sts::IsoSts::WiNumber + attribute :release_version_id, ::Sts::IsoSts::ReleaseVersionId xml do element "reg-meta" diff --git a/lib/sts/iso_sts/release_version.rb b/lib/sts/iso_sts/release_version.rb new file mode 100644 index 0000000..ff06313 --- /dev/null +++ b/lib/sts/iso_sts/release_version.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + # ISOSTS declares as type="xs:string" -- no attributes. + class ReleaseVersion < Lutaml::Model::Serializable + attribute :content, :string + + xml do + element "release-version" + + map_content to: :content + end + end + end +end diff --git a/lib/sts/iso_sts/release_version_id.rb b/lib/sts/iso_sts/release_version_id.rb new file mode 100644 index 0000000..debf9d4 --- /dev/null +++ b/lib/sts/iso_sts/release_version_id.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + class ReleaseVersionId < Lutaml::Model::Serializable + attribute :content, :string + attribute :id, :string + + xml do + element "release-version-id" + + map_content to: :content + map_attribute "id", to: :id + end + end + end +end diff --git a/lib/sts/iso_sts/sdo.rb b/lib/sts/iso_sts/sdo.rb new file mode 100644 index 0000000..54285d3 --- /dev/null +++ b/lib/sts/iso_sts/sdo.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + # ISOSTS declares as type="xs:string" -- no attributes. + class Sdo < Lutaml::Model::Serializable + attribute :content, :string + + xml do + element "sdo" + + map_content to: :content + end + end + end +end diff --git a/lib/sts/iso_sts/secretariat.rb b/lib/sts/iso_sts/secretariat.rb index debfb54..3dbd3a7 100644 --- a/lib/sts/iso_sts/secretariat.rb +++ b/lib/sts/iso_sts/secretariat.rb @@ -2,13 +2,12 @@ module Sts module IsoSts + # ISOSTS declares as type="xs:string" -- no attributes. class Secretariat < Lutaml::Model::Serializable - attribute :id, :string - attribute :content, :string, collection: true + attribute :content, :string + xml do element "secretariat" - map_attribute "id", to: :id - mixed_content map_content to: :content end diff --git a/lib/sts/iso_sts/standard_identification.rb b/lib/sts/iso_sts/standard_identification.rb index e3cfbff..6a301b4 100644 --- a/lib/sts/iso_sts/standard_identification.rb +++ b/lib/sts/iso_sts/standard_identification.rb @@ -4,15 +4,15 @@ module Sts module IsoSts class StandardIdentification < Lutaml::Model::Serializable attribute :id, :string - attribute :originator, ::Sts::NisoSts::Originator - attribute :doc_type, ::Sts::NisoSts::DocType - attribute :doc_number, ::Sts::NisoSts::DocNumber - attribute :part_number, ::Sts::NisoSts::PartNumber + attribute :originator, ::Sts::IsoSts::Originator + attribute :doc_type, ::Sts::IsoSts::DocType + attribute :doc_number, ::Sts::IsoSts::DocNumber + attribute :part_number, ::Sts::IsoSts::PartNumber attribute :edition, ::Sts::IsoSts::Edition - attribute :version, ::Sts::NisoSts::Version - attribute :suppl_type, ::Sts::NisoSts::SupplType - attribute :suppl_number, ::Sts::NisoSts::SupplNumber - attribute :suppl_version, ::Sts::NisoSts::SupplVersion + attribute :version, ::Sts::IsoSts::Version + attribute :suppl_type, ::Sts::IsoSts::SupplType + attribute :suppl_number, ::Sts::IsoSts::SupplNumber + attribute :suppl_version, ::Sts::IsoSts::SupplVersion xml do element "std-ident" diff --git a/lib/sts/iso_sts/std_ref.rb b/lib/sts/iso_sts/std_ref.rb index c0154ac..6feefdc 100644 --- a/lib/sts/iso_sts/std_ref.rb +++ b/lib/sts/iso_sts/std_ref.rb @@ -5,14 +5,14 @@ module IsoSts class StdRef < Lutaml::Model::Serializable attribute :id, :string attribute :type, :string - attribute :originator, ::Sts::NisoSts::Originator - attribute :doc_type, ::Sts::NisoSts::DocType - attribute :doc_number, ::Sts::NisoSts::DocNumber - attribute :part_number, ::Sts::NisoSts::PartNumber + attribute :originator, ::Sts::IsoSts::Originator + attribute :doc_type, ::Sts::IsoSts::DocType + attribute :doc_number, ::Sts::IsoSts::DocNumber + attribute :part_number, ::Sts::IsoSts::PartNumber attribute :edition, ::Sts::IsoSts::Edition - attribute :suppl_type, ::Sts::NisoSts::SupplType - attribute :suppl_number, ::Sts::NisoSts::SupplNumber - attribute :year, ::Sts::NisoSts::Year + attribute :suppl_type, ::Sts::IsoSts::SupplType + attribute :suppl_number, ::Sts::IsoSts::SupplNumber + attribute :year, ::Sts::IsoSts::Year attribute :content, :string, collection: true xml do element "std-ref" diff --git a/lib/sts/iso_sts/styled_content.rb b/lib/sts/iso_sts/styled_content.rb index 085afd2..61996ef 100644 --- a/lib/sts/iso_sts/styled_content.rb +++ b/lib/sts/iso_sts/styled_content.rb @@ -25,7 +25,6 @@ class StyledContent < Lutaml::Model::Serializable attribute :sc, ::Sts::IsoSts::Sc, collection: true attribute :strike, ::Sts::IsoSts::Strike, collection: true attribute :underline, ::Sts::IsoSts::Underline, collection: true - attribute :ruby, ::Sts::NisoSts::Ruby, collection: true attribute :break, ::Sts::IsoSts::Break, collection: true attribute :styled_content, ::Sts::IsoSts::StyledContent, collection: true @@ -57,7 +56,6 @@ class StyledContent < Lutaml::Model::Serializable map_element "sc", to: :sc map_element "strike", to: :strike map_element "underline", to: :underline - map_element "ruby", to: :ruby map_element "break", to: :break map_element "styled-content", to: :styled_content end diff --git a/lib/sts/iso_sts/suppl_number.rb b/lib/sts/iso_sts/suppl_number.rb new file mode 100644 index 0000000..98f156d --- /dev/null +++ b/lib/sts/iso_sts/suppl_number.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + # ISOSTS declares as type="xs:string" -- no attributes. + class SupplNumber < Lutaml::Model::Serializable + attribute :content, :string + + xml do + element "suppl-number" + + map_content to: :content + end + end + end +end diff --git a/lib/sts/iso_sts/suppl_type.rb b/lib/sts/iso_sts/suppl_type.rb new file mode 100644 index 0000000..0d83ea5 --- /dev/null +++ b/lib/sts/iso_sts/suppl_type.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + # ISOSTS declares as type="xs:string" -- no attributes. + class SupplType < Lutaml::Model::Serializable + attribute :content, :string + + xml do + element "suppl-type" + + map_content to: :content + end + end + end +end diff --git a/lib/sts/iso_sts/suppl_version.rb b/lib/sts/iso_sts/suppl_version.rb new file mode 100644 index 0000000..997b837 --- /dev/null +++ b/lib/sts/iso_sts/suppl_version.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + # ISOSTS declares as type="xs:string" -- no attributes. + class SupplVersion < Lutaml::Model::Serializable + attribute :content, :string + + xml do + element "suppl-version" + + map_content to: :content + end + end + end +end diff --git a/lib/sts/iso_sts/table_wrap_foot.rb b/lib/sts/iso_sts/table_wrap_foot.rb index 5be0991..2492fd5 100644 --- a/lib/sts/iso_sts/table_wrap_foot.rb +++ b/lib/sts/iso_sts/table_wrap_foot.rb @@ -11,7 +11,7 @@ class TableWrapFoot < Lutaml::Model::Serializable attribute :fn_group, ::Sts::IsoSts::FnGroup attribute :fn, ::Sts::IsoSts::Fn attribute :attrib, ::Sts::NisoSts::Attrib - attribute :permissions, ::Sts::NisoSts::Permissions + attribute :permissions, ::Sts::IsoSts::Permissions xml do element "table-wrap-foot" diff --git a/lib/sts/iso_sts/tex_math.rb b/lib/sts/iso_sts/tex_math.rb new file mode 100644 index 0000000..3c0ab9c --- /dev/null +++ b/lib/sts/iso_sts/tex_math.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + class TexMath < Lutaml::Model::Serializable + attribute :content, :string + attribute :id, :string + attribute :content_type, :string + attribute :notation, :string + attribute :version, :string + + xml do + element "tex-math" + + map_content to: :content + map_attribute "id", to: :id + map_attribute "content-type", to: :content_type + map_attribute "notation", to: :notation + map_attribute "version", to: :version + end + end + end +end diff --git a/lib/sts/iso_sts/urn.rb b/lib/sts/iso_sts/urn.rb new file mode 100644 index 0000000..09f2fcc --- /dev/null +++ b/lib/sts/iso_sts/urn.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + # ISOSTS declares as type="xs:string" -- no attributes. + class Urn < Lutaml::Model::Serializable + attribute :content, :string + + xml do + element "urn" + + map_content to: :content + end + end + end +end diff --git a/lib/sts/iso_sts/version.rb b/lib/sts/iso_sts/version.rb new file mode 100644 index 0000000..9c11fc7 --- /dev/null +++ b/lib/sts/iso_sts/version.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + # ISOSTS declares as type="xs:string" -- no attributes. + class Version < Lutaml::Model::Serializable + attribute :content, :string + + xml do + element "version" + + map_content to: :content + end + end + end +end diff --git a/lib/sts/iso_sts/volume.rb b/lib/sts/iso_sts/volume.rb new file mode 100644 index 0000000..47e5d6a --- /dev/null +++ b/lib/sts/iso_sts/volume.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + class Volume < Lutaml::Model::Serializable + attribute :content, :string + attribute :content_type, :string + attribute :seq, :string + attribute :specific_use, :string + attribute :xml_lang, :string + + xml do + element "volume" + + map_content to: :content + map_attribute "content-type", to: :content_type + map_attribute "seq", to: :seq + map_attribute "specific-use", to: :specific_use + map_attribute "xml:lang", to: :xml_lang + end + end + end +end diff --git a/lib/sts/iso_sts/wi_number.rb b/lib/sts/iso_sts/wi_number.rb new file mode 100644 index 0000000..01e8de9 --- /dev/null +++ b/lib/sts/iso_sts/wi_number.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + # ISOSTS declares as mixed content carrying an @id. Modelling it + # as a plain string silently discarded that @id. + class WiNumber < Lutaml::Model::Serializable + attribute :content, :string + attribute :id, :string + + xml do + element "wi-number" + + map_content to: :content + map_attribute "id", to: :id + end + end + end +end diff --git a/lib/sts/iso_sts/year.rb b/lib/sts/iso_sts/year.rb new file mode 100644 index 0000000..736bb4d --- /dev/null +++ b/lib/sts/iso_sts/year.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Sts + module IsoSts + class Year < Lutaml::Model::Serializable + attribute :content, :string + attribute :content_type, :string + attribute :specific_use, :string + attribute :xml_lang, :string + + xml do + element "year" + + map_content to: :content + map_attribute "content-type", to: :content_type + map_attribute "specific-use", to: :specific_use + map_attribute "xml:lang", to: :xml_lang + end + end + end +end diff --git a/spec/iso_sts/iso_sts_element_spec.rb b/spec/iso_sts/iso_sts_element_spec.rb index 0997ea1..4e8c4b2 100644 --- a/spec/iso_sts/iso_sts_element_spec.rb +++ b/spec/iso_sts/iso_sts_element_spec.rb @@ -427,4 +427,183 @@ expect(math).to be_a(Lutaml::Model::Serializable) end end + + # ISOSTS declares these elements as type="xs:string": text content, no + # attributes. They are modelled as IsoSts classes rather than plain strings + # because an empty element () does not survive a :string round-trip -- + # for a collection it parses to [], losing the element entirely. + describe "ISOSTS xs:string elements" do + { + Originator: "originator", + DocType: "doc-type", + DocNumber: "doc-number", + PartNumber: "part-number", + Version: "version", + SupplType: "suppl-type", + SupplNumber: "suppl-number", + SupplVersion: "suppl-version", + Urn: "urn", + Sdo: "sdo", + ProjId: "proj-id", + ReleaseVersion: "release-version", + Ics: "ics", + Secretariat: "secretariat", + }.each do |klass, element| + it "IsoSts::#{klass} maps <#{element}>" do + model = described_class.const_get(klass) + expect(model.mappings_for(:xml).root_element).to eq(element) + end + + it "IsoSts::#{klass} has only content, as ISOSTS defines no attributes" do + model = described_class.const_get(klass) + expect(model.attributes.keys).to eq(%i[content]) + end + + it "IsoSts::#{klass} round-trips an empty <#{element}/>" do + model = described_class.const_get(klass) + xml = "<#{element}/>" + expect(model.to_xml(model.from_xml(xml)).strip).to eq(xml) + end + end + end + + describe "IsoSts models that reference the xs:string element classes" do + { + "StandardIdentification" => { + originator: :Originator, doc_type: :DocType, doc_number: :DocNumber, + part_number: :PartNumber, version: :Version, suppl_type: :SupplType, + suppl_number: :SupplNumber, suppl_version: :SupplVersion + }, + "StdRef" => { + originator: :Originator, doc_type: :DocType, doc_number: :DocNumber, + part_number: :PartNumber, suppl_type: :SupplType, + suppl_number: :SupplNumber + }, + "DocumentIdentification" => { + sdo: :Sdo, proj_id: :ProjId, release_version: :ReleaseVersion, + urn: :Urn + }, + "RegMeta" => { secretariat: :Secretariat, ics: :Ics }, + "NatMeta" => { secretariat: :Secretariat, ics: :Ics }, + "IsoMeta" => { secretariat: :Secretariat, ics: :Ics }, + }.each do |model_name, expectations| + expectations.each do |attr, klass| + it "#{model_name}##{attr} uses IsoSts::#{klass}" do + model = described_class.const_get(model_name) + expect(model.attributes[attr].type) + .to eq(described_class.const_get(klass)) + end + end + end + end + + # Attribute sets below are transcribed from ISOSTS.xsd. Round-tripping does + # not prove them: a model that invents or drops an attribute still parses and + # serialises symmetrically. Asserting the exact set is what catches that. + describe "ISOSTS-modelled element classes" do + { + Year: %i[content content_type specific_use xml_lang], + PubDate: %i[content pub_type], + ReleaseVersionId: %i[content id], + AltText: %i[content id content_type specific_use xml_lang], + LongDesc: %i[content id content_type specific_use xml_lang], + TexMath: %i[content id content_type notation version], + PubId: %i[content pub_id_type specific_use], + Volume: %i[content content_type seq specific_use xml_lang], + Issue: %i[content content_type seq specific_use xml_lang], + Fpage: %i[content content_type seq specific_use xml_lang], + Lpage: %i[content content_type specific_use xml_lang], + PageRange: %i[content content_type specific_use xml_lang], + }.each do |klass, expected| + it "IsoSts::#{klass} models exactly the ISOSTS attribute set" do + expect(described_class.const_get(klass).attributes.keys) + .to match_array(expected) + end + end + + it "IsoSts::Year has no id, which ISOSTS does not define" do + expect(described_class::Year.attributes).not_to have_key(:id) + end + + it "IsoSts::Fpage carries seq but IsoSts::Lpage does not" do + expect(described_class::Fpage.attributes).to have_key(:seq) + expect(described_class::Lpage.attributes).not_to have_key(:seq) + end + + it "IsoSts::Fpage models no bold or italic children" do + expect(described_class::Fpage.attributes).not_to have_key(:bold) + expect(described_class::Fpage.attributes).not_to have_key(:italic) + end + + it "IsoSts::IsProof is an empty element" do + expect(described_class::IsProof.attributes).to be_empty + round_tripped = described_class::IsProof + .to_xml(described_class::IsProof.new).strip + expect(round_tripped).to eq("") + end + end + + describe "IsoSts models that reference the ISOSTS-modelled classes" do + { + "StdRef" => { year: :Year }, + "MixedCitation" => { + year: :Year, volume: :Volume, issue: :Issue, fpage: :Fpage, + lpage: :Lpage, page_range: :PageRange, pub_id: :PubId + }, + "RegMeta" => { + pub_date: :PubDate, release_version_id: :ReleaseVersionId + }, + "IsoMeta" => { pub_date: :PubDate, is_proof: :IsProof }, + "NatMeta" => { pub_date: :PubDate }, + "Graphic" => { alt_text: :AltText, long_desc: :LongDesc }, + "DispFormula" => { tex_math: :TexMath }, + }.each do |model_name, expectations| + expectations.each do |attr, klass| + it "#{model_name}##{attr} uses IsoSts::#{klass}" do + model = described_class.const_get(model_name) + expect(model.attributes[attr].type) + .to eq(described_class.const_get(klass)) + end + end + end + end + + # ISOSTS is copyright-statement*, copyright-year*, + # copyright-holder*, license* -- exactly what IsoSts::Permissions models. + describe "permissions uses the IsoSts model" do + %w[Array App TableWrapFoot].each do |model_name| + it "#{model_name}#permissions is IsoSts::Permissions" do + model = described_class.const_get(model_name) + expect(model.attributes[:permissions].type) + .to eq(described_class::Permissions) + end + end + end + + # ISOSTS defines no element; only NISO STS does. + describe "styled-content matches the ISOSTS content model" do + it "does not model , which ISOSTS does not define" do + expect(described_class::StyledContent.attributes).not_to have_key(:ruby) + end + end + + # ISOSTS gives an @id; modelling it as a plain string dropped it. + describe "IsoSts::WiNumber" do + it "models the @id that ISOSTS defines" do + expect(described_class::WiNumber.attributes.keys) + .to match_array(%i[content id]) + end + + it "preserves @id through a round-trip" do + xml = '12345' + parsed = described_class::WiNumber.from_xml(xml) + expect(parsed.id).to eq("wi-1") + expect(described_class::WiNumber.to_xml(parsed).strip).to eq(xml) + end + + it "is used by RegMeta rather than a plain string" do + expect(described_class::RegMeta.attributes[:wi_number].type) + .to eq(described_class::WiNumber) + end + end end