sts 0.6.8 → 0.6.9

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd02c307a6e1d5f238e8e05db78958bf73d1bc05c98c170ad89005fb205c7e88
4
- data.tar.gz: 0bc64d38cf7f5ffd0151db213b9cc99dd25ca979b9eacabf0fb5b7f78b4b19af
3
+ metadata.gz: 55257a4c0bd11d4374ea655ffdef2d2a51c1e2fe8761cabbe450188065b85cb6
4
+ data.tar.gz: afe4c91a2e1b739eeffee4d044e8ea03b28774d43146aafdbd2d744d7577880a
5
5
  SHA512:
6
- metadata.gz: 421983c008de22adbe0ca5b7e101590d1a025b76582acb6c750d4d7ebad8582abd9bfa12386ed119d11e9ac2e5ccfe6d6f1216f991ce457fc18e98dd5a86ed91
7
- data.tar.gz: d0d7b00dbd0c8c53268c561fdb965228e753f90c7cc52c0c9c3fa842ba22d4a105232a2b0b0bec778387e86e1c76e6fd3f95e6bad3f1593e5c566e126f3d8b42
6
+ metadata.gz: be6961ce1a6581c1fa6d7a51cb80253a99e01d53fabb31cb3d4feff43f8d87f7a2b283b3fbb8aa07ad31a91e4ac0a634e1bbbcbbf5dce733f45ad93bc77fdfc8
7
+ data.tar.gz: 9c476be12a1c7d51c9552892c2ad0bd677f16677e10587865d5a127ff20cf1be67b9e241a17e90155845ba704b58b404317cd1292bd138ddd00a182b875cb01e
data/.rubocop_todo.yml CHANGED
@@ -24,6 +24,7 @@ Layout/LineLength:
24
24
  - 'lib/sts/tbx_iso_tml/lang_set.rb'
25
25
  - 'lib/sts/tbx_iso_tml/term_type.rb'
26
26
  - 'spec/elements/new_elements_spec.rb'
27
+ - 'spec/iso_sts/schema_validation_spec.rb'
27
28
  - 'spec/round_trip/reference_docs_spec.rb'
28
29
  - 'spec/spec_helper.rb'
29
30
 
@@ -73,6 +74,7 @@ RSpec/DescribeClass:
73
74
  - 'spec/fn_namespace_spec.rb'
74
75
  - 'spec/mathml_version_spec.rb'
75
76
  - 'spec/round_trip/reference_docs_spec.rb'
77
+ - 'spec/iso_sts/schema_validation_spec.rb'
76
78
 
77
79
  # Offense count: 1
78
80
  # This cop supports safe autocorrection (--autocorrect).
@@ -124,3 +126,4 @@ RSpec/SpecFilePathFormat:
124
126
  - 'spec/sts_table_spec.rb'
125
127
  - 'spec/iso_sts/iso_sts_spec.rb'
126
128
  - 'spec/iso_sts/iso_sts_element_spec.rb'
129
+ - 'spec/iso_sts/schema_validation_spec.rb'
data/CLAUDE.md CHANGED
@@ -1,58 +1,84 @@
1
1
  # CLAUDE.md
2
2
 
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
3
+ This file provides guidance to Claude Code (claude.ai/code) for working with code in this repository.
4
4
 
5
5
  ## Build and Test Commands
6
6
 
7
7
  ```bash
8
- # Run tests
8
+ # Run all tests + rubocop (default task)
9
+ bundle exec rake
10
+
11
+ # Run tests only
9
12
  bundle exec rspec
10
13
 
11
- # Run tests with specific file
12
- bundle exec rspec spec/sts_spec.rb
14
+ # Run a specific spec file
15
+ bundle exec rspec spec/iso_sts/iso_sts_element_spec.rb
13
16
 
14
- # Run RuboCop linting
17
+ # Run rubocop
15
18
  bundle exec rubocop
16
19
 
17
- # Run both (default task)
18
- bundle exec rake
20
+ # Auto-fix linting offenses
21
+ bundle exec rubocop -a
19
22
  ```
20
23
 
21
24
  ## Architecture
22
25
 
23
- This is a Ruby gem for parsing NISO STS (National Information Standards Organization - Science and Technical Standards) XML documents. It uses `lutaml-model` for XML serialization/deserialization.
26
+ sts-ruby is a Ruby gem for parsing and serialising NISO STS, ISOSTS, and
27
+ TBX-ISO-TML XML documents. It uses `lutaml-model` for declarative
28
+ serialisation.
24
29
 
