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
data/lib/chemicalml.rb CHANGED
@@ -1,49 +1,111 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "lutaml/model"
3
+ require 'lutaml/model'
4
4
 
5
5
  # Chemicalml provides a Ruby object model for the Chemical Markup Language
6
6
  # (CML).
7
7
  #
8
8
  # Top-level entry points:
9
9
  # Chemicalml::Cml::Document.from_xml(xml) # parse CML
10
- # document.to_xml # serialize back to XML
10
+ # document.to_xml # serialize back to XML
11
11
  #
12
12
  # Each CML element is a Lutaml::Model::Serializable subclass with
13
13
  # declared attributes and an XML mapping block. Serialization is
14
14
  # framework-backed — no hand-rolled XML in this library.
15
15
  module Chemicalml
16
- autoload :Cml, "chemicalml/cml"
17
- autoload :ContextConfiguration, "chemicalml/context_configuration"
18
- autoload :Convention, "chemicalml/convention"
19
- autoload :Dictionary, "chemicalml/dictionary"
20
- autoload :Error, "chemicalml/errors"
21
- autoload :Model, "chemicalml/model"
22
- autoload :ParseError, "chemicalml/errors"
23
- autoload :Schema, "chemicalml/schema"
24
- autoload :VERSION, "chemicalml/version"
25
- autoload :VersionedParser, "chemicalml/versioned_parser"
16
+ autoload :Cml, 'chemicalml/cml'
17
+ autoload :Cli, 'chemicalml/cli'
18
+ autoload :ContextConfiguration, 'chemicalml/context_configuration'
19
+ autoload :Convention, 'chemicalml/convention'
20
+ autoload :Dictionary, 'chemicalml/dictionary'
21
+ autoload :Error, 'chemicalml/errors'
22
+ autoload :Logger, 'chemicalml/logger'
23
+ autoload :ParseError, 'chemicalml/errors'
24
+ autoload :Schema, 'chemicalml/schema'
25
+ autoload :VERSION, 'chemicalml/version'
26
+ autoload :VersionedParser, 'chemicalml/versioned_parser'
27
+ autoload :Dictionary, 'chemicalml/dictionary'
28
+ autoload :Error, 'chemicalml/errors'
29
+ autoload :ParseError, 'chemicalml/errors'
30
+ autoload :Schema, 'chemicalml/schema'
31
+ autoload :VERSION, 'chemicalml/version'
32
+ autoload :VersionedParser, 'chemicalml/versioned_parser'
26
33
 
27
34
  module_function
28
35
 
36
+ # Parse a CML XML string into a wire document.
37
+ #
38
+ # @param xml [String] the XML content.
39
+ # @param schema [Symbol] one of `:schema3` (default) or `:schema24`.
40
+ # @param namespace_exist [Boolean] whether the XML already declares
41
+ # the CML namespace. When false, the parser injects it.
42
+ # @return [Chemicalml::Cml::Schema3::Document,
43
+ # Chemicalml::Cml::Schema24::Document,
44
+ # Chemicalml::Cml::Schema3::Molecule, ...] the parsed root,
45
+ # dispatch by detected root element.
46
+ # @raise [ArgumentError] if `xml` is nil/empty or `schema` is unknown.
47
+ # @raise [Chemicalml::ParseError] on malformed XML.
48
+ #
49
+ # @example Parse a Schema3 document
50
+ # doc = Chemicalml.parse("<cml xmlns='...'>...</cml>")
51
+ # @example Parse a Schema2.4 document
52
+ # doc = Chemicalml.parse(xml, schema: :schema24)
29
53
  def parse(xml, schema: :schema3, namespace_exist: true)
30
- raise ArgumentError, "xml must not be nil" if xml.nil?
31
- raise ArgumentError, "xml must not be empty" if xml.to_s.strip.empty?
54
+ raise ArgumentError, 'xml must not be nil' if xml.nil?
55
+ raise ArgumentError, 'xml must not be empty' if xml.to_s.strip.empty?
32
56
 
