chemicalml 0.2.0 → 0.3.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 (450) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +7 -1
  3. data/.rubocop.yml +23 -0
  4. data/CLAUDE.md +95 -40
  5. data/README.adoc +154 -26
  6. data/TODO.align/36-molecular-convention-coverage.md +46 -0
  7. data/TODO.align/37-compchem-convention-coverage.md +55 -0
  8. data/TODO.align/38-dictionary-convention-coverage.md +34 -0
  9. data/TODO.align/39-unit-dictionary-convention-coverage.md +33 -0
  10. data/TODO.align/40-unit-type-dictionary-convention-coverage.md +28 -0
  11. data/TODO.align/41-expand-canonical-model-layer.md +43 -0
  12. data/TODO.align/42-translator-wire-new-models.md +31 -0
  13. data/TODO.align/43-dictionary-model-no-to-h.md +39 -0
  14. data/TODO.align/44-schema3-only-and-schema24-module.md +44 -0
  15. data/TODO.align/45-documentation-and-final-verification.md +23 -0
  16. data/TODO.align/46-lattice-vector-content.md +24 -0
  17. data/TODO.align/47-molecule-crystal-spectra-property-lists.md +23 -0
  18. data/TODO.align/48-model-list-and-module-lists.md +21 -0
  19. data/TODO.align/49-more-chemistry-models.md +34 -0
  20. data/TODO.align/50-validation-report.md +29 -0
  21. data/TODO.align/51-convention-auto-detection.md +30 -0
  22. data/TODO.align/52-schema24-legacy-elements.md +28 -0
  23. data/TODO.align/53-round-trip-integration-specs.md +30 -0
  24. data/TODO.align/54-document-versioned-parser-shim.md +38 -0
  25. data/TODO.align/55-molecule-missing-attributes.md +20 -0
  26. data/TODO.align/56-geometry-models.md +24 -0
  27. data/TODO.align/57-compchem-models.md +26 -0
  28. data/TODO.align/58-remaining-models.md +24 -0
  29. data/TODO.align/59-wire-new-models-translator.md +18 -0
  30. data/TODO.align/60-docs-and-final-verification.md +19 -0
  31. data/TODO.align/61-parallel-array-attrs.md +54 -0
  32. data/TODO.align/62-per-element-attribute-gaps.md +49 -0
  33. data/TODO.align/63-universal-children-mixin.md +42 -0
  34. data/TODO.align/64-per-element-child-gaps.md +81 -0
  35. data/TODO.align/65-simple-unit-convention.md +40 -0
  36. data/TODO.align/66-schema24-legacy-children.md +32 -0
  37. data/TODO.align/67-final-verification.md +32 -0
  38. data/TODO.align/68-detection-coverage.md +33 -0
  39. data/TODO.align/69-json-yaml-roundtrip.md +27 -0
  40. data/TODO.align/70-iterative-walker.md +40 -0
  41. data/TODO.align/71-violation-value.md +25 -0
  42. data/TODO.align/72-close-child-gaps.md +66 -0
  43. data/TODO.align/73-schema24-parse-limitation.md +64 -0
  44. data/TODO.align/74-final-verification-round18.md +15 -0
  45. data/TODO.align/75-key-value-mappings.md +43 -0
  46. data/TODO.align/76-schema24-workaround.md +57 -0
  47. data/TODO.align/77-convention-coverage-matrix.md +31 -0
  48. data/TODO.align/78-fixture-audit.md +23 -0
  49. data/TODO.align/79-final-verification-round19.md +15 -0
  50. data/TODO.align/80-xsd-enum-constants.md +35 -0
  51. data/TODO.align/81-enum-validation-constraints.md +29 -0
  52. data/TODO.align/82-reference-resolver.md +33 -0
  53. data/TODO.align/83-toml-roundtrip.md +20 -0
  54. data/TODO.align/84-final-verification-round21.md +16 -0
  55. data/TODO.align/85-bond-distinct-atoms.md +25 -0
  56. data/TODO.align/86-references-should-resolve.md +20 -0
  57. data/TODO.align/87-cli-utility.md +26 -0
  58. data/TODO.align/88-diverse-fixtures.md +26 -0
  59. data/TODO.align/89-final-verification-round22.md +15 -0
  60. data/TODO.align/90-bond-stereo-completeness.md +31 -0
  61. data/TODO.align/91-atomparity-distinct.md +21 -0
  62. data/TODO.align/92-unit-dictionary-completeness.md +28 -0
  63. data/TODO.align/93-yard-docs.md +23 -0
  64. data/TODO.align/94-final-verification-round23.md +15 -0
  65. data/TODO.align/README.round17.md +28 -0
  66. data/TODO.align/README.round18.md +37 -0
  67. data/TODO.align/README.round19.md +40 -0
  68. data/TODO.align/README.round20.md +49 -0
  69. data/TODO.align/README.round21.md +35 -0
  70. data/TODO.align/README.round22.md +48 -0
  71. data/TODO.align/README.round23.md +46 -0
  72. data/TODO.align/README.round24.md +53 -0
  73. data/TODO.align/README.round25.md +66 -0
  74. data/TODO.align/README.round26.md +55 -0
  75. data/TODO.align/README.round27.md +51 -0
  76. data/TODO.align/README.round28.md +61 -0
  77. data/TODO.align/README.round29.md +64 -0
  78. data/TODO.align/README.round30.md +50 -0
  79. data/TODO.align/README.round31.md +54 -0
  80. data/TODO.align/README.round32.md +67 -0
  81. data/TODO.align/README.round33.md +62 -0
  82. data/TODO.align/README.round34.md +70 -0
  83. data/docs/constraints.md +161 -0
  84. data/docs/generate_constraint_docs.rb +44 -0
  85. data/exe/chemicalml +6 -0
  86. data/lib/chemicalml/cli/base.rb +68 -0
  87. data/lib/chemicalml/cli/command.rb +43 -0
  88. data/lib/chemicalml/cli/constraints_command.rb +27 -0
  89. data/lib/chemicalml/cli/conventions_command.rb +14 -0
  90. data/lib/chemicalml/cli/dictionaries_command.rb +14 -0
  91. data/lib/chemicalml/cli/elements_command.rb +17 -0
  92. data/lib/chemicalml/cli/enums_command.rb +17 -0
  93. data/lib/chemicalml/cli/info_command.rb +77 -0
  94. data/lib/chemicalml/cli/inspect_command.rb +39 -0
  95. data/lib/chemicalml/cli/validate_command.rb +54 -0
  96. data/lib/chemicalml/cli.rb +43 -0
  97. data/lib/chemicalml/cml/base/abundance.rb +21 -5
  98. data/lib/chemicalml/cml/base/action.rb +44 -6
  99. data/lib/chemicalml/cml/base/action_list.rb +44 -8
  100. data/lib/chemicalml/cml/base/alternative.rb +37 -0
  101. data/lib/chemicalml/cml/base/amount.rb +15 -5
  102. data/lib/chemicalml/cml/base/angle.rb +34 -7
  103. data/lib/chemicalml/cml/base/annotation.rb +38 -0
  104. data/lib/chemicalml/cml/base/any_cml.rb +12 -5
  105. data/lib/chemicalml/cml/base/appinfo.rb +40 -0
  106. data/lib/chemicalml/cml/base/arg.rb +80 -0
  107. data/lib/chemicalml/cml/base/array.rb +51 -8
  108. data/lib/chemicalml/cml/base/array_list.rb +15 -5
  109. data/lib/chemicalml/cml/base/atom.rb +93 -19
  110. data/lib/chemicalml/cml/base/atom_array.rb +77 -2
  111. data/lib/chemicalml/cml/base/atom_parity.rb +18 -2
  112. data/lib/chemicalml/cml/base/atom_set.rb +15 -5
  113. data/lib/chemicalml/cml/base/atom_type.rb +27 -7
  114. data/lib/chemicalml/cml/base/atom_type_list.rb +23 -7
  115. data/lib/chemicalml/cml/base/atomic_basis_function.rb +35 -11
  116. data/lib/chemicalml/cml/base/band.rb +22 -7
  117. data/lib/chemicalml/cml/base/band_list.rb +15 -7
  118. data/lib/chemicalml/cml/base/basis_set.rb +26 -7
  119. data/lib/chemicalml/cml/base/bond.rb +39 -7
  120. data/lib/chemicalml/cml/base/bond_array.rb +41 -2
  121. data/lib/chemicalml/cml/base/bond_set.rb +15 -5
  122. data/lib/chemicalml/cml/base/bond_stereo.rb +25 -4
  123. data/lib/chemicalml/cml/base/bond_type.rb +24 -7
  124. data/lib/chemicalml/cml/base/bond_type_list.rb +23 -7
  125. data/lib/chemicalml/cml/base/cell_parameter.rb +30 -8
  126. data/lib/chemicalml/cml/base/cml_module.rb +36 -11
  127. data/lib/chemicalml/cml/base/common_children.rb +35 -0
  128. data/lib/chemicalml/cml/base/complex_object.rb +37 -0
  129. data/lib/chemicalml/cml/base/condition_list.rb +28 -8
  130. data/lib/chemicalml/cml/base/crystal.rb +27 -9
  131. data/lib/chemicalml/cml/base/definition.rb +12 -5
  132. data/lib/chemicalml/cml/base/description.rb +15 -5
  133. data/lib/chemicalml/cml/base/dictionary.rb +30 -7
  134. data/lib/chemicalml/cml/base/dictionary_entry.rb +67 -8
  135. data/lib/chemicalml/cml/base/dimension.rb +31 -7
  136. data/lib/chemicalml/cml/base/document.rb +18 -8
  137. data/lib/chemicalml/cml/base/documentation.rb +12 -5
  138. data/lib/chemicalml/cml/base/eigen.rb +33 -7
  139. data/lib/chemicalml/cml/base/electron.rb +29 -9
  140. data/lib/chemicalml/cml/base/enumeration.rb +41 -0
  141. data/lib/chemicalml/cml/base/expression.rb +47 -0
  142. data/lib/chemicalml/cml/base/float.rb +52 -0
  143. data/lib/chemicalml/cml/base/float_array.rb +55 -0
  144. data/lib/chemicalml/cml/base/formula.rb +31 -10
  145. data/lib/chemicalml/cml/base/fragment.rb +33 -9
  146. data/lib/chemicalml/cml/base/fragment_list.rb +26 -7
  147. data/lib/chemicalml/cml/base/gradient.rb +19 -6
  148. data/lib/chemicalml/cml/base/identifier.rb +22 -4
  149. data/lib/chemicalml/cml/base/integer.rb +52 -0
  150. data/lib/chemicalml/cml/base/integer_array.rb +55 -0
  151. data/lib/chemicalml/cml/base/isotope.rb +29 -8
  152. data/lib/chemicalml/cml/base/isotope_list.rb +18 -7
  153. data/lib/chemicalml/cml/base/join.rb +28 -6
  154. data/lib/chemicalml/cml/base/kpoint.rb +17 -6
  155. data/lib/chemicalml/cml/base/kpoint_list.rb +15 -7
  156. data/lib/chemicalml/cml/base/label.rb +15 -5
  157. data/lib/chemicalml/cml/base/lattice.rb +36 -9
  158. data/lib/chemicalml/cml/base/lattice_vector.rb +17 -6
  159. data/lib/chemicalml/cml/base/length.rb +31 -7
  160. data/lib/chemicalml/cml/base/line3.rb +21 -5
  161. data/lib/chemicalml/cml/base/link.rb +51 -10
  162. data/lib/chemicalml/cml/base/list.rb +23 -9
  163. data/lib/chemicalml/cml/base/map.rb +38 -5
  164. data/lib/chemicalml/cml/base/matrix.rb +38 -9
  165. data/lib/chemicalml/cml/base/mechanism.rb +20 -7
  166. data/lib/chemicalml/cml/base/mechanism_component.rb +14 -6
  167. data/lib/chemicalml/cml/base/metadata.rb +20 -6
  168. data/lib/chemicalml/cml/base/metadata_list.rb +30 -5
  169. data/lib/chemicalml/cml/base/molecule.rb +95 -21
  170. data/lib/chemicalml/cml/base/molecule_list.rb +27 -7
  171. data/lib/chemicalml/cml/base/name.rb +11 -3
  172. data/lib/chemicalml/cml/base/object.rb +20 -6
  173. data/lib/chemicalml/cml/base/observation.rb +18 -5
  174. data/lib/chemicalml/cml/base/operator.rb +40 -0
  175. data/lib/chemicalml/cml/base/parameter.rb +38 -8
  176. data/lib/chemicalml/cml/base/parameter_list.rb +30 -5
  177. data/lib/chemicalml/cml/base/particle.rb +23 -6
  178. data/lib/chemicalml/cml/base/peak.rb +86 -11
  179. data/lib/chemicalml/cml/base/peak_group.rb +81 -7
  180. data/lib/chemicalml/cml/base/peak_list.rb +25 -8
  181. data/lib/chemicalml/cml/base/peak_structure.rb +47 -6
  182. data/lib/chemicalml/cml/base/plane3.rb +15 -5
  183. data/lib/chemicalml/cml/base/point3.rb +15 -5
  184. data/lib/chemicalml/cml/base/potential.rb +17 -6
  185. data/lib/chemicalml/cml/base/potential_form.rb +31 -6
  186. data/lib/chemicalml/cml/base/potential_list.rb +15 -7
  187. data/lib/chemicalml/cml/base/product.rb +53 -2
  188. data/lib/chemicalml/cml/base/product_list.rb +36 -2
  189. data/lib/chemicalml/cml/base/property.rb +32 -8
  190. data/lib/chemicalml/cml/base/property_list.rb +26 -5
  191. data/lib/chemicalml/cml/base/reactant.rb +53 -2
  192. data/lib/chemicalml/cml/base/reactant_list.rb +36 -2
  193. data/lib/chemicalml/cml/base/reaction.rb +82 -12
  194. data/lib/chemicalml/cml/base/reaction_list.rb +29 -2
  195. data/lib/chemicalml/cml/base/reaction_scheme.rb +48 -8
  196. data/lib/chemicalml/cml/base/reaction_step.rb +33 -9
  197. data/lib/chemicalml/cml/base/reaction_step_list.rb +34 -7
  198. data/lib/chemicalml/cml/base/reactive_centre.rb +31 -6
  199. data/lib/chemicalml/cml/base/region.rb +24 -5
  200. data/lib/chemicalml/cml/base/related_entry.rb +34 -0
  201. data/lib/chemicalml/cml/base/sample.rb +32 -5
  202. data/lib/chemicalml/cml/base/scalar.rb +41 -6
  203. data/lib/chemicalml/cml/base/spectator.rb +26 -5
  204. data/lib/chemicalml/cml/base/spectator_list.rb +20 -7
  205. data/lib/chemicalml/cml/base/spectrum.rb +63 -12
  206. data/lib/chemicalml/cml/base/spectrum_data.rb +20 -8
  207. data/lib/chemicalml/cml/base/spectrum_list.rb +30 -7
  208. data/lib/chemicalml/cml/base/sphere3.rb +15 -5
  209. data/lib/chemicalml/cml/base/stmml.rb +12 -5
  210. data/lib/chemicalml/cml/base/string.rb +40 -0
  211. data/lib/chemicalml/cml/base/string_array.rb +52 -0
  212. data/lib/chemicalml/cml/base/substance.rb +42 -8
  213. data/lib/chemicalml/cml/base/substance_list.rb +32 -7
  214. data/lib/chemicalml/cml/base/symmetry.rb +33 -7
  215. data/lib/chemicalml/cml/base/system.rb +23 -8
  216. data/lib/chemicalml/cml/base/table.rb +43 -8
  217. data/lib/chemicalml/cml/base/table_cell.rb +12 -5
  218. data/lib/chemicalml/cml/base/table_content.rb +20 -8
  219. data/lib/chemicalml/cml/base/table_header.rb +15 -7
  220. data/lib/chemicalml/cml/base/table_header_cell.rb +27 -5
  221. data/lib/chemicalml/cml/base/table_row.rb +15 -7
  222. data/lib/chemicalml/cml/base/table_row_list.rb +15 -7
  223. data/lib/chemicalml/cml/base/tcell.rb +43 -0
  224. data/lib/chemicalml/cml/base/torsion.rb +34 -7
  225. data/lib/chemicalml/cml/base/transform3.rb +12 -5
  226. data/lib/chemicalml/cml/base/transition_state.rb +23 -5
  227. data/lib/chemicalml/cml/base/trow.rb +48 -0
  228. data/lib/chemicalml/cml/base/unit.rb +45 -10
  229. data/lib/chemicalml/cml/base/unit_list.rb +40 -6
  230. data/lib/chemicalml/cml/base/unit_type.rb +32 -5
  231. data/lib/chemicalml/cml/base/unit_type_list.rb +34 -6
  232. data/lib/chemicalml/cml/base/vector3.rb +15 -5
  233. data/lib/chemicalml/cml/base/xaxis.rb +33 -7
  234. data/lib/chemicalml/cml/base/yaxis.rb +33 -7
  235. data/lib/chemicalml/cml/base/z_matrix.rb +26 -5
  236. data/lib/chemicalml/cml/base.rb +139 -121
  237. data/lib/chemicalml/cml/canonical_comparison.rb +54 -0
  238. data/lib/chemicalml/cml/elements.rb +168 -124
  239. data/lib/chemicalml/cml/enums.rb +55 -0
  240. data/lib/chemicalml/cml/namespace.rb +2 -3
  241. data/lib/chemicalml/cml/patterns.rb +33 -0
  242. data/lib/chemicalml/cml/reference_resolver.rb +137 -0
  243. data/lib/chemicalml/cml/role/alternative.rb +10 -0
  244. data/lib/chemicalml/cml/role/annotation.rb +10 -0
  245. data/lib/chemicalml/cml/role/appinfo.rb +10 -0
  246. data/lib/chemicalml/cml/role/arg.rb +10 -0
  247. data/lib/chemicalml/cml/role/cml_module.rb +2 -3
  248. data/lib/chemicalml/cml/role/complex_object.rb +10 -0
  249. data/lib/chemicalml/cml/role/enumeration.rb +10 -0
  250. data/lib/chemicalml/cml/role/expression.rb +10 -0
  251. data/lib/chemicalml/cml/role/float.rb +10 -0
  252. data/lib/chemicalml/cml/role/float_array.rb +10 -0
  253. data/lib/chemicalml/cml/role/integer.rb +10 -0
  254. data/lib/chemicalml/cml/role/integer_array.rb +10 -0
  255. data/lib/chemicalml/cml/role/operator.rb +10 -0
  256. data/lib/chemicalml/cml/role/related_entry.rb +10 -0
  257. data/lib/chemicalml/cml/role/string.rb +10 -0
  258. data/lib/chemicalml/cml/role/string_array.rb +10 -0
  259. data/lib/chemicalml/cml/role/tcell.rb +10 -0
  260. data/lib/chemicalml/cml/role/trow.rb +10 -0
  261. data/lib/chemicalml/cml/role.rb +138 -121
  262. data/lib/chemicalml/cml/schema24/alternative.rb +14 -0
  263. data/lib/chemicalml/cml/schema24/annotation.rb +15 -0
  264. data/lib/chemicalml/cml/schema24/appinfo.rb +15 -0
  265. data/lib/chemicalml/cml/schema24/arg.rb +14 -0
  266. data/lib/chemicalml/cml/schema24/cml_module.rb +18 -0
  267. data/lib/chemicalml/cml/schema24/complex_object.rb +14 -0
  268. data/lib/chemicalml/cml/schema24/configuration.rb +15 -2
  269. data/lib/chemicalml/cml/schema24/enumeration.rb +16 -0
  270. data/lib/chemicalml/cml/schema24/expression.rb +14 -0
  271. data/lib/chemicalml/cml/schema24/float.rb +14 -0
  272. data/lib/chemicalml/cml/schema24/float_array.rb +14 -0
  273. data/lib/chemicalml/cml/schema24/integer.rb +14 -0
  274. data/lib/chemicalml/cml/schema24/integer_array.rb +14 -0
  275. data/lib/chemicalml/cml/schema24/operator.rb +14 -0
  276. data/lib/chemicalml/cml/schema24/related_entry.rb +14 -0
  277. data/lib/chemicalml/cml/schema24/string.rb +14 -0
  278. data/lib/chemicalml/cml/schema24/string_array.rb +14 -0
  279. data/lib/chemicalml/cml/schema24/tcell.rb +14 -0
  280. data/lib/chemicalml/cml/schema24/trow.rb +14 -0
  281. data/lib/chemicalml/cml/schema24.rb +154 -126
  282. data/lib/chemicalml/cml/schema3.rb +123 -123
  283. data/lib/chemicalml/cml/visitable.rb +77 -3
  284. data/lib/chemicalml/cml/wire_class_registry.rb +3 -3
  285. data/lib/chemicalml/cml.rb +164 -132
  286. data/lib/chemicalml/context_configuration.rb +12 -4
  287. data/lib/chemicalml/convention/base.rb +17 -3
  288. data/lib/chemicalml/convention/cascade/constraints/reaction_scheme_must_have_content.rb +33 -0
  289. data/lib/chemicalml/convention/cascade/constraints/reaction_step_list_must_contain_steps.rb +31 -0
  290. data/lib/chemicalml/convention/cascade/constraints/reaction_step_must_have_reaction_or_lists.rb +33 -0
  291. data/lib/chemicalml/convention/cascade/constraints/reactive_centre_atom_refs_should_be_present.rb +34 -0
  292. data/lib/chemicalml/convention/cascade/constraints.rb +18 -0
  293. data/lib/chemicalml/convention/cascade.rb +43 -0
  294. data/lib/chemicalml/convention/compchem/constraints/array_rules.rb +44 -0
  295. data/lib/chemicalml/convention/compchem/constraints/calculation_requires_finalization.rb +28 -0
  296. data/lib/chemicalml/convention/compchem/constraints/compchem_module_must_contain_job_list.rb +5 -4
  297. data/lib/chemicalml/convention/compchem/constraints/environment_at_most_one_property_list.rb +26 -0
  298. data/lib/chemicalml/convention/compchem/constraints/environment_must_not_contain_parameter.rb +28 -0
  299. data/lib/chemicalml/convention/compchem/constraints/finalization_at_most_one_molecule.rb +26 -0
  300. data/lib/chemicalml/convention/compchem/constraints/finalization_at_most_one_property_list.rb +26 -0
  301. data/lib/chemicalml/convention/compchem/constraints/finalization_must_have_content.rb +44 -0
  302. data/lib/chemicalml/convention/compchem/constraints/finalization_must_not_contain_parameter.rb +27 -0
  303. data/lib/chemicalml/convention/compchem/constraints/initialization_at_most_one_molecule.rb +26 -0
  304. data/lib/chemicalml/convention/compchem/constraints/initialization_at_most_one_parameter_list.rb +26 -0
  305. data/lib/chemicalml/convention/compchem/constraints/initialization_must_have_content.rb +54 -0
  306. data/lib/chemicalml/convention/compchem/constraints/initialization_must_not_contain_property.rb +28 -0
  307. data/lib/chemicalml/convention/compchem/constraints/job_list_module_must_have_id.rb +25 -0
  308. data/lib/chemicalml/convention/compchem/constraints/job_module_at_most_one_environment.rb +25 -0
  309. data/lib/chemicalml/convention/compchem/constraints/job_module_at_most_one_finalization.rb +25 -0
  310. data/lib/chemicalml/convention/compchem/constraints/job_module_must_have_id.rb +25 -0
  311. data/lib/chemicalml/convention/compchem/constraints/job_must_contain_initialization.rb +7 -9
  312. data/lib/chemicalml/convention/compchem/constraints/matrix_rules.rb +48 -0
  313. data/lib/chemicalml/convention/compchem/constraints/module_predicates.rb +50 -0
  314. data/lib/chemicalml/convention/compchem/constraints/scalar_units.rb +42 -0
  315. data/lib/chemicalml/convention/compchem/constraints.rb +40 -2
  316. data/lib/chemicalml/convention/compchem.rb +20 -2
  317. data/lib/chemicalml/convention/constraint.rb +68 -24
  318. data/lib/chemicalml/convention/coordinator.rb +90 -0
  319. data/lib/chemicalml/convention/detection.rb +39 -0
  320. data/lib/chemicalml/convention/dictionary/constraints/dictionary_must_have_namespace.rb +29 -0
  321. data/lib/chemicalml/convention/dictionary/constraints/dictionary_namespace_should_end_with_slash_or_hash.rb +32 -0
  322. data/lib/chemicalml/convention/dictionary/constraints/entry_id_must_match_pattern.rb +33 -0
  323. data/lib/chemicalml/convention/dictionary/constraints/entry_ids_unique_within_dictionary.rb +4 -4
  324. data/lib/chemicalml/convention/dictionary/constraints/entry_must_contain_definition.rb +29 -0
  325. data/lib/chemicalml/convention/dictionary/constraints/entry_must_have_id_and_term.rb +6 -6
  326. data/lib/chemicalml/convention/dictionary/constraints/entry_must_have_unit_type.rb +31 -0
  327. data/lib/chemicalml/convention/dictionary/constraints/entry_units_co_constraints.rb +44 -0
  328. data/lib/chemicalml/convention/dictionary/constraints.rb +14 -2
  329. data/lib/chemicalml/convention/dictionary.rb +8 -2
  330. data/lib/chemicalml/convention/molecular/constraints/atom_array_must_be_child_of_molecule_or_formula.rb +41 -0
  331. data/lib/chemicalml/convention/molecular/constraints/atom_array_must_contain_atoms.rb +18 -9
  332. data/lib/chemicalml/convention/molecular/constraints/atom_coordinates_must_be_paired.rb +6 -6
  333. data/lib/chemicalml/convention/molecular/constraints/atom_element_type_should_be_in_periodic_table.rb +39 -0
  334. data/lib/chemicalml/convention/molecular/constraints/atom_id_must_match_pattern.rb +7 -6
  335. data/lib/chemicalml/convention/molecular/constraints/atom_ids_unique_within_molecule.rb +3 -2
  336. data/lib/chemicalml/convention/molecular/constraints/atom_must_have_element_type.rb +4 -4
  337. data/lib/chemicalml/convention/molecular/constraints/atom_must_have_id.rb +5 -4
  338. data/lib/chemicalml/convention/molecular/constraints/atom_parity_atom_refs4_should_be_distinct.rb +44 -0
  339. data/lib/chemicalml/convention/molecular/constraints/atom_parity_should_include_parent_atom.rb +65 -0
  340. data/lib/chemicalml/convention/molecular/constraints/bond_array_must_be_child_of_molecule.rb +37 -0
  341. data/lib/chemicalml/convention/molecular/constraints/bond_atom_refs2_should_be_distinct.rb +44 -0
  342. data/lib/chemicalml/convention/molecular/constraints/bond_id_should_match_pattern.rb +38 -0
  343. data/lib/chemicalml/convention/molecular/constraints/bond_ids_unique_within_molecule.rb +44 -0
  344. data/lib/chemicalml/convention/molecular/constraints/bond_must_have_atom_refs2.rb +4 -4
  345. data/lib/chemicalml/convention/molecular/constraints/bond_must_have_order.rb +5 -4
  346. data/lib/chemicalml/convention/molecular/constraints/bond_must_reference_atoms_in_same_molecule.rb +4 -3
  347. data/lib/chemicalml/convention/molecular/constraints/bond_order_other_must_have_dict_ref.rb +34 -0
  348. data/lib/chemicalml/convention/molecular/constraints/bond_order_should_be_in_enum.rb +39 -0
  349. data/lib/chemicalml/convention/molecular/constraints/bond_order_should_not_be_numeric.rb +3 -3
  350. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_atom_refs4_should_be_distinct.rb +43 -0
  351. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_cis_trans_must_have_atom_refs4.rb +45 -0
  352. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_other_must_have_dict_ref.rb +33 -0
  353. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_should_be_in_enum.rb +38 -0
  354. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_wedge_hash_must_have_atom_refs2.rb +45 -0
  355. data/lib/chemicalml/convention/molecular/constraints/dict_ref_should_resolve.rb +59 -0
  356. data/lib/chemicalml/convention/molecular/constraints/molecule_atom_array_mutually_exclusive_with_children.rb +33 -0
  357. data/lib/chemicalml/convention/molecular/constraints/molecule_bond_array_mutually_exclusive_with_children.rb +32 -0
  358. data/lib/chemicalml/convention/molecular/constraints/molecule_chirality_should_be_in_enum.rb +38 -0
  359. data/lib/chemicalml/convention/molecular/constraints/molecule_count_must_not_appear_on_top_level.rb +35 -0
  360. data/lib/chemicalml/convention/molecular/constraints/molecule_id_should_match_pattern.rb +38 -0
  361. data/lib/chemicalml/convention/molecular/constraints/molecule_must_have_id.rb +4 -4
  362. data/lib/chemicalml/convention/molecular/constraints/property_must_have_dict_ref.rb +4 -4
  363. data/lib/chemicalml/convention/molecular/constraints/property_scalar_data_type_matches_dictionary.rb +58 -0
  364. data/lib/chemicalml/convention/molecular/constraints/references_should_resolve.rb +37 -0
  365. data/lib/chemicalml/convention/molecular/constraints/scalar_must_have_data_type.rb +4 -4
  366. data/lib/chemicalml/convention/molecular/constraints.rb +59 -13
  367. data/lib/chemicalml/convention/molecular.rb +25 -2
  368. data/lib/chemicalml/convention/registry.rb +95 -10
  369. data/lib/chemicalml/convention/simple_unit/constraints/root_must_be_unit_list.rb +24 -0
  370. data/lib/chemicalml/convention/simple_unit/constraints/unit_must_have_power.rb +32 -0
  371. data/lib/chemicalml/convention/simple_unit/constraints/unit_must_have_symbol.rb +31 -0
  372. data/lib/chemicalml/convention/simple_unit/constraints.rb +16 -0
  373. data/lib/chemicalml/convention/simple_unit.rb +37 -0
  374. data/lib/chemicalml/convention/spectroscopy/constraints/peak_list_must_contain_peaks.rb +31 -0
  375. data/lib/chemicalml/convention/spectroscopy/constraints/peak_should_have_values.rb +34 -0
  376. data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_content.rb +31 -0
  377. data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_convention.rb +32 -0
  378. data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_format.rb +31 -0
  379. data/lib/chemicalml/convention/spectroscopy/constraints.rb +20 -0
  380. data/lib/chemicalml/convention/spectroscopy.rb +41 -0
  381. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_list_must_contain_at_least_one_unit.rb +29 -0
  382. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_list_must_have_namespace.rb +36 -0
  383. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_contain_definition.rb +28 -0
  384. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_id.rb +28 -0
  385. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_multiplier_or_constant_to_si.rb +34 -0
  386. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_parent_si.rb +32 -0
  387. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_symbol_and_unit_type.rb +4 -4
  388. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_title.rb +32 -0
  389. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_parent_si_should_resolve.rb +45 -0
  390. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_unit_type_should_resolve.rb +46 -0
  391. data/lib/chemicalml/convention/unit_dictionary/constraints.rb +19 -1
  392. data/lib/chemicalml/convention/unit_dictionary.rb +11 -2
  393. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_id_must_match_pattern.rb +31 -0
  394. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_list_must_contain_at_least_one_unit_type.rb +28 -0
  395. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_list_must_have_namespace.rb +35 -0
  396. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_contain_definition.rb +28 -0
  397. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_have_id_and_name.rb +6 -6
  398. data/lib/chemicalml/convention/unit_type_dictionary/constraints.rb +9 -1
  399. data/lib/chemicalml/convention/unit_type_dictionary.rb +6 -2
  400. data/lib/chemicalml/convention/validation_report.rb +90 -0
  401. data/lib/chemicalml/convention/violation.rb +9 -3
  402. data/lib/chemicalml/convention.rb +49 -11
  403. data/lib/chemicalml/dictionary/entry.rb +6 -6
  404. data/lib/chemicalml/dictionary/enum.rb +1 -1
  405. data/lib/chemicalml/dictionary/link.rb +3 -3
  406. data/lib/chemicalml/dictionary/loader.rb +19 -19
  407. data/lib/chemicalml/dictionary/model.rb +5 -5
  408. data/lib/chemicalml/dictionary/registry.rb +9 -9
  409. data/lib/chemicalml/dictionary.rb +13 -9
  410. data/lib/chemicalml/logger.rb +47 -0
  411. data/lib/chemicalml/schema/definition.rb +2 -2
  412. data/lib/chemicalml/schema.rb +7 -7
  413. data/lib/chemicalml/version.rb +1 -1
  414. data/lib/chemicalml/versioned_parser.rb +28 -10
  415. data/lib/chemicalml.rb +79 -17
  416. data/lib/tasks/reference_fetch.rake +30 -31
  417. data/lib/tasks/scrape_fixtures.rake +23 -24
  418. metadata +235 -35
  419. data/lib/chemicalml/cml/child_mappings.rb +0 -168
  420. data/lib/chemicalml/cml/translator/value_translations.rb +0 -270
  421. data/lib/chemicalml/cml/translator.rb +0 -484
  422. data/lib/chemicalml/model/atom.rb +0 -64
  423. data/lib/chemicalml/model/atom_parity.rb +0 -21
  424. data/lib/chemicalml/model/bond.rb +0 -39
  425. data/lib/chemicalml/model/bond_stereo.rb +0 -29
  426. data/lib/chemicalml/model/cml_array.rb +0 -27
  427. data/lib/chemicalml/model/cml_module.rb +0 -45
  428. data/lib/chemicalml/model/document.rb +0 -29
  429. data/lib/chemicalml/model/formula.rb +0 -34
  430. data/lib/chemicalml/model/identifier.rb +0 -21
  431. data/lib/chemicalml/model/label.rb +0 -21
  432. data/lib/chemicalml/model/matrix.rb +0 -29
  433. data/lib/chemicalml/model/metadata.rb +0 -23
  434. data/lib/chemicalml/model/metadata_list.rb +0 -25
  435. data/lib/chemicalml/model/molecule.rb +0 -45
  436. data/lib/chemicalml/model/name.rb +0 -21
  437. data/lib/chemicalml/model/node.rb +0 -45
  438. data/lib/chemicalml/model/parameter.rb +0 -27
  439. data/lib/chemicalml/model/parameter_list.rb +0 -25
  440. data/lib/chemicalml/model/product.rb +0 -21
  441. data/lib/chemicalml/model/product_list.rb +0 -21
  442. data/lib/chemicalml/model/property.rb +0 -28
  443. data/lib/chemicalml/model/property_list.rb +0 -25
  444. data/lib/chemicalml/model/reactant.rb +0 -21
  445. data/lib/chemicalml/model/reactant_list.rb +0 -21
  446. data/lib/chemicalml/model/reaction.rb +0 -41
  447. data/lib/chemicalml/model/reaction_list.rb +0 -23
  448. data/lib/chemicalml/model/scalar.rb +0 -25
  449. data/lib/chemicalml/model/substance.rb +0 -25
  450. data/lib/chemicalml/model.rb +0 -40
