mml 2.3.7 → 2.4.1

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.
Files changed (125) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +96 -30
  3. data/TODO.complete/1-v3-autoload.md +63 -0
  4. data/TODO.complete/2-v2-autoload.md +46 -0
  5. data/TODO.complete/3-no-require-relative.md +32 -0
  6. data/TODO.complete/4-remove-duplicate-register-model.md +31 -0
  7. data/TODO.complete/5-context-options-type-check.md +44 -0
  8. data/TODO.complete/6-context-configuration-type-check.md +32 -0
  9. data/TODO.complete/7-specs-for-refactor.md +38 -0
  10. data/lib/mml/base/maction.rb +1 -1
  11. data/lib/mml/base/math.rb +1 -1
  12. data/lib/mml/base/menclose.rb +1 -1
  13. data/lib/mml/base/merror.rb +1 -1
  14. data/lib/mml/base/mfenced.rb +1 -4
  15. data/lib/mml/base/mfrac.rb +1 -1
  16. data/lib/mml/base/mfraction.rb +1 -1
  17. data/lib/mml/base/mi.rb +2 -0
  18. data/lib/mml/base/mlabeledtr.rb +1 -1
  19. data/lib/mml/base/mlongdiv.rb +1 -1
  20. data/lib/mml/base/mmultiscripts.rb +1 -1
  21. data/lib/mml/base/mn.rb +2 -0
  22. data/lib/mml/base/mo.rb +6 -1
  23. data/lib/mml/base/mover.rb +1 -1
  24. data/lib/mml/base/mpadded.rb +1 -1
  25. data/lib/mml/base/mphantom.rb +1 -1
  26. data/lib/mml/base/mroot.rb +1 -1
  27. data/lib/mml/base/mrow.rb +1 -3
  28. data/lib/mml/base/ms.rb +4 -0
  29. data/lib/mml/base/mscarries.rb +1 -1
  30. data/lib/mml/base/mscarry.rb +1 -1
  31. data/lib/mml/base/msgroup.rb +1 -3
  32. data/lib/mml/base/msqrt.rb +1 -1
  33. data/lib/mml/base/msrow.rb +1 -1
  34. data/lib/mml/base/mstack.rb +1 -1
  35. data/lib/mml/base/mstyle.rb +1 -1
  36. data/lib/mml/base/msub.rb +1 -1
  37. data/lib/mml/base/msubsup.rb +1 -1
  38. data/lib/mml/base/msup.rb +1 -1
  39. data/lib/mml/base/mtable.rb +1 -1
  40. data/lib/mml/base/mtd.rb +1 -1
  41. data/lib/mml/base/mtext.rb +6 -1
  42. data/lib/mml/base/mtr.rb +1 -1
  43. data/lib/mml/base/munder.rb +1 -3
  44. data/lib/mml/base/munderover.rb +1 -1
  45. data/lib/mml/base/semantics.rb +1 -1
  46. data/lib/mml/context_configuration.rb +3 -1
  47. data/lib/mml/context_options.rb +10 -1
  48. data/lib/mml/v2/configuration.rb +0 -2
  49. data/lib/mml/v2.rb +382 -246
  50. data/lib/mml/v3/annotation.rb +0 -1
  51. data/lib/mml/v3/annotation_xml.rb +0 -1
  52. data/lib/mml/v3/apply.rb +0 -1
  53. data/lib/mml/v3/arith.rb +0 -22
  54. data/lib/mml/v3/bind.rb +0 -1
  55. data/lib/mml/v3/calculus.rb +0 -7
  56. data/lib/mml/v3/cbytes.rb +0 -1
  57. data/lib/mml/v3/cerror.rb +0 -1
  58. data/lib/mml/v3/ci.rb +0 -1
  59. data/lib/mml/v3/cn.rb +0 -1
  60. data/lib/mml/v3/configuration.rb +0 -2
  61. data/lib/mml/v3/constants.rb +0 -15
  62. data/lib/mml/v3/constructs.rb +0 -10
  63. data/lib/mml/v3/cs.rb +0 -1
  64. data/lib/mml/v3/csymbol.rb +0 -1
  65. data/lib/mml/v3/deprecated_content.rb +0 -7
  66. data/lib/mml/v3/elementary_functions.rb +0 -28
  67. data/lib/mml/v3/functions.rb +0 -6
  68. data/lib/mml/v3/linear_algebra.rb +0 -9
  69. data/lib/mml/v3/logic.rb +0 -7
  70. data/lib/mml/v3/maction.rb +0 -1
  71. data/lib/mml/v3/maligngroup.rb +0 -1
  72. data/lib/mml/v3/malignmark.rb +0 -1
  73. data/lib/mml/v3/math.rb +0 -2
  74. data/lib/mml/v3/menclose.rb +0 -1
  75. data/lib/mml/v3/merror.rb +0 -1
  76. data/lib/mml/v3/mfenced.rb +0 -1
  77. data/lib/mml/v3/mfrac.rb +0 -1
  78. data/lib/mml/v3/mfraction.rb +0 -1
  79. data/lib/mml/v3/mglyph.rb +0 -1
  80. data/lib/mml/v3/mi.rb +0 -1
  81. data/lib/mml/v3/mlabeledtr.rb +0 -1
  82. data/lib/mml/v3/mlongdiv.rb +0 -1
  83. data/lib/mml/v3/mmultiscripts.rb +0 -1
  84. data/lib/mml/v3/mn.rb +0 -1
  85. data/lib/mml/v3/mo.rb +0 -1
  86. data/lib/mml/v3/mover.rb +0 -1
  87. data/lib/mml/v3/mpadded.rb +0 -1
  88. data/lib/mml/v3/mphantom.rb +0 -1
  89. data/lib/mml/v3/mprescripts.rb +0 -1
  90. data/lib/mml/v3/mroot.rb +0 -1
  91. data/lib/mml/v3/mrow.rb +0 -1
  92. data/lib/mml/v3/ms.rb +0 -1
  93. data/lib/mml/v3/mscarries.rb +0 -1
  94. data/lib/mml/v3/mscarry.rb +0 -1
  95. data/lib/mml/v3/msgroup.rb +0 -1
  96. data/lib/mml/v3/msline.rb +0 -1
  97. data/lib/mml/v3/mspace.rb +0 -1
  98. data/lib/mml/v3/msqrt.rb +0 -1
  99. data/lib/mml/v3/msrow.rb +0 -1
  100. data/lib/mml/v3/mstack.rb +0 -1
  101. data/lib/mml/v3/mstyle.rb +0 -1
  102. data/lib/mml/v3/msub.rb +0 -1
  103. data/lib/mml/v3/msubsup.rb +0 -1
  104. data/lib/mml/v3/msup.rb +0 -1
  105. data/lib/mml/v3/mtable.rb +0 -1
  106. data/lib/mml/v3/mtd.rb +0 -1
  107. data/lib/mml/v3/mtext.rb +0 -1
  108. data/lib/mml/v3/mtr.rb +0 -1
  109. data/lib/mml/v3/munder.rb +0 -1
  110. data/lib/mml/v3/munderover.rb +0 -1
  111. data/lib/mml/v3/none.rb +0 -1
  112. data/lib/mml/v3/relations.rb +0 -9
  113. data/lib/mml/v3/semantics.rb +0 -1
  114. data/lib/mml/v3/sets.rb +0 -13
  115. data/lib/mml/v3/statistics.rb +0 -7
  116. data/lib/mml/v3/vector_calculus.rb +0 -4
  117. data/lib/mml/v3.rb +451 -73
  118. data/lib/mml/v4/annotation.rb +0 -1
  119. data/lib/mml/v4/configuration.rb +0 -2
  120. data/lib/mml/v4/elementary_functions.rb +0 -1
  121. data/lib/mml/v4/functions.rb +0 -1
  122. data/lib/mml/v4.rb +21 -17
  123. data/lib/mml/version.rb +1 -1
  124. data/lib/mml.rb +2 -0
  125. metadata +9 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 47af8c20b7e74aa165f2c37b79c42114fefd3271cb69ede3c80dc684c6009295
