mml 2.1.0 → 2.2.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 (151) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +14 -22
  3. data/README.adoc +238 -166
  4. data/lib/mml/v3/common_attributes.rb +22 -0
  5. data/lib/mml/v3/configuration.rb +105 -0
  6. data/lib/mml/v3/maction.rb +22 -0
  7. data/lib/mml/v3/maligngroup.rb +20 -0
  8. data/lib/mml/v3/malignmark.rb +20 -0
  9. data/lib/mml/v3/math.rb +20 -0
  10. data/lib/mml/v3/menclose.rb +21 -0
  11. data/lib/mml/v3/merror.rb +19 -0
  12. data/lib/mml/v3/mfenced.rb +28 -0
  13. data/lib/mml/v3/mfrac.rb +27 -0
  14. data/lib/mml/v3/mfraction.rb +27 -0
  15. data/lib/mml/v3/mglyph.rb +46 -0
  16. data/lib/mml/v3/mi.rb +38 -0
  17. data/lib/mml/v3/mlabeledtr.rb +31 -0
  18. data/lib/mml/v3/mlongdiv.rb +25 -0
  19. data/lib/mml/v3/mmultiscripts.rb +27 -0
  20. data/lib/mml/v3/mn.rb +38 -0
  21. data/lib/mml/v3/mo.rb +86 -0
  22. data/lib/mml/v3/mover.rb +23 -0
  23. data/lib/mml/v3/mpadded.rb +29 -0
  24. data/lib/mml/v3/mphantom.rb +19 -0
  25. data/lib/mml/v3/mprescripts.rb +18 -0
  26. data/lib/mml/v3/mroot.rb +19 -0
  27. data/lib/mml/v3/mrow.rb +25 -0
  28. data/lib/mml/v3/ms.rb +43 -0
  29. data/lib/mml/v3/mscarries.rb +27 -0
  30. data/lib/mml/v3/mscarry.rb +23 -0
  31. data/lib/mml/v3/msgroup.rb +25 -0
  32. data/lib/mml/v3/msline.rb +28 -0
  33. data/lib/mml/v3/mspace.rb +58 -0
  34. data/lib/mml/v3/msqrt.rb +19 -0
  35. data/lib/mml/v3/msrow.rb +21 -0
  36. data/lib/mml/v3/mstack.rb +27 -0
  37. data/lib/mml/v3/mstyle.rb +211 -0
  38. data/lib/mml/v3/msub.rb +21 -0
  39. data/lib/mml/v3/msubsup.rb +23 -0
  40. data/lib/mml/v3/msup.rb +21 -0
  41. data/lib/mml/v3/mtable.rb +61 -0
  42. data/lib/mml/v3/mtd.rb +23 -0
  43. data/lib/mml/v3/mtext.rb +38 -0
  44. data/lib/mml/v3/mtr.rb +29 -0
  45. data/lib/mml/v3/munder.rb +25 -0
  46. data/lib/mml/v3/munderover.rb +25 -0
  47. data/lib/mml/v3/namespace.rb +10 -0
  48. data/lib/mml/v3/none.rb +18 -0
  49. data/lib/mml/v3/semantics.rb +19 -0
  50. data/lib/mml/v3/version.rb +7 -0
  51. data/lib/mml/v3.rb +85 -0
  52. data/lib/mml/v4/a.rb +31 -0
  53. data/lib/mml/v4/common_attributes.rb +26 -0
  54. data/lib/mml/v4/configuration.rb +106 -0
  55. data/lib/mml/v4/maction.rb +30 -0
  56. data/lib/mml/v4/maligngroup.rb +28 -0
  57. data/lib/mml/v4/malignmark.rb +28 -0
  58. data/lib/mml/v4/math.rb +27 -0
  59. data/lib/mml/v4/menclose.rb +29 -0
  60. data/lib/mml/v4/merror.rb +27 -0
  61. data/lib/mml/v4/mfenced.rb +36 -0
  62. data/lib/mml/v4/mfrac.rb +35 -0
  63. data/lib/mml/v4/mfraction.rb +31 -0
  64. data/lib/mml/v4/mglyph.rb +46 -0
  65. data/lib/mml/v4/mi.rb +41 -0
  66. data/lib/mml/v4/mlabeledtr.rb +34 -0
  67. data/lib/mml/v4/mlongdiv.rb +29 -0
  68. data/lib/mml/v4/mmultiscripts.rb +35 -0
  69. data/lib/mml/v4/mn.rb +40 -0
  70. data/lib/mml/v4/mo.rb +86 -0
  71. data/lib/mml/v4/mover.rb +31 -0
  72. data/lib/mml/v4/mpadded.rb +37 -0
  73. data/lib/mml/v4/mphantom.rb +27 -0
  74. data/lib/mml/v4/mprescripts.rb +22 -0
  75. data/lib/mml/v4/mroot.rb +27 -0
  76. data/lib/mml/v4/mrow.rb +31 -0
  77. data/lib/mml/v4/ms.rb +45 -0
  78. data/lib/mml/v4/mscarries.rb +31 -0
  79. data/lib/mml/v4/mscarry.rb +27 -0
  80. data/lib/mml/v4/msgroup.rb +29 -0
  81. data/lib/mml/v4/msline.rb +32 -0
  82. data/lib/mml/v4/mspace.rb +60 -0
  83. data/lib/mml/v4/msqrt.rb +27 -0
  84. data/lib/mml/v4/msrow.rb +29 -0
  85. data/lib/mml/v4/mstack.rb +35 -0
  86. data/lib/mml/v4/mstyle.rb +197 -0
  87. data/lib/mml/v4/msub.rb +29 -0
  88. data/lib/mml/v4/msubsup.rb +31 -0
  89. data/lib/mml/v4/msup.rb +29 -0
  90. data/lib/mml/v4/mtable.rb +66 -0
  91. data/lib/mml/v4/mtd.rb +31 -0
  92. data/lib/mml/v4/mtext.rb +40 -0
  93. data/lib/mml/v4/mtr.rb +36 -0
  94. data/lib/mml/v4/munder.rb +33 -0
  95. data/lib/mml/v4/munderover.rb +33 -0
  96. data/lib/mml/v4/namespace.rb +10 -0
  97. data/lib/mml/v4/none.rb +22 -0
  98. data/lib/mml/v4/opal_setup.rb.erb +6 -0
  99. data/lib/mml/v4/semantics.rb +23 -0
  100. data/lib/mml/v4/version.rb +7 -0
  101. data/lib/mml/v4.rb +81 -0
  102. data/lib/mml/version.rb +1 -1
  103. data/lib/mml.rb +7 -93
  104. metadata +101 -49
  105. data/lib/mml/common_attributes.rb +0 -21
  106. data/lib/mml/configuration.rb +0 -103
  107. data/lib/mml/maction.rb +0 -20
  108. data/lib/mml/maligngroup.rb +0 -18
  109. data/lib/mml/malignmark.rb +0 -18
  110. data/lib/mml/math.rb +0 -18
  111. data/lib/mml/menclose.rb +0 -19
  112. data/lib/mml/merror.rb +0 -17
  113. data/lib/mml/mfenced.rb +0 -26
  114. data/lib/mml/mfrac.rb +0 -25
  115. data/lib/mml/mfraction.rb +0 -25
  116. data/lib/mml/mglyph.rb +0 -44
  117. data/lib/mml/mi.rb +0 -36
  118. data/lib/mml/mlabeledtr.rb +0 -29
  119. data/lib/mml/mlongdiv.rb +0 -23
  120. data/lib/mml/mmultiscripts.rb +0 -25
  121. data/lib/mml/mn.rb +0 -36
  122. data/lib/mml/mo.rb +0 -84
  123. data/lib/mml/mover.rb +0 -21
  124. data/lib/mml/mpadded.rb +0 -27
  125. data/lib/mml/mphantom.rb +0 -17
  126. data/lib/mml/mprescripts.rb +0 -16
  127. data/lib/mml/mroot.rb +0 -17
  128. data/lib/mml/mrow.rb +0 -23
  129. data/lib/mml/ms.rb +0 -41
  130. data/lib/mml/mscarries.rb +0 -25
  131. data/lib/mml/mscarry.rb +0 -21
  132. data/lib/mml/msgroup.rb +0 -23
  133. data/lib/mml/msline.rb +0 -26
  134. data/lib/mml/mspace.rb +0 -56
  135. data/lib/mml/msqrt.rb +0 -17
  136. data/lib/mml/msrow.rb +0 -19
  137. data/lib/mml/mstack.rb +0 -25
  138. data/lib/mml/mstyle.rb +0 -209
  139. data/lib/mml/msub.rb +0 -19
  140. data/lib/mml/msubsup.rb +0 -21
  141. data/lib/mml/msup.rb +0 -19
  142. data/lib/mml/mtable.rb +0 -59
  143. data/lib/mml/mtd.rb +0 -21
  144. data/lib/mml/mtext.rb +0 -36
  145. data/lib/mml/mtr.rb +0 -27
  146. data/lib/mml/munder.rb +0 -23
  147. data/lib/mml/munderover.rb +0 -23
  148. data/lib/mml/namespace.rb +0 -9
  149. data/lib/mml/none.rb +0 -16
  150. data/lib/mml/semantics.rb +0 -17
  151. /data/lib/mml/{opal_setup.rb.erb → v3/opal_setup.rb.erb} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0edf00bb97f74177c28a416132046bd265a61a8720de3a0a9d1b9515721170db
