mml 2.0.3 → 2.2.0

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 (156) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +10 -4
  3. data/.gitignore +2 -0
  4. data/.rubocop.yml +1 -1
  5. data/.rubocop_todo.yml +20 -91
  6. data/Gemfile +2 -1
  7. data/README.adoc +365 -4
  8. data/lib/mml/v3/common_attributes.rb +22 -0
  9. data/lib/mml/v3/configuration.rb +105 -0
  10. data/lib/mml/v3/maction.rb +22 -0
  11. data/lib/mml/v3/maligngroup.rb +20 -0
  12. data/lib/mml/v3/malignmark.rb +20 -0
  13. data/lib/mml/v3/math.rb +20 -0
  14. data/lib/mml/v3/menclose.rb +21 -0
  15. data/lib/mml/v3/merror.rb +19 -0
  16. data/lib/mml/v3/mfenced.rb +28 -0
  17. data/lib/mml/v3/mfrac.rb +27 -0
  18. data/lib/mml/v3/mfraction.rb +27 -0
  19. data/lib/mml/v3/mglyph.rb +46 -0
  20. data/lib/mml/v3/mi.rb +38 -0
  21. data/lib/mml/v3/mlabeledtr.rb +31 -0
  22. data/lib/mml/v3/mlongdiv.rb +25 -0
  23. data/lib/mml/v3/mmultiscripts.rb +27 -0
  24. data/lib/mml/v3/mn.rb +38 -0
  25. data/lib/mml/v3/mo.rb +86 -0
  26. data/lib/mml/v3/mover.rb +23 -0
  27. data/lib/mml/v3/mpadded.rb +29 -0
  28. data/lib/mml/v3/mphantom.rb +19 -0
  29. data/lib/mml/v3/mprescripts.rb +18 -0
  30. data/lib/mml/v3/mroot.rb +19 -0
  31. data/lib/mml/v3/mrow.rb +25 -0
  32. data/lib/mml/v3/ms.rb +43 -0
  33. data/lib/mml/v3/mscarries.rb +27 -0
  34. data/lib/mml/v3/mscarry.rb +23 -0
  35. data/lib/mml/v3/msgroup.rb +25 -0
  36. data/lib/mml/v3/msline.rb +28 -0
  37. data/lib/mml/v3/mspace.rb +58 -0
  38. data/lib/mml/v3/msqrt.rb +19 -0
  39. data/lib/mml/v3/msrow.rb +21 -0
  40. data/lib/mml/v3/mstack.rb +27 -0
  41. data/lib/mml/v3/mstyle.rb +211 -0
  42. data/lib/mml/v3/msub.rb +21 -0
  43. data/lib/mml/v3/msubsup.rb +23 -0
  44. data/lib/mml/v3/msup.rb +21 -0
  45. data/lib/mml/v3/mtable.rb +61 -0
  46. data/lib/mml/v3/mtd.rb +23 -0
  47. data/lib/mml/v3/mtext.rb +38 -0
  48. data/lib/mml/v3/mtr.rb +29 -0
  49. data/lib/mml/v3/munder.rb +25 -0
  50. data/lib/mml/v3/munderover.rb +25 -0
  51. data/lib/mml/v3/namespace.rb +10 -0
  52. data/lib/mml/v3/none.rb +18 -0
  53. data/lib/mml/v3/semantics.rb +19 -0
  54. data/lib/mml/v3/version.rb +7 -0
  55. data/lib/mml/v3.rb +85 -0
  56. data/lib/mml/v4/a.rb +31 -0
  57. data/lib/mml/v4/common_attributes.rb +26 -0
  58. data/lib/mml/v4/configuration.rb +106 -0
  59. data/lib/mml/v4/maction.rb +30 -0
  60. data/lib/mml/v4/maligngroup.rb +28 -0
  61. data/lib/mml/v4/malignmark.rb +28 -0
  62. data/lib/mml/v4/math.rb +27 -0
  63. data/lib/mml/v4/menclose.rb +29 -0
  64. data/lib/mml/v4/merror.rb +27 -0
  65. data/lib/mml/v4/mfenced.rb +36 -0
  66. data/lib/mml/v4/mfrac.rb +35 -0
  67. data/lib/mml/v4/mfraction.rb +31 -0
  68. data/lib/mml/v4/mglyph.rb +46 -0
  69. data/lib/mml/v4/mi.rb +41 -0
  70. data/lib/mml/v4/mlabeledtr.rb +34 -0
  71. data/lib/mml/v4/mlongdiv.rb +29 -0
  72. data/lib/mml/v4/mmultiscripts.rb +35 -0
  73. data/lib/mml/v4/mn.rb +40 -0
  74. data/lib/mml/v4/mo.rb +86 -0
  75. data/lib/mml/v4/mover.rb +31 -0
  76. data/lib/mml/v4/mpadded.rb +37 -0
  77. data/lib/mml/v4/mphantom.rb +27 -0
  78. data/lib/mml/v4/mprescripts.rb +22 -0
  79. data/lib/mml/v4/mroot.rb +27 -0
  80. data/lib/mml/v4/mrow.rb +31 -0
  81. data/lib/mml/v4/ms.rb +45 -0
  82. data/lib/mml/v4/mscarries.rb +31 -0
  83. data/lib/mml/v4/mscarry.rb +27 -0
  84. data/lib/mml/v4/msgroup.rb +29 -0
  85. data/lib/mml/v4/msline.rb +32 -0
  86. data/lib/mml/v4/mspace.rb +60 -0
  87. data/lib/mml/v4/msqrt.rb +27 -0
  88. data/lib/mml/v4/msrow.rb +29 -0
  89. data/lib/mml/v4/mstack.rb +35 -0
  90. data/lib/mml/v4/mstyle.rb +197 -0
  91. data/lib/mml/v4/msub.rb +29 -0
  92. data/lib/mml/v4/msubsup.rb +31 -0
  93. data/lib/mml/v4/msup.rb +29 -0
  94. data/lib/mml/v4/mtable.rb +66 -0
  95. data/lib/mml/v4/mtd.rb +31 -0
  96. data/lib/mml/v4/mtext.rb +40 -0
  97. data/lib/mml/v4/mtr.rb +36 -0
  98. data/lib/mml/v4/munder.rb +33 -0
  99. data/lib/mml/v4/munderover.rb +33 -0
  100. data/lib/mml/v4/namespace.rb +10 -0
  101. data/lib/mml/v4/none.rb +22 -0
  102. data/lib/mml/v4/opal_setup.rb.erb +6 -0
  103. data/lib/mml/v4/semantics.rb +23 -0
  104. data/lib/mml/v4/version.rb +7 -0
  105. data/lib/mml/v4.rb +81 -0
  106. data/lib/mml/version.rb +1 -1
  107. data/lib/mml.rb +25 -43
  108. data/mml.gemspec +1 -1
  109. metadata +105 -53
  110. data/lib/mml/common_attributes.rb +0 -23
  111. data/lib/mml/configuration.rb +0 -104
  112. data/lib/mml/maction.rb +0 -19
  113. data/lib/mml/maligngroup.rb +0 -17
  114. data/lib/mml/malignmark.rb +0 -17
  115. data/lib/mml/math_with_namespace.rb +0 -14
  116. data/lib/mml/math_with_nil_namespace.rb +0 -13
  117. data/lib/mml/menclose.rb +0 -17
  118. data/lib/mml/merror.rb +0 -15
  119. data/lib/mml/mfenced.rb +0 -24
  120. data/lib/mml/mfrac.rb +0 -23
  121. data/lib/mml/mfraction.rb +0 -23
  122. data/lib/mml/mglyph.rb +0 -43
  123. data/lib/mml/mi.rb +0 -35
  124. data/lib/mml/mlabeledtr.rb +0 -27
  125. data/lib/mml/mlongdiv.rb +0 -21
  126. data/lib/mml/mmultiscripts.rb +0 -23
  127. data/lib/mml/mn.rb +0 -35
  128. data/lib/mml/mo.rb +0 -83
  129. data/lib/mml/mover.rb +0 -19
  130. data/lib/mml/mpadded.rb +0 -25
  131. data/lib/mml/mphantom.rb +0 -15
  132. data/lib/mml/mprescripts.rb +0 -15
  133. data/lib/mml/mroot.rb +0 -15
  134. data/lib/mml/mrow.rb +0 -21
  135. data/lib/mml/ms.rb +0 -39
  136. data/lib/mml/mscarries.rb +0 -24
  137. data/lib/mml/mscarry.rb +0 -19
  138. data/lib/mml/msgroup.rb +0 -21
  139. data/lib/mml/msline.rb +0 -25
  140. data/lib/mml/mspace.rb +0 -55
  141. data/lib/mml/msqrt.rb +0 -15
  142. data/lib/mml/msrow.rb +0 -17
  143. data/lib/mml/mstack.rb +0 -23
  144. data/lib/mml/mstyle.rb +0 -212
  145. data/lib/mml/msub.rb +0 -17
  146. data/lib/mml/msubsup.rb +0 -19
  147. data/lib/mml/msup.rb +0 -17
  148. data/lib/mml/mtable.rb +0 -57
  149. data/lib/mml/mtd.rb +0 -19
  150. data/lib/mml/mtext.rb +0 -35
  151. data/lib/mml/mtr.rb +0 -25
  152. data/lib/mml/munder.rb +0 -21
  153. data/lib/mml/munderover.rb +0 -21
  154. data/lib/mml/none.rb +0 -15
  155. data/lib/mml/semantics.rb +0 -15
  156. /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: b4534e22a8c030145f41918044bbfb90390f9dc769cb6cc6c00494bfd308f959