33
57
  parser_for(schema).parse(xml, namespace_exist: namespace_exist)
34
58
  end
35
59
 
36
- def serialize(document, **opts)
37
- document.to_xml(**opts)
60
+ # Parse a CML file from disk. Convenience wrapper around `parse`
61
+ # that handles the `File.read` boilerplate.
62
+ #
63
+ # @param path [String] path to the CML file.
64
+ # @param schema [Symbol] `:schema3` (default) or `:schema24`.
65
+ # @param namespace_exist [Boolean] forwarded to `parse`.
66
+ # @return (see .parse)
67
+ # @raise [ArgumentError] if the file doesn't exist or is empty.
68
+ def parse_file(path, schema: :schema3, namespace_exist: true)
69
+ raise ArgumentError, "file not found: #{path}" unless File.exist?(path)
70
+
71
+ parse(File.read(path), schema: schema, namespace_exist: namespace_exist)
72
+ end
73
+
74
+ # Serialize a wire document back to XML.
75
+ #
76
+ # @param document [Lutaml::Model::Serializable] a CML wire instance.
77
+ # @param opts [Hash] forwarded to `Lutaml::Model::Serializable#to_xml`.
78
+ # @return [String] the serialized XML.
79
+ def serialize(document, **)
80
+ document.to_xml(**)
38
81
  end
39
82
 
83
+ # Resolve the versioned-parser module for a schema id.
84
+ #
85
+ # @param schema [Symbol] `:schema3` or `:schema24`.
86
+ # @return [Module] the matching `Schema3` or `Schema24` module.
87
+ # @raise [ArgumentError] if `schema` is unknown.
40
88
  def parser_for(schema)
41
89
  case schema.to_sym
42
90
  when :schema3 then Chemicalml::Cml::Schema3
43
91
  when :schema24 then Chemicalml::Cml::Schema24
44
92
  else
45
93
  raise ArgumentError, "unsupported schema: #{schema.inspect} " \
46
- "(supported: :schema3, :schema24)"
94
+ '(supported: :schema3, :schema24)'
47
95
  end
48
96
  end
97
+
98
+ # Auto-detect the convention from the document's root and validate.
99
+ # Top-level convenience for `Convention.detect_and_validate`.
100
+ #
101
+ # @param document [Lutaml::Model::Serializable] the CML document.
102
+ # @param logger [Chemicalml::Logger, nil] optional logger for progress.
103
+ # @return [Chemicalml::Convention::ValidationReport] the report.
104
+ # @raise [ArgumentError] if no convention is declared.
105
+ def validate(document, logger: nil)
106
+ logger&.info 'Auto-detecting convention…'
107
+ report = Convention.detect_and_validate(document)
108
+ logger&.info "Validation complete: #{report.size} violation(s)"
109
+ report
110
+ end
49
111
  end
@@ -1,41 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "net/http"
4
- require "uri"
5
- require "fileutils"
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'fileutils'
6
6
 
7
7
  # Idempotent fetcher for xml-cml.org reference material.
8
8
  # Re-running this task only downloads files that are missing locally.
9
9
  module ReferenceFetch
10
- BASE = "http://www.xml-cml.org".freeze
11
- LICENSE = "Source: <url>. Distributed by xml-cml.org under CC-BY-3.0.".freeze
10
+ BASE = 'http://www.xml-cml.org'
11
+ LICENSE = 'Source: <url>. Distributed by xml-cml.org under CC-BY-3.0.'
12
12
 
13
13
  # Map of remote URL => local path under reference-docs/.