@@ -0,0 +1,62 @@
1
+ # Round 33 — CLI run(options) signature alignment
2
+
3
+ This round refactors every CLI command class so the run method
4
+ takes a single `options` hash, matching the user-requested
5
+ invocation pattern exactly:
6
+
7
+ MyCommandClass.new.run(options)
8
+
9
+ ## Files
10
+
11
+ - [134 — CLI run(options) alignment](134-cli-run-options-alignment.md)
12
+ - [135 — Final verification round 33](135-final-verification-round33.md)
13
+
14
+ ## Outcomes
15
+
16
+ - Every `Chemicalml::Cli::*Command` now implements
17
+ `def run(options)` (single hash argument). The hash carries both
18
+ positional args (keyed by name: `:file`, `:element`) and flags
19
+ (`:json`, etc.).
20
+
21
+ - `Chemicalml::Cli::Base` (Thor dispatcher) builds the options hash
22
+ for each command and calls `<Name>Command.new.run(hash)`. Example:
23
+ ```ruby
24
+ desc "validate FILE", "..."
25
+ method_option :json, type: :boolean, aliases: "-j"
26
+ def validate(file)
27
+ ValidateCommand.new.run(file: file, json: options[:json])
28
+ end
29
+ ```
30
+
31
+ - Each command supports both invocation styles:
32
+ - Instance: `MyCommand.new.run(options)` (primary, per user spec)
33
+ - Class: `MyCommand.run(options)` (convenience, delegates to instance)
34
+
35
+ - Bug fix: validation guards `return warn(...), 2 unless path`
36
+ were returning `[warn_result, 2]` (array) instead of integer 2.
37
+ Rewrote as explicit `warn; return 2` two-line guards.
38
+
39
+ - CLI spec updated to test both invocation styles. 17 specs covering
40
+ every command via `MyCommandClass.new.run(options)` direct call.
41
+
42
+ - **593 examples, 0 failures, 3 pending**. Zero forbidden patterns.
43
+
44
+ ## Architectural insight
45
+
46
+ The single-hash `run(options)` signature is more flexible than the
47
+ prior `(args, options)` split:
48
+
49
+ - Positional args are named (`file:`, `element:`), not positional.
50
+ Easier to extend (add a new arg = add a new hash key, no method
51
+ signature change).
52
+ - Same hash carries flags (`json:`, etc.). One argument, one source
53
+ of truth.
54
+ - Commands are unit-testable in isolation without Thor:
55
+ ```ruby
56
+ Chemicalml::Cli::ValidateCommand.new.run(file: "x.cml", json: true)
57
+ ```
58
+
59
+ The Thor dispatcher is now truly just routing — it parses CLI input
60
+ and constructs the options hash. Every command class can be reused
61
+ from Rake tasks, RSpec tests, or web handlers without going through
62
+ Thor at all.
@@ -0,0 +1,70 @@
1
+ # Round 34 — Constraint descriptions, diverse fixtures, perf baseline, BIG perf fix
2
+
3
+ This round adds constraint-class descriptions, more diverse fixtures,
4
+ a performance baseline spec, and crucially **fixes a 680× performance
5
+ regression** discovered by the baseline spec.
6
+
7
+ ## Files
8
+
9
+ - [136 — Constraint descriptions](136-constraint-descriptions.md)
10
+ - [137 — More diverse fixtures](137-more-diverse-fixtures.md)
11
+ - [138 — Performance baseline spec](138-performance-baseline-spec.md)
12
+ - [139 — Final verification round 34](139-final-verification-round34.md)
13
+
14
+ ## Outcomes
15
+
16
+ - **Constraint `description` class method** added to
17
+ `Convention::Constraint`. Each constraint can now carry a
18
+ human-readable description of the rule it enforces. Two molecular
19
+ constraints (`AtomMustHaveId`, `BondMustHaveOrder`) updated as
20
+ examples. `chemicalml constraints` command now shows descriptions
21
+ when present.
22
+
23
+ - **Three new diverse fixtures**:
24
+ - `molecular/ethylene_with_cis_trans_stereo.cml` — bondStereo with
25
+ `atomRefs4` (cis/trans case)
26
+ - `reactions/free_radical_halogenation.cml` — multi-reactant
27
+ reaction with substanceList catalyst
28
+ - `spectroscopy/ethanol_nmr.cml` — NMR spectrum with xaxis/yaxis
29
+ arrays, peakList with multiplicity, metadataList
30
+
31
+ - **Performance baseline spec** — establishes timing baselines for:
32
+ - XML/JSON serialisation of a 1000-atom molecule (< 2s)
33
+ - Molecular validation of a 1000-atom molecule (< 2s)
34
+ - `atom_count` / `find_atom` queries (< 500ms)
35
+ - Fixture parsing (< 20ms)
36
+
37
+ - **MAJOR performance fix**: the `ReferencesShouldResolve` constraint
38
+ was taking **47.8 seconds** on a 1000-atom document. Root cause:
39
+ `ReferenceResolver#unresolved_for` called `containing_molecule`
40
+ for every bond, which walked the entire document every time
41
+ (O(n²) for n bonds × n molecules × n atoms).
42
+
43
+ Fixed by:
44
+ - Adding lazily-built `atom_index` (id → Atom hash, O(1) lookup)
45
+ - Removing the `containing_molecule` indirection in
46
+ `unresolved_for` — the resolver now uses the global atom index
47
+ directly. CML semantics already enforce that bond refs live in
48
+ the parent molecule (that's `BondMustReferenceAtomsInSameMolecule`'s
49
+ job).
50
+
51
+ Result: **47.8s → 68ms (680× faster)**.
52
+
53
+ - **609 examples, 0 failures, 3 pending** (TOML adapter pending).
54
+ Zero forbidden patterns.
55
+
56
+ ## Architectural insight
57
+
58
+ The performance baseline spec paid for itself immediately by catching
59
+ a 47-second regression that I'd introduced in round 22 when adding
60
+ the `ReferencesShouldResolve` constraint. Without the baseline,
61
+ this would have shipped to users.
62
+
63
+ The fix follows the **build indices once, query many times** pattern.
64
+ The resolver now builds an `atom_index` lazily on first lookup; all
65
+ subsequent `find_atom` calls are O(1). This is a Ruby-native
66
+ performance pattern that fits the existing architecture cleanly.
67
+
68
+ Lesson: baseline specs aren't just for regression detection — they
69
+ catch bugs that you didn't know you had. Always write the baseline
70
+ before optimising.
@@ -0,0 +1,161 @@
1
+ # Registered Convention Constraints
2
+
3
+ Auto-generated from `Chemicalml::Convention::Registry.each_constraint`.
4
+ Last regenerated: 2026-07-20T09:43:53Z.
5
+
6
+ Total constraints: **91**
7
+
8
+ ## convention:cascade
9
+
10
+ - **Namespace**: `http://www.xml-cml.org/convention/cascade`
11
+ - **Constraint count**: 4
12
+
13
+ | Constraint | Applies to | Description |
14
+ |---|---|---|
15
+ | `ReactionSchemeMustHaveContent` | ReactionScheme | A `<reactionScheme>` MUST contain at least one `<reactionStepList>` or `<reaction>` child. An empty scheme carries no cascade information. |
16
+ | `ReactionStepListMustContainSteps` | ReactionStepList | A `<reactionStepList>` MUST contain at least one `<reactionStep>` child. An empty step list breaks the cascade chain. |
17
+ | `ReactionStepMustHaveReactionOrLists` | ReactionStep | A `<reactionStep>` MUST contain either a `<reaction>` child or explicit `<reactantList>` + `<productList>` children. An empty step is a dead-end in the cascade. |
18
+ | `ReactiveCentreAtomRefsShouldBePresent` | ReactiveCentre | A `<reactiveCentre>` SHOULD declare its `atomRefs` attribute. Without atom references the reactive centre is indistinguishable from a placeholder. Warning severity — |
19
+
20
+ ## convention:compchem
21
+
22
+ - **Namespace**: `http://www.xml-cml.org/convention/compchem`
23
+ - **Constraint count**: 20
24
+
25
+ | Constraint | Applies to | Description |
26
+ |---|---|---|
27
+ | `CompchemModuleMustContainJobList` | _(document-wide)_ | A CompChem module MUST contain at least one jobList module child per the compchem convention. |
28
+ | `JobMustContainInitialization` | Module | A job module MUST contain exactly one initialization module child per the compchem convention. |
29
+ | `JobListModuleMustHaveId` | Module | A jobList module MUST have an `id` unique within the compchem module. |
30
+ | `JobModuleMustHaveId` | Module | A job module MUST have an `id` unique within the compchem module. |
31
+ | `JobModuleAtMostOneFinalization` | Module | A job module MUST contain at most one finalization module. |
32
+ | `JobModuleAtMostOneEnvironment` | Module | A job module MUST contain at most one environment module. |
33
+ | `CalculationRequiresFinalization` | Module | Co-constraint: if a calculation module is present inside a job, a finalization MUST also be present. |
34
+ | `InitializationAtMostOneMolecule` | Module | The initialization module MUST NOT contain more than one molecule child. |
35
+ | `InitializationAtMostOneParameterList` | Module | The initialization module MUST NOT contain more than one parameterList child. |
36
+ | `InitializationMustNotContainProperty` | Module | The initialization module MUST NOT contain property or propertyList children directly. (Properties belong in finalization.) |
37
+ | `FinalizationAtMostOneMolecule` | Module | The finalization module MUST NOT contain more than one molecule child. |
38
+ | `FinalizationAtMostOnePropertyList` | Module | The finalization module MUST NOT contain more than one propertyList child. |
39
+ | `FinalizationMustNotContainParameter` | Module | The finalization module MUST NOT contain parameter or parameterList children directly. (Parameters belong in initialization.) |
40
+ | `EnvironmentAtMostOnePropertyList` | Module | The environment module MUST NOT contain more than one propertyList child. |
41
+ | `EnvironmentMustNotContainParameter` | Module | The environment module MUST NOT contain parameter or parameterList children directly. It MAY contain a propertyList (which itself can hold parameters). |
42
+ | `ScalarUnits` | Scalar | CompChem value-container rules: `scalar` with dataType `xsd:integer` or `xsd:double` MUST have `units`; `scalar` with dataType `xsd:string` MUST NOT have `units`. |
43
+ | `ArrayRules` | Array | CompChem `array` value-container rules: MUST have `size` attribute (≥ 1); `dataType` MUST be integer or double; MUST have `units`. |
44
+ | `MatrixRules` | Matrix | CompChem `matrix` value-container rules: MUST have `rows` and `columns` attributes (each ≥ 1); `dataType` MUST be integer or double; MUST have `units`. |
45
+ | `InitializationMustHaveContent` | Module | An `initialization` module MUST contain at least one of: `molecule`, `parameterList`, or a user-defined module child. Per the compchem convention spec. |
46
+ | `FinalizationMustHaveContent` | Module | A `finalization` module MUST contain at least one of: `molecule`, `propertyList`, or a user-defined module child. Per the compchem convention spec. |
47
+
48
+ ## convention:dictionary
49
+
50
+ - **Namespace**: `http://www.xml-cml.org/convention/dictionary`
51
+ - **Constraint count**: 8
52
+
53
+ | Constraint | Applies to | Description |
54
+ |---|---|---|
55
+ | `EntryMustHaveIdAndTerm` | DictionaryEntry | Every entry MUST have an `id` and a `term` attribute per the dictionary convention. |
56
+ | `EntryIdsUniqueWithinDictionary` | Dictionary | Entry ids MUST be unique within the parent dictionary per the dictionary convention. |
57
+ | `DictionaryMustHaveNamespace` | Dictionary | A `<dictionary>` element MUST have a `namespace` attribute whose value is a valid URI defining the scope within which the entry terms are unique. |
58
+ | `DictionaryNamespaceShouldEndWithSlashOrHash` | Dictionary | The `namespace` URI SHOULD end with `/` or `#` so terms can be referenced by appending them to the URI. Warning level. |
59
+ | `EntryMustContainDefinition` | DictionaryEntry | An `<entry>` MUST contain a single `definition` child element. The schema allows it as a string; this constraint enforces presence. |
60
+ | `EntryIdMustMatchPattern` | DictionaryEntry | An `<entry>` `id` MUST match `[A-Za-z][A-Za-z0-9._-]*` — start with a letter, followed by letters/digits/dot/hyphen/ underscore. Mirrors the upstream IdStartChar / IdChar BNF. |
61
+ | `EntryMustHaveUnitType` | DictionaryEntry | An `<entry>` MUST have a `unitType` attribute. Entries describing concepts that should not have units (e.g. name of a program) should reference `none` in the standard CML |
62
+ | `EntryUnitsCoConstraints` | DictionaryEntry | Co-constraint between `unitType` and `units`: - If `unitType` is `unknown`, `units` MUST NOT be present. - If `unitType` is `none`, `units` MUST be present and point |
63
+
64
+ ## convention:molecular
65
+
66
+ - **Namespace**: `http://www.xml-cml.org/convention/molecular`
67
+ - **Constraint count**: 36
68
+
69
+ | Constraint | Applies to | Description |
70
+ |---|---|---|
71
+ | `AtomArrayMustContainAtoms` | Molecule | `atomArray` MUST contain at least one atom. The atoms can be in the child form (`<atom>` elements) or the parallel-array form (`atomID` attribute with whitespace-separated ids). |
72
+ | `AtomIdsUniqueWithinMolecule` | _(document-wide)_ | Atom ids MUST be unique within the eldest containing molecule per the molecular convention. |
73
+ | `BondMustReferenceAtomsInSameMolecule` | _(document-wide)_ | Bonds MUST reference atoms within the same parent molecule per the molecular convention. |
74
+ | `AtomMustHaveId` | Atom | An <atom> inside an atomArray in a molecule MUST have an id attribute. |
75
+ | `AtomMustHaveElementType` | Atom | An <atom> inside an atomArray in a molecule MUST have an elementType attribute. |
76
+ | `BondMustHaveAtomRefs2` | Bond | A <bond> MUST have an atomRefs2 attribute referencing two distinct atoms in the same molecule. |
77
+ | `BondMustHaveOrder` | Bond | A <bond> MUST have an order attribute (S/D/T/A recommended). |
78
+ | `MoleculeMustHaveId` | Molecule | A <molecule> in a molecular document MUST have an id attribute unique within document scope. |
79
+ | `AtomCoordinatesMustBePaired` | Atom | <atom> 2D coordinates (x2, y2) must appear together; 3D coordinates (x3, y3, z3) must appear together. |
80
+ | `PropertyMustHaveDictRef` | Property | A <property> MUST have a dictRef attribute that identifies the property type via a dictionary entry. |
81
+ | `ScalarMustHaveDataType` | Scalar | A <scalar> child of a <property> MUST have a dataType attribute (e.g. xsd:float, xsd:integer). |
82
+ | `BondOrderShouldNotBeNumeric` | Bond | A <bond>'s order attribute SHOULD be one of S/D/T/Q/A or 'other' (numeric values are not recommended). |
83
+ | `AtomIdMustMatchPattern` | Atom | An <atom>'s id attribute SHOULD start with a letter and contain only letters, digits, dot, hyphen, or underscore. |
84
+ | `MoleculeCountMustNotAppearOnTopLevel` | _(document-wide)_ | Molecular convention: `count` MUST NOT appear on top-level molecules (direct children of `<cml>`). It is REQUIRED on molecules nested inside another `<molecule>`. |
85
+ | `MoleculeAtomArrayMutuallyExclusiveWithChildren` | Molecule | Molecular convention: a `<molecule>` MAY hold an `atomArray` OR child `<molecule>` elements, but not both. The two are mutually exclusive ways of describing composition. |
86
+ | `MoleculeBondArrayMutuallyExclusiveWithChildren` | Molecule | Molecular convention: a `<molecule>` MAY hold a `bondArray` OR child `<molecule>` elements, but not both. |
87
+ | `BondStereoWedgeHashMustHaveAtomRefs2` | BondStereo | Molecular convention: `<bondStereo>` value W (wedge) or H (hatch) MUST have `atomRefs2` and MUST NOT have `atomRefs4`. |
88
+ | `BondStereoCisTransMustHaveAtomRefs4` | BondStereo | Molecular convention: `<bondStereo>` value C (cis) or T (trans) MUST have `atomRefs4` and MUST NOT have `atomRefs2`. |
89
+ | `BondStereoOtherMustHaveDictRef` | BondStereo | Molecular convention: `<bondStereo>` with value `other` MUST have a `dictRef` pointing to the convention that defines the stereo semantics. |
90
+ | `BondIdsUniqueWithinMolecule` | _(document-wide)_ | Molecular convention: a `<bond>` `id` MUST be unique within the eldest containing molecule. Mirrors the existing `AtomIdsUniqueWithinMolecule` constraint for bonds. |
91
+ | `BondOrderOtherMustHaveDictRef` | Bond | Molecular convention: `<bond>` with `order="other"` MUST have a `dictRef` pointing to the dictionary that defines the order semantics. The warning-level "should not be |
92
+ | `AtomArrayMustBeChildOfMoleculeOrFormula` | _(document-wide)_ | Molecular convention: `<atomArray>` MUST be a child of `<molecule>` or `<formula>`. Any other parent is invalid. Checked from the parent's perspective during the walk — |
93
+ | `BondArrayMustBeChildOfMolecule` | _(document-wide)_ | Molecular convention: `<bondArray>` MUST be a child of `<molecule>`. Any other parent is invalid. |
94
+ | `BondOrderShouldBeInEnum` | Bond | A `<bond>`'s `order` attribute SHOULD be one of the XSD orderType enum values: S/1/D/2/T/3/A/unknown/other. Warning severity — CML permits extension values via |
95
+ | `BondStereoShouldBeInEnum` | BondStereo | A `<bondStereo>`'s `value` attribute SHOULD be one of the XSD stereoType enum values: C/T/W/H/undefined/other. Warning severity. |
96
+ | `MoleculeChiralityShouldBeInEnum` | Molecule | A `<molecule>`'s `chirality` attribute SHOULD be one of the XSD chiralityType enum values: enantiomer/racemate/unknown/other. Warning severity. |
97
+ | `BondAtomRefs2ShouldBeDistinct` | Bond | A `<bond>`'s `atomRefs2` SHOULD reference two distinct atom ids. A self-bond (`a1 a1`) is chemically meaningless. Warning severity — there are edge cases in non-classical |
98
+ | `ReferencesShouldResolve` | _(document-wide)_ | Walks the document via `Cml::ReferenceResolver` and reports every bond whose `atomRefs2` references atoms that don't exist in the parent molecule. Warning severity — there are |
99
+ | `BondStereoAtomRefs4ShouldBeDistinct` | BondStereo | A `<bondStereo>` with `atomRefs4` (used by C/T stereo) SHOULD reference four distinct atom ids. Duplicate ids make the stereo descriptor meaningless. Warning severity. |
100
+ | `AtomParityAtomRefs4ShouldBeDistinct` | AtomParity | An `<atomParity>` element's `atomRefs4` attribute SHOULD reference four distinct atom ids around a chiral center. Duplicate ids make the parity descriptor meaningless. |
101
+ | `AtomElementTypeShouldBeInPeriodicTable` | Atom | An `<atom>`'s `elementType` attribute SHOULD be one of the XSD elementTypeType enum values (the periodic table plus the special "Du" dummy and "R" group placeholder). Warns |
102
+ | `DictRefShouldResolve` | Atom, Bond, Molecule, Property, Scalar, Array, Matrix, Name, Label | Walks the document and warns on any element whose `dictRef` attribute cannot be resolved against the built-in dictionaries. Catches typos like `dictRef="cml:bpingpoint"`. |
103
+ | `MoleculeIdShouldMatchPattern` | Molecule | A `<molecule>`'s `id` attribute SHOULD match the XSD moleculeIDType pattern (letter/underscore start, alphanumeric body, optional namespace prefix). Warning severity. |
104
+ | `BondIdShouldMatchPattern` | Bond | A `<bond>`'s `id` attribute SHOULD match the XSD idType pattern (letter start, alphanumeric body with dots/dashes). Warning severity. |
105
+ | `AtomParityShouldIncludeParentAtom` | _(document-wide)_ | Walks the document and warns on `<atomParity>` elements whose parent `<atom>`'s id does not appear in atomRefs4. The CML convention requires the parent atom to be one of |
106
+ | `PropertyScalarDataTypeMatchesDictionary` | Property | A `<property>` with a `dictRef` whose dictionary entry declares a `dataType` SHOULD have a child `<scalar>` whose `dataType` attribute matches. Catches inconsistencies like a property |
107
+
108
+ ## convention:simpleUnit
109
+
110
+ - **Namespace**: `http://www.xml-cml.org/convention/simpleUnit`
111
+ - **Constraint count**: 3
112
+
113
+ | Constraint | Applies to | Description |
114
+ |---|---|---|
115
+ | `UnitMustHavePower` | Unit | A `<unit>` under simpleUnit MUST declare a `power` attribute (integer). Without a power the unit's exponent is undefined. |
116
+ | `UnitMustHaveSymbol` | Unit | A `<unit>` under simpleUnit MUST declare a non-empty `symbol` attribute. Without a symbol the unit cannot be rendered in formulae. |
117
+ | `RootMustBeUnitList` | _(document-wide)_ | The root of a simpleUnit document MUST be a `<unitList>` declaring `convention="convention:simpleUnit"`. Any other root shape is rejected. |
118
+
119
+ ## convention:spectroscopy
120
+
121
+ - **Namespace**: `http://www.xml-cml.org/convention/spectroscopy`
122
+ - **Constraint count**: 5
123
+
124
+ | Constraint | Applies to | Description |
125
+ |---|---|---|
126
+ | `SpectrumMustHaveConvention` | Spectrum | A `<spectrum>` MUST declare its own convention attribute so consumers know how to interpret its format and peaks. Per the molecular convention: "spectrum — any number, each MUST |
127
+ | `SpectrumMustHaveFormat` | Spectrum | A `<spectrum>` MUST have a `format` attribute (e.g. "mass", "ir", "nmr", "uv") so consumers know the measurement type. |
128
+ | `SpectrumMustHaveContent` | Spectrum | A `<spectrum>` MUST contain at least one of: `<xaxis>`, `<yaxis>`, `<peakList>`. An empty spectrum carries no data. |
129
+ | `PeakListMustContainPeaks` | PeakList | A `<peakList>` MUST contain at least one `<peak>` or `<peakGroup>` child. An empty peakList carries no data. |
130
+ | `PeakShouldHaveValues` | Peak | A `<peak>` SHOULD declare at least one of `xValue` or `yValue`. A peak with neither carries no position information. Warning severity — there are edge cases |
131
+
132
+ ## convention:unit-dictionary
133
+
134
+ - **Namespace**: `http://www.xml-cml.org/convention/unit-dictionary`
135
+ - **Constraint count**: 10
136
+
137
+ | Constraint | Applies to | Description |
138
+ |---|---|---|
139
+ | `UnitMustHaveSymbolAndUnitType` | Unit | Every unit MUST have `id`, `title`, `symbol`, `parentSI`, at least one of `multiplierToSI`/`constantToSI`, and `unitType` per the unit-dictionary convention. |
140
+ | `UnitMustHaveId` | Unit | A `<unit>` element MUST have an `id` attribute, unique within the unitList. |
141
+ | `UnitMustContainDefinition` | Unit | A `<unit>` element MUST contain a single `<definition>` child element with XHTML content. |
142
+ | `UnitListMustHaveNamespace` | UnitList | A `<unitList>` element MUST have a `namespace` attribute whose value is a valid URI defining the scope within which the unit ids are unique. SHOULD end with `/` or `#`. |
143
+ | `UnitListMustContainAtLeastOneUnit` | UnitList | A `<unitList>` element MUST contain one or more `<unit>` children, and MUST NOT contain any other CML-namespace child elements. |
144
+ | `UnitMustHaveTitle` | Unit | A `<unit>` element MUST have a `title` attribute (the full human-readable name of the unit). Per the unit-dictionary convention spec. |
145
+ | `UnitMustHaveParentSi` | Unit | A `<unit>` element MUST have a `parentSI` attribute — a QName referencing the SI unit it derives from. Per the unit-dictionary convention spec. |
146
+ | `UnitMustHaveMultiplierOrConstantToSi` | Unit | A `<unit>` element MUST have at least one of `multiplierToSI` or `constantToSI` — to define the conversion to the parent SI unit. Per the unit-dictionary convention spec. |
147
+ | `UnitUnitTypeShouldResolve` | Unit | A `<unit>`'s `unitType` attribute SHOULD reference a unitType that exists in a built-in unitType-dictionary. Catches typos like `unitType="unitType:lenght"` (misspelled). |
148
+ | `UnitParentSiShouldResolve` | Unit | A `<unit>`'s `parentSI` attribute SHOULD reference a unit that exists in a built-in dictionary (typically the SI dictionary). Catches typos like `parentSI="si:metr"`. |
149
+
150
+ ## convention:unitType-dictionary
151
+
152
+ - **Namespace**: `http://www.xml-cml.org/convention/unitType-dictionary`
153
+ - **Constraint count**: 5
154
+
155
+ | Constraint | Applies to | Description |
156
+ |---|---|---|
157
+ | `UnitTypeMustHaveIdAndName` | UnitType | Every unitType MUST have an `id` and a `name` attribute per the unitType-dictionary convention. |
158
+ | `UnitTypeIdMustMatchPattern` | UnitType | A `<unitType>` `id` MUST match `[A-Za-z][A-Za-z0-9._-]*`. |
159
+ | `UnitTypeMustContainDefinition` | UnitType | A `<unitType>` MUST contain a single `<definition>` child with XHTML content. |
160
+ | `UnitTypeListMustHaveNamespace` | UnitTypeList | A `<unitTypeList>` MUST have a `namespace` attribute (valid URI, SHOULD end with `/` or `#`). |
161
+ | `UnitTypeListMustContainAtLeastOneUnitType` | UnitTypeList | A `<unitTypeList>` MUST contain one or more `<unitType>` children. |
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "chemicalml"
4
+
5
+ # Auto-generated documentation of every registered convention
6
+ # constraint. Run via `bundle exec rake docs:constraints` or
7
+ # `bundle exec ruby docs/generate_constraint_docs.rb`.
8
+ #
9
+ # Single source of truth: derived from `Convention::Registry.each_constraint`.
10
+ # Do not edit `docs/constraints.md` by hand — regenerate.
11
+
12
+ output = []
13
+ output << "# Registered Convention Constraints"
14
+ output << ""
15
+ output << "Auto-generated from `Chemicalml::Convention::Registry.each_constraint`."
16
+ output << "Last regenerated: #{Time.now.utc.iso8601}."
17
+ output << ""
18
+ output << "Total constraints: **#{Chemicalml::Convention::Registry.total_constraint_count}**"
19
+ output << ""
20
+
21
+ Chemicalml::Convention::Registry.each do |conv|
22
+ output << "## #{conv.qname}"
23
+ output << ""
24
+ output << "- **Namespace**: `#{conv.namespace_uri}`"
25
+ output << "- **Constraint count**: #{conv.constraint_count}"
26
+ output << ""
27
+
28
+ output << "| Constraint | Applies to | Description |"
29
+ output << "|---|---|---|"
30
+ conv.constraints.each do |klass|
31
+ name = klass.name.split("::").last
32
+ roles = klass.applies_to_roles
33
+ applies = roles.nil? ? "_(document-wide)_" : roles.map { |r| r.name.split("::").last }.join(", ")
34
+ desc = klass.description
35
+ desc = "" if desc == name
36
+ desc_escaped = desc.gsub("|", "\\|")
37
+ output << "| `#{name}` | #{applies} | #{desc_escaped} |"
38
+ end
39
+ output << ""
40
+ end
41
+
42
+ path = File.expand_path("constraints.md", __dir__)
43
+ File.write(path, output.join("\n"))
44
+ puts "Wrote #{path} (#{Chemicalml::Convention::Registry.total_constraint_count} constraints)"
data/exe/chemicalml ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
5
+ require "chemicalml"
6
+ exit Chemicalml::Cli.run(ARGV)
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ module Chemicalml
6
+ module Cli
7
+ # Thor-based dispatcher. Each command delegates to a dedicated
8
+ # `Chemicalml::Cli::*Command` class invoked as
9
+ # `MyCommand.new.run(options)`, where `options` is a single hash
10
+ # carrying both positional args (keyed by name) and flags.
11
+ #
12
+ # A `Chemicalml::Logger` bridged to Thor's shell is injected into
13
+ # every command — so info/warn/error get coloured output on the
14
+ # terminal while remaining loggable for library callers.
15
+ class Base < Thor
16
+ private
17
+
18
+ # Build a logger bridged to this Thor instance for coloured output.
19
+ def bridged_logger
20
+ Chemicalml::Logger.default.tap { |l| l.thor_shell = self }
21
+ end
22
+
23
+ public
24
+
25
+ desc 'validate FILE', 'Validate a CML file against its declared convention'
26
+ method_option :json, type: :boolean, aliases: '-j',
27
+ desc: 'Emit machine-readable JSON output'
28
+ def validate(file)
29
+ ValidateCommand.new(logger: bridged_logger).run(file: file, json: options[:json])
30
+ end
31
+
32
+ desc 'inspect FILE', 'Print a tree-style summary of the document'
33
+ def inspect(file)
34
+ InspectCommand.new(logger: bridged_logger).run(file: file)
35
+ end
36
+
37
+ desc 'conventions', 'List registered conventions'
38
+ def conventions
39
+ ConventionsCommand.new(logger: bridged_logger).run({})
40
+ end
41
+
42
+ desc 'dictionaries', 'List built-in dictionaries'
43
+ def dictionaries
44
+ DictionariesCommand.new(logger: bridged_logger).run({})
45
+ end
46
+
47
+ desc 'elements', 'List all CML wire classes'
48
+ def elements
49
+ ElementsCommand.new(logger: bridged_logger).run({})
50
+ end
51
+
52
+ desc 'constraints', 'List all registered constraints'
53
+ def constraints
54
+ ConstraintsCommand.new(logger: bridged_logger).run({})
55
+ end
56
+
57
+ desc 'enums', 'List all XSD enum constants'
58
+ def enums
59
+ EnumsCommand.new(logger: bridged_logger).run({})
60
+ end
61
+
62
+ desc 'info ELEMENT', 'Show details about a CML element'
63
+ def info(element)
64
+ InfoCommand.new(logger: bridged_logger).run(element: element)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Cli
5
+ # Base class for every command. Subclasses implement `#run(options)`.
6
+ #
7
+ # Each command receives a `Chemicalml::Logger` in its constructor.
8
+ # When dispatched via Thor (the normal CLI path), the logger is
9
+ # bridged to Thor's shell for coloured output. When invoked
10
+ # directly (MyCommand.new.run(options)), the logger writes plain
11
+ # text to $stderr.
12
+ class Command
13
+ attr_reader :logger
14
+
15
+ # @param logger [Chemicalml::Logger] the logger to use. Defaults
16
+ # to a plain stderr logger. The Thor dispatcher passes one
17
+ # bridged to Thor's shell for colour.
18
+ def initialize(logger: Chemicalml::Logger.default)
19
+ @logger = logger
20
+ end
21
+
22
+ class << self
23
+ def run(options = {}, logger: Chemicalml::Logger.default)
24
+ new(logger: logger).run(options)
25
+ end
26
+ end
27
+
28
+ def run(_options = {})
29
+ raise NotImplementedError, "#{self.class} must implement #run"
30
+ end
31
+
32
+ private
33
+
34
+ def puts(*args)
35
+ $stdout.puts(*args)
36
+ end
37
+
38
+ def stderr(*args)
39
+ $stderr.puts(*args)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Cli
5
+ # `chemicalml constraints` — list every registered constraint
6
+ # across every convention with its applies_to roles.
7
+ class ConstraintsCommand < Command
8
+ def run(_options = {})
9
+ current = nil
10
+ Chemicalml::Convention::Registry.each_constraint do |conv, klass|
11
+ unless conv.qname == current
12
+ current = conv.qname
13
+ puts ''
14
+ puts "=== #{current} (#{conv.constraint_count}) ==="
15
+ end
16
+ name = klass.name.split('::').last
17
+ applies = klass.applies_to_roles
18
+ roles_str = applies.nil? ? '' : " → #{applies.map { |r| r.name.split('::').last }.join(', ')}"
19
+ puts " #{name}#{roles_str}"
20
+ desc = klass.description
21
+ puts " #{desc}" unless desc == name
22
+ end
23
+ 0
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Cli
5
+ # `chemicalml conventions` — list every registered convention
6
+ # by QName, sorted.
7
+ class ConventionsCommand < Command
8
+ def run(_options = {})
9
+ Chemicalml::Convention::Registry.builtin_qnames.sort.each { |q| puts q }
10
+ 0
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Cli
5
+ # `chemicalml dictionaries` — list built-in YAML dictionaries
6
+ # by short name.
7
+ class DictionariesCommand < Command
8
+ def run(_options = {})
9
+ Chemicalml::Dictionary::Registry.builtin_names.each { |n| puts n }
10
+ 0
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Cli
5
+ # `chemicalml elements` — list every CML wire class registered
6
+ # in Schema3 with its XML element name.
7
+ class ElementsCommand < Command
8
+ def run(_options = {})
9
+ Chemicalml::Cml::Schema3::Configuration.ensure_registered!
10
+ Chemicalml::Cml::Elements::ALL.sort.each do |cls, xml_id|
11
+ puts "#{cls.to_s.ljust(25)} → <#{xml_id}>"
12
+ end
13
+ 0
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Cli
5
+ # `chemicalml enums` — list every `Cml::Enums` constant with
6
+ # its allowed values.
7
+ class EnumsCommand < Command
8
+ def run(_options = {})
9
+ Chemicalml::Cml::Enums.constants(false).sort.each do |name|
10
+ values = Chemicalml::Cml::Enums.const_get(name)
11
+ puts "#{name} (#{values.size}): #{values.to_a.sort.first(20).join(', ')}"
12
+ end
13
+ 0
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Cli
5
+ # `chemicalml info <element>` — show details about a CML element
6
+ # by XML name: Ruby class, role, attributes, applicable constraints.
7
+ class InfoCommand < Command
8
+ def run(options)
9
+ name = options[:element]
10
+ unless name
11
+ logger.error 'info requires an <element-name> argument (e.g. "atom")'
12
+ return 2
13
+ end
14
+
15
+ klass = Chemicalml::Cml.for_xml_name(name)
16
+ unless klass
17
+ stderr "unknown element: #{name}"
18
+ return 2
19
+ end
20
+
21
+ Chemicalml::Cml::Schema3::Configuration.ensure_registered!
22
+ puts "Element: <#{name}>"
23
+ puts "Class: #{klass}"
24
+ role = role_of(klass)
25
+ puts "Role: #{role || '(none)'}"
26
+ puts ''
27
+ puts "Attributes (#{klass.attributes.size}):"
28
+ klass.attributes.each_value do |attr|
29
+ col = attr.collection? ? '[]' : ' '
30
+ puts " #{attr.name}#{col}"
31
+ end
32
+ applicable = constraints_for_role(role)
33
+ return 0 if applicable.empty?
34
+
35
+ puts ''
36
+ puts "Applicable constraints (#{applicable.size}):"
37
+ applicable.each do |(conv, c)|
38
+ puts " [#{conv.qname}] #{c.name.split('::').last}"
39
+ end
40
+ 0
41
+ end
42
+
43
+ private
44
+
45
+ def role_of(klass)
46
+ # Find the most specific Role module included in klass
47
+ # (the one with the longest name that matches Cml::Role::*).
48
+ klass.ancestors.find do |a|
49
+ next false unless a.is_a?(Module)
50
+ next false unless a.name
51
+
52
+ a.name.start_with?('Chemicalml::Cml::Role::')
53
+ end
54
+ end
55
+
56
+ def constraints_for_role(role)
57
+ return [] unless role
58
+
59
+ Chemicalml::Convention::Registry.each_constraint.with_object([]) do |(conv, c), acc|
60
+ roles = c.applies_to_roles
61
+ next if roles.nil?
62
+
63
+ matched = roles.any? do |r|
64
+ role <= r
65
+ rescue StandardError
66
+ false
67
+ end
68
+ next unless matched
69
+
70
+ acc << [conv, c]
71
+ end
72
+ rescue StandardError
73
+ []
74
+ end
75
+ end
76
+ end
77
+ end