4
- data.tar.gz: 935e60485c754b7c5cc9d0a3a7e7a07e4674ba584f5f439de2d77f2428b0a406
3
+ metadata.gz: efbc2e4b8156916d34e524d2f7b6456168f8ef4a0c0c2ade8fbfb2b02bdc2efb
4
+ data.tar.gz: 8f826d13adfc7f0b556aa5ab6f32f8e8390bf06002a76570845605af7282058a
5
5
  SHA512:
6
- metadata.gz: e535b06933158eed3b043dc21464c2b2f1d199a7b3f8d563bf4065173e28219dd76dbd5c8d874f5e7f6617d5ffa13140befe1c4d92c5bbe79b8603c16245d5bb
7
- data.tar.gz: d45a10ba72fe8c6830cfc4d7e481837e8035807a22f7f4e16f7c117e99d967fd014bea1b5d0eb778fca11daa1754ba7443891729a89d74b7e6f1db09125a1c0d
6
+ metadata.gz: 694df3fcc6b3611d6bac5b5c380b420b91823e697b219ba93a5d9ec22c70d910f8ecef6fef6c419b1acf80660d726259056717380faf8e77d235879b056e3c85
7
+ data.tar.gz: 72b8f2d1960adec6a3777e43c792cf3d1d3aa4658be47e5bdbf9f7e5cd428ac1dbd3990d7c1685ae4f6ed1a90566b80932046c15424bf4b6a94efa4e39360a29
@@ -2,20 +2,26 @@
2
2
  # See https://github.com/metanorma/cimas