4
- data.tar.gz: 9b11f8ecc3869145617584f9afb422ae3ced36db62994f89750483f039e1bb99
3
+ metadata.gz: b681a8e9ba48bcb41372e146fa0f16c95eae47303df8db3eb48a9561e959ce6d
4
+ data.tar.gz: d0d046fceba746e66519e9c6c44b395b22d0078d06140e1db04bb0aadb29f37d
5
5
  SHA512:
6
- metadata.gz: ce86364277e4aae63f893d64ccbabb3752beb2acb11f634caaab20fb01b2768902180eab714dbcb69ab91fcc183778004dcbbf50fb3da4f976071effc687d293
7
- data.tar.gz: b3ae2643d010604f0a293fbf35ca7d5ad776055a67b9c90293e4fd7a97a7b8ae70735526c5cd3bf2c96cd2528f3d27048069a481590d6119f7fed7a2d9a365da
6
+ metadata.gz: 5e21cda0df4bada88b50899cb98b58d78ec46f3edca237ae54f064a33038fef2b7b0f5a6ce053709c2113adc0d01ffdea6efb436ca40debb85a6c54c21dbde26
7
+ data.tar.gz: 9544c69937ff05d4df59cd16943c9f393df5bd8a8f3a8a9cd24ba84d3bf9cb457bf6681e24e824faa6bb72c06a30729ef42963741c4034cffbb86a65925a689e
data/.rubocop_todo.yml CHANGED
@@ -1,20 +1,12 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2026-03-26 05:47:46 UTC using RuboCop version 1.86.0.
3
+ # on 2026-03-31 12:25:08 UTC using RuboCop version 1.86.0.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 1
10
- # This cop supports safe autocorrection (--autocorrect).
11
- # Configuration parameters: EnforcedStyle, IndentationWidth.
12
- # SupportedStyles: with_first_argument, with_fixed_indentation
13
- Layout/ArgumentAlignment:
14
- Exclude:
15
- - 'spec/mml_spec.rb'
16
-
17
- # Offense count: 5
9
+ # Offense count: 3
18
10
  # This cop supports safe autocorrection (--autocorrect).