14
14
  SOURCES = {
15
15
  # Conventions
16
- "#{BASE}/convention/" => "reference-docs/conventions/index.md",
17
- "#{BASE}/convention/molecular" => "reference-docs/conventions/molecular.md",
18
- "#{BASE}/convention/compchem" => "reference-docs/conventions/compchem.md",
19
- "#{BASE}/convention/dictionary" => "reference-docs/conventions/dictionary.md",
20
- "#{BASE}/convention/unit-dictionary" => "reference-docs/conventions/unit-dictionary.md",
21
- "#{BASE}/convention/unitType-dictionary" => "reference-docs/conventions/unitType-dictionary.md",
16
+ "#{BASE}/convention/" => 'reference-docs/conventions/index.md',
17
+ "#{BASE}/convention/molecular" => 'reference-docs/conventions/molecular.md',
18
+ "#{BASE}/convention/compchem" => 'reference-docs/conventions/compchem.md',
19
+ "#{BASE}/convention/dictionary" => 'reference-docs/conventions/dictionary.md',
20
+ "#{BASE}/convention/unit-dictionary" => 'reference-docs/conventions/unit-dictionary.md',
21
+ "#{BASE}/convention/unitType-dictionary" => 'reference-docs/conventions/unitType-dictionary.md',
22
22
  # Dictionaries
23
- "#{BASE}/dictionary/" => "reference-docs/dictionaries/index.md",
24
- "#{BASE}/dictionary/compchem/" => "reference-docs/dictionaries/compchem.md",
25
- "#{BASE}/dictionary/cml/" => "reference-docs/dictionaries/cml.md",
26
- "#{BASE}/dictionary/cml/name/" => "reference-docs/dictionaries/cml-name.md",
27
- "#{BASE}/dictionary/cml/formula/" => "reference-docs/dictionaries/cml-formula.md",
28
- "#{BASE}/dictionary/cif/" => "reference-docs/dictionaries/cif.md",
23
+ "#{BASE}/dictionary/" => 'reference-docs/dictionaries/index.md',
24
+ "#{BASE}/dictionary/compchem/" => 'reference-docs/dictionaries/compchem.md',
25
+ "#{BASE}/dictionary/cml/" => 'reference-docs/dictionaries/cml.md',
26
+ "#{BASE}/dictionary/cml/name/" => 'reference-docs/dictionaries/cml-name.md',
27
+ "#{BASE}/dictionary/cml/formula/" => 'reference-docs/dictionaries/cml-formula.md',
28
+ "#{BASE}/dictionary/cif/" => 'reference-docs/dictionaries/cif.md',
29
29
  # Unit dictionaries
30
- "#{BASE}/unit/si/" => "reference-docs/dictionaries/unit-si.md",
31
- "#{BASE}/unit/nonSi/" => "reference-docs/dictionaries/unit-nonSi.md",
32
- "#{BASE}/unit/unitType/" => "reference-docs/dictionaries/unit-type.md",
30
+ "#{BASE}/unit/si/" => 'reference-docs/dictionaries/unit-si.md',
31
+ "#{BASE}/unit/nonSi/" => 'reference-docs/dictionaries/unit-nonSi.md',
32
+ "#{BASE}/unit/unitType/" => 'reference-docs/dictionaries/unit-type.md',
33
33
  # Schema / spec
34
- "https://www.xml-cml.org/schema/cmllite.html" => "reference-docs/cmllite.html",
34
+ 'https://www.xml-cml.org/schema/cmllite.html' => 'reference-docs/cmllite.html',
35
35
  # Examples
36
- "#{BASE}/examples/schema3/molecular/" => "reference-docs/examples/schema3-molecular-index.md",
37
- "#{BASE}/examples/schema3/compchem/" => "reference-docs/examples/schema3-compchem-index.md",
38
- "#{BASE}/examples/schema24/" => "reference-docs/examples/schema24-index.md"
36
+ "#{BASE}/examples/schema3/molecular/" => 'reference-docs/examples/schema3-molecular-index.md',
37
+ "#{BASE}/examples/schema3/compchem/" => 'reference-docs/examples/schema3-compchem-index.md',
38
+ "#{BASE}/examples/schema24/" => 'reference-docs/examples/schema24-index.md'
39
39
  }.freeze
40
40
 
