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,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module UnitDictionary
6
+ module Constraints
7
+ # A `<unitList>` element MUST have a `namespace` attribute
8
+ # whose value is a valid URI defining the scope within which
9
+ # the unit ids are unique. SHOULD end with `/` or `#`.
10
+ class UnitListMustHaveNamespace < Chemicalml::Convention::Constraint::NodeConstraint
11
+ self.description = '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 `#`.'
12
+ applies_to Chemicalml::Cml::Role::UnitList
13
+ def check_node(node, path)
14
+ violations = []
15
+ ns = node.namespace.to_s
16
+ if ns.empty?
17
+ violations << violation(path: path.join('/'),
18
+ message: 'unitList must have a namespace attribute')
19
+ elsif !ns.end_with?('/', '#')
20
+ violations << violation(path: path.join('/'),
21
+ message: "unitList namespace #{ns.inspect} should end with / or #",
22
+ severity: :warning)
23
+ end
24
+ violations
25
+ end
26
+
27
+ private
28
+
29
+ def unit_list?(node)
30
+ node.is_a?(Chemicalml::Cml::Role::UnitList)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module UnitDictionary
6
+ module Constraints
7
+ # A `<unit>` element MUST contain a single `<definition>`
8
+ # child element with XHTML content.
9
+ class UnitMustContainDefinition < Chemicalml::Convention::Constraint::NodeConstraint
10
+ self.description = 'A `<unit>` element MUST contain a single `<definition>` child element with XHTML content.'
11
+ applies_to Chemicalml::Cml::Role::Unit
12
+ def check_node(node, path)
13
+ return [] unless node.definition.to_s.strip.empty?
14
+
15
+ [violation(path: path.join('/'),
16
+ message: "unit #{node.id.inspect} must contain a single definition child")]
17
+ end
18
+
19
+ private
20
+
21
+ def unit?(node)
22
+ node.is_a?(Chemicalml::Cml::Role::Unit)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module UnitDictionary
6
+ module Constraints
7
+ # A `<unit>` element MUST have an `id` attribute, unique
8
+ # within the unitList.
9
+ class UnitMustHaveId < Chemicalml::Convention::Constraint::NodeConstraint
10
+ self.description = 'A `<unit>` element MUST have an `id` attribute, unique within the unitList.'
11
+ applies_to Chemicalml::Cml::Role::Unit
12
+ def check_node(node, path)
13
+ return [] unless node.id.to_s.empty?
14
+
15
+ [violation(path: path.join('/'),
16
+ message: 'unit must have an id attribute unique within the unitList')]
17
+ end
18
+
19
+ private
20
+
21
+ def unit?(node)
22
+ node.is_a?(Chemicalml::Cml::Role::Unit)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module UnitDictionary
6
+ module Constraints
7
+ # A `<unit>` element MUST have at least one of `multiplierToSI`
8
+ # or `constantToSI` — to define the conversion to the parent
9
+ # SI unit. Per the unit-dictionary convention spec.
10
+ class UnitMustHaveMultiplierOrConstantToSi < Chemicalml::Convention::Constraint::NodeConstraint
11
+ self.description = '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.'
12
+ applies_to Chemicalml::Cml::Role::Unit
13
+
14
+ def check_node(node, _path)
15
+ has_multiplier = !node.multiplier_to_si.to_s.strip.empty?
16
+ has_constant = !node.constant_to_si.to_s.strip.empty?
17
+ return [] if has_multiplier || has_constant
18
+
19
+ [violation(path: yield_path(node),
20
+ message: "unit #{node.id.inspect} must have at least one of " \
21
+ 'multiplierToSI or constantToSI to convert to parent SI')]
22
+ end
23
+
24
+ private
25
+
26
+ def yield_path(node)
27
+ id = node.node_id
28
+ id ? "unit[#{id}]" : 'unit'
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module UnitDictionary
6
+ module Constraints
7
+ # A `<unit>` element MUST have a `parentSI` attribute — a
8
+ # QName referencing the SI unit it derives from. Per the
9
+ # unit-dictionary convention spec.
10
+ class UnitMustHaveParentSi < Chemicalml::Convention::Constraint::NodeConstraint
11
+ self.description = 'A `<unit>` element MUST have a `parentSI` attribute — a QName referencing the SI unit it derives from. Per the unit-dictionary convention spec.'
12
+ applies_to Chemicalml::Cml::Role::Unit
13
+
14
+ def check_node(node, _path)
15
+ return [] unless node.parent_si.to_s.strip.empty?
16
+
17
+ [violation(path: yield_path(node),
18
+ message: "unit #{node.id.inspect} must have a parentSI attribute " \
19
+ '(QName referencing the parent SI unit)')]
20
+ end
21
+
22
+ private
23
+
24
+ def yield_path(node)
25
+ id = node.node_id
26
+ id ? "unit[#{id}]" : 'unit'
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -8,22 +8,22 @@ module Chemicalml
8
8
  # at least one of `multiplierToSI`/`constantToSI`, and