19
11
  # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
20
12
  # URISchemes: http, https
@@ -25,28 +17,28 @@ Layout/LineLength:
25
17
  - 'spec/spec_helper.rb'
26
18
 
27
19
  # Offense count: 1
28
- # This cop supports safe autocorrection (--autocorrect).
29
- # Configuration parameters: AllowInHeredoc.
30
- Layout/TrailingWhitespace:
31
- Exclude:
32
- - 'spec/mml_spec.rb'
20
+ # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
21
+ # AllowedMethods: refine
22
+ Metrics/BlockLength:
23
+ Max: 27
33
24
 
34
- # Offense count: 1
25
+ # Offense count: 3
35
26
  # Configuration parameters: Prefixes, AllowedPatterns.
36
27
  # Prefixes: when, with, without
37
28
  RSpec/ContextWording:
38
29
  Exclude:
30
+ - 'spec/mml4_spec.rb'
39
31
  - 'spec/mml_spec.rb'
40
32
 
41
33
  # Offense count: 1
42
- RSpec/PendingWithoutReason:
34
+ RSpec/MultipleDescribes:
43
35
  Exclude:
44
36
  - 'spec/mml_spec.rb'
45
37
 
46
38
  # Offense count: 1
47
- # This cop supports safe autocorrection (--autocorrect).
48
- # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
49
- # SupportedStyles: single_quotes, double_quotes
50
- Style/StringLiterals:
39
+ # Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata, InflectorPath, EnforcedInflector.
40
+ # SupportedInflectors: default, active_support
41
+ RSpec/SpecFilePathFormat:
51
42
  Exclude:
52
- - 'spec/mml_spec.rb'
43
+ - '**/spec/routing/**/*'
44
+ - 'spec/mml4_spec.rb'
data/README.adoc CHANGED
@@ -5,299 +5,371 @@
5
5
 
6
6
  == Purpose
7
7
 
8
- Mml provides MathML 3 XML parsing and serialization for Ruby. It maps the full
9
- MathML element set into Ruby model classes using the
8
+ Mml provides MathML 3 and MathML 4 XML parsing and serialization for Ruby. It maps
9
+ the full MathML element set into Ruby model classes using the
10
10
  https://github.com/lutaml/lutaml-model[lutaml-model] framework and is used by
11
11
  https://github.com/plurimath/plurimath[Plurimath] for mathematical formula
12
12
  representation.
13
13
 
14
14
  Key features:
15
15
 