41
41
  def self.fetch_all
@@ -60,7 +60,7 @@ module ReferenceFetch
60
60
  return
61
61
  end
62
62
 
63
- if path.end_with?(".html")
63
+ if path.end_with?('.html')
64
64
  File.write(full, body)
65
65
  else
66
66
  File.write(full, header(url) + body)
@@ -73,19 +73,18 @@ module ReferenceFetch
73
73
  end
74
74
 
75
75
  def self.http_get(url, limit = 5)
76
- raise "too many redirects" if limit.zero?
76
+ raise 'too many redirects' if limit.zero?
77
77
 
78
78
  uri = URI(url)
79
79
  http = Net::HTTP.new(uri.host, uri.port)
80
80
  http.use_ssl = true if uri.is_a?(URI::HTTPS)
81
81
  http.read_timeout = 30
82
- req = Net::HTTP::Get.new(uri, "User-Agent" => "chemicalml-gem-reference-fetch")
82
+ req = Net::HTTP::Get.new(uri, 'User-Agent' => 'chemicalml-gem-reference-fetch')
83
83
  res = http.request(req)
84
84
 
85
85
  case res
86
86
  when Net::HTTPSuccess then res.body
87
- when Net::HTTPRedirection then http_get(res["location"], limit - 1)
88
- else nil
87
+ when Net::HTTPRedirection then http_get(res['location'], limit - 1)
89
88
  end
90
89
  rescue StandardError => e
91
90
  warn " error fetching #{url}: #{e.message}"
@@ -93,12 +92,12 @@ module ReferenceFetch
93
92
  end
94
93
 
95
94
  def self.project_root
96
- File.expand_path("..", __dir__)
95
+ File.expand_path('..', __dir__)
97
96
  end
98
97
  end
99
98
 
100
99
  namespace :reference do
101
- desc "Download all xml-cml.org reference material into reference-docs/. Idempotent."
100
+ desc 'Download all xml-cml.org reference material into reference-docs/. Idempotent.'
102
101
  task :fetch do
103
102
  ReferenceFetch.fetch_all
104
103
  end
@@ -1,21 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "net/http"
4
- require "uri"
5
- require "fileutils"
6
- require "cgi"
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'fileutils'
6
+ require 'cgi'
7
7
 
8
8
  # Idempotent scraper for xml-cml.org example CML files. Walks the
9
9
  # HTML index pages, extracts the embedded CML, and writes one `.cml`
10
10
  # file per example under `spec/fixtures/`.
11
11
  module FixturesScrape
12
- BASE = "http://www.xml-cml.org".freeze
12
+ BASE = 'http://www.xml-cml.org'
13
13
 
14
14
  # Map of remote index URL => local target directory.
15
15
  INDEXES = {
16
- "#{BASE}/examples/schema3/molecular/" => "spec/fixtures/schema3/molecular",
17
- "#{BASE}/examples/schema3/compchem/" => "spec/fixtures/schema3/compchem",
18
- "#{BASE}/examples/schema24/" => "spec/fixtures/schema24"
16
+ "#{BASE}/examples/schema3/molecular/" => 'spec/fixtures/schema3/molecular',
17
+ "#{BASE}/examples/schema3/compchem/" => 'spec/fixtures/schema3/compchem',
18
+ "#{BASE}/examples/schema24/" => 'spec/fixtures/schema24'
19
19
  }.freeze
20
20
 
21
21
  def self.scrape_all
@@ -41,7 +41,7 @@ module FixturesScrape
41
41
  end
42
42
 
43
43
  def self.extract_example_links(html, base_url)
44
- hrefs = html.scan(/<a[^>]+href=["']([^"']+\.html?)["'][^>]*>([^<]+)<\/a>/i)
44
+ hrefs = html.scan(%r{<a[^>]+href=["']([^"']+\.html?)["'][^>]*>([^<]+)</a>}i)
45
45
  hrefs.map do |raw_href, raw_title|