3
3
  name: release
4
4
 
5
+ permissions:
6
+ contents: write
7
+ packages: write
8
+ id-token: write
9
+
5
10
  on:
6
11
  workflow_dispatch:
7
12
  inputs:
8
13
  next_version:
9
14
  description: |
10
- Next release version. Possible values: x.y.z, major, minor, patch or pre|rc|etc
15
+ Next release version. Possible values: x.y.z, major, minor, patch (or pre|rc|etc).
16
+ Also, you can pass 'skip' to skip 'git tag' and do 'gem push' for the current version
11
17
  required: true
12
18
  default: 'skip'
13
- push:
14
- tags: [ v* ]
19
+ repository_dispatch:
20
+ types: [ do-release ]
15
21
 
16
22
  jobs:
17
23
  release:
18
- uses: metanorma/ci/.github/workflows/rubygems-release.yml@main
24
+ uses: relaton/support/.github/workflows/release.yml@main
19
25
  with:
20
26
  next_version: ${{ github.event.inputs.next_version }}
21
27
  secrets:
data/.gitignore CHANGED
@@ -9,3 +9,5 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+
13
+ Gemfile.lock
data/.rubocop.yml CHANGED
@@ -2,7 +2,7 @@ inherit_from:
2
2
  - https://raw.githubusercontent.com/riboseinc/oss-guides/master/ci/rubocop.yml