16
- * **MathML 3 element support**: 43 elements including presentation markup,
17
- tables, scripts, fractions, and more
18
- * **Namespace handling**: Default `xmlns`, prefixed `mml:`, and namespace-less
19
- input
20
- * **Round-trip fidelity**: Parse XML to an object graph, modify, and serialize
21
- back
16
+ * **Dual MathML version support**: Separate class hierarchies for MathML 3 and
17
+ MathML 4 with explicit version selection
18
+ * **Round-trip fidelity**: Parse XML to an object graph, modify, and serialize back
19
+ * **Namespace handling**: Default `xmlns`, prefixed `mml:`, and namespace-less input
22
20
  * **Opal support**: Runs in the browser via Ruby-to-JavaScript compilation
23
21
 
24
22
  == Installation
25
23
 
26
- Add to your application's Gemfile:
27
-
28
24
  [source,ruby]
29
25
  ----
30
26
  gem 'mml'
31
27
  ----
32
28
 
33
- Then execute:
34
-
35
29
  [source,bash]
36
30
  ----
37
31
  $ bundle install
32
+ # or
33
+ $ gem install mml
38
34
  ----
39
35
 
40
- Or install directly:
36
+ == Quick start
41
37
 
42
- [source,bash]
38
+ [source,ruby]
43
39
  ----
44
- $ gem install mml
40
+ require "mml"
41
+
42
+ # Parse MathML with explicit version
43
+ math = Mml.parse('<math xmlns="http://www.w3.org/1998/Math/MathML"><mi>x</mi></math>', version: 3)
44
+ math4 = Mml.parse(input, version: 4)
45
+
46
+ # Serialize back to XML
47
+ math.to_xml
48
+
49
+ # Or use versioned modules directly
50
+ Mml::V3::Math.from_xml(input)
51
+ Mml::V4::Math.from_xml(input)
45
52
  ----
46
53
 
47
- == Quick start
54
+ == MathML version architecture
55
+
56
+ Mml maintains two parallel class hierarchies under `Mml::V3` and `Mml::V4`.
57
+ Both versions share the same namespace URI (`http://www.w3.org/1998/Math/MathML`)
58
+ for backward compatibility.
59
+
60
+ [source]
61
+ ----
62
+ ┌───────────────────────────────────────────┐
63
+ │ Mml │
64
+ │ parse() delegates to V3 or V4 │
65
+ └────────────────────┬──────────────────────┘
66
+
67
+ ┌───────────────────┴────────────────────┐
68
+ │ │
69
+ ┌────┴────┐ ┌────┴────┐
70
+ │ Mml::V3 │ │ Mml::V4 │
71
+ └────┬────┘ └────┬────┘
72
+ │ │
73
+ ┌───────────────┼─────────────────────┐ ┌───────────────┼───────────────┐
74
+ │ ┌────────────┴───────────────┐ │ │ ┌────────────┴────────────┐ │
75
+ │ │ Element classes │ │ │ │ Element classes │ │
76
+ │ │ (Mi, Mn, Mo, Mrow, ...) │ │ │ │ (Mi, Mn, Mo, Mrow, ...) │ │
77
+ │ │ │ │ │ │ + intent, arg, │ │
78
+ │ │ Inherits from │ │ │ │ displaystyle, │ │
79
+ │ │ Lutaml::Model::Serializable│ │ │ │ scriptlevel │ │
80
+ │ └────────────┬───────────────┘ │ │ │ + <a> element │ │
81
+ │ │ │ │ └────────────┬────────────┘ │
82
+ │ ┌────────────┴────────────┐ │ │ ┌────────────┴────────────┐ │
83
+ │ │ CommonAttributes │ │ │ │ CommonAttributes │ │
84
+ │ │ (child element mixin) │ │ │ │ (v4 version) │ │
85
+ │ └─────────────────────────┘ │ | └─────────────────────────┘ |
86
+ └─────────────────────────────────────┘ └───────────────────────────────┘
87
+
88
+ ┌──────────────────┴──────────────────────┐
89
+ │ Lutaml::Model::Serializable │
90
+ │ (XML mapping framework) │
91
+ └─────────────────────────────────────────┘
92
+ ----
93
+
94
+ === Version selection
95
+
96
+ [source,ruby]
97
+ ----
98
+ Mml.parse(input) # Default: MathML 3 (Mml::V3)
99
+ Mml.parse(input, version: 3) # Explicit MathML 3
100
+ Mml.parse(input, version: 4) # MathML 4 with intent/arg attributes
101
+ Mml::V3.parse(input) # Direct v3 parsing
102
+ Mml::V4.parse(input) # Direct v4 parsing
103
+ ----
104
+
105
+ === Key differences between MathML 3 and MathML 4
106
+
107
+ [cols="1,2",options="header"]
108
+ |===
109
+ |Feature |MathML 4 additions
110
+ |Universal attributes |`intent`, `arg`, `displaystyle`, `scriptlevel` available on all presentation elements
111
+ |New element |`<a>` hyperlink element with `href`, `hreflang`
112
+ |Deprecated (not serialized) a|
113
+ * `fontfamily`, `fontweight`, `fontstyle`, `fontsize`, `color`, `background` on `mstyle`, `mglyph`, `mspace`
114
+ * `groupalign` on `mtable`, `mtr`, `mlabeledtr`
115
+ * `fence`, `separator` on `mo`
116
+ |Deprecated (recognized but hidden) |`<mlabeledtr>`, `<none>` removed from `CommonAttributes` but classes still exist
117
+ |===
118
+
119
+ === Migration from previous versions
48
120
 