25
- ### Namespace Structure
30
+ ### Namespace structure
26
31
 
27
32
  ```
28
33
  Sts
29
- ├── NisoSts # NISO STS elements (lib/sts/niso_sts/)
30
- ├── MmlContent # MathML content elements (autoloaded)
31
- │ └── Xi, Ali # Module elements
32
- ├── TbxIsoTml # TBX ISO TML elements (lib/sts/tbx_iso_tml/)
33
- ├── Mathml # MathML namespace wrapper
34
- └── Namespaces # XML namespace definitions
34
+ ├── IsoSts # ISOSTS v1.1 (frozen legacy, 2013 DTD)
35
+ ├── NisoSts # NISO STS (evolving: 1.0, 1.2)
36
+ ├── TbxIsoTml # TBX-ISO-TML (ISO 30042, shared)
37
+ ├── Namespaces # XML namespace class declarations (TbxNamespace)
38
+ ├── Profiles # ISO/IEC document profile validator
39
+ └── VERSION
35
40
  ```
36
41
 
37
- ### Key Implementation Patterns
42
+ ### Architectural principle: namespace independence
43
+
44
+ ISOSTS and NISO STS are independent schemas that genuinely diverge.
45
+ Each `Sts::<Namespace>::<Class>` is modelled from its own schema source
46
+ (`reference-docs/isosts-v1/xsd/ISOSTS.xsd` for IsoSts,
47
+ `reference-docs/NISO-STS-extended-1-MathML3-XSD/` for NisoSts). NEVER:
48
+
49
+ - Alias `IsoSts::<X> = NisoSts::<X>` (couples the namespaces)
50
+ - Create a shared `Sts::Base::<X>` that both inherit from
51
+ - Reuse NisoSts types inside IsoSts or vice versa
52
+
53
+ TBX is shared (used by both IsoSts and NisoSts hosts), so cross-references
54
+ to `TbxIsoTml::*` are permitted — but each TbxIsoTml class must be a
55
+ true TBX-ISO-TML element, not a JATS/NISO STS element misplaced.
56
+
57
+ See `TODO.roadmap/00-overview.md` for the prioritised roadmap of remaining
58
+ work.
59
+
60
+ ### MathML host binding
61
+
62
+ ISOSTS imports `ncbi-mathml2/mathml2.xsd` (MathML 2). NISO STS uses
63
+ MathML 3. TBX-ISO-TML is hosted under either. Bind per host:
38
64
 
39
- **Element Classes** (in `lib/sts/niso_sts/`):
40
- - Inherit from `Lutaml::Model::Serializable`
41
- - Define attributes and XML mapping in `xml do ... end` block
42
- - Use `map_element` to map child elements, `map_content` for text content
43
- - Use `mixed_content` for elements that contain both text and child elements
65
+ - `IsoSts::*` math attributes → `Mml::V2::Math`
66
+ - `TbxIsoTml::*` math attributes → `Mml::V2::Math` (TBX-in-ISOSTS only)
67
+ - `NisoSts::*` math attributes `Mml::V3::Math`
68
+
69
+ ### Element class pattern
44
70
 
45
- **Example element structure:**
46
71
  ```ruby
47
72
  module Sts
48
- module NisoSts
73
+ module IsoSts
49
74
  class SomeElement < Lutaml::Model::Serializable
50
75
  attribute :id, :string
51
- attribute :content, :string
52
- attribute :child, ::Sts::NisoSts::ChildElement
76
+ attribute :content, :string, collection: true
77
+ attribute :child, ::Sts::IsoSts::ChildElement, collection: true
53
78
 
54
79
  xml do
55
80
  element "some-element"
81
+ ordered # preserve child order on serialise
56
82
  map_attribute "id", to: :id
57
83
  map_content to: :content
58
84
  map_element "child", to: :child
@@ -62,11 +88,55 @@ module Sts
62
88
  end
63
89
  ```
64
90
 