4
- data.tar.gz: 146194a451aefd1f06fdddbe0d914944650eefd3f555a1a09f792b110d64ffc6
3
+ metadata.gz: 753f58cf7a8516296624dc7eb472621cd4ecaab7a7d1c3f34d7e8d67eff9bbce
4
+ data.tar.gz: 062164c14aa4475421fc48ca37de907be75938d8b574a768cc8e8e374bd91252
5
5
  SHA512:
6
- metadata.gz: 6ecd1f5b414c7ee585196a3576d2a34a76962e5ec4008013b1b3b6fada27df01d3cbe68891e80cc7077a7ccfc8996aa952d8617ebaf11b49b3bed11831cdf767
7
- data.tar.gz: 2dddd5b009a917449239df9b663e31afc532e6f1b4562b37f6bb8edc9c3e4f1c6737bfafe4c0cf88344a6c5bec92038b2050442234fe2ea4573d816720164101
6
+ metadata.gz: b43c4d0bd97523c911860ff881eb7375ae9755ac5755985408c27c4aa43246d57cd4b26d7a8bb00c8e5020ee1b9cf9d331f49a6e3a5524957f36469d3c5b9549
7
+ data.tar.gz: 5d93d2e051d43efb46e83e36bc5464339020e73742183369e6b45813cf3fcd90203c3d3440768c8259f0e174c6435b20863a994debf4417f30308391c9df2e90
data/CLAUDE.md CHANGED
@@ -4,70 +4,129 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
4
4
 
5
5
  ## Project Overview
6
6
 
7
- `mml` is a Ruby gem that provides MathML 3 and MathML 4 XML parsing and serialization. It maps MathML elements into Ruby model classes using the `lutaml-model` framework. Part of the [Plurimath](https://github.com/plurimath/mml) ecosystem.
7
+ `mml` is a Ruby gem that provides MathML 2, MathML 3, and MathML 4 XML parsing and serialization. It maps MathML elements into Ruby model classes using the `lutaml-model` framework. Part of the [Plurimath](https://github.com/plurimath/mml) ecosystem.
8
8
 
9
9
  ## Commands
10
10
 
11
11
  ```bash
12
- rake # Run specs + rubocop (default task)
13
- bundle exec rspec # Run tests
14
- bundle exec rspec spec/mml_spec.rb:42 # Run single test by line
15
- bundle exec rspec --only-failures # Run only previously failing tests
16
- bundle exec rubocop # Lint
17
- bundle exec rubocop -a # Auto-fix lint issues
18
- bin/console # IRB with gem loaded
12
+ rake # Run specs + rubocop (default task)
13
+ bundle exec rspec # Run all tests
14
+ bundle exec rspec spec/mml/v3/msub_spec.rb:14 # Run single test by line
15
+ bundle exec rspec --only-failures # Previously failing tests only
16
+ bundle exec rubocop # Lint
17
+ bundle exec rubocop -a # Auto-fix lint issues
18
+ bin/console # IRB with gem loaded
19
19
  ```
20
20
 
21
21
  ## Versioned Architecture
22
22
 
23
- **MathML 3 vs MathML 4:** The gem maintains separate class hierarchies for MathML 3 (`Mml::V3::`) and MathML 4 (`Mml::V4::`). Users must reference the versioned namespace explicitly — no backward-compat aliases.
23
+ The gem maintains **three** separate class hierarchies: `Mml::V2::` (MathML 2), `Mml::V3::` (MathML 3), and `Mml::V4::` (MathML 4). Users must reference the versioned namespace explicitly — no backward-compat aliases.
24
24
 
25
25
  ```ruby
26
- Mml.parse(input) # Default: MathML 3
27
- Mml.parse(input, version: 3) # Explicit MathML 3
28
- Mml.parse(input, version: 4) # MathML 4 with intent/arg attributes
26
+ Mml.parse(input) # Default: MathML 3
27
+ Mml.parse(input, version: 2) # MathML 2
28
+ Mml.parse(input, version: 3) # Explicit MathML 3
29
+ Mml.parse(input, version: 4) # MathML 4 with intent/arg attributes
29
30
  ```
30
31
 
31
32
  **Directory structure:**
32
- - `lib/mml/v3/` — MathML 3 element classes (original)
33
- - `lib/mml/v4/` — MathML 4 element classes (intent/arg added, deprecated attrs removed)
33
+ - `lib/mml/base/` — version-agnostic element modules, mixed into each version's class via `include Base::XYZ`. The single source of truth for the XML mapping DSL.
34
+ - `lib/mml/v2/`, `lib/mml/v3/`, `lib/mml/v4/` — per-version class declarations (each ~3 lines: declare class, mix in base module, register).
34
35
 
35
- **Key difference:** MathML 4 adds `intent`, `arg`, `displaystyle`, and `scriptlevel` attributes as universal presentation attributes for accessibility markup.
36
+ **Key difference (V3 → V4):** MathML 4 adds `intent`, `arg`, `displaystyle`, and `scriptlevel` as universal presentation attributes for accessibility markup; removes some deprecated attributes.
36
37
 
37
38
  **No hidden delegation:** The `Mml` module does not alias or delegate constants. Use `Mml::V3::Math`, `Mml::V4::Mi`, etc. directly.
38
39
 
39
40
  ## Entry Points
40
41
 
41
- - `Mml.parse(input, version: N)` — parse XML, returns `Mml::V3::Math` or `Mml::V4::Math` object graph
42
+ - `Mml.parse(input, version: N)` — parse XML, returns `Mml::V{2,3,4}::Math` object graph
42
43
  - `Mml::V3.parse(input)` / `Mml::V4.parse(input)` — version-specific parsing
43
44
  - `Mml::V4::Math.from_xml(input)` — directly parse with v4 classes
44
45
  - Call `to_xml` on any element to serialize back
45
46
 
46
- ## Pattern
47
+ ## Element Mapping Pattern
47
48
 
48
- Each MathML element is a `Mml::V3::` or `Mml::V4::` class inheriting from `Lutaml::Model::Serializable` with an `xml do...end` DSL block. Two element types:
49
- - **Leaf elements** (e.g., `Mi`, `Mn`, `Mo`): use `map_content to: :value` for text content
50
- - **Container elements** (e.g., `Math`, `Mrow`, `Mfrac`): use `mixed_content` to accept arbitrary child elements
49
+ Each MathML element is a `Lutaml::Model::Serializable` subclass with an `xml do...end` DSL block. The XML mapping lives in `lib/mml/base/<tag>.rb` as a module included into each version's class (e.g., `Mml::V3::Msub` includes `Mml::Base::Msub`).
51
50
 
52
- **CommonAttributes:** A `no_root` Lutaml model imported into container elements via `import_model`. It dynamically creates `#{tag}_value` attributes for each tag in `Configuration::SUPPORTED_TAGS`. Classes that receive it are listed in `Configuration::COMMON_ATTRIBUTES_CLASSES`.
51
+ ### Content model `mixed_content` vs `ordered` vs `map_content`
53
52
 
54
- **Autoloading:** Each version (`lib/mml/v3.rb`, `lib/mml/v4.rb`) autoloads its element classes. `CommonAttributes` is required after all classes exist, then `update_attributes` mixes it into the configured classes.
53
+ This is the most important decision when adding or editing an element. Pick based on the **schema** (see `schemas/mathml3/`, `schemas/mathml4/`, `reference-docs/mathml-source/`):
55
54
 
56
- **Namespace:** Both versions use the same URI (`http://www.w3.org/1998/Math/MathML`) — MathML 4 chose backward compatibility over a new namespace.
55
+ | Schema content model | Use | Examples |
56
+ |---------------------------------------------------|--------------------------------------|--------------------------------|
57
+ | True mixed `(#PCDATA \| mglyph \| malignmark)*` | `mixed_content` + `map_content` | `Mi`, `Mn`, `Mo`, `Ms`, `Mtext`|
58
+ | Element-only (`ImpliedMrow`, `<xs:sequence>`, etc.) | `ordered` | `Msub`, `Mfrac`, `Mrow`, `Math`, `Munder`, `Mfenced`, `Msgroup`, … |
59
+
60
+ **Why this matters:** lutaml-model's `each_mixed_content` iteration exposes all children to consumers (Plurimath's translator does positional indexing like `children[0]`, `children[1]` for `msub`/`mfrac`/etc.).
61
+ - Under `mixed_content`, whitespace between elements is yielded as a String child, shifting positional indices.
62
+ - Under `ordered`, whitespace-only text nodes are skipped — only elements and non-whitespace text are yielded.
63
+
64
+ If you mistakenly use `mixed_content` on an element-only schema element, downstream consumers see phantom whitespace children and silently misinterpret the tree (e.g., render `<msub><mi>t</mi><mn>90</mn></msub>` with whitespace between children as base=whitespace, sub=`<mi>t</mi>`, dropping `<mn>90</mn>`).
65
+
66
+ **Verification workflow for any element edit:**
67
+ ```bash
68
+ grep -A15 'name="<tag>"' schemas/mathml3/mathml3-presentation.xsd schemas/mathml3/mathml3-common.xsd
69
+ grep -A3 '<tag>\s*=\s*element' schemas/mathml4/mathml4-core.rnc schemas/mathml4/mathml4-presentation.rnc
70
+ ```
71
+
72
+ If the schema says `ImpliedMrow`, `MathExpression, MathExpression`, `MstackExpression*`, `TableRowExpression*`, etc. (any element-only sequence), use `ordered`. If it says `(#PCDATA | mglyph | malignmark)*`, use `mixed_content` + `map_content` + `map_element` for the inline children.
73
+
74
+ ### Token elements are the only true mixed content
75
+
76
+ Per MathML schema, token elements (`mi`, `mn`, `mo`, `ms`, `mtext`) accept `(#PCDATA | mglyph | malignmark)*` — text interleaved with inline elements. They use:
77
+
78
+ ```ruby
79
+ attribute :value, :string, collection: true # collection required for mixed_content
80
+ attribute :mglyph_value, :mglyph, collection: true
81
+ attribute :malignmark_value, :malignmark, collection: true
82
+
83
+ xml do
84
+ element "<tag>"
85
+ mixed_content
86
+ map_content to: :value
87
+ map_element "mglyph", to: :mglyph_value
88
+ map_element "malignmark", to: :malignmark_value
89
+ end
90
+ ```
91
+
92
+ `value` is a `String` **collection** (Array) because text can be split by intervening inline elements (e.g., `<mi>x<malignmark/>y</mi>` → `["x", "y"]`).
93
+
94
+ ### CommonAttributes
95
+
96
+ A `no_root` Lutaml model imported into container elements via `import_model`. It dynamically creates `#{tag}_value` attributes for each tag in `Configuration::SUPPORTED_TAGS`. Classes that receive it are listed in `Configuration::COMMON_ATTRIBUTES_CLASSES`.
97
+
98
+ ### Version module body registration
99
+
100
+ Each `lib/mml/v{2,3,4}.rb` declares `autoload :Klass, "mml/v{2,3,4}/klass"` for every element class, then calls `Configuration.register_model(Klass, id: :tag)` in the module body. The register_model call triggers autoload on first reference, preserving eager-registration semantics while using Ruby autoload (no `require_relative` for internal code).
101
+
102
+ Per-element files (e.g. `lib/mml/v3/math.rb`) define only the class — they no longer call `register_model` themselves. The version module is the single source of truth for registration.
103
+
104
+ For grouped files (e.g. `lib/mml/v3/arith.rb` defines 22 classes), each class has its own `autoload` entry pointing at the shared file.
105
+
106
+ `spec/mml/code_quality_spec.rb` enforces the lint rules and verifies every expected element ID is registered in its version's context.
107
+
108
+ **Namespace:** All versions use the same URI (`http://www.w3.org/1998/Math/MathML`) — MathML 4 chose backward compatibility over a new namespace.
109
+
110
+ ## Adapter
111
+
112
+ `Mml.default_adapter` returns `:oga` under Opal, otherwise delegates to `Lutaml::Model::Config.xml_adapter_type` (defaults to `:nokogiri`). Specs pin `:nokogiri` in `spec_helper.rb`. Users can override globally via `Lutaml::Model::Config`.
57
113
 
58
114
  ## Spec Structure
59
115
 
60
- - `spec/mml_spec.rb` — tests `Mml::V3` and `Mml::V4` with separate fixture directories
61
- - `spec/fixtures/with_namespace/`v3 fixtures (MathML 3)
62
- - `spec/fixtures/with_namespace_prefix/`v3 fixtures with namespace prefix
63
- - `spec/fixtures/v4/`v4 fixtures (MathML 4 with intent attributes)
116
+ - `spec/mml/v2/`, `spec/mml/v3/`, `spec/mml/v4/` per-version element specs (round-trip + attribute preservation)
117
+ - `spec/mml/v3_spec.rb`, `spec/mml/v4_spec.rb` whole-testsuite round-trip tests against `spec/fixtures/mml3-testsuite/` and `spec/fixtures/mmlcore-testsuite/`
118
+ - `spec/mml/ordered_content_spec.rb`regression specs locking in the mixed_content vs ordered distinction (whitespace handling + token element inline children)
119
+ - `spec/mml/adapter_configuration_spec.rb`adapter delegation
120
+ - `spec/context_support_spec.rb`, `spec/lutaml_default_register_spec.rb` — registry/context behavior
121
+ - `spec/fixtures/mml2-testsuite/`, `mml3-testsuite/`, `mmlcore-testsuite/` — W3C test suites (submodules)
122
+ - `spec/fixtures/v2/`, `v4/` — version-specific fixtures
64
123
 
65
- Specs use the `:nokogiri` Lutaml adapter (configured in `spec_helper.rb`). Runtime uses `:ox` adapter by default.
124
+ Specs use `canon`'s `be_xml_equivalent_to` matcher with the `:spec_friendly` profile (whitespace-tolerant). When adding a regression spec for an ordering bug, **explicitly test the inter-element-whitespace scenario** — the suite's whitespace tolerance masks positional bugs.
66
125
 
67
126
  ## Key Dependencies
68
127
 
69
- - `lutaml-model` (~ 0.8.0) — data mapper framework; all element classes inherit from `Lutaml::Model::Serializable`
70
- - `moxml` — XML parsing library (uses `:ox` adapter by default)
128
+ - `lutaml-model` (~> 0.8.0) — data mapper framework; all element classes inherit from `Lutaml::Model::Serializable`
129
+ - `moxml` — XML parsing backbone (adapter selected via `Lutaml::Model::Config`)
71
130
  - `canon` — XML comparison for specs
72
131
 
73
132
  ## Conventions
@@ -77,3 +136,10 @@ Specs use the `:nokogiri` Lutaml adapter (configured in `spec_helper.rb`). Runti
77
136
  - CI workflows are auto-generated by Cimas — do not edit manually
78
137
  - `Gemfile.lock` is gitignored; dependencies come from the gemspec
79
138
  - Type signatures exist in `sig/mml.rbs`
139
+
140
+ ## Reference Materials
141
+
142
+ - `schemas/mathml2/`, `schemas/mathml3/`, `schemas/mathml4/` — official W3C schemas (XSD for 2/3, RelaxNG for 4). **Authoritative** source for content models and attribute lists.
143
+ - `reference-docs/mathml-source/` — W3C spec source XML (presentation-markup.xml, validation-grammar.xml, etc.)
144
+
145
+ When deciding whether an element should have `mixed_content`, `ordered`, or `map_content`, **always** verify against the schema first.
@@ -0,0 +1,63 @@
1
+ # TODO 1 — Migrate `lib/mml/v3.rb` from `require_relative` to `autoload`
2
+
3
+ ## Why
4
+ Global rule: never use `require_relative` for code inside our own library.
5
+ Use Ruby `autoload` declared in the immediate parent namespace's file
6
+ (`lib/mml/v3.rb`). `lib/mml/v4.rb` already follows this pattern; V3 should
7
+ match it for consistency (MECE/SSOT).
8
+
9
+ ## Scope
10
+ - `lib/mml/v3.rb` — replace every `require_relative "v3/..."` with the
11
+ corresponding `autoload :Klass, "mml/v3/..."`.
12
+ - Grouped files (e.g. `v3/arith.rb`) contain multiple classes — emit one
13
+ `autoload` line per class, all pointing at the same file path.
14
+ - Keep the existing `Configuration.register_model(...)` calls in the module
15
+ body — they trigger autoload on first reference and preserve eager
16
+ registration behavior.
17
+ - Foundational files (`configuration`, `namespace`, `common_elements`) also
18
+ switch to `autoload`.
19
+
20
+ ## Class → file map (V3)
21
+ ### One-class files (presentation)
22
+ Maction, Maligngroup, Malignmark, Menclose, Merror, Mfenced, Mfrac,
23
+ Mfraction, Mglyph, Mi, Mlabeledtr, Mlongdiv, Mmultiscripts, Mn, Mo, Mover,
24
+ Mpadded, Mphantom, Mprescripts, Mroot, Mrow, Ms, Mscarries, Mscarry,
25
+ Msgroup, Msline, Mspace, Msqrt, Msrow, Mstack, Mstyle, Msub, Msubsup, Msup,
26
+ Mtable, Mtd, Mtext, Mtr, Munder, Munderover, None, Semantics, Math
27
+
28
+ ### One-class files (content)
29
+ Apply, Cbytes, Cerror, Cs, Ci, Cn, Csymbol, AnnotationXml, Annotation,
30
+ Bind, Bvar, Condition, Degree, Interval, Inverse, Lambda, Otherwise, Piece,
31
+ Piecewise, Reln, Sep, Fn, Declare, Share
32
+
33
+ ### Grouped files (multiple classes per file)
34
+ - `v3/arith.rb` → Abs, Conjugate, Arg, Real, Imaginary, Floor, Ceiling,
35
+ Quotient, Divide, Rem, Minus, Plus, Times, Power, Root, Max, Min, Gcd, Lcm,
36
+ Sum, Product, Factorial
37
+ - `v3/calculus.rb` → Int, Diff, Partialdiff, Limit, Lowlimit, Uplimit, Tendsto
38
+ - `v3/constants.rb` → Naturalnumbers, Primes, Integers, Rationals, Reals,
39
+ Complexes, Emptyset, Exponentiale, Imaginaryi, Pi, Eulergamma, Mtrue,
40
+ Mfalse, Infinity, Notanumber
41
+ - `v3/elementary_functions.rb` → Exp, Ln, Log, Logbase, Sin, Cos, Tan, Sec,
42
+ Csc, Cot, Arcsin, Arccos, Arctan, Arccot, Arccsc, Arcsec, Sinh, Cosh, Tanh,
43
+ Sech, Csch, Coth, Arcsinh, Arccosh, Arctanh, Arccoth, Arccsch, Arcsech
44
+ - `v3/functions.rb` → Compose, Domain, Codomain, Image, Ident,
45
+ Domainofapplication
46
+ - `v3/linear_algebra.rb` → Vector, Matrix, Matrixrow, Determinant, Transpose,
47
+ Selector, Vectorproduct, Scalarproduct, Outerproduct
48
+ - `v3/logic.rb` → And, Or, Xor, Not, Exists, Forall, Implies
49
+ - `v3/relations.rb` → Eq, Neq, Leq, Lt, Geq, Gt, Equivalent, Approx, Factorof
50
+ - `v3/sets.rb` → Set, Mlist, Union, Intersect, In, Notin, Subset, Prsubset,
51
+ Notsubset, Notprsubset, Setdiff, Card, Cartesianproduct
52
+ - `v3/statistics.rb` → Mean, Sdev, Variance, Median, Mode, Moment, Momentabout
53
+ - `v3/vector_calculus.rb` → Divergence, Grad, Curl, Laplacian
54
+ - `v3/constructs.rb` → Bvar, Condition, Interval, Lambda, Inverse, Degree,
55
+ Piecewise, Piece, Otherwise, Sep
56
+ - `v3/deprecated_content.rb` → Fn, Reln, Declare, Share
57
+
58
+ ## Acceptance
59
+ - `grep -rn "require_relative" lib/mml/v3.rb` returns nothing.
60
+ - `bundle exec rspec` passes (2940+ examples, 36 pre-existing pending).
61
+ - `bundle exec rubocop lib/mml/v3.rb` clean.
62
+ - All `Mml::V3::*` constants remain accessible and registered after
63
+ `require "mml"`.
@@ -0,0 +1,46 @@
1
+ # TODO 2 — Migrate `lib/mml/v2.rb` from `require_relative` to `autoload`
2
+
3
+ ## Why
4
+ Same as TODO 1 but for V2. Mirror the V4 pattern.
5
+
6
+ ## Scope
7
+ - Replace every `require_relative "v2/..."` with `autoload :Klass, "mml/v2/..."`.
8
+ - Grouped files have multiple classes per file.
9
+
10
+ ## Class → file map (V2)
11
+ ### One-class files (presentation + content)
12
+ Same shape as V3 but in `v2/` directory: Maction, Maligngroup, Malignmark,
13
+ Menclose, Merror, Mfenced, Mfrac, Mfraction, Mglyph, Mi, Mlabeledtr, Mlongdiv,
14
+ Mmultiscripts, Mn, Mo, Mover, Mpadded, Mphantom, Mprescripts, Mroot, Mrow,
15
+ Ms, Mscarries, Mscarry, Msgroup, Msline, Mspace, Msqrt, Msrow, Mstack,
16
+ Mstyle, Msub, Msubsup, Msup, Mtable, Mtd, Mtext, Mtr, Munder, Munderover,
17
+ None, Semantics, Math, Annotation, AnnotationXml, Apply, Bvar, Bind, Ci,
18
+ Cn, Condition, Csymbol, Degree, Fn, Interval, Inverse, Lambda, Otherwise,
19
+ Piece, Piecewise, Reln, Sep
20
+
21
+ ### Grouped files
22
+ - `v2/arith.rb` → Abs, Conjugate, Arg, Real, Imaginary, Floor, Ceiling,
23
+ Quotient, Divide, Rem, Minus, Plus, Times, Power, Root, Max, Min, Gcd, Lcm,
24
+ Sum, Product, Factorial
25
+ - `v2/calculus.rb` → Int, Diff, Partialdiff, Limit, Lowlimit, Uplimit, Tendsto
26
+ - `v2/constants.rb` → Naturalnumbers, Primes, Integers, Rationals, Reals,
27
+ Complexes, Emptyset, Exponentiale, Imaginaryi, Pi, Eulergamma, Mtrue,
28
+ Mfalse, Infinity, Notanumber
29
+ - `v2/elementary_functions.rb` → Exp, Ln, Log, Logbase, Sin, Cos, Tan, Sec,
30
+ Csc, Cot, Arcsin, Arccos, Arctan, Arccot, Arccsc, Arcsec, Sinh, Cosh, Tanh,
31
+ Sech, Csch, Coth, Arcsinh, Arccosh, Arctanh, Arccoth, Arccsch, Arcsech
32
+ - `v2/functions.rb` → Compose, Domain, Codomain, Image, Ident,
33
+ Domainofapplication
34
+ - `v2/linear_algebra.rb` → Vector, Matrix, Matrixrow, Determinant, Transpose,
35
+ Selector, Vectorproduct, Scalarproduct, Outerproduct
36
+ - `v2/logic.rb` → And, Or, Xor, Not, Exists, Forall, Implies
37
+ - `v2/relations.rb` → Eq, Neq, Leq, Lt, Geq, Gt, Equivalent, Approx, Factorof
38
+ - `v2/sets.rb` → Set, Mlist, Union, Intersect, In, Notin, Subset, Prsubset,
39
+ Notsubset, Notprsubset, Setdiff, Card, Cartesianproduct
40
+ - `v2/statistics.rb` → Mean, Sdev, Variance, Median, Mode, Moment, Momentabout
41
+ - `v2/vector_calculus.rb` → Divergence, Grad, Curl, Laplacian
42
+
43
+ ## Acceptance
44
+ - `grep -rn "require_relative" lib/mml/v2.rb` returns nothing.
45
+ - `bundle exec rspec` passes.
46
+ - `bundle exec rubocop lib/mml/v2.rb` clean.
@@ -0,0 +1,32 @@
1
+ # TODO 3 — Eliminate remaining `require_relative` in `lib/mml/`
2
+
3
+ ## Why
4
+ After TODO 1 & 2, the only remaining `require_relative` calls in `lib/mml/`
5
+ are:
6
+ - `lib/mml/v2.rb:4` — `require_relative "versioned_parser"`
7
+ - `lib/mml/v3.rb:4` — `require_relative "versioned_parser"`
8
+ - `lib/mml/v4.rb:4` — `require_relative "versioned_parser"`
9
+ - `lib/mml/v{2,3,4}/configuration.rb:3` — `require_relative "../context_configuration"`
10
+ - `lib/mml/v{2,3,4}.rb` top — `require_relative "v{2,3,4}/configuration"`,
11
+ `"v{2,3,4}/namespace"`, `"v{2,3,4}/common_elements"` (V4 only today, but if
12
+ V2/V3 also adopt the same pattern during TODO 1/2 they need the same fix).
13
+
14
+ All of these can be replaced with autoloads declared in `lib/mml.rb` and in
15
+ the version module files.
16
+
17
+ ## Scope
18
+ 1. `lib/mml.rb` — add `autoload :VersionedParser, "mml/versioned_parser"`
19
+ (currently `ContextOptions` and others are autoloaded; `VersionedParser`
20
+ is missing).
21
+ 2. `lib/mml/v{2,3,4}/configuration.rb` — remove the
22
+ `require_relative "../context_configuration"` line. `Mml::ContextConfiguration`
23
+ is autoloaded from `lib/mml.rb`, so the `extend Mml::ContextConfiguration`
24
+ call resolves correctly.
25
+ 3. `lib/mml/v{2,3,4}.rb` — replace `require_relative "v{2,3,4}/configuration"`
26
+ and the foundational requires with `autoload` declarations.
27
+
28
+ ## Acceptance
29
+ - `grep -rn "require_relative" lib/mml/` returns zero matches.
30
+ - `bundle exec rspec` passes.
31
+ - `bundle exec rubocop lib/` clean.
32
+ - Smoke spec (TODO 7) confirms every `Mml::V{2,3,4}::*` class resolves.
@@ -0,0 +1,31 @@
1
+ # TODO 4 — Remove duplicate per-element `Configuration.register_model` (DRY/SSOT)
2
+
3
+ ## Why
4
+ After TODO 1 & 2, V2 and V3 will mirror V4's pattern of `autoload + module-body
5
+ register_model`. The per-element files (`lib/mml/v{2,3}/<tag>.rb`) currently
6
+ also end with `Configuration.register_model(Klass, id: :tag)`. That is
7
+ duplicate work — `register_model` overwrites the same key with the same class.
8
+
9
+ V4 is inconsistent: most files omit the per-element call, but `annotation.rb`,
10
+ `elementary_functions.rb`, and `functions.rb` still have it.
11
+
12
+ Single source of truth: the version module body (`lib/mml/v{2,3,4}.rb`).
13
+
14
+ ## Scope
15
+ - Remove the trailing `Configuration.register_model(...)` line from every
16
+ per-element file under `lib/mml/v2/`, `lib/mml/v3/`, `lib/mml/v4/`.
17
+ - Do NOT touch `lib/mml/v{2,3,4}.rb` (module-body registrations stay).
18
+
19
+ ## Files affected
20
+ - V3: 66 per-element files (every file under `lib/mml/v3/` except
21
+ `configuration.rb`, `namespace.rb`, `common_elements.rb`, `version.rb`).
22
+ - V2: ~62 per-element files.
23
+ - V4: 3 files — `annotation.rb`, `elementary_functions.rb`, `functions.rb`.
24
+
25
+ ## Acceptance
26
+ - `grep -rn "Configuration.register_model" lib/mml/v2 lib/mml/v3 lib/mml/v4 |
27
+ grep -v "v[234]\.rb:"` returns nothing (only version module files have it).
28
+ - `bundle exec rspec` passes.
29
+ - `bundle exec rubocop lib/` clean.
30
+ - Registration spec (TODO 7) confirms all element IDs are still registered
31
+ in their respective contexts.
@@ -0,0 +1,44 @@
1
+ # TODO 5 — Replace `respond_to?(:id)` with explicit type check in `context_options.rb`
2
+
3
+ ## Why
4
+ Global rule: never use `respond_to?` for type checking. Use `is_a?` or
5
+ design the type hierarchy so the check isn't needed.
6
+
7
+ `lib/mml/context_options.rb:34` uses `reference.respond_to?(:id)` to duck-type
8
+ any object that exposes an `id`. The actual types that can reach this code are:
9
+ - `Symbol` — handled earlier
10
+ - `String` — handled earlier
11
+ - `Lutaml::Model::TypeContext` (lutaml-model) — has `attr_reader :id`
12
+ - `Lutaml::Model::Register` (lutaml-model) — has `attr_reader :id`
13
+
14
+ ## Scope
15
+ - Replace the `respond_to?` branch with explicit `is_a?` checks against the
16
+ two known lutaml-model classes.
17
+ - Keep the `ArgumentError` fallback for unknown types — preserves
18
+ fail-fast behavior.
19
+
20
+ ## Implementation
21
+ ```ruby
22
+ def normalize_context_reference(reference)
23
+ return nil if reference.nil?
24
+ return reference if reference.is_a?(Symbol)
25
+ return reference.to_sym if reference.is_a?(String)
26
+ return reference.id.to_sym if context_reference_object?(reference)
27
+
28
+ raise ArgumentError,
29
+ "Unsupported context/register reference: #{reference.inspect}"
30
+ end
31
+
32
+ private
33
+
34
+ def context_reference_object?(reference)
35
+ reference.is_a?(Lutaml::Model::TypeContext) ||
36
+ reference.is_a?(Lutaml::Model::Register)
37
+ end
38
+ ```
39
+
40
+ ## Acceptance
41
+ - `grep -n "respond_to?" lib/mml/context_options.rb` returns nothing.
42
+ - `bundle exec rspec spec/mml/context_options_spec.rb` passes (new spec from
43
+ TODO 7).
44
+ - Existing parse/configuration specs still pass.
@@ -0,0 +1,32 @@
1
+ # TODO 6 — Replace `respond_to?(:clear_cache)` with explicit type check in `context_configuration.rb`
2
+
3
+ ## Why
4
+ Global rule: never use `respond_to?` for type checking.
5
+
6
+ `lib/mml/context_configuration.rb:144` uses
7
+ `klass.clear_cache(context_id) if klass.respond_to?(:clear_cache)`.
8
+
9
+ Every class registered via `Configuration.register_model` inherits from
10
+ `Lutaml::Model::Serializable`, which defines `clear_cache` as a class method
11
+ (see lutaml-model `lib/lutaml/model/serialize/initialization.rb:180`).
12
+
13
+ ## Scope
14
+ - Replace the `respond_to?` guard with an explicit type check:
15
+ `klass.is_a?(Class) && klass <= Lutaml::Model::Serialize`.
16
+ - Note the use of `<=` (subclass-or-equal) rather than `<` so that
17
+ `Serializable` itself would also qualify, matching the intent of "any class
18
+ in the Serializable hierarchy".
19
+
20
+ ## Implementation
21
+ ```ruby
22
+ def clear_cache(klass)
23
+ return unless klass.is_a?(Class) && (klass <= Lutaml::Model::Serialize)
24
+
25
+ klass.clear_cache(context_id)
26
+ end
27
+ ```
28
+
29
+ ## Acceptance
30
+ - `grep -n "respond_to?" lib/mml/context_configuration.rb` returns nothing.
31
+ - `bundle exec rspec` passes.
32
+ - `bundle exec rubocop lib/mml/context_configuration.rb` clean.
@@ -0,0 +1,38 @@
1
+ # TODO 7 — Add specs covering the refactorings
2
+
3
+ ## Why
4
+ Lock in the behavior of the refactorings in TODOs 1–6 so future regressions
5
+ are caught. MECE with existing specs:
6
+ - `spec/mml/adapter_configuration_spec.rb` — adapter selection
7
+ - `spec/mml/ordered_content_spec.rb` — mixed_content vs ordered
8
+ - `spec/mml/nested_in_mixed_content_spec.rb` — entity preservation in host
9
+ - `spec/lutaml_default_register_spec.rb` — every Serializable class has
10
+ `lutaml_default_register`
11
+ - `spec/context_support_spec.rb` — context creation/substitution
12
+
13
+ None of these cover:
14
+ - `ContextOptions.normalize_context_reference` for `TypeContext`/`Register`
15
+ inputs.
16
+ - Lint: no `require_relative` in `lib/mml/**/*.rb`.
17
+ - Lint: no `respond_to?` in `lib/mml/**/*.rb`.
18
+ - Lint: no `.send(` / `instance_variable_set` / `instance_variable_get` in
19
+ `lib/mml/**/*.rb`.
20
+ - Smoke: every `Mml::V{2,3,4}` autoload constant resolves and is registered
21
+ after `require "mml"`.
22
+
23
+ ## Scope
24
+ Create `spec/mml/code_quality_spec.rb` covering all four lint checks plus a
25
+ smoke test that iterates the Configuration register and asserts every
26
+ expected element ID is present for V2, V3, V4.
27
+
28
+ Update `spec/context_support_spec.rb` (or add a new spec) to cover
29
+ `ContextOptions.normalize_context_reference` with `TypeContext` and `Register`
30
+ inputs — these now flow through the new `is_a?` branches.
31
+
32
+ ## Acceptance
33
+ - New spec file(s) added.
34
+ - `bundle exec rspec` passes.
35
+ - `bundle exec rubocop` clean.
36
+ - Removing any autoload declaration or per-element class breaks the smoke
37
+ spec.
38
+ - Adding a new `require_relative` to `lib/mml/` breaks the lint spec.
@@ -15,7 +15,7 @@ module Mml
15
15
  xml do
16
16
  namespace Mml::Namespace
17
17
  element "maction"
18
- mixed_content
18
+ ordered
19
19
 
20
20
  map_attribute "mathcolor", to: :mathcolor
21
21
  map_attribute "mathbackground", to: :mathbackground
data/lib/mml/base/math.rb CHANGED
@@ -37,7 +37,7 @@ module Mml
37
37
  xml do
38
38
  namespace Mml::Namespace
39
39
  element "math"
40
- mixed_content
40
+ ordered
41
41
 
42
42
  map_attribute :display, to: :display
43
43
  map_attribute "mode", to: :mode
@@ -14,7 +14,7 @@ module Mml
14
14
  xml do
15
15
  namespace Mml::Namespace
16
16
  element "menclose"
17
- mixed_content
17
+ ordered
18
18
 
19
19
  map_attribute "mathcolor", to: :mathcolor
20
20
  map_attribute "mathbackground", to: :mathbackground
@@ -13,7 +13,7 @@ module Mml
13
13
  xml do
14
14
  namespace Mml::Namespace
15
15
  element "merror"
16
- mixed_content
16
+ ordered
17
17
 
18
18
  map_attribute "mathcolor", to: :mathcolor
19
19
  map_attribute "mathbackground", to: :mathbackground
@@ -10,16 +10,13 @@ module Mml
10
10
  attribute :mathbackground, :string
11
11
  attribute :separators, :string
12
12
  attribute :mathcolor, :string
13
- attribute :content, :string, collection: true
14
13
  attribute :close, :string
15
14
  attribute :open, :string
16
15
 
17
16
  xml do
18
17
  namespace Mml::Namespace
19
18
  element "mfenced"
20
- mixed_content
21
-
22
- map_content to: :content
19
+ ordered
23
20
 
24
21
  map_attribute "mathbackground", to: :mathbackground
25
22
  map_attribute "separators", to: :separators, render_empty: true
@@ -17,7 +17,7 @@ module Mml
17
17
  xml do
18
18
  namespace Mml::Namespace
19
19
  element "mfrac"
20
- mixed_content
20
+ ordered
21
21
 
22
22
  map_attribute "mathcolor", to: :mathcolor
23
23
  map_attribute "mathbackground", to: :mathbackground
@@ -17,7 +17,7 @@ module Mml
17
17
  xml do
18
18
  namespace Mml::Namespace
19
19
  element "mfraction"
20
- mixed_content
20
+ ordered
21
21
 
22
22
  map_attribute "mathcolor", to: :mathcolor
23
23
  map_attribute "mathbackground", to: :mathbackground
data/lib/mml/base/mi.rb CHANGED
@@ -13,6 +13,7 @@ module Mml
13
13
  attribute :mathsize, :string
14
14
  attribute :mathvariant, :string
15
15
  attribute :mglyph_value, :mglyph, collection: true
16
+ attribute :malignmark_value, :malignmark, collection: true
16
17
  attribute :lang, :string
17
18
 
18
19
  xml do
@@ -27,6 +28,7 @@ module Mml
27
28
  map_attribute "mathvariant", to: :mathvariant
28
29
  map_attribute "xml:lang", to: :lang
29
30
  map_element "mglyph", to: :mglyph_value
31
+ map_element "malignmark", to: :malignmark_value
30
32
  end
31
33
  end
32
34
  end
@@ -18,7 +18,7 @@ module Mml
18
18
  xml do
19
19
  namespace Mml::Namespace
20
20
  element "mlabeledtr"
21
- mixed_content
21
+ ordered
22
22
 
23
23
  map_attribute "mathbackground", to: :mathbackground
24
24
  map_attribute "columnalign", to: :columnalign
@@ -16,7 +16,7 @@ module Mml
16
16
  xml do
17
17
  namespace Mml::Namespace
18
18
  element "mlongdiv"
19
- mixed_content
19
+ ordered
20
20
 
21
21
  map_attribute "mathbackground", to: :mathbackground
22
22
  map_attribute "longdivstyle", to: :longdivstyle
@@ -16,7 +16,7 @@ module Mml
16
16
  xml do
17
17
  namespace Mml::Namespace
18
18
  element "mmultiscripts"
19
- mixed_content
19
+ ordered
20
20
 
21
21
  map_attribute "mathcolor", to: :mathcolor
22
22
  map_attribute "mathbackground", to: :mathbackground
data/lib/mml/base/mn.rb CHANGED
@@ -13,6 +13,7 @@ module Mml
13
13
  attribute :mathvariant, :string
14
14
  attribute :mathsize, :string
15
15
  attribute :mglyph_value, :mglyph, collection: true
16
+ attribute :malignmark_value, :malignmark, collection: true
16
17
 
17
18
  xml do
18
19
  namespace Mml::Namespace
@@ -25,6 +26,7 @@ module Mml
25
26
  map_attribute "mathvariant", to: :mathvariant
26
27
  map_attribute "mathsize", to: :mathsize
27
28
  map_element "mglyph", to: :mglyph_value
29
+ map_element "malignmark", to: :malignmark_value
28
30
  end
29
31
  end
30
32
  end