9
9
  # `unitType` per the unit-dictionary convention.
10
10
  class UnitMustHaveSymbolAndUnitType < Chemicalml::Convention::Constraint::NodeConstraint
11
+ self.description = 'Every unit MUST have `id`, `title`, `symbol`, `parentSI`, at least one of `multiplierToSI`/`constantToSI`, and `unitType` per the unit-dictionary convention.'
12
+ applies_to Chemicalml::Cml::Role::Unit
11
13
  REQUIRED = %i[title symbol parent_si unit_type].freeze
12
14
 
13
15
  def check_node(node, path)
14
- return [] unless unit?(node)
15
-
16
16
  violations = []
17
17
  REQUIRED.each do |attr|
18
18
  val = node.public_send(attr)
19
19
  next unless val.to_s.empty?
20
20
 
21
- violations << violation(path: path.join("/"),
21
+ violations << violation(path: path.join('/'),
22
22
  message: "unit must have #{attr} (id=#{node.id.inspect})")
23
23
  end
24
24
 
25
25
  unless has_si_conversion?(node)
26
- violations << violation(path: path.join("/"),
26
+ violations << violation(path: path.join('/'),
27
27
  message: "unit must have multiplierToSI or constantToSI (id=#{node.id.inspect})")
28
28
  end
29
29
 
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module UnitDictionary
6
+ module Constraints
7
+ # A `<unit>` element MUST have a `title` attribute (the full
8
+ # human-readable name of the unit). Per the unit-dictionary
9
+ # convention spec.
10
+ class UnitMustHaveTitle < Chemicalml::Convention::Constraint::NodeConstraint
11
+ self.description = 'A `<unit>` element MUST have a `title` attribute (the full human-readable name of the unit). Per the unit-dictionary convention spec.'
12
+ applies_to Chemicalml::Cml::Role::Unit
13
+
14
+ def check_node(node, _path)
15
+ return [] unless node.title.to_s.strip.empty?
16
+
17
+ [violation(path: yield_path(node),
18
+ message: "unit #{node.id.inspect} must have a title attribute " \
19
+ '(the full human-readable name of the unit)')]
20
+ end
21
+
22
+ private
23
+
24
+ def yield_path(node)
25
+ id = node.node_id
26
+ id ? "unit[#{id}]" : 'unit'
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module UnitDictionary
6
+ module Constraints
7
+ # A `<unit>`'s `parentSI` attribute SHOULD reference a unit
8
+ # that exists in a built-in dictionary (typically the SI
9
+ # dictionary). Catches typos like `parentSI="si:metr"`.
10
+ #
11
+ # Cross-component check parallel to UnitUnitTypeShouldResolve
12
+ # and the molecular convention's DictRefShouldResolve.
13
+ class UnitParentSiShouldResolve < Chemicalml::Convention::Constraint::NodeConstraint
14
+ self.description = "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\"`."
15
+ applies_to Chemicalml::Cml::Role::Unit
16
+
17
+ def check_node(node, _path)
18
+ parent_si = node.parent_si.to_s.strip
19
+ return [] if parent_si.empty?
20
+ return [] if resolves?(parent_si)
21
+
22
+ [violation(path: yield_path(node),
23
+ message: "unit #{node.id.inspect} parentSI #{parent_si.inspect} does not " \
24
+ 'resolve against any built-in unit dictionary',
25
+ severity: :warning,
26
+ value: parent_si)]
27
+ end
28
+
29
+ private
30
+
31
+ def resolves?(parent_si)
32
+ !Chemicalml::Dictionary::Registry.lookup(parent_si).nil?
33
+ rescue StandardError
34
+ false
35
+ end
36
+
37
+ def yield_path(node)
38
+ id = node.node_id
39
+ id ? "unit[#{id}]" : 'unit'
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module UnitDictionary
6
+ module Constraints
7
+ # A `<unit>`'s `unitType` attribute SHOULD reference a unitType
8
+ # that exists in a built-in unitType-dictionary. Catches typos
9
+ # like `unitType="unitType:lenght"` (misspelled).
10
+ #
11
+ # The lookup uses the dictionary registry, which inspects every
12
+ # built-in dictionary. Cross-component check parallel to
13
+ # DictRefShouldResolve in the molecular convention.
14
+ class UnitUnitTypeShouldResolve < Chemicalml::Convention::Constraint::NodeConstraint
15
+ self.description = "A `<unit>`'s `unitType` attribute SHOULD reference a unitType that exists in a built-in unitType-dictionary. Catches typos like `unitType=\"unitType:lenght\"` (misspelled)."
16
+ applies_to Chemicalml::Cml::Role::Unit
17
+
18
+ def check_node(node, _path)
19
+ unit_type = node.unit_type.to_s.strip
20
+ return [] if unit_type.empty?
21
+ return [] if resolves?(unit_type)
22
+
23
+ [violation(path: yield_path(node),
24
+ message: "unit #{node.id.inspect} unitType #{unit_type.inspect} does not " \
25
+ 'resolve against any built-in unitType-dictionary',
26
+ severity: :warning,
27
+ value: unit_type)]
28
+ end
29
+
30
+ private
31
+
32
+ def resolves?(unit_type)
33
+ !Chemicalml::Dictionary::Registry.lookup(unit_type).nil?
34
+ rescue StandardError
35
+ false
36
+ end
37
+
38
+ def yield_path(node)
39
+ id = node.node_id
40
+ id ? "unit[#{id}]" : 'unit'
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -5,7 +5,25 @@ module Chemicalml
5
5
  module UnitDictionary
6
6
  module Constraints
7
7
  autoload :UnitMustHaveSymbolAndUnitType,
8
- "chemicalml/convention/unit_dictionary/constraints/unit_must_have_symbol_and_unit_type"
8
+ 'chemicalml/convention/unit_dictionary/constraints/unit_must_have_symbol_and_unit_type'
9
+ autoload :UnitMustHaveId,
10
+ 'chemicalml/convention/unit_dictionary/constraints/unit_must_have_id'
11
+ autoload :UnitMustContainDefinition,
12
+ 'chemicalml/convention/unit_dictionary/constraints/unit_must_contain_definition'
13
+ autoload :UnitListMustHaveNamespace,
14
+ 'chemicalml/convention/unit_dictionary/constraints/unit_list_must_have_namespace'
15
+ autoload :UnitListMustContainAtLeastOneUnit,
16
+ 'chemicalml/convention/unit_dictionary/constraints/unit_list_must_contain_at_least_one_unit'
17
+ autoload :UnitMustHaveTitle,
18
+ 'chemicalml/convention/unit_dictionary/constraints/unit_must_have_title'
19
+ autoload :UnitMustHaveParentSi,
20
+ 'chemicalml/convention/unit_dictionary/constraints/unit_must_have_parent_si'
21
+ autoload :UnitMustHaveMultiplierOrConstantToSi,
22
+ 'chemicalml/convention/unit_dictionary/constraints/unit_must_have_multiplier_or_constant_to_si'
23
+ autoload :UnitUnitTypeShouldResolve,
24
+ 'chemicalml/convention/unit_dictionary/constraints/unit_unit_type_should_resolve'
25
+ autoload :UnitParentSiShouldResolve,
26
+ 'chemicalml/convention/unit_dictionary/constraints/unit_parent_si_should_resolve'
9
27
  end
10
28
  end
11
29
  end
@@ -8,9 +8,9 @@ module Chemicalml
8
8
  module UnitDictionary
9
9
  extend Base
10
10
 
11
- autoload :Constraints, "chemicalml/convention/unit_dictionary/constraints"
11
+ autoload :Constraints, 'chemicalml/convention/unit_dictionary/constraints'
12
12
 
13
- QNAME = "convention:unit-dictionary".freeze
13
+ QNAME = 'convention:unit-dictionary'
14
14
  NAMESPACE_URI = "#{Chemicalml::Convention::CONVENTION_NAMESPACE}unit-dictionary".freeze
15
15
 
16
16
  def self.qname
@@ -22,6 +22,15 @@ module Chemicalml
22
22
  end
23
23
 
24
24
  register Constraints::UnitMustHaveSymbolAndUnitType
25
+ register Constraints::UnitMustHaveId
26
+ register Constraints::UnitMustContainDefinition
27
+ register Constraints::UnitListMustHaveNamespace
28
+ register Constraints::UnitListMustContainAtLeastOneUnit
29
+ register Constraints::UnitMustHaveTitle
30
+ register Constraints::UnitMustHaveParentSi
31
+ register Constraints::UnitMustHaveMultiplierOrConstantToSi
32
+ register Constraints::UnitUnitTypeShouldResolve
33
+ register Constraints::UnitParentSiShouldResolve
25
34
  end
26
35
  end
27
36
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module UnitTypeDictionary
6
+ module Constraints
7
+ # A `<unitType>` `id` MUST match `[A-Za-z][A-Za-z0-9._-]*`.
8
+ class UnitTypeIdMustMatchPattern < Chemicalml::Convention::Constraint::NodeConstraint
9
+ self.description = 'A `<unitType>` `id` MUST match `[A-Za-z][A-Za-z0-9._-]*`.'
10
+ applies_to Chemicalml::Cml::Role::UnitType
11
+ PATTERN = /\A[A-Za-z][A-Za-z0-9._-]*\z/
12
+
13
+ def check_node(node, path)
14
+ id = node.id.to_s
15
+ return [] if id.empty?
16
+ return [] if id.match?(PATTERN)
17
+
18
+ [violation(path: path.join('/'),
19
+ message: "unitType id #{id.inspect} must match [A-Za-z][A-Za-z0-9._-]*")]
20
+ end
21
+
22
+ private
23
+
24
+ def unit_type?(node)
25
+ node.is_a?(Chemicalml::Cml::Role::UnitType)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module UnitTypeDictionary
6
+ module Constraints
7
+ # A `<unitTypeList>` MUST contain one or more `<unitType>`
8
+ # children.
9
+ class UnitTypeListMustContainAtLeastOneUnitType < Chemicalml::Convention::Constraint::NodeConstraint
10
+ self.description = 'A `<unitTypeList>` MUST contain one or more `<unitType>` children.'
11
+ applies_to Chemicalml::Cml::Role::UnitTypeList
12
+ def check_node(node, path)
13
+ return [] if (node.unit_types || []).length.positive?
14
+
15
+ [violation(path: path.join('/'),
16
+ message: 'unitTypeList must contain at least one unitType child')]
17
+ end
18
+
19
+ private
20
+
21
+ def unit_type_list?(node)
22
+ node.is_a?(Chemicalml::Cml::Role::UnitTypeList)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module UnitTypeDictionary
6
+ module Constraints
7
+ # A `<unitTypeList>` MUST have a `namespace` attribute (valid
8
+ # URI, SHOULD end with `/` or `#`).
9
+ class UnitTypeListMustHaveNamespace < Chemicalml::Convention::Constraint::NodeConstraint
10
+ self.description = 'A `<unitTypeList>` MUST have a `namespace` attribute (valid URI, SHOULD end with `/` or `#`).'
11
+ applies_to Chemicalml::Cml::Role::UnitTypeList
12
+ def check_node(node, path)
13
+ violations = []
14
+ ns = node.namespace.to_s
15
+ if ns.empty?
16
+ violations << violation(path: path.join('/'),
17
+ message: 'unitTypeList must have a namespace attribute')
18
+ elsif !ns.end_with?('/', '#')
19
+ violations << violation(path: path.join('/'),
20
+ message: "unitTypeList namespace #{ns.inspect} should end with / or #",
21
+ severity: :warning)
22
+ end
23
+ violations
24
+ end
25
+
26
+ private
27
+
28
+ def unit_type_list?(node)
29
+ node.is_a?(Chemicalml::Cml::Role::UnitTypeList)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ module UnitTypeDictionary
6
+ module Constraints
7
+ # A `<unitType>` MUST contain a single `<definition>` child
8
+ # with XHTML content.
9
+ class UnitTypeMustContainDefinition < Chemicalml::Convention::Constraint::NodeConstraint
10
+ self.description = 'A `<unitType>` MUST contain a single `<definition>` child with XHTML content.'
11
+ applies_to Chemicalml::Cml::Role::UnitType
12
+ def check_node(node, path)
13
+ return [] unless node.definition.to_s.strip.empty?
14
+
15
+ [violation(path: path.join('/'),
16
+ message: "unitType #{node.id.inspect} must contain a single definition child")]
17
+ end
18
+
19
+ private
20
+
21
+ def unit_type?(node)
22
+ node.is_a?(Chemicalml::Cml::Role::UnitType)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -7,17 +7,17 @@ module Chemicalml
7
7
  # Every unitType MUST have an `id` and a `name` attribute per
8
8
  # the unitType-dictionary convention.
9
9
  class UnitTypeMustHaveIdAndName < Chemicalml::Convention::Constraint::NodeConstraint
10
+ self.description = 'Every unitType MUST have an `id` and a `name` attribute per the unitType-dictionary convention.'
11
+ applies_to Chemicalml::Cml::Role::UnitType
10
12
  def check_node(node, path)
11
- return [] unless unit_type?(node)
12
-
13
13
  violations = []
14
14
  if node.id.to_s.empty?
15
- violations << violation(path: path.join("/"),
16
- message: "unitType must have an id")
15
+ violations << violation(path: path.join('/'),
16
+ message: 'unitType must have an id')
17
17
  end
18
18
  if node.name.to_s.empty?
19
- violations << violation(path: path.join("/"),
20
- message: "unitType must have a name")
19
+ violations << violation(path: path.join('/'),
20
+ message: 'unitType must have a name')
21
21
  end
22
22
  violations
23
23
  end
@@ -5,7 +5,15 @@ module Chemicalml
5
5
  module UnitTypeDictionary
6
6
  module Constraints
7
7
  autoload :UnitTypeMustHaveIdAndName,
8
- "chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_have_id_and_name"
8
+ 'chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_have_id_and_name'
9
+ autoload :UnitTypeIdMustMatchPattern,
10
+ 'chemicalml/convention/unit_type_dictionary/constraints/unit_type_id_must_match_pattern'
11
+ autoload :UnitTypeMustContainDefinition,
12
+ 'chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_contain_definition'
13
+ autoload :UnitTypeListMustHaveNamespace,
14
+ 'chemicalml/convention/unit_type_dictionary/constraints/unit_type_list_must_have_namespace'
15
+ autoload :UnitTypeListMustContainAtLeastOneUnitType,
16
+ 'chemicalml/convention/unit_type_dictionary/constraints/unit_type_list_must_contain_at_least_one_unit_type'
9
17
  end
10
18
  end
11
19
  end
@@ -8,9 +8,9 @@ module Chemicalml
8
8
  module UnitTypeDictionary
9
9
  extend Base
10
10
 
11
- autoload :Constraints, "chemicalml/convention/unit_type_dictionary/constraints"
11
+ autoload :Constraints, 'chemicalml/convention/unit_type_dictionary/constraints'
12
12
 
13
- QNAME = "convention:unitType-dictionary".freeze
13
+ QNAME = 'convention:unitType-dictionary'
14
14
  NAMESPACE_URI = "#{Chemicalml::Convention::CONVENTION_NAMESPACE}unitType-dictionary".freeze
15
15
 
16
16
  def self.qname
@@ -22,6 +22,10 @@ module Chemicalml
22
22
  end
23
23
 
24
24
  register Constraints::UnitTypeMustHaveIdAndName
25
+ register Constraints::UnitTypeIdMustMatchPattern
26
+ register Constraints::UnitTypeMustContainDefinition
27
+ register Constraints::UnitTypeListMustHaveNamespace
28
+ register Constraints::UnitTypeListMustContainAtLeastOneUnitType
25
29
  end
26
30
  end
27
31
  end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chemicalml
4
+ module Convention
5
+ # Value object wrapping the violations returned by `validate`.
6
+ # Gives callers severity-based views without losing the raw array
7
+ # (still exposed as `#violations`).
8
+ #
9
+ # Constructed by `Convention.validate_report` (new API) —
10
+ # `Convention.validate` keeps its array return shape so existing
11
+ # callers don't break.
12
+ class ValidationReport
13
+ attr_reader :violations
14
+
15
+ def initialize(violations)
16
+ @violations = violations.to_a.freeze
17
+ end
18
+
19
+ def errors
20
+ @errors ||= violations.select(&:error?).freeze
21
+ end
22
+
23
+ def warnings
24
+ @warnings ||= violations.select(&:warning?).freeze
25
+ end
26
+
27
+ def ok?
28
+ errors.empty?
29
+ end
30
+
31
+ def has_warnings?
32
+ warnings.any?
33
+ end
34
+
35
+ def size
36
+ violations.length
37
+ end
38
+
39
+ def +(other)
40
+ return other if other.nil?
41
+
42
+ raise ArgumentError, "cannot combine ValidationReport with #{other.class}" unless other.is_a?(ValidationReport)
43
+
44
+ ValidationReport.new(violations + other.violations)
45
+ end
46
+
47
+ def ==(other)
48
+ other.is_a?(ValidationReport) && violations == other.violations
49
+ end
50
+ alias eql? ==
51
+
52
+ def hash
53
+ violations.hash
54
+ end
55
+
56
+ def to_s
57
+ "#{size} violation(s): #{errors.length} error(s), #{warnings.length} warning(s)"
58
+ end
59
+
60
+ # Human-readable multi-line summary. Suitable for CLI output
61
+ # and logging. Each violation is rendered with severity, path,
62
+ # and message (and value when present).
63
+ #
64
+ # @return [String]
65
+ def summary
66
+ return 'OK — no violations' if ok? && !has_warnings?
67
+
68
+ lines = []
69
+ lines << "Errors: #{errors.size}, Warnings: #{warnings.size}"
70
+ unless errors.empty?
71
+ lines << ''
72
+ lines << 'Errors:'
73
+ errors.each { |v| lines << " ERROR #{v.path}: #{v.message}#{value_suffix(v)}" }
74
+ end
75
+ unless warnings.empty?
76
+ lines << ''
77
+ lines << 'Warnings:'
78
+ warnings.each { |v| lines << " WARN #{v.path}: #{v.message}#{value_suffix(v)}" }
79
+ end
80
+ lines.join("\n")
81
+ end
82
+
83
+ private
84
+
85
+ def value_suffix(violation)
86
+ violation.value.nil? ? '' : " (value=#{violation.value.inspect})"
87
+ end
88
+ end
89
+ end
90
+ end