chemicalml 0.1.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 (248) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rubocop.yml +20 -0
  4. data/CHANGELOG.md +21 -0
  5. data/CLAUDE.md +198 -0
  6. data/Gemfile +14 -0
  7. data/LICENSE +24 -0
  8. data/NOTICES.adoc +115 -0
  9. data/README.adoc +87 -0
  10. data/Rakefile +8 -0
  11. data/TODO.align/01-cml-base-mixins.md +122 -0
  12. data/TODO.align/02-real-schema-hierarchies.md +88 -0
  13. data/TODO.align/03-configuration.md +50 -0
  14. data/TODO.align/04-parse-entry-points.md +59 -0
  15. data/TODO.align/05-schema-aware-translator.md +33 -0
  16. data/TODO.align/06-no-respond-to.md +44 -0
  17. data/TODO.align/07-canon-xml-comparison.md +43 -0
  18. data/TODO.align/08-expand-fixtures.md +37 -0
  19. data/TODO.align/09-final-spec-lint.md +29 -0
  20. data/TODO.align/10-role-marker-modules.md +54 -0
  21. data/TODO.align/11-dry-configuration.md +63 -0
  22. data/TODO.align/12-wire-class-macro.md +51 -0
  23. data/TODO.align/13-wire-namespace.md +44 -0
  24. data/TODO.align/14-schema24-correctness.md +37 -0
  25. data/TODO.align/15-canon-fixture-roundtrip.md +38 -0
  26. data/TODO.align/16-schema24-convention-coverage.md +33 -0
  27. data/TODO.align/17-final-spec-lint.md +28 -0
  28. data/TODO.align/README.md +27 -0
  29. data/TODO.align/README.round2.md +24 -0
  30. data/TODO.cml-full/01-reference-docs.md +43 -0
  31. data/TODO.cml-full/02-schema-versioned-model.md +70 -0
  32. data/TODO.cml-full/03-fixtures-scrape.md +52 -0
  33. data/TODO.cml-full/04-convention-framework.md +69 -0
  34. data/TODO.cml-full/05-dictionary-layer.md +77 -0
  35. data/TODO.cml-full/06-extend-canonical-translator.md +60 -0
  36. data/TODO.cml-full/07-attribution-notices.md +42 -0
  37. data/TODO.cml-full/08-autoload-only.md +48 -0
  38. data/TODO.cml-full/09-spec-coverage.md +38 -0
  39. data/TODO.cml-full/README.md +23 -0
  40. data/chemicalml.gemspec +35 -0
  41. data/data/dictionaries/_index.yaml +14 -0
  42. data/data/dictionaries/compchem.yaml +481 -0
  43. data/data/dictionaries/unit_non_si.yaml +53 -0
  44. data/data/dictionaries/unit_si.yaml +87 -0
  45. data/data/dictionaries/unit_type.yaml +113 -0
  46. data/lib/chemicalml/cml/array.rb +10 -0
  47. data/lib/chemicalml/cml/atom.rb +10 -0
  48. data/lib/chemicalml/cml/atom_array.rb +10 -0
  49. data/lib/chemicalml/cml/atom_parity.rb +10 -0
  50. data/lib/chemicalml/cml/base/array.rb +36 -0
  51. data/lib/chemicalml/cml/base/atom.rb +38 -0
  52. data/lib/chemicalml/cml/base/atom_array.rb +22 -0
  53. data/lib/chemicalml/cml/base/atom_parity.rb +24 -0
  54. data/lib/chemicalml/cml/base/bond.rb +28 -0
  55. data/lib/chemicalml/cml/base/bond_array.rb +22 -0
  56. data/lib/chemicalml/cml/base/bond_stereo.rb +28 -0
  57. data/lib/chemicalml/cml/base/cml_module.rb +40 -0
  58. data/lib/chemicalml/cml/base/dictionary.rb +32 -0
  59. data/lib/chemicalml/cml/base/dictionary_entry.rb +34 -0
  60. data/lib/chemicalml/cml/base/document.rb +26 -0
  61. data/lib/chemicalml/cml/base/formula.rb +38 -0
  62. data/lib/chemicalml/cml/base/identifier.rb +26 -0
  63. data/lib/chemicalml/cml/base/label.rb +28 -0
  64. data/lib/chemicalml/cml/base/list.rb +36 -0
  65. data/lib/chemicalml/cml/base/matrix.rb +38 -0
  66. data/lib/chemicalml/cml/base/metadata.rb +30 -0
  67. data/lib/chemicalml/cml/base/metadata_list.rb +28 -0
  68. data/lib/chemicalml/cml/base/molecule.rb +40 -0
  69. data/lib/chemicalml/cml/base/name.rb +26 -0
  70. data/lib/chemicalml/cml/base/parameter.rb +34 -0
  71. data/lib/chemicalml/cml/base/parameter_list.rb +28 -0
  72. data/lib/chemicalml/cml/base/product.rb +22 -0
  73. data/lib/chemicalml/cml/base/product_list.rb +22 -0
  74. data/lib/chemicalml/cml/base/property.rb +34 -0
  75. data/lib/chemicalml/cml/base/property_list.rb +28 -0
  76. data/lib/chemicalml/cml/base/reactant.rb +22 -0
  77. data/lib/chemicalml/cml/base/reactant_list.rb +22 -0
  78. data/lib/chemicalml/cml/base/reaction.rb +32 -0
  79. data/lib/chemicalml/cml/base/reaction_list.rb +22 -0
  80. data/lib/chemicalml/cml/base/scalar.rb +32 -0
  81. data/lib/chemicalml/cml/base/substance.rb +30 -0
  82. data/lib/chemicalml/cml/base/unit.rb +38 -0
  83. data/lib/chemicalml/cml/base/unit_list.rb +30 -0
  84. data/lib/chemicalml/cml/base/unit_type.rb +28 -0
  85. data/lib/chemicalml/cml/base/unit_type_list.rb +30 -0
  86. data/lib/chemicalml/cml/base.rb +51 -0
  87. data/lib/chemicalml/cml/bond.rb +10 -0
  88. data/lib/chemicalml/cml/bond_array.rb +10 -0
  89. data/lib/chemicalml/cml/bond_stereo.rb +10 -0
  90. data/lib/chemicalml/cml/cml_module.rb +10 -0
  91. data/lib/chemicalml/cml/dictionary.rb +10 -0
  92. data/lib/chemicalml/cml/dictionary_entry.rb +10 -0
  93. data/lib/chemicalml/cml/document.rb +10 -0
  94. data/lib/chemicalml/cml/elements.rb +58 -0
  95. data/lib/chemicalml/cml/formula.rb +10 -0
  96. data/lib/chemicalml/cml/identifier.rb +10 -0
  97. data/lib/chemicalml/cml/label.rb +10 -0
  98. data/lib/chemicalml/cml/list.rb +10 -0
  99. data/lib/chemicalml/cml/matrix.rb +10 -0
  100. data/lib/chemicalml/cml/metadata.rb +10 -0
  101. data/lib/chemicalml/cml/metadata_list.rb +10 -0
  102. data/lib/chemicalml/cml/molecule.rb +10 -0
  103. data/lib/chemicalml/cml/name.rb +10 -0
  104. data/lib/chemicalml/cml/namespace.rb +17 -0
  105. data/lib/chemicalml/cml/parameter.rb +10 -0
  106. data/lib/chemicalml/cml/parameter_list.rb +10 -0
  107. data/lib/chemicalml/cml/product.rb +10 -0
  108. data/lib/chemicalml/cml/product_list.rb +10 -0
  109. data/lib/chemicalml/cml/property.rb +10 -0
  110. data/lib/chemicalml/cml/property_list.rb +10 -0
  111. data/lib/chemicalml/cml/reactant.rb +10 -0
  112. data/lib/chemicalml/cml/reactant_list.rb +10 -0
  113. data/lib/chemicalml/cml/reaction.rb +10 -0
  114. data/lib/chemicalml/cml/reaction_list.rb +10 -0
  115. data/lib/chemicalml/cml/role/array.rb +13 -0
  116. data/lib/chemicalml/cml/role/atom.rb +13 -0
  117. data/lib/chemicalml/cml/role/atom_array.rb +13 -0
  118. data/lib/chemicalml/cml/role/atom_parity.rb +13 -0
  119. data/lib/chemicalml/cml/role/bond.rb +13 -0
  120. data/lib/chemicalml/cml/role/bond_array.rb +13 -0
  121. data/lib/chemicalml/cml/role/bond_stereo.rb +13 -0
  122. data/lib/chemicalml/cml/role/cml_module.rb +14 -0
  123. data/lib/chemicalml/cml/role/dictionary.rb +13 -0
  124. data/lib/chemicalml/cml/role/dictionary_entry.rb +13 -0
  125. data/lib/chemicalml/cml/role/document.rb +13 -0
  126. data/lib/chemicalml/cml/role/formula.rb +13 -0
  127. data/lib/chemicalml/cml/role/identifier.rb +13 -0
  128. data/lib/chemicalml/cml/role/label.rb +13 -0
  129. data/lib/chemicalml/cml/role/list.rb +13 -0
  130. data/lib/chemicalml/cml/role/matrix.rb +13 -0
  131. data/lib/chemicalml/cml/role/metadata.rb +13 -0
  132. data/lib/chemicalml/cml/role/metadata_list.rb +13 -0
  133. data/lib/chemicalml/cml/role/molecule.rb +13 -0
  134. data/lib/chemicalml/cml/role/name.rb +13 -0
  135. data/lib/chemicalml/cml/role/parameter.rb +13 -0
  136. data/lib/chemicalml/cml/role/parameter_list.rb +13 -0
  137. data/lib/chemicalml/cml/role/product.rb +13 -0
  138. data/lib/chemicalml/cml/role/product_list.rb +13 -0
  139. data/lib/chemicalml/cml/role/property.rb +13 -0
  140. data/lib/chemicalml/cml/role/property_list.rb +13 -0
  141. data/lib/chemicalml/cml/role/reactant.rb +13 -0
  142. data/lib/chemicalml/cml/role/reactant_list.rb +13 -0
  143. data/lib/chemicalml/cml/role/reaction.rb +13 -0
  144. data/lib/chemicalml/cml/role/reaction_list.rb +13 -0
  145. data/lib/chemicalml/cml/role/scalar.rb +13 -0
  146. data/lib/chemicalml/cml/role/substance.rb +13 -0
  147. data/lib/chemicalml/cml/role/unit.rb +13 -0
  148. data/lib/chemicalml/cml/role/unit_list.rb +13 -0
  149. data/lib/chemicalml/cml/role/unit_type.rb +13 -0
  150. data/lib/chemicalml/cml/role/unit_type_list.rb +13 -0
  151. data/lib/chemicalml/cml/role.rb +54 -0
  152. data/lib/chemicalml/cml/scalar.rb +10 -0
  153. data/lib/chemicalml/cml/schema24/configuration.rb +19 -0
  154. data/lib/chemicalml/cml/schema24.rb +40 -0
  155. data/lib/chemicalml/cml/schema3/configuration.rb +17 -0
  156. data/lib/chemicalml/cml/schema3.rb +40 -0
  157. data/lib/chemicalml/cml/substance.rb +10 -0
  158. data/lib/chemicalml/cml/translator/value_translations.rb +265 -0
  159. data/lib/chemicalml/cml/translator.rb +233 -0
  160. data/lib/chemicalml/cml/unit.rb +10 -0
  161. data/lib/chemicalml/cml/unit_list.rb +10 -0
  162. data/lib/chemicalml/cml/unit_type.rb +10 -0
  163. data/lib/chemicalml/cml/unit_type_list.rb +10 -0
  164. data/lib/chemicalml/cml/visitable.rb +65 -0
  165. data/lib/chemicalml/cml/wire_class_macro.rb +45 -0
  166. data/lib/chemicalml/cml.rb +55 -0
  167. data/lib/chemicalml/context_configuration.rb +127 -0
  168. data/lib/chemicalml/convention/base.rb +48 -0
  169. data/lib/chemicalml/convention/compchem/constraints/compchem_module_must_contain_job_list.rb +33 -0
  170. data/lib/chemicalml/convention/compchem/constraints/job_must_contain_initialization.rb +38 -0
  171. data/lib/chemicalml/convention/compchem/constraints.rb +14 -0
  172. data/lib/chemicalml/convention/compchem.rb +28 -0
  173. data/lib/chemicalml/convention/constraint.rb +79 -0
  174. data/lib/chemicalml/convention/dictionary/constraints/entry_ids_unique_within_dictionary.rb +33 -0
  175. data/lib/chemicalml/convention/dictionary/constraints/entry_must_have_id_and_term.rb +34 -0
  176. data/lib/chemicalml/convention/dictionary/constraints.rb +14 -0
  177. data/lib/chemicalml/convention/dictionary.rb +28 -0
  178. data/lib/chemicalml/convention/molecular/constraints/atom_array_must_contain_atoms.rb +30 -0
  179. data/lib/chemicalml/convention/molecular/constraints/atom_ids_unique_within_molecule.rb +42 -0
  180. data/lib/chemicalml/convention/molecular/constraints/bond_must_reference_atoms_in_same_molecule.rb +54 -0
  181. data/lib/chemicalml/convention/molecular/constraints.rb +19 -0
  182. data/lib/chemicalml/convention/molecular.rb +29 -0
  183. data/lib/chemicalml/convention/registry.rb +50 -0
  184. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_symbol_and_unit_type.rb +46 -0
  185. data/lib/chemicalml/convention/unit_dictionary/constraints.rb +12 -0
  186. data/lib/chemicalml/convention/unit_dictionary.rb +27 -0
  187. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_have_id_and_name.rb +34 -0
  188. data/lib/chemicalml/convention/unit_type_dictionary/constraints.rb +12 -0
  189. data/lib/chemicalml/convention/unit_type_dictionary.rb +27 -0
  190. data/lib/chemicalml/convention/violation.rb +36 -0
  191. data/lib/chemicalml/convention.rb +39 -0
  192. data/lib/chemicalml/dictionary/entry.rb +59 -0
  193. data/lib/chemicalml/dictionary/enum.rb +51 -0
  194. data/lib/chemicalml/dictionary/link.rb +32 -0
  195. data/lib/chemicalml/dictionary/loader.rb +60 -0
  196. data/lib/chemicalml/dictionary/model.rb +64 -0
  197. data/lib/chemicalml/dictionary/registry.rb +75 -0
  198. data/lib/chemicalml/dictionary.rb +26 -0
  199. data/lib/chemicalml/errors.rb +7 -0
  200. data/lib/chemicalml/model/atom.rb +40 -0
  201. data/lib/chemicalml/model/bond.rb +33 -0
  202. data/lib/chemicalml/model/cml_array.rb +27 -0
  203. data/lib/chemicalml/model/cml_module.rb +45 -0
  204. data/lib/chemicalml/model/document.rb +29 -0
  205. data/lib/chemicalml/model/formula.rb +34 -0
  206. data/lib/chemicalml/model/identifier.rb +21 -0
  207. data/lib/chemicalml/model/label.rb +21 -0
  208. data/lib/chemicalml/model/matrix.rb +29 -0
  209. data/lib/chemicalml/model/metadata.rb +23 -0
  210. data/lib/chemicalml/model/metadata_list.rb +25 -0
  211. data/lib/chemicalml/model/molecule.rb +38 -0
  212. data/lib/chemicalml/model/name.rb +21 -0
  213. data/lib/chemicalml/model/node.rb +45 -0
  214. data/lib/chemicalml/model/parameter.rb +27 -0
  215. data/lib/chemicalml/model/parameter_list.rb +25 -0
  216. data/lib/chemicalml/model/product.rb +21 -0
  217. data/lib/chemicalml/model/product_list.rb +21 -0
  218. data/lib/chemicalml/model/property.rb +28 -0
  219. data/lib/chemicalml/model/property_list.rb +25 -0
  220. data/lib/chemicalml/model/reactant.rb +21 -0
  221. data/lib/chemicalml/model/reactant_list.rb +21 -0
  222. data/lib/chemicalml/model/reaction.rb +41 -0
  223. data/lib/chemicalml/model/reaction_list.rb +23 -0
  224. data/lib/chemicalml/model/scalar.rb +25 -0
  225. data/lib/chemicalml/model/substance.rb +25 -0
  226. data/lib/chemicalml/model.rb +38 -0
  227. data/lib/chemicalml/schema/definition.rb +41 -0
  228. data/lib/chemicalml/schema/registry.rb +30 -0
  229. data/lib/chemicalml/schema.rb +35 -0
  230. data/lib/chemicalml/version.rb +5 -0
  231. data/lib/chemicalml/versioned_parser.rb +43 -0
  232. data/lib/chemicalml.rb +47 -0
  233. data/lib/tasks/reference_fetch.rake +105 -0
  234. data/lib/tasks/scrape_fixtures.rake +133 -0
  235. data/reference-docs/conventions/compchem.md +141 -0
  236. data/reference-docs/conventions/dictionary.md +119 -0
  237. data/reference-docs/conventions/index.md +56 -0
  238. data/reference-docs/conventions/molecular.md +115 -0
  239. data/reference-docs/conventions/unit-dictionary.md +92 -0
  240. data/reference-docs/conventions/unitType-dictionary.md +51 -0
  241. data/reference-docs/dictionaries/compchem.md +335 -0
  242. data/reference-docs/dictionaries/index.md +52 -0
  243. data/reference-docs/examples/schema24-index.md +50 -0
  244. data/reference-docs/examples/schema3-compchem-index.md +14 -0
  245. data/reference-docs/examples/schema3-molecular-index.md +28 -0
  246. data/reference-docs/schemas/schema24/schema.xsd +10624 -0
  247. data/reference-docs/schemas/schema3/schema.xsd +11459 -0
  248. metadata +306 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 07b63ae96c1ef6187081c9f14ab76722b91f4438516a46c993abadac25f37a8f