49
- === Parse with a namespace (compliant)
121
+ ==== require and configuration
50
122
 
51
- `Mml.parse` expects a namespace by default (`namespace_exist: true`). It accepts
52
- both default and prefixed forms:
123
+ The `Mml` module no longer aliases versioned constants. Use the explicit version
124
+ namespace:
53
125
 
54
126
  [source,ruby]
55
127
  ----
128
+ # Before (no longer supported)
129
+ require "mml/configuration"
130
+ Mml::Configuration.adapter = :nokogiri
131
+ Mml::Configuration.custom_models = { Mi => MyCustomMi }
132
+ Mml::Math.new(...)
133
+
134
+ # After — explicit version
56
135
  require "mml"
136
+ Mml::V3::Configuration.adapter = :nokogiri
137
+ Mml::V3::Configuration.custom_models = { Mi => MyCustomMi }
138
+ Mml::V3::Math.new(...)
139
+
140
+ # Or for MathML 4
141
+ Mml::V4::Configuration.adapter = :nokogiri
142
+ Mml::V4::Configuration.custom_models = { Mi => MyCustomMi }
143
+ Mml::V4::Math.new(...)
144
+ ----
57
145
 
58
- # Default namespace
59
- math = Mml.parse('<math xmlns="http://www.w3.org/1998/Math/MathML"><mi>x</mi></math>')
146
+ ==== Element class references
60
147
 
61
- # Prefixed namespace
62
- math = Mml.parse('<mml:math xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mi>x</mml:mi></mml:math>')
148
+ All element classes live under their version namespace:
149
+
150
+ [source,ruby]
151
+ ----
152
+ # Before (no longer supported)
153
+ Mml::Mi.new(value: "x")
154
+ Mml::Mrow.new(mi_value: [...])
155
+
156
+ # After
157
+ Mml::V3::Mi.new(value: "x")
158
+ Mml::V3::Mrow.new(mi_value: [...])
159
+
160
+ # Or for MathML 4
161
+ Mml::V4::Mi.new(value: "x", intent: "$x")
162
+ Mml::V4::Mrow.new(mi_value: [...])
163
+ ----
164
+
165
+ ==== Parsing
166
+
167
+ `Mml.parse` still works with a `version:` keyword, defaulting to version 3:
168
+
169
+ [source,ruby]
170
+ ----
171
+ Mml.parse(input) # unchanged — defaults to MathML 3
172
+ Mml.parse(input, version: 4) # use MathML 4
63
173
  ----
64
174
 
65
- === Parse without namespace (non-compliant)
175
+ == Parsing and serialization
66
176
 
67
- Some XML schemas embed MathML without a namespace declaration, which is
68
- non-compliant but common in practice. Pass `namespace_exist: false` to handle
69
- this — the namespace is injected internally before parsing:
177
+ === Parsing
70
178
 
71
179
  [source,ruby]
72
180
  ----
73
- math = Mml.parse("<math><mi>x</mi></math>", namespace_exist: false)
181
+ # Default namespace
182
+ Mml::V3.parse('<math xmlns="http://www.w3.org/1998/Math/MathML"><mi>x</mi></math>')
183
+
184
+ # Prefixed namespace
185
+ Mml::V3.parse('<mml:math xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mi>x</mml:mi></mml:math>')
186
+
187
+ # No namespace (namespace injected internally)
188
+ Mml::V3.parse("<math><mi>x</mi></math>", namespace_exist: false)
189
+
190
+ # MathML 4
191
+ Mml::V4.parse(input)
74
192
  ----
75
193
 
76
- === Serialize to XML
194
+ === Serialization
77
195
 
78
196
  [source,ruby]
79
197
  ----
80
- # Default namespace (default)
81
198
  math.to_xml
82
199
  # => "<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mi>x</mi></math>"
83
200
 
84
- # Prefixed namespace
85
201
  math.to_xml(prefix: true)
86
202
  # => "<mml:math xmlns:mml=\"http://www.w3.org/1998/Math/MathML\"><mml:mi>x</mml:mi></mml:math>"
87
203
 
88
- # Omit XML declaration
89
204
  math.to_xml(declaration: false)