65
- **Autoload Pattern**: Elements are autoloaded via `lib/sts/niso_sts.rb`. When adding new elements, add autoloads in alphabetical order.
91
+ ### Autoload convention
92
+
93
+ `lib/sts.rb` autoloads top-level namespaces. `lib/sts/<namespace>.rb`
94
+ autoloads every class in that namespace, organised by category with
95
+ leading comments and alphabetical order within each group.
96
+
97
+ External gems are eager-required at the top of `lib/sts.rb`
98
+ (`require "lutaml/model"`, `require "mml"`). Internal code never uses
99
+ `require_relative` or `require` with internal paths — autoload only.
100
+ Enforced by `spec/anti_patterns_spec.rb`.
101
+
102
+ ### Anti-patterns (forbidden; enforced by spec)
103
+
104
+ `spec/anti_patterns_spec.rb` runs 9 per-file checks on every `lib/` file:
105
+
106
+ - `method_missing`, `respond_to_missing?` — forbidden (hides interfaces)
107
+ - `Object.const_get` — forbidden (runtime string lookup)
108
+ - `.send(` — forbidden (bypasses access control)
109
+ - `instance_variable_set` / `instance_variable_get` — forbidden (breaks encapsulation)
110
+ - `respond_to?` type-checks — forbidden (use `is_a?` or redesign so the check isn't needed)
111
+ - Hand-rolled serialization methods on `Serializable` subclasses:
112
+ `to_h`, `to_hash`, `from_h`, `from_hash`, `to_json`, `from_json`,
113
+ `serialize`, `deserialize`, `to_xml`, `from_xml` — forbidden
114
+ (use `attribute` + `mapping`; framework-generated serialisation is correct)
115
+ - `require_relative` in lib/ — forbidden (use autoload)
116
+ - `require` with internal library path — forbidden (use autoload;
117
+ external gems like `lutaml/model` are allowlisted)
118
+
119
+ ### Schema authority
120
+
121
+ For any new IsoSts element, generate the attribute list from
122
+ `reference-docs/isosts-v1/xsd/ISOSTS.xsd` directly. Don't copy from
123
+ NisoSts — the schemas disagree on most elements. Attribute-list
124
+ specifications ("X models its configured attribute set") are the
125
+ project's defence against drift, since round-tripping alone cannot prove
126
+ schema conformance.
127
+
128
+ ### Spec patterns
66
129
 
67
- **Bold/Italic Elements**: These use `::Sts::TbxIsoTml::Bold` and `::Sts::TbxIsoTml::Italic` types (not `:string`), enabling proper round-tripping of nested markup.
130
+ - **Attribute-set assertions** `expect(Foo.attributes.keys).to match_array(%i[id content_type ...])`
131
+ - **Child-type assertions** — `expect(Bar.attributes[:foo].type).to eq(Baz)`
132
+ - **Round-trip tests** — `expect(Foo.to_xml(Foo.from_xml(xml))).to be_xml_equivalent_to(xml)`
133
+ - **Schema-validation tests** — `spec/schema_validation_spec.rb` validates
134
+ IsoSts output against ISOSTS.xsd directly via Nokogiri::XML::Schema
68
135
 
69
- ### Reference Data
136
+ ### Reference data
70
137
 
71
- - XSD schema: `reference-docs/NISO-STS-extended-1-MathML3-XSD/`
138
+ - ISOSTS XSD: `reference-docs/isosts-v1/xsd/ISOSTS.xsd`
139
+ - NISO STS XSD: `reference-docs/NISO-STS-extended-1-MathML3-XSD/`
140
+ - TBX schema (under ISOSTS): `reference-docs/isosts-v1/xsd/tbx.xsd`
72
141
  - Test fixtures: `spec/fixtures/`
142
+ - TODO roadmap: `TODO.roadmap/`
data/README.adoc CHANGED
@@ -140,23 +140,26 @@ sts.class.name # => "Sts::IsoSts::Standard"
140
140
 
141
141
  ==== ISOSTS MathML 2
142
142
 
143
- ISOSTS uses MathML 2 (not MathML 3+ like NISO STS). A custom Ruby implementation is provided in `Sts::IsoSts::Mathml2::*`:
143
+ ISOSTS uses MathML 2 (not MathML 3 like NISO STS). All MathML is provided
144
+ by the mml gem, bound per host:
145
+
146
+ * `IsoSts::*` math attributes → `Mml::V2::Math`
147
+ * `TbxIsoTml::*` math attributes → `Mml::V2::Math` (TBX-in-ISOSTS only)
148
+ * `NisoSts::*` math attributes → `Mml::V3::Math`
144
149
 
145
150
  [source,ruby]
146
151
  ----
147
- # Access MathML 2 content
148
152
  disp_formula = sts.body.sec.first.disp_formula
149
- math = disp_formula.math # => Sts::IsoSts::Mathml2::Math
150
-
151
- # MathML 2 elements
152
- math.id # => "mml_1"
153
- math.display # => "block" or "inline"
153
+ math = disp_formula.math # => Mml::V2::Math
154
154
 
155
- # Serialized as mml:math
156
- math.to_xml # => <mml:math xmlns:mml="http://www.w3.org/1998/Math/MathML" ...>
155
+ # Round-trip preserves mml: prefix
156
+ disp_formula.to_xml # => <disp-formula><mml:math ...>...</mml:math></disp-formula>
157
157
  ----
158
158
 
159
- Available MathML 2 elements: `Math`, `Mrow`, `Mi`, `Mn`, `Mo`, `Mtext`, `Mspace`, `Msub`, `Msup`, `Msubsup`, `Mfrac`, `Msqrt`, `Mroot`, `Mstyle`, `Menclose`, `Mpadded`, `Mphantom`, `Mtable`, `Mtr`, `Mtd`, `Mlabeledtr`, `Mfenced`, `Semantics`, `Annotation`, `AnnotationXml`
159
+ MathML 2 and 3 share the same XML namespace (`http://www.w3.org/1998/Math/MathML`),
160
+ so `Mml::Namespace` (the mml gem's top-level namespace class) is used
161
+ directly in both `IsoSts::Standard#namespace_scope` and
162
+ `NisoSts::Standard#namespace_scope`.
160
163
 
161
164
  ==== ISOSTS TBX Terminology
162
165
 
@@ -191,6 +194,42 @@ sts = Sts::NisoSts::Standard.from_xml(xml_with_tbx)
191
194
  sts = Sts::IsoSts::Standard.from_xml(xml_with_tbx)
192
195
  ----
193
196
 
197
+ == Architecture principles
198
+
199
+ === Namespace independence
200
+
201
+ `IsoSts`, `NisoSts`, and `TbxIsoTml` are independent schemas that
202
+ genuinely diverge. Each `Sts::<Namespace>::<Class>` is modelled from its
203
+ own schema source — never aliased across namespaces, never sharing base
204
+ classes. The schemas disagree on most elements (e.g., `IsoSts::Fig` has
205
+ `title`/`alternatives` that `NisoSts::Figure` lacks), so sharing types
206
+ would fight the schemas and violate the Open/Closed principle.
207
+
208
+ === Anti-pattern enforcement
209
+
210
+ `spec/anti_patterns_spec.rb` runs 9 per-file checks across `lib/` to
211
+ forbid: `method_missing`, `respond_to_missing?`, `Object.const_get`,
212
+ `.send(`, `instance_variable_set`/`get`, `respond_to?` type-checks,
213
+ hand-rolled serialization methods on `Serializable` subclasses
214
+ (`to_h`, `to_xml`, `from_xml`, etc.), `require_relative`, and
215
+ internal `require`. See `CLAUDE.md` for the rationale.
216
+
217
+ === Schema validation
218
+
219
+ `spec/schema_validation_spec.rb` validates IsoSts output against the
220
+ canonical `reference-docs/isosts-v1/xsd/ISOSTS.xsd` via
221
+ `Nokogiri::XML::Schema`. Round-tripping alone cannot prove schema
222
+ conformance (a model that invents or drops an attribute still
223
+ round-trips symmetrically); the XSD is the only authoritative check.
224
+
225
+ === Autoload convention
226
+
227
+ `lib/sts.rb` autoloads top-level namespaces. Each namespace's
228
+ `lib/sts/<namespace>.rb` autoloads every class in alphabetical order
229
+ within category groups. External gems (`lutaml/model`, `mml`) are
230
+ eager-required at the top of `lib/sts.rb`; internal code uses autoload
231
+ only — no `require_relative`, no `require "sts/..."`.
232
+
194
233
  == Credits
195
234
 
196
235
  This gem is developed, maintained and funded by
data/lib/sts/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sts
4
- VERSION = "0.6.8"
4
+ VERSION = "0.6.9"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sts
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.8
4
+ version: 0.6.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose