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.
- checksums.yaml +4 -4
- data/CLAUDE.md +96 -30
- data/TODO.complete/1-v3-autoload.md +63 -0
- data/TODO.complete/2-v2-autoload.md +46 -0
- data/TODO.complete/3-no-require-relative.md +32 -0
- data/TODO.complete/4-remove-duplicate-register-model.md +31 -0
- data/TODO.complete/5-context-options-type-check.md +44 -0
- data/TODO.complete/6-context-configuration-type-check.md +32 -0
- data/TODO.complete/7-specs-for-refactor.md +38 -0
- data/lib/mml/base/maction.rb +1 -1
- data/lib/mml/base/math.rb +1 -1
- data/lib/mml/base/menclose.rb +1 -1
- data/lib/mml/base/merror.rb +1 -1
- data/lib/mml/base/mfenced.rb +1 -4
- data/lib/mml/base/mfrac.rb +1 -1
- data/lib/mml/base/mfraction.rb +1 -1
- data/lib/mml/base/mi.rb +2 -0
- data/lib/mml/base/mlabeledtr.rb +1 -1
- data/lib/mml/base/mlongdiv.rb +1 -1
- data/lib/mml/base/mmultiscripts.rb +1 -1
- data/lib/mml/base/mn.rb +2 -0
- data/lib/mml/base/mo.rb +6 -1
- data/lib/mml/base/mover.rb +1 -1
- data/lib/mml/base/mpadded.rb +1 -1
- data/lib/mml/base/mphantom.rb +1 -1
- data/lib/mml/base/mroot.rb +1 -1
- data/lib/mml/base/mrow.rb +1 -3
- data/lib/mml/base/ms.rb +4 -0
- data/lib/mml/base/mscarries.rb +1 -1
- data/lib/mml/base/mscarry.rb +1 -1
- data/lib/mml/base/msgroup.rb +1 -3
- data/lib/mml/base/msqrt.rb +1 -1
- data/lib/mml/base/msrow.rb +1 -1
- data/lib/mml/base/mstack.rb +1 -1
- data/lib/mml/base/mstyle.rb +1 -1
- data/lib/mml/base/msub.rb +1 -1
- data/lib/mml/base/msubsup.rb +1 -1
- data/lib/mml/base/msup.rb +1 -1
- data/lib/mml/base/mtable.rb +1 -1
- data/lib/mml/base/mtd.rb +1 -1
- data/lib/mml/base/mtext.rb +6 -1
- data/lib/mml/base/mtr.rb +1 -1
- data/lib/mml/base/munder.rb +1 -3
- data/lib/mml/base/munderover.rb +1 -1
- data/lib/mml/base/semantics.rb +1 -1
- data/lib/mml/context_configuration.rb +3 -1
- data/lib/mml/context_options.rb +10 -1
- data/lib/mml/v2/configuration.rb +0 -2
- data/lib/mml/v2.rb +382 -246
- data/lib/mml/v3/annotation.rb +0 -1
- data/lib/mml/v3/annotation_xml.rb +0 -1
- data/lib/mml/v3/apply.rb +0 -1
- data/lib/mml/v3/arith.rb +0 -22
- data/lib/mml/v3/bind.rb +0 -1
- data/lib/mml/v3/calculus.rb +0 -7
- data/lib/mml/v3/cbytes.rb +0 -1
- data/lib/mml/v3/cerror.rb +0 -1
- data/lib/mml/v3/ci.rb +0 -1
- data/lib/mml/v3/cn.rb +0 -1
- data/lib/mml/v3/configuration.rb +0 -2
- data/lib/mml/v3/constants.rb +0 -15
- data/lib/mml/v3/constructs.rb +0 -10
- data/lib/mml/v3/cs.rb +0 -1
- data/lib/mml/v3/csymbol.rb +0 -1
- data/lib/mml/v3/deprecated_content.rb +0 -7
- data/lib/mml/v3/elementary_functions.rb +0 -28
- data/lib/mml/v3/functions.rb +0 -6
- data/lib/mml/v3/linear_algebra.rb +0 -9
- data/lib/mml/v3/logic.rb +0 -7
- data/lib/mml/v3/maction.rb +0 -1
- data/lib/mml/v3/maligngroup.rb +0 -1
- data/lib/mml/v3/malignmark.rb +0 -1
- data/lib/mml/v3/math.rb +0 -2
- data/lib/mml/v3/menclose.rb +0 -1
- data/lib/mml/v3/merror.rb +0 -1
- data/lib/mml/v3/mfenced.rb +0 -1
- data/lib/mml/v3/mfrac.rb +0 -1
- data/lib/mml/v3/mfraction.rb +0 -1
- data/lib/mml/v3/mglyph.rb +0 -1
- data/lib/mml/v3/mi.rb +0 -1
- data/lib/mml/v3/mlabeledtr.rb +0 -1
- data/lib/mml/v3/mlongdiv.rb +0 -1
- data/lib/mml/v3/mmultiscripts.rb +0 -1
- data/lib/mml/v3/mn.rb +0 -1
- data/lib/mml/v3/mo.rb +0 -1
- data/lib/mml/v3/mover.rb +0 -1
- data/lib/mml/v3/mpadded.rb +0 -1
- data/lib/mml/v3/mphantom.rb +0 -1
- data/lib/mml/v3/mprescripts.rb +0 -1
- data/lib/mml/v3/mroot.rb +0 -1
- data/lib/mml/v3/mrow.rb +0 -1
- data/lib/mml/v3/ms.rb +0 -1
- data/lib/mml/v3/mscarries.rb +0 -1
- data/lib/mml/v3/mscarry.rb +0 -1
- data/lib/mml/v3/msgroup.rb +0 -1
- data/lib/mml/v3/msline.rb +0 -1
- data/lib/mml/v3/mspace.rb +0 -1
- data/lib/mml/v3/msqrt.rb +0 -1
- data/lib/mml/v3/msrow.rb +0 -1
- data/lib/mml/v3/mstack.rb +0 -1
- data/lib/mml/v3/mstyle.rb +0 -1
- data/lib/mml/v3/msub.rb +0 -1
- data/lib/mml/v3/msubsup.rb +0 -1
- data/lib/mml/v3/msup.rb +0 -1
- data/lib/mml/v3/mtable.rb +0 -1
- data/lib/mml/v3/mtd.rb +0 -1
- data/lib/mml/v3/mtext.rb +0 -1
- data/lib/mml/v3/mtr.rb +0 -1
- data/lib/mml/v3/munder.rb +0 -1
- data/lib/mml/v3/munderover.rb +0 -1
- data/lib/mml/v3/none.rb +0 -1
- data/lib/mml/v3/relations.rb +0 -9
- data/lib/mml/v3/semantics.rb +0 -1
- data/lib/mml/v3/sets.rb +0 -13
- data/lib/mml/v3/statistics.rb +0 -7
- data/lib/mml/v3/vector_calculus.rb +0 -4
- data/lib/mml/v3.rb +451 -73
- data/lib/mml/v4/annotation.rb +0 -1
- data/lib/mml/v4/configuration.rb +0 -2
- data/lib/mml/v4/elementary_functions.rb +0 -1
- data/lib/mml/v4/functions.rb +0 -1
- data/lib/mml/v4.rb +21 -17
- data/lib/mml/version.rb +1 -1
- data/lib/mml.rb +2 -0
- metadata +9 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 753f58cf7a8516296624dc7eb472621cd4ecaab7a7d1c3f34d7e8d67eff9bbce
|
|
4
|
+
data.tar.gz: 062164c14aa4475421fc48ca37de907be75938d8b574a768cc8e8e374bd91252
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
13
|
-
bundle exec rspec
|
|
14
|
-
bundle exec rspec spec/
|
|
15
|
-
bundle exec rspec --only-failures
|
|
16
|
-
bundle exec rubocop
|
|
17
|
-
bundle exec rubocop -a
|
|
18
|
-
bin/console
|
|
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
|
-
|
|
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)
|
|
27
|
-
Mml.parse(input, version:
|
|
28
|
-
Mml.parse(input, version:
|
|
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/
|
|
33
|
-
- `lib/mml/v4/` —
|
|
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`
|
|
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::
|
|
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 `
|
|
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
|
-
|
|
51
|
+
### Content model — `mixed_content` vs `ordered` vs `map_content`
|
|
53
52
|
|
|
54
|
-
|
|
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
|
-
|
|
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/
|
|
61
|
-
- `spec/
|
|
62
|
-
- `spec/
|
|
63
|
-
- `spec/
|
|
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 `:
|
|
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` (
|
|
70
|
-
- `moxml` — XML parsing
|
|
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.
|
data/lib/mml/base/maction.rb
CHANGED
data/lib/mml/base/math.rb
CHANGED
data/lib/mml/base/menclose.rb
CHANGED
data/lib/mml/base/merror.rb
CHANGED
data/lib/mml/base/mfenced.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
data/lib/mml/base/mfrac.rb
CHANGED
data/lib/mml/base/mfraction.rb
CHANGED
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
|
data/lib/mml/base/mlabeledtr.rb
CHANGED
data/lib/mml/base/mlongdiv.rb
CHANGED
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
|