3
3
  - .rubocop_todo.yml
4
4
 
5
- require:
5
+ plugins:
6
6
  - rubocop-performance
7
7
  - rubocop-rake
8
8
  - rubocop-rspec
data/.rubocop_todo.yml CHANGED
@@ -1,115 +1,44 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2025-03-29 09:22:55 UTC using RuboCop version 1.75.1.
3
+ # on 2026-03-31 03:44: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: 2
9
+ # Offense count: 3
10
10
  # This cop supports safe autocorrection (--autocorrect).
11
- # Configuration parameters: Include.
12
- # Include: **/*.gemspec
13
- Gemspec/AddRuntimeDependency:
14
- Exclude:
15
- - 'mml.gemspec'
16
-
17
- # Offense count: 1
18
- # This cop supports safe autocorrection (--autocorrect).
19
- # Configuration parameters: Severity, Include.
20
- # Include: **/*.gemspec
21
- Gemspec/DeprecatedAttributeAssignment:
22
- Exclude:
23
- - 'mml.gemspec'
24
-
25
- # Offense count: 1
26
- # This cop supports safe autocorrection (--autocorrect).
27
- # Configuration parameters: Severity, Include.
28
- # Include: **/*.gemspec
29
- Gemspec/RequireMFA:
30
- Exclude:
31
- - 'mml.gemspec'
32
-
33
- # Offense count: 1
34
- # This cop supports safe autocorrection (--autocorrect).
35
- # Configuration parameters: EnforcedStyle, IndentationWidth.
36
- # SupportedStyles: with_first_argument, with_fixed_indentation
37
- Layout/ArgumentAlignment:
38
- Exclude:
39
- - 'lib/mml/common_attributes.rb'
40
-
41
- # Offense count: 6
42
- # This cop supports safe autocorrection (--autocorrect).
43
- # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
44
- # SupportedHashRocketStyles: key, separator, table
45
- # SupportedColonStyles: key, separator, table
46
- # SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
47
- Layout/HashAlignment:
48
- Exclude:
49
- - 'lib/mml/mscarries.rb'
50
- - 'lib/mml/mstyle.rb'
51
-
52
- # Offense count: 1
53
- # This cop supports safe autocorrection (--autocorrect).
54
- # Configuration parameters: EnforcedStyle.
55
- # SupportedStyles: normal, indented_internal_methods
56
- Layout/IndentationConsistency:
57
- Exclude:
58
- - 'mml.gemspec'
59
-
60
- # Offense count: 8
61
- # This cop supports safe autocorrection (--autocorrect).
62
- # Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
11
+ # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
63
12
  # URISchemes: http, https
64
13
  Layout/LineLength:
65
14
  Exclude:
66
- - 'lib/mml/common_attributes.rb'
67
- - 'lib/mml/mscarries.rb'
68
- - 'lib/mml/mstyle.rb'
69
15
  - 'mml.gemspec'
70
-
71
- # Offense count: 7
72
- # This cop supports safe autocorrection (--autocorrect).
73
- # Configuration parameters: AllowInHeredoc.
74
- Layout/TrailingWhitespace:
75
- Exclude:
76
- - 'lib/mml/common_attributes.rb'
77
- - 'lib/mml/mscarries.rb'
78
- - 'lib/mml/mstyle.rb'
16
+ - 'spec/mml_spec.rb'
17
+ - 'spec/spec_helper.rb'
79
18
 
80
19
  # Offense count: 1
20
+ # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
21
+ # AllowedMethods: refine
22
+ Metrics/BlockLength:
23
+ Max: 27
24
+
25
+ # Offense count: 3
81
26
  # Configuration parameters: Prefixes, AllowedPatterns.
82
27
  # Prefixes: when, with, without
83
28
  RSpec/ContextWording:
84
29
  Exclude:
30
+ - 'spec/mml4_spec.rb'
85
31
  - 'spec/mml_spec.rb'
86
32
 