205
+ # => "<math xmlns=\"...\"><mi>x</mi></math>"
90
206
  ----
91
207
 
92
- == Building MathML with the API
93
-
94
- === Leaf elements
95
-
96
- Leaf elements (`Mi`, `Mn`, `Mo`, `Ms`, `Mtext`) hold text content in the `value`
97
- attribute:
208
+ === Round-trip (parse, modify, serialize)
98
209
 
99
210
  [source,ruby]
100
211
  ----
101
- # An identifier: <mi>x</mi>
102
- Mml::Mi.new(value: "x")
212
+ math = Mml.parse('<math xmlns="http://www.w3.org/1998/Math/MathML"><mi>x</mi></math>')
213
+ math.display = "block"
214
+ math.to_xml
215
+ # => "<math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\"><mi>x</mi></math>"
216
+ ----
103
217
 
104
- # A number: <mn>42</mn>
105
- Mml::Mn.new(value: "42")
218
+ == Element reference
106
219
 
107
- # An operator: <mo>+</mo>
108
- Mml::Mo.new(value: "+")
220
+ === Element types
109
221
 
110
- # With attributes: <mi mathvariant="bold">x</mi>
111
- Mml::Mi.new(value: "x", mathvariant: "bold")
112
- ----
222
+ *Token elements*: `mi`, `mn`, `mo`, `ms`, `mtext`, `mspace`, `mglyph`
113
223
 
114
- === Container elements
224
+ *General layout*: `mrow`, `mfrac`, `msqrt`, `mroot`, `mstyle`, `merror`, `mpadded`, `mphantom`, `mfenced`, `menclose`, `maction`
225
+
226
+ *Script elements*: `msub`, `msup`, `msubsup`, `munder`, `mover`, `munderover`, `mmultiscripts`, `mprescripts`
227
+
228
+ *Table elements*: `mtable`, `mtr`, `mtd`
229
+
230
+ *Row and stack elements*: `mstack`, `msrow`, `mscarries`, `mscarry`, `msline`, `msgroup`, `mlongdiv`
115
231
 
116
- Container elements hold child elements via collection attributes named
117
- `#{tag}_value`. The supported child types come from the `CommonAttributes` model
118
- mixed into container classes.
232
+ *Semantic elements*: `mfraction`, `semantics`
233
+
234
+ *v4 only*: `a` (hyperlink)
235
+
236
+ *Deprecated*: `mlabeledtr`, `none` (classes exist but hidden from CommonAttributes in v4)
237
+
238
+ === Token elements (leaf nodes)
239
+
240
+ Token elements hold text content in the `value` attribute:
119
241
 
120
242
  [source,ruby]
121
243
  ----
122
- # <mrow><mi>x</mi><mo>+</mo><mn>1</mn></mrow>
123
- Mml::Mrow.new(
124
- mi_value: [Mml::Mi.new(value: "x")],
125
- mo_value: [Mml::Mo.new(value: "+")],
126
- mn_value: [Mml::Mn.new(value: "1")],
127
- )
244
+ Mml::V3::Mi.new(value: "x")
245
+ Mml::V3::Mn.new(value: "42")
246
+ Mml::V3::Mo.new(value: "+")
247
+ Mml::V3::Ms.new(value: "text")
248
+ Mml::V3::Mtext.new(value: "label")
249
+ Mml::V3::Mspace.new(width: "1em")
250
+ Mml::V3::Mglyph.new(alt: "symbol")
128
251
  ----
129
252
 
130
- === Composing complex expressions
253
+ === Container elements
131
254
 
132
- Build an expression tree by nesting elements, then wrap in `Mml::Math`:
255
+ Container elements hold child elements via `#{tag}_value` collection attributes:
133
256
 
134
257
  [source,ruby]
135
258
  ----