46
46
  href = URI.join(base_url, raw_href).to_s
47
47
  title = CGI.unescapeHTML(raw_title.strip)
@@ -70,63 +70,62 @@ module FixturesScrape
70
70
 
71
71
  cml = extract_cml(body)
72
72
  unless cml
73
- log_failure(url, target_dir, reason: "no <cml> block found")
73
+ log_failure(url, target_dir, reason: 'no <cml> block found')
74
74
  return
75
75
  end
76
76
 
77
- File.write(path, cml.strip + "\n")
77
+ File.write(path, "#{cml.strip}\n")
78
78
  puts " saved #{slug}.cml"
79
79
  end
80
80
 
81
81
  def self.extract_cml(html)
82
- match = html.match(/<cml[^>]*>.*<\/cml>/m)
82
+ match = html.match(%r{<cml[^>]*>.*</cml>}m)
83
83
  return match[0] if match
84
84
 
85
- match = html.match(/<(?:cml|module|dictionary|molecule|reaction|unitList|unitTypeList)[^>]*>.*<\/(?:cml|module|dictionary|molecule|reaction|unitList|unitTypeList)>/m)
85
+ match = html.match(%r{<(?:cml|module|dictionary|molecule|reaction|unitList|unitTypeList)[^>]*>.*</(?:cml|module|dictionary|molecule|reaction|unitList|unitTypeList)>}m)
86
86
  match && match[0]
87
87
  end
88
88
 
89
89
  def self.slugify(title)
90
90
  title.downcase
91
- .gsub(/[^a-z0-9]+/, "_")
92
- .gsub(/^_+|_+$/, "")
91
+ .gsub(/[^a-z0-9]+/, '_')
92
+ .gsub(/^_+|_+$/, '')
93
93
  end
94
94
 
95
95
  def self.http_get(url, limit = 5)
96
- raise "too many redirects" if limit.zero?
96
+ raise 'too many redirects' if limit.zero?
97
97
 
98
98
  uri = URI(url)
99
99
  http = Net::HTTP.new(uri.host, uri.port)
100
100
  http.use_ssl = true if uri.is_a?(URI::HTTPS)
101
101
  http.read_timeout = 30
102
- req = Net::HTTP::Get.new(uri, "User-Agent" => "chemicalml-gem-fixtures-scrape")
102
+ req = Net::HTTP::Get.new(uri, 'User-Agent' => 'chemicalml-gem-fixtures-scrape')
103
103
  res = http.request(req)
104
104
 
105
105
  case res
106
106
  when Net::HTTPSuccess then res.body
107
- when Net::HTTPRedirection then http_get(res["location"], limit - 1)
108
- else nil
107
+ when Net::HTTPRedirection then http_get(res['location'], limit - 1)
109
108
  end
110
109
  rescue StandardError => e
111
110
  warn " error fetching #{url}: #{e.message}"
112
111
  nil
113
112
  end
114
113
 
115
- def self.log_failure(url, target_dir, reason: "fetch failed")
116
- File.open(File.join(target_dir, ".scrape-log"), "a") do |f|
114
+ def self.log_failure(url, target_dir, reason: 'fetch failed')
115
+ File.open(File.join(target_dir, '.scrape-log'), 'a') do |f|
117
116
  f.puts "#{Time.now.utc.iso8601}\t#{url}\t#{reason}"
118
117
  end
119
118
  end
120
119
 
121
120
  def self.project_root
122
- File.expand_path("../..", __dir__)
121
+ File.expand_path('../..', __dir__)
123
122
  end
124
123
  end
125
124
 
126
- require "time"
125
+ require 'time'
127
126
 
128
127
  namespace :fixtures do
129
- desc "Scrape every xml-cml.org example CML into spec/fixtures/. Idempotent."
128
+ desc 'Scrape every xml-cml.org example CML into spec/fixtures/. Idempotent.'
130
129
  task :scrape do
131
130
  FixturesScrape.scrape_all
132
131
  end