87
- # Offense count: 5
88
- # This cop supports unsafe autocorrection (--autocorrect-all).
89
- # Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants.
90
- # SupportedStyles: described_class, explicit
91
- RSpec/DescribedClass:
92
- Exclude:
93
- - 'spec/mml_spec.rb'
94
-
95
- # Offense count: 2
96
- # This cop supports safe autocorrection (--autocorrect).
97
- Style/FileRead:
33
+ # Offense count: 1
34
+ RSpec/MultipleDescribes:
98
35
  Exclude:
99
36
  - 'spec/mml_spec.rb'
100
37
 
101
- # Offense count: 2
102
- # This cop supports safe autocorrection (--autocorrect).
103
- # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
104
- # SupportedStyles: single_quotes, double_quotes
105
- Style/StringLiterals:
106
- Exclude:
107
- - 'mml.gemspec'
108
-
109
- # Offense count: 2
110
- # This cop supports safe autocorrection (--autocorrect).
111
- # Configuration parameters: EnforcedStyle.
112
- # SupportedStyles: single_quotes, double_quotes
113
- Style/StringLiteralsInInterpolation:
38
+ # Offense count: 1
39
+ # Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata, InflectorPath, EnforcedInflector.
40
+ # SupportedInflectors: default, active_support
41
+ RSpec/SpecFilePathFormat:
114
42
  Exclude:
115
- - 'spec/mml_spec.rb'
43
+ - '**/spec/routing/**/*'
44
+ - 'spec/mml4_spec.rb'
data/Gemfile CHANGED
@@ -5,7 +5,8 @@ source "https://rubygems.org"
5
5
  gemspec
6
6
  git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
7
7
 
8
- gem "equivalent-xml"
8
+ gem "canon"
9
+ gem "lutaml-model", github: "lutaml/lutaml-model", branch: "main"
9
10
  gem "ox"
10
11
  gem "pry"
11
12
  gem "rake", "~> 12.0"
data/README.adoc CHANGED
@@ -1,9 +1,370 @@
1
- = Plurimath MathML library
1
+ = Mml: MathML parser and builder
2
+
3
+ :toc:
4
+ :toclevels: 2
2
5
 
3
6
  == Purpose
4
7
 
5
- Mml is the MathML library used by Plurimath.
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
+ https://github.com/lutaml/lutaml-model[lutaml-model] framework and is used by
11
+ https://github.com/plurimath/plurimath[Plurimath] for mathematical formula
12
+ representation.
13
+
14
+ Key features:
15
+
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
20
+ * **Opal support**: Runs in the browser via Ruby-to-JavaScript compilation
21
+
22
+ == Installation
23
+
24
+ [source,ruby]
25
+ ----
26
+ gem 'mml'
27
+ ----
28
+
29
+ [source,bash]
30
+ ----
31
+ $ bundle install
32
+ # or
33
+ $ gem install mml
34
+ ----
35
+
36
+ == Quick start
37
+
38
+ [source,ruby]
39
+ ----
40
+ require "mml"
41
+
42
+ # Parse MathML (defaults to MathML 3)
43
+ math = Mml.parse('<math xmlns="http://www.w3.org/1998/Math/MathML"><mi>x</mi></math>')
44
+
45
+ # Serialize back to XML
46
+ math.to_xml
47
+ # => "<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mi>x</mi></math>"
48
+
49
+ # Parse with explicit version
50
+ math4 = Mml.parse(input, version: 4)
51
+ ----
52
+
53
+ == MathML version architecture
54
+
55
+ Mml maintains two parallel class hierarchies under `Mml::V3` and `Mml::V4`.
56
+ Both versions share the same namespace URI (`http://www.w3.org/1998/Math/MathML`)
57
+ for backward compatibility.
58
+
59
+ [source]
60
+ ----
61
+ ┌───────────────────────────────────────────┐
62
+ │ Mml │
63
+ │ (aliases Mml::V3 for backward compat) │
64
+ └────────────────────┬──────────────────────┘
65
+
66
+ ┌───────────────────┴────────────────────┐
67
+ │ │
68
+ ┌────┴────┐ ┌────┴────┐
69
+ │ Mml::V3 │ │ Mml::V4 │
70
+ └────┬────┘ └────┬────┘
71
+ │ │
72
+ ┌───────────────┼─────────────────────┐ ┌───────────────┼───────────────┐
73
+ │ ┌────────────┴───────────────┐ │ │ ┌────────────┴────────────┐ │
74
+ │ │ Element classes │ │ │ │ Element classes │ │
75
+ │ │ (Mi, Mn, Mo, Mrow, ...) │ │ │ │ (Mi, Mn, Mo, Mrow, ...) │ │
76
+ │ │ │ │ │ │ + intent, arg, │ │
77
+ │ │ Inherits from │ │ │ │ displaystyle, │ │
78
+ │ │ Lutaml::Model::Serializable│ │ │ │ scriptlevel │ │
79
+ │ └────────────┬───────────────┘ │ │ │ + <a> element │ │
80
+ │ │ │ │ └────────────┬────────────┘ │
81
+ │ ┌────────────┴────────────┐ │ │ ┌────────────┴────────────┐ │
82
+ │ │ CommonAttributes │ │ │ │ CommonAttributes │ │
83
+ │ │ (child element mixin) │ │ │ │ (v4 version) │ │
84
+ │ └─────────────────────────┘ │ | └─────────────────────────┘ |
85
+ └─────────────────────────────────────┘ └───────────────────────────────┘
86
+
87
+ ┌──────────────────┴──────────────────────┐
88
+ │ Lutaml::Model::Serializable │
89
+ │ (XML mapping framework) │
90
+ └─────────────────────────────────────────┘
91
+ ----
92
+
93
+ === Version selection
94
+
95
+ [source,ruby]
96
+ ----
97
+ Mml.parse(input) # Default: MathML 3 (Mml::V3)
98
+ Mml.parse(input, version: 3) # Explicit MathML 3
99
+ Mml.parse(input, version: 4) # MathML 4 with intent/arg attributes
100
+ Mml::V4.parse(input) # Direct v4 parsing (no default fallback)
101
+ ----
102
+
103
+ === Key differences between MathML 3 and MathML 4
104
+
105
+ [cols="1,2",options="header"]
106
+ |===
107
+ |Feature |MathML 4 additions
108
+ |Universal attributes |`intent`, `arg`, `displaystyle`, `scriptlevel` available on all presentation elements
109
+ |New element |`<a>` hyperlink element with `href`, `hreflang`
110
+ |Deprecated (not serialized) a|
111
+ * `fontfamily`, `fontweight`, `fontstyle`, `fontsize`, `color`, `background` on `mstyle`, `mglyph`, `mspace`
112
+ * `groupalign` on `mtable`, `mtr`, `mlabeledtr`
113
+ * `fence`, `separator` on `mo`
114
+ |Deprecated (recognized but hidden) |`<mlabeledtr>`, `<none>` removed from `CommonAttributes` but classes still exist
115
+ |===
116
+
117
+ === Migration from MathML 3 to MathML 4
118
+
119
+ ==== For gem users (upgrading parsing)
120
+
121
+ If you currently parse MathML and want to use MathML 4 features:
122
+
123
+ [source,ruby]
124
+ ----
125
+ # Change this:
126
+ math = Mml.parse(input)
127
+
128
+ # To this:
129
+ math = Mml.parse(input, version: 4)
130
+ ----
131
+
132
+ ==== For gem users (upgrading serialized output)
133
+
134
+ If you serialize MathML 4 documents and want to ensure compatibility:
135
+
136
+ [source,ruby]
137
+ ----
138
+ # MathML 4 serialization removes deprecated attributes from XML output
139
+ math = Mml.parse(input, version: 4)
140
+ math.to_xml # No fontfamily, fontweight, color, groupalign, etc. in output
141
+ ----
142
+
143
+ ==== For gem extenders (adding custom elements)
144
+
145
+ Custom elements registered via `Configuration.custom_models` are version-specific:
146
+
147
+ [source,ruby]
148
+ ----
149
+ # Register a custom element for MathML 4 only
150
+ Mml::V4::Configuration.custom_models = { Mi => MyCustomMiV4 }
151
+ ----
152
+
153
+ CommonAttributes is version-specific. Elements that exist only in v4 (like `<a>`)
154
+ are automatically handled:
155
+
156
+ [source,ruby]
157
+ ----
158
+ # The <a> element is only in v4 CommonAttributes
159
+ Mml::V4::Mrow.new(a_value: [Mml::V4::A.new(href: "https://...")])
160
+ ----
161
+
162
+ == Parsing and serialization
163
+
164
+ === Parsing
165
+
166
+ [source,ruby]
167
+ ----
168
+ # Default namespace
169
+ Mml.parse('<math xmlns="http://www.w3.org/1998/Math/MathML"><mi>x</mi></math>')
170
+
171
+ # Prefixed namespace
172
+ Mml.parse('<mml:math xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mi>x</mml:mi></mml:math>')
173
+
174
+ # No namespace (namespace injected internally)
175
+ Mml.parse("<math><mi>x</mi></math>", namespace_exist: false)
176
+ ----
177
+
178
+ === Serialization
179
+
180
+ [source,ruby]
181
+ ----
182
+ math.to_xml
183
+ # => "<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mi>x</mi></math>"
184
+
185
+ math.to_xml(prefix: true)
186
+ # => "<mml:math xmlns:mml=\"http://www.w3.org/1998/Math/MathML\"><mml:mi>x</mml:mi></mml:math>"
187
+
188
+ math.to_xml(declaration: false)
189
+ # => "<math xmlns=\"...\"><mi>x</mi></math>"
190
+ ----
191
+
192
+ === Round-trip (parse, modify, serialize)
193
+
194
+ [source,ruby]
195
+ ----
196
+ math = Mml.parse('<math xmlns="http://www.w3.org/1998/Math/MathML"><mi>x</mi></math>')
197
+ math.display = "block"
198
+ math.to_xml
199
+ # => "<math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\"><mi>x</mi></math>"
200
+ ----
201
+
202
+ == Element reference
203
+
204
+ === Element types
205
+
206
+ *Token elements*: `mi`, `mn`, `mo`, `ms`, `mtext`, `mspace`, `mglyph`
207
+
208
+ *General layout*: `mrow`, `mfrac`, `msqrt`, `mroot`, `mstyle`, `merror`, `mpadded`, `mphantom`, `mfenced`, `menclose`, `maction`
209
+
210
+ *Script elements*: `msub`, `msup`, `msubsup`, `munder`, `mover`, `munderover`, `mmultiscripts`, `mprescripts`
211
+
212
+ *Table elements*: `mtable`, `mtr`, `mtd`
213
+
214
+ *Row and stack elements*: `mstack`, `msrow`, `mscarries`, `mscarry`, `msline`, `msgroup`, `mlongdiv`
215
+
216
+ *Semantic elements*: `mfraction`, `semantics`
217
+
218
+ *v4 only*: `a` (hyperlink)
219
+
220
+ *Deprecated*: `mlabeledtr`, `none` (classes exist but hidden from CommonAttributes in v4)
221
+
222
+ === Token elements (leaf nodes)
223
+
224
+ Token elements hold text content in the `value` attribute:
225
+
226
+ [source,ruby]
227
+ ----
228
+ Mml::Mi.new(value: "x")
229
+ Mml::Mn.new(value: "42")
230
+ Mml::Mo.new(value: "+")
231
+ Mml::Ms.new(value: "text")
232
+ Mml::Mtext.new(value: "label")
233
+ Mml::Mspace.new(width: "1em")
234
+ Mml::Mglyph.new(alt: "symbol")
235
+ ----
236
+
237
+ === Container elements
238
+
239
+ Container elements hold child elements via `#{tag}_value` collection attributes:
240
+
241
+ [source,ruby]
242
+ ----
243
+ Mml::Mrow.new(
244
+ mi_value: [Mml::Mi.new(value: "x")],
245
+ mo_value: [Mml::Mo.new(value: "+")],
246
+ mn_value: [Mml::Mn.new(value: "1")],
247
+ )
248
+ # => <mrow><mi>x</mi><mo>+</mo><mn>1</mn></mrow>
249
+ ----
250
+
251
+ === Composing expressions
252
+
253
+ Build an expression tree by nesting elements:
254
+
255
+ [source,ruby]
256
+ ----
257
+ Mml::Math.new(
258
+ mfrac_value: [
259
+ Mml::Mfrac.new(
260
+ mi_value: [Mml::Mi.new(value: "a"), Mml::Mi.new(value: "b")],
261
+ ),
262
+ ],
263
+ )
264
+ # => <math><mfrac><mi>a</mi><mi>b</mi></mfrac></math>
265
+ ----
266
+
267
+ === Tables
268
+
269
+ [source,ruby]
270
+ ----
271
+ Mml::Mtable.new(
272
+ mtr_value: [
273
+ Mml::Mtr.new(
274
+ mtd_value: [
275
+ Mml::Mtd.new(mi_value: [Mml::Mi.new(value: "a")]),
276
+ Mml::Mtd.new(mi_value: [Mml::Mi.new(value: "b")]),
277
+ ],
278
+ ),
279
+ ],
280
+ )
281
+ ----
282
+
283
+ === Hyperlinks (MathML 4 only)
284
+
285
+ [source,ruby]
286
+ ----
287
+ Mml::V4::A.new(
288
+ href: "https://example.com",
289
+ hreflang: "en",
290
+ mi_value: [Mml::V4::Mi.new(value: "click")]
291
+ )
292
+ # => <a href="https://example.com" hreflang="en"><mi>click</mi></a>
293
+ ----
294
+
295
+ == Internal architecture
296
+
297
+ === Element class patterns
298
+
299
+ All element classes inherit from `Lutaml::Model::Serializable`:
300
+
301
+ * *Leaf elements*: use `map_content to: :value` for text content
302
+ * *Container elements*: use `mixed_content` for child elements
303
+
304
+ [source,ruby]
305
+ ----
306
+ # Leaf — text content
307
+ class Mi < Lutaml::Model::Serializable
308
+ attribute :value, :string
309
+ xml do
310
+ element "mi"
311
+ map_content to: :value
312
+ end
313
+ end
314
+
315
+ # Container — child elements
316
+ class Mrow < Lutaml::Model::Serializable
317
+ xml do
318
+ element "mrow"
319
+ mixed_content
320
+ end
321
+ end
322
+ ----
323
+
324
+ === CommonAttributes
325
+
326
+ Container elements that accept arbitrary MathML children import `CommonAttributes`,
327
+ which defines `#{tag}_value` collection attributes for all supported child elements.
328
+ The list of classes that receive this mixin is in
329
+ `Configuration::COMMON_ATTRIBUTES_CLASSES`.
330
+
331
+ === Namespace
332
+
333
+ All elements use the MathML namespace URI (`http://www.w3.org/1998/Math/MathML`).
334
+ Three input forms are supported:
335
+
336
+ * Default namespace: `<math xmlns="http://www.w3.org/1998/Math/MathML">`
337
+ * Prefixed: `<mml:math xmlns:mml="http://www.w3.org/1998/Math/MathML">`
338
+ * No namespace: namespace is injected before parsing when `namespace_exist: false`
339
+
340
+ == Configuration
341
+
342
+ [source,ruby]
343
+ ----
344
+ # Switch XML adapter (default: :ox, also supports :nokogiri)
345
+ Mml::Configuration.adapter = :nokogiri
346
+
347
+ # Register custom model replacements
348
+ Mml::Configuration.custom_models = { Mi => MyCustomMi }
349
+ ----
350
+
351
+ == Development
352
+
353
+ [source,bash]
354
+ ----
355
+ rake # Run specs + rubocop
356
+ bundle exec rspec # Run tests
357
+ bundle exec rubocop # Lint
358
+ bin/console # Interactive IRB
359
+ ----
360
+
361
+ == Contributing
362
+
363
+ Bug reports and pull requests are welcome on GitHub at
364
+ https://github.com/plurimath/mml.
365
+
366
+ == Copyright and license
6
367
 
7
- == Copyright and License
368
+ Copyright Ribose Inc.
8
369
 
9
- Copyright (c) 2024 Ribose Inc.
370
+ https://opensource.org/licenses/BSD-2-Clause[BSD-2-Clause License].
@@ -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