4
+ data.tar.gz: 5130f3b5538741239be68d6632eaeecc0175ecb44f7a85cdf88e9414657c1689
5
+ SHA512:
6
+ metadata.gz: 29433a22bbfd290ce7146fefda2d4e68944bda3098902e3d8c6f282d05c7b99ee95de9097685ed55e9ff365799e17eee785bb3b5414dff39076616c65db87189
7
+ data.tar.gz: 300c20a1df5cf4cbad657ce97fc0b26b6aec9f2ecb84ac524ab4f30a209636aeebbf6756637ee0e3fc6dec96685f4334aa8ee90e34a1058078ea2ccba4d2f4a8
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /spec/examples.txt
10
+ /tmp/
11
+ .rspec_status
data/.rubocop.yml ADDED
@@ -0,0 +1,20 @@
1
+ require:
2
+ - rubocop
3
+
4
+ AllCops:
5
+ NewCops: enable
6
+ TargetRubyVersion: 3.3
7
+ Exclude:
8
+ - "pkg/**/*"
9
+ - "vendor/**/*"
10
+ - "coverage/**/*"
11
+ - "reference-docs/**/*"
12
+ - "spec/fixtures/**/*"
13
+
14
+ Metrics/BlockLength:
15
+ Exclude:
16
+ - "spec/**/*"
17
+ - "*.gemspec"
18
+
19
+ Style/Documentation:
20
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ All notable changes to ChemML are documented here.
4
+ This project follows [Semantic Versioning](https://semver.org/).
5
+
6
+ ## [Unreleased]
7
+
8
+ ### Added
9
+ - Initial gem scaffold: autoload tree, version, errors.
10
+ - CML namespace declaration (`Chemicalml::Cml::Namespace`) for the CML
11
+ default XML namespace.
12
+ - CML model classes for the core CML element set:
13
+ `Document`, `Molecule`, `AtomArray`, `Atom`, `BondArray`, `Bond`,
14
+ `Reaction`, `ReactantList`, `Reactant`, `ProductList`, `Product`,
15
+ `ReactionList`, `Name`, `Identifier`.
16
+ - AsciiChem extension namespace (`Chemicalml::Ext::Namespace`) for
17
+ attributes CML doesn't natively cover (lone pairs, radical
18
+ electrons, stereochemistry).
19
+ - Round-trip specs covering canonical CML fragments.
20
+
21
+ [Unreleased]: https://github.com/lutaml/chemicalml/commits/main
data/CLAUDE.md ADDED
@@ -0,0 +1,198 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## What this gem is
6
+
7
+ Chemicalml is a Ruby object model for the Chemical Markup Language (CML),
8
+ the XML standard published at http://www.xml-cml.org/. It is a sibling
9
+ model layer to other chemistry tooling (AsciiChem consumes it via the
10
+ translator adapter) and is built on
11
+ [lutaml-model](https://github.com/lutaml/lutaml-model) — there is no
12
+ hand-rolled XML serialization anywhere in this library.
13
+
14
+ The repo targets **two schema versions** simultaneously:
15
+ `reference-docs/schemas/schema24/schema.xsd` and
16
+ `reference-docs/schemas/schema3/schema.xsd`. Both XSDs ship in-tree;
17
+ they are the source of truth for element/attribute names.
18
+
19
+ ## Commands
20
+
21
+ ```sh
22
+ bundle install # one-time
23
+ bundle exec rspec # full suite
24
+ bundle exec rspec spec/chemicalml/cml/molecule_spec.rb # one file
25
+ bundle exec rspec -e "round-trips" # by name match
26
+ bundle exec rake # alias for rspec
27
+ bundle exec rubocop # lint
28
+ bundle exec rubocop -A # autocorrect
29
+ ```
30
+
31
+ `spec/examples.txt` and `Gemfile.lock` are gitignored — `spec/examples.txt`
32
+ is regenerated by RSpec on every run, and `Gemfile.lock` is development-only.
33
+
34
+ ## Architecture (big picture)
35
+
36
+ There are **five namespaces** under `Chemicalml`, deliberately kept
37
+ independent:
38
+
39
+ ```
40
+ Chemicalml
41
+ ├── parse(xml, schema: :schema3) # top-level entry point
42
+ ├── Model # canonical, format-agnostic chemistry objects
43
+ ├── Cml # CML XML wire-format classes
44
+ │ ├── Base::* # shared attribute+mapping mixins
45
+ │ ├── Schema3::* # Schema 3 wire classes
46
+ │ ├── Schema24::* # Schema 2.4 wire classes
47
+ │ └── Translator # the only adapter between Model and Cml
48
+ ├── Schema # version registry
49
+ ├── Convention # 5 conventions + constraint framework
50
+ └── Dictionary # dictionary model + YAML store
51
+ ```
52
+
53
+ ### Schema-versioned wire classes (mirrors `mml` V3/V4 pattern)
54
+
55
+ Both Schema 3 and Schema 2.4 have **real, distinct** class hierarchies
56
+ at `Chemicalml::Cml::Schema3::*` and `Chemicalml::Cml::Schema24::*`.
57
+ They share attribute and XML-mapping declarations via
58
+ `Chemicalml::Cml::Base::*` mixins (each Base module declares
59
+ attributes and an `xml do ... end` block inside a `self.included`
60
+ hook). Schema-specific deviations would live in additional
61
+ `Base::Schema3Only::*` / `Base::Schema24Only::*` modules included
62
+ only by the relevant version's classes — Open/Closed Principle.
63
+
64
+ Each schema owns a `Configuration` sub-module that registers its
65
+ wire classes with `Lutaml::Model::GlobalContext` under a context id
66
+ (`:chemicalml_schema3`, `:chemicalml_schema24`). Each wire class
67
+ declares `def self.lutaml_default_register` returning its context id,
68
+ which lets `from_xml(xml, register: ctx)` correctly resolve child
69
+ element types.
70
+
71
+ The top-level `Chemicalml::Cml::*` constants (e.g. `Cml::Atom`,
72
+ `Cml::Molecule`) are **backward-compatible aliases** pointing at the
73
+ Schema3 versions. New code should prefer the versioned namespaces
74
+ directly.
75
+
76
+ ### Top-level entry points
77
+
78
+ ```ruby
79
+ Chemicalml.parse(xml) # → Schema3::Document (default)
80
+ Chemicalml.parse(xml, schema: :schema24) # → Schema24::Document
81
+ Chemicalml.serialize(document) # → XML string
82
+ Chemicalml::Cml::Schema3.parse(xml) # version-specific
83
+ Chemicalml::Cml::Schema24.parse(xml) # version-specific
84
+ ```
85
+
86
+ ### The canonical model (`Chemicalml::Model::*`)
87
+
88
+ Plain Ruby `Node` subclasses. **No XML, no JSON, no YAML**. Format-agnostic.
89
+ Every adapter speaks this model; adapters never talk to each other directly.
90
+ `Node` provides `accept(visitor)`, `value_attributes`, `children`, and
91
+ class-level `short_name` so visitors written against the canonical model
92
+ work across every adapter.
93
+
94
+ ### The CML wire layer (`Chemicalml::Cml::*`)
95
+
96
+ Every CML element is a `Lutaml::Model::Serializable` subclass that
97
+ includes three mixins: a `Base::*` module (shared declarations +
98
+ xml mapping + `namespace Chemicalml::Cml::Namespace`), a
99
+ `Role::*` marker module (type identity used by constraints and the
100
+ translator — independent of which schema version's class it is), and
101
+ `Cml::Visitable` (uniform interface for the constraint walker —
102
+ `wire_children`, `node_id`, `element_name`).
103
+
104
+ Wire classes are generated in-place by each schema module from the
105
+ shared `Chemicalml::Cml::Elements::ALL` table — no 70+ boilerplate
106
+ files. Adding a new CML element = adding one entry to `Elements::ALL`,
107
+ one `Base::*` mixin, and one `Role::*` module. Both `Schema3::*` and
108
+ `Schema24::*` pick up the new element automatically (unless it's
109
+ listed in `Elements::SCHEMA3_ONLY`).
110
+
111
+ The framework handles (de)serialization; **never** add `def to_xml` /
112
+ `def from_xml` /
113
+ `def to_h` to these classes.
114
+
115
+ ### The translator (`Chemicalml::Cml::Translator`)
116
+
117
+ Pure transformation. `to_canonical(wire_doc)` accepts either Schema3
118
+ or Schema24 wire documents (dispatches via `is_a?`).
119
+ `from_canonical(model_doc, schema: :schema3)` produces the requested
120
+ schema's wire format. It is the **only** place that imports both
121
+ `Chemicalml::Model` and `Chemicalml::Cml`. Adding a new CML element
122
+ means updating translator rules — the two namespaces stay decoupled.
123
+
124
+ ### Schema versions
125
+
126
+ `Chemicalml::Schema::Registry` is the registry for schema versions
127
+ (`schema24`, `schema3`). The schema XSDs are **archival source** —
128
+ they must never be deleted, edited, or regenerated from code.
129
+
130
+ ### Conventions
131
+
132
+ `Chemicalml::Convention` is a registry of named constraint sets
133
+ (`molecular`, `compchem`, `dictionary`, `unit-dictionary`,
134
+ `unitType-dictionary`). Each convention owns:
135
+
136
+ - a namespace URI (e.g. `http://www.xml-cml.org/convention/molecular`)
137
+ - a set of constraint classes registered against it
138
+
139
+ Constraints are **registered**, not switch-statement'd — adding a new
140
+ rule = registering a new constraint class, not editing existing
141
+ renderer code (Open/Closed Principle).
142
+
143
+ The constraint walker uses `Cml::Visitable#wire_children` to traverse
144
+ the tree — no `respond_to?` duck typing.
145
+
146
+ ### Dictionaries
147
+
148
+ `Chemicalml::Dictionary` is the model; entries have `id`, `term`,
149
+ `definition`, `description`, `dataType`, `unitType`, `units`, optional
150
+ `enum` (open or closed set), `links`, and `source_code`. Built-in
151
+ dictionaries ship as YAML under `data/dictionaries/` and are loaded
152
+ into a registry at startup.
153
+
154
+ ## Coding standards (project-specific)
155
+
156
+ These are non-negotiable and apply in addition to the user's global rules:
157
+
158
+ 1. **No `require_relative` and no intra-library `require`** anywhere in
159
+ `lib/`. Use Ruby `autoload`, declared in the immediate parent
160
+ namespace's file (create the file if it doesn't exist). Example:
161
+ `lib/chemicalml/foo.rb` declares `autoload :Bar, "chemicalml/foo/bar"`.
162
+
163
+ 2. **Never hand-roll serialization.** No `def to_h`, `def from_h`,
164
+ `def to_xml`, `def from_xml`, `def to_json`, `def from_json` on
165
+ model classes. Wire names live in `mapping` blocks; round-trips go
166
+ through lutaml-model.
167
+
168
+ 3. **Never use `double` in specs.** Use real model instances or
169
+ `Struct.new` for plain data.
170
+
171
+ 4. **Never use `send` to call private methods, `instance_variable_set`/`get`,
172
+ or `respond_to?` for type checks.** Use `is_a?` or design the type
173
+ hierarchy so the check isn't needed.
174
+
175
+ 5. **One concern, one place (MECE).** Adding a CML element = adding one
176
+ model class + one Cml class + one translator rule + specs. Don't
177
+ entangle them.
178
+
179
+ ## Reference docs (in-tree)
180
+
181
+ `reference-docs/` holds archival source material downloaded from
182
+ xml-cml.org:
183
+
184
+ - `schemas/schema24/schema.xsd`, `schemas/schema3/schema.xsd` — the XSDs
185
+ - `conventions/` — convention specs (molecular, compchem, dictionary, etc.)
186
+ - `cmllite.html` — the CMLLite paper
187
+
188
+ These files are **source material, not derived output**. Never delete
189
+ or edit them. Treat them as canonical references when designing model
190
+ classes.
191
+
192
+ ## Fixtures
193
+
194
+ `spec/fixtures/` holds CML example documents scraped from
195
+ xml-cml.org/examples/, organized by schema version and category
196
+ (`schema3/molecular/`, `schema3/compchem/`, `schema24/`). The scrape
197
+ is one-shot via `bundle exec rake fixtures:scrape` — re-run only when
198
+ the upstream examples change.
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ group :development do
8
+ gem "canon"
9
+ gem "nokogiri"
10
+ gem "rake", "~> 13.2"
11
+ gem "rspec", "~> 3.13"
12
+ gem "rubocop", "~> 1.66", require: false
13
+ gem "simplecov", "~> 0.22", require: false
14
+ end
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2026, Ribose Inc.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/NOTICES.adoc ADDED
@@ -0,0 +1,115 @@
1
+ = Chemicalml — Third-Party Notices
2
+
3
+ Chemicalml is distributed under the BSD-2-Clause license. See
4
+ link:LICENSE[LICENSE] for the full text.
5
+
6
+ == Acknowledgements
7
+
8
+ Chemicalml builds directly on the Chemical Markup Language (CML), a
9
+ long-running community project hosted at http://www.xml-cml.org/. The
10
+ CML schema, conventions, dictionaries, and example documents are the
11
+ work of many people over more than two decades. We acknowledge with
12
+ gratitude the people who built CML and continue to maintain it.
13
+
14
+ === Editors and authors of the convention specifications
15
+
16
+ * **Sam Adams** (University of Cambridge) — editor of the molecular,
17
+ compchem, dictionary, unit-dictionary, and unitType-dictionary
18
+ convention specifications
19
+ * **Joe Townsend** (University of Cambridge) — editor of the molecular,
20
+ compchem, dictionary, unit-dictionary, and unitType-dictionary
21
+ convention specifications
22
+ * **Weerapong Phadungsukanan** (University of Cambridge) — editor of
23
+ the CompChem convention; author of the CompChem dictionary
24
+ * **Jens Thomas** (STFC Daresbury Laboratory) — editor of the CompChem
25
+ convention
26
+ * **Peter Murray-Rust** (University of Cambridge) — originator of CML,
27
+ author of the CompChem dictionary, driving force behind CML's
28
+ evolution
29
+ * **Alex Wade**, **Nick England**, **Daniel Lowe**, **Hannah Barjat**,
30
+ **Egon Willighagen** — contributors to the convention specifications
31
+ and dictionaries
32
+
33
+ === The xml-cml.org project
34
+
35
+ The http://www.xml-cml.org/[xml-cml.org] website is the canonical home
36
+ of CML — schema files, convention specifications, dictionaries, example
37
+ documents, and validation tooling. We are grateful to the entire CML
38
+ community for keeping the project alive and openly licensed.
39
+
40
+ == Third-party content redistributed in this repository
41
+
42
+ The following files derive from upstream sources at xml-cml.org and are
43
+ licensed under the
44
+ http://creativecommons.org/licenses/by/3.0/[Creative Commons Attribution
45
+ 3.0 Unported License (CC-BY-3.0)]:
46
+
47
+ === CML schema XSDs
48
+
49
+ * `reference-docs/schemas/schema3/schema.xsd` — the official CML
50
+ Schema 3 XSD
51
+ * `reference-docs/schemas/schema24/schema.xsd` — the official CML
52
+ Schema 2.4 XSD
53
+
54
+ === Convention specifications (HTML originals archived as Markdown)
55
+
56
+ * `reference-docs/conventions/index.md`
57
+ * `reference-docs/conventions/molecular.md`
58
+ * `reference-docs/conventions/compchem.md`
59
+ * `reference-docs/conventions/dictionary.md`
60
+ * `reference-docs/conventions/unit-dictionary.md`
61
+ * `reference-docs/conventions/unitType-dictionary.md`
62
+
63
+ === Dictionary source content
64
+
65
+ * `reference-docs/dictionaries/index.md` — landing page
66
+ * `reference-docs/dictionaries/compchem.md` — CompChem dictionary
67
+ source content
68
+
69
+ The YAML representation of the CompChem dictionary at
70
+ `data/dictionaries/compchem.yaml` is derived from the upstream XML at
71
+ http://www.xml-cml.org/dictionary/compchem/[xml-cml.org/dictionary/compchem/].
72
+
73
+ === Example CML documents
74
+
75
+ Example CML files under `spec/fixtures/` are derived from
76
+ http://www.xml-cml.org/examples/[xml-cml.org/examples/]. Each
77
+ subdirectory corresponds to an upstream category:
78
+
79
+ * `spec/fixtures/schema3/molecular/` — Schema 3 molecular examples
80
+ * `spec/fixtures/schema3/compchem/` — Schema 3 compchem examples
81
+ * `spec/fixtures/schema24/` — Schema 2.4 examples
82
+
83
+ Some seed fixtures in this directory are synthetic (created for this
84
+ gem) and are not subject to the upstream license; the BSD-2-Clause
85
+ license of this gem applies to those.
86
+
87
+ == Creative Commons Attribution 3.0 Unported License
88
+
89
+ For the upstream content listed above:
90
+
91
+ ----
92
+ THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS
93
+ CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS
94
+ PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE
95
+ WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS
96
+ PROHIBITED.
97
+
98
+ BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND
99
+ AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS
100
+ LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU
101
+ THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH
102
+ TERMS AND CONDITIONS.
103
+
104
+ Full license text: http://creativecommons.org/licenses/by/3.0/legalcode
105
+ ----
106
+
107
+ == Attribution for derived works
108
+
109
+ The Ruby wire-format classes in `lib/chemicalml/cml/`, the canonical
110
+ model in `lib/chemicalml/model/`, the convention framework in
111
+ `lib/chemicalml/convention/`, and the dictionary layer in
112
+ `lib/chemicalml/dictionary/` are original work of the Chemicalml gem
113
+ authors, distributed under the BSD-2-Clause license of this gem. They
114
+ implement the semantics described by the upstream CML specifications
115
+ but contain no upstream source code or text.
data/README.adoc ADDED
@@ -0,0 +1,87 @@
1
+ = ChemML
2
+
3
+ ChemML provides a https://www.xml-cml.org/[Chemical Markup Language (CML)]
4
+ object model for Ruby, built on
5
+ {lutaml-model}[https://github.com/lutaml/lutaml-model].
6
+
7
+ == Purpose
8
+
9
+ CML is the established XML standard for chemistry. Existing Ruby
10
+ libraries either don't exist or use hand-rolled XML manipulation that
11
+ drifts from the CML schema. ChemML gives CML a proper model layer
12
+ with declarative attribute/mapping definitions and framework-backed
13
+ serialization.
14
+
15
+ == Install
16
+
17
+ Add to your Gemfile:
18
+
19
+ [source,ruby]
20
+ ----
21
+ gem "chemicalml"
22
+ ----
23
+
24
+ Or install directly:
25
+
26
+ [source,sh]
27
+ ----
28
+ $ gem install chemicalml
29
+ ----
30
+
31
+ == Usage
32
+
33
+ Parse a CML document:
34
+
35
+ [source,ruby]
36
+ ----
37
+ require "chemicalml"
38
+
39
+ doc = Chemicalml::Cml::Document.from_xml(File.read("ethanol.cml"))
40
+ doc.molecules.first.atoms.first.element_type # => "C"
41
+ ----
42
+
43
+ Build a document programmatically:
44
+
45
+ [source,ruby]
46
+ ----
47
+ atom = Chemicalml::Cml::Atom.new(id: "a1", element_type: "C")
48
+ mol = Chemicalml::Cml::Molecule.new(id: "m1", atoms: [atom])
49
+ doc = Chemicalml::Cml::Document.new(molecules: [mol])
50
+
51
+ doc.to_xml # => well-formed CML XML
52
+ ----
53
+
54
+ == Design
55
+
56
+ Each CML element is a `Lutaml::Model::Serializable` subclass with
57
+ declared attributes and an XML mapping block. Serialization goes
58
+ through lutaml-model — no hand-rolled XML.
59
+
60
+ The library is designed as a sibling model layer to other chemistry
61
+ tooling. AsciiChem (https://www.asciichem.org) uses ChemML for CML
62
+ round-trip support via a translator adapter.
63
+
64
+ Both Schema 2.4 and Schema 3 are supported via
65
+ `Chemicalml::Cml::Schema3` and `Chemicalml::Cml::Schema24` namespaces
66
+ (see `lib/chemicalml/schema.rb`).
67
+
68
+ The CML conventions (molecular, compchem, dictionary, unit-dictionary,
69
+ unitType-dictionary) are implemented as registered constraint sets in
70
+ `Chemicalml::Convention`. Built-in dictionaries ship as YAML under
71
+ `data/dictionaries/`.
72
+
73
+ == Acknowledgements
74
+
75
+ CML is the work of the http://www.xml-cml.org/[xml-cml.org] community,
76
+ edited by Sam Adams, Joe Townsend, Weerapong Phadungsukanan, and Jens
77
+ Thomas, with originator Peter Murray-Rust and contributors Alex Wade,
78
+ Nick England, Daniel Lowe, Hannah Barjat, and Egon Willighagen. The
79
+ schema, conventions, dictionaries, and examples are licensed by
80
+ xml-cml.org under
81
+ http://creativecommons.org/licenses/by/3.0/[CC-BY-3.0]. See
82
+ link:NOTICES.adoc[NOTICES.adoc] for full attribution of upstream
83
+ content redistributed in this gem.
84
+
85
+ == License
86
+
87
+ BSD-2-Clause. See link:LICENSE[LICENSE].
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,122 @@
1
+ # 01 — Cml::Base shared mixins
2
+
3
+ **Status:** complete
4
+ **Depends on:** —
5
+
6
+ ## Goal
7
+
8
+ Extract every CML element's `attribute` + `xml do ... end` declarations
9
+ into a reusable mixin under `Chemicalml::Cml::Base::*`. Each schema
10
+ version's wire class then includes the relevant Base module and adds
11
+ only its version-specific deviations.
12
+
13
+ This mirrors `mml`'s `lib/mml/base/` directory: shared declarations
14
+ live in modules, version namespaces consume them via `include`.
15
+
16
+ ## Why
17
+
18
+ Without this factoring:
19
+
20
+ - Adding a new schema version means duplicating every class body.
21
+ - Schema-specific deviations touch the shared class — violates OCP.
22
+ - Constraint code can't reason about a uniform interface.
23
+
24
+ With Base mixins:
25
+
26
+ - Adding Schema5 later = new namespace that includes the same Base
27
+ modules plus its own additions.
28
+ - Schema24-specific behavior is added in `Base::Schema24Only::*`
29
+ modules, included only by Schema24 classes.
30
+
31
+ ## Deliverables
32
+
33
+ - [ ] `lib/chemicalml/cml/base.rb` — namespace file with autoloads
34
+ - [ ] `lib/chemicalml/cml/base/atom.rb` — `Base::Atom` mixin
35
+ - [ ] `lib/chemicalml/cml/base/atom_array.rb`
36
+ - [ ] `lib/chemicalml/cml/base/bond.rb`
37
+ - [ ] `lib/chemicalml/cml/base/bond_array.rb`
38
+ - [ ] `lib/chemicalml/cml/base/document.rb`
39
+ - [ ] `lib/chemicalml/cml/base/formula.rb`
40
+ - [ ] `lib/chemicalml/cml/base/identifier.rb`
41
+ - [ ] `lib/chemicalml/cml/base/label.rb`
42
+ - [ ] `lib/chemicalml/cml/base/list.rb`
43
+ - [ ] `lib/chemicalml/cml/base/matrix.rb`
44
+ - [ ] `lib/chemicalml/cml/base/metadata.rb`
45
+ - [ ] `lib/chemicalml/cml/base/metadata_list.rb`
46
+ - [ ] `lib/chemicalml/cml/base/module.rb`
47
+ - [ ] `lib/chemicalml/cml/base/molecule.rb`
48
+ - [ ] `lib/chemicalml/cml/base/name.rb`
49
+ - [ ] `lib/chemicalml/cml/base/parameter.rb`
50
+ - [ ] `lib/chemicalml/cml/base/parameter_list.rb`
51
+ - [ ] `lib/chemicalml/cml/base/product.rb`
52
+ - [ ] `lib/chemicalml/cml/base/product_list.rb`
53
+ - [ ] `lib/chemicalml/cml/base/property.rb`
54
+ - [ ] `lib/chemicalml/cml/base/property_list.rb`
55
+ - [ ] `lib/chemicalml/cml/base/reaction.rb`
56
+ - [ ] `lib/chemicalml/cml/base/reaction_list.rb`
57
+ - [ ] `lib/chemicalml/cml/base/reactant.rb`
58
+ - [ ] `lib/chemicalml/cml/base/reactant_list.rb`
59
+ - [ ] `lib/chemicalml/cml/base/scalar.rb`
60
+ - [ ] `lib/chemicalml/cml/base/array.rb`
61
+ - [ ] `lib/chemicalml/cml/base/substance.rb`
62
+ - [ ] `lib/chemicalml/cml/base/atom_parity.rb`
63
+ - [ ] `lib/chemicalml/cml/base/bond_stereo.rb`
64
+ - [ ] `lib/chemicalml/cml/base/dictionary.rb`
65
+ - [ ] `lib/chemicalml/cml/base/dictionary_entry.rb`
66
+ - [ ] `lib/chemicalml/cml/base/unit.rb`
67
+ - [ ] `lib/chemicalml/cml/base/unit_list.rb`
68
+ - [ ] `lib/chemicalml/cml/base/unit_type.rb`
69
+ - [ ] `lib/chemicalml/cml/base/unit_type_list.rb`
70
+
71
+ ## Pattern
72
+
73
+ Each Base module follows the same shape:
74
+
75
+ ```ruby
76
+ # lib/chemicalml/cml/base/atom.rb
77
+ module Chemicalml
78
+ module Cml
79
+ module Base
80
+ module Atom
81
+ def self.included(klass)
82
+ klass.class_eval do
83
+ attribute :id, :string
84
+ attribute :element_type, :string
85
+ # ...
86
+
87
+ xml do
88
+ root "atom"
89
+ map_attribute "id", to: :id
90
+ map_attribute "elementType", to: :element_type
91
+ # ...
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ ```
100
+
101
+ The schema-versioned wire class is then a thin shell:
102
+
103
+ ```ruby
104
+ # lib/chemicalml/cml/schema3/atom.rb
105
+ module Chemicalml
106
+ module Cml
107
+ module Schema3
108
+ class Atom < Lutaml::Model::Serializable
109
+ include Base::Atom
110
+ end
111
+ end
112
+ end
113
+ end
114
+ ```
115
+
116
+ ## Acceptance
117
+
118
+ - All 36 Base mixin files exist, one per CML element.
119
+ - Existing `Chemicalml::Cml::*` classes still load (they will be
120
+ refactored in task 02 to include the Base modules).
121
+ - No `require_relative` — Base autoloads declared in
122
+ `lib/chemicalml/cml/base.rb`.