136
- # <math xmlns="http://www.w3.org/1998/Math/MathML">
137
- # <mfrac><mi>a</mi><mi>b</mi></mfrac>
138
- # </math>
139
- math = Mml::Math.new(
140
- mfrac_value: [
141
- Mml::Mfrac.new(
142
- mi_value: [Mml::Mi.new(value: "a"), Mml::Mi.new(value: "b")],
143
- ),
144
- ],
259
+ Mml::V3::Mrow.new(
260
+ mi_value: [Mml::V3::Mi.new(value: "x")],
261
+ mo_value: [Mml::V3::Mo.new(value: "+")],
262
+ mn_value: [Mml::V3::Mn.new(value: "1")],
145
263
  )
146
-
147
- math.to_xml
264
+ # => <mrow><mi>x</mi><mo>+</mo><mn>1</mn></mrow>
148
265
  ----
149
266
 
150
- === Fractions, subscripts, and superscripts
267
+ === Composing expressions
268
+
269
+ Build an expression tree by nesting elements:
151
270
 
152
271
  [source,ruby]
153
272
  ----
154
- # <msup><mi>x</mi><mn>2</mn></msup> — x²
155
- Mml::Msup.new(mi_value: [Mml::Mi.new(value: "x")],
156
- mn_value: [Mml::Mn.new(value: "2")])
157
-
158
- # <msub><mi>a</mi><mn>1</mn></msup> — a₁
159
- Mml::Msub.new(mi_value: [Mml::Mi.new(value: "a")],
160
- mn_value: [Mml::Mn.new(value: "1")])
161
-
162
- # <msubsup><mi>b</mi><mn>1</mn><mn>2</mn></msubsup> — b₁²
163
- Mml::Msubsup.new(mi_value: [Mml::Mi.new(value: "b")],
164
- mn_value: [Mml::Mn.new(value: "1"),
165
- Mml::Mn.new(value: "2")])
166
-
167
- # <mfrac linethickness="0"><mi>a</mi><mi>b</mi></mfrac> — a/b
168
- Mml::Mfrac.new(
169
- linethickness: "0",
170
- mi_value: [Mml::Mi.new(value: "a"), Mml::Mi.new(value: "b")],
273
+ Mml::V3::Math.new(
274
+ mfrac_value: [
275
+ Mml::V3::Mfrac.new(
276
+ mi_value: [Mml::V3::Mi.new(value: "a"), Mml::V3::Mi.new(value: "b")],
277
+ ),
278
+ ],
171
279
  )
280
+ # => <math><mfrac><mi>a</mi><mi>b</mi></mfrac></math>
172
281
  ----
173
282
 
174
283
  === Tables
175
284
 
176
- `Mtable` uses typed `mtr_value` and `mlabeledtr_value` attributes for its rows:
177
-
178
285
  [source,ruby]
179
286
  ----
180
- # <mtable>
181
- # <mtr><mtd><mi>a</mi></mtd><mtd><mi>b</mi></mtd></mtr>
182
- # </mtable>
183
- Mml::Mtable.new(
287
+ Mml::V3::Mtable.new(
184
288
  mtr_value: [
185
- Mml::Mtr.new(
289
+ Mml::V3::Mtr.new(
186
290
  mtd_value: [
187
- Mml::Mtd.new(mi_value: [Mml::Mi.new(value: "a")]),
188
- Mml::Mtd.new(mi_value: [Mml::Mi.new(value: "b")]),
291
+ Mml::V3::Mtd.new(mi_value: [Mml::V3::Mi.new(value: "a")]),
292
+ Mml::V3::Mtd.new(mi_value: [Mml::V3::Mi.new(value: "b")]),
189
293
  ],
190
294
  ),
191
295
  ],
192
296
  )
193
297
  ----
194
298
 
195
- === Parsing, modifying, and re-serializing
196
-
197
- The object graph is fully mutable — parse, modify attributes or children, then
198
- serialize back:
299
+ === Hyperlinks (MathML 4 only)
199
300
 
200
301
  [source,ruby]
201
302
  ----
202
- math = Mml.parse('<math xmlns="http://www.w3.org/1998/Math/MathML"><mi>x</mi></math>')
203
-
204
- # Modify the root element's display attribute
205
- math.display = "block"
206
-
207
- # Serialize with the changes
208
- math.to_xml
209
- # => "<math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\">...</math>"
303
+ Mml::V4::A.new(
304
+ href: "https://example.com",
305
+ hreflang: "en",
306
+ mi_value: [Mml::V4::Mi.new(value: "click")]
307
+ )
308
+ # => <a href="https://example.com" hreflang="en"><mi>click</mi></a>
210
309
  ----
211
310
 
212
- == Architecture
213
-
214
- Mml follows a data mapper pattern where each MathML element is a Ruby class
215
- under the `Mml::` namespace, inheriting from `Lutaml::Model::Serializable`.
311
+ == Internal architecture
216
312
 
217
- === Element classes
313
+ === Element class patterns
218
314
 
219
- Two element patterns:
315
+ All element classes inherit from `Lutaml::Model::Serializable`:
220
316
 
221
- * *Leaf elements* (`Mi`, `Mn`, `Mo`, `Ms`, `Mtext`, `Mspace`): hold text content
222
- via `map_content to: :value`
223
- * *Container elements* (`Math`, `Mrow`, `Mfrac`, `Mtable`, `Msubsup`): hold
224
- child elements via `mixed_content`
317
+ * *Leaf elements*: use `map_content to: :value` for text content
318
+ * *Container elements*: use `mixed_content` for child elements
225
319
 
226
320
  [source,ruby]
227
321
  ----
228
- # Leaf element — text content
229
- module Mml
230
- class Mi < Lutaml::Model::Serializable
231
- attribute :value, :string
232
-
233
- xml do
234
- namespace Namespace
235
- element "mi"
236
- map_content to: :value
237
- end
322
+ # Leaf — text content
323
+ class Mi < Lutaml::Model::Serializable
324
+ attribute :value, :string
325
+ xml do
326
+ element "mi"
327
+ map_content to: :value
238
328
  end
239
329
  end
240
330
 
241
- # Container element — child elements
242
- module Mml
243
- class Mrow < Lutaml::Model::Serializable
244
- xml do
245
- namespace Namespace
246
- element "mrow"
247
- mixed_content
248
- end
331
+ # Container — child elements
332
+ class Mrow < Lutaml::Model::Serializable
333
+ xml do
334
+ element "mrow"
335
+ mixed_content
249
336
  end
250
337
  end
251
338
  ----
252
339
 
253
340
  === CommonAttributes
254
341
 
255
- Container elements that can hold arbitrary MathML children use a shared
256
- `CommonAttributes` model.
257
-
258
- The classes that receive it are listed in
342
+ Container elements that accept arbitrary MathML children import `CommonAttributes`,
343
+ which defines `#{tag}_value` collection attributes for all supported child elements.
344
+ The list of classes that receive this mixin is in
259
345
  `Configuration::COMMON_ATTRIBUTES_CLASSES`.
260
346
 
261
347
  === Namespace
262
348
 
263
- All elements declare `namespace Namespace` using the MathML namespace URI
264
- (`http://www.w3.org/1998/Math/MathML`) with prefix `mml`. The `Mml.parse`
265
- method handles three input forms:
349
+ All elements use the MathML namespace URI (`http://www.w3.org/1998/Math/MathML`).
350
+ Three input forms are supported:
266
351
 
267
352
  * Default namespace: `<math xmlns="http://www.w3.org/1998/Math/MathML">`
268
353
  * Prefixed: `<mml:math xmlns:mml="http://www.w3.org/1998/Math/MathML">`
269
- * No namespace: handled by injecting the namespace before parsing
354
+ * No namespace: namespace is injected before parsing when `namespace_exist: false`
355
+
356
+ == Configuration
270
357
 
271
- === Configuration
358
+ Configuration is version-specific. Use the namespace matching your target version:
272
359
 
273
360
  [source,ruby]
274
361
  ----
275
- # Switch XML adapter
276
- Mml::Configuration.adapter = :nokogiri
362
+ # Switch XML adapter (default: :ox, also supports :nokogiri)
363
+ Mml::V3::Configuration.adapter = :nokogiri
364
+ Mml::V4::Configuration.adapter = :nokogiri
277
365
 
278
366
  # Register custom model replacements
279
- Mml::Configuration.custom_models = { Mi => MyCustomMi }
367
+ Mml::V3::Configuration.custom_models = { Mi => MyCustomMi }
368
+ Mml::V4::Configuration.custom_models = { Mi => MyCustomMi }
280
369
  ----
281
370
 
282
- == Supported elements
283
-
284
- Mml supports the following MathML 3 presentation elements:
285
-
286
- `math`, `mi`, `mn`, `mo`, `ms`, `mtext`, `mspace`, `mglyph`
287
- `mrow`, `mfrac`, `msqrt`, `mroot`, `mstyle`, `merror`, `mpadded`,
288
- `mphantom`, `mfenced`, `menclose`, `maction`
289
- `msub`, `msup`, `msubsup`, `munder`, `mover`, `munderover`,
290
- `mmultiscripts`, `mprescripts`
291
- `mtable`, `mtr`, `mtd`, `mlabeledtr`
292
- `mstack`, `msrow`, `mscarries`, `mscarry`, `msline`, `msgroup`, `mlongdiv`
293
- `mfraction`, `semantics`, `none`
294
-
295
371
  == Development
296
372
 
297
- After checking out the repo, run `bin/setup` to install dependencies. Then run
298
- `rake` to run the tests and linter. You can also run `bin/console` for an
299
- interactive prompt.
300
-
301
373
  [source,bash]
302
374
  ----
303
375
  rake # Run specs + rubocop
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mml
4
+ module V3
5
+ Mml::V3::Configuration::SUPPORTED_TAGS.each do |tag|
6
+ autoload(tag.to_s.capitalize.to_sym, "mml/v3/#{tag}.rb")
7
+ end
8
+
9
+ class CommonAttributes < Lutaml::Model::Serializable
10
+ Mml::V3::Configuration::SUPPORTED_TAGS.each do |tag|
11
+ attribute :"#{tag}_value", Mml::V3.const_get(tag.to_s.capitalize),
12
+ collection: true
13
+ end
14
+
15
+ xml do
16
+ Mml::V3::Configuration::SUPPORTED_TAGS.each do |tag|
17
+ map_element tag, to: :"#{tag}_value"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end