asciichem 0.31.11 → 0.31.13
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/README.adoc +86 -1
- data/lib/asciichem/citation.rb +10 -4
- data/lib/asciichem/resolver/common_chemistry.rb +8 -9
- data/lib/asciichem/resolver.rb +22 -11
- data/lib/asciichem/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 13b35ffffbfa3349f95961b56e7dae9e74d55a116554a76c1175f3f74e347183
|
|
4
|
+
data.tar.gz: ea78a952280a73728fbd27cff80af8e3eebf163e45b8428ece6171a588471b55
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 538a97d2151764c9c84026ecccf32380ca82393158ac56d85af050aceab459ca47eb23a7828fb48bb5ed9fbe8289b11d2a5583bd8bd8b96f40ee20002247d5d8
|
|
7
|
+
data.tar.gz: 6edff89d3e29935de3227db81ef9126d6d7ce46cf0c39d58343114522d88205de9d969d99f760e6bd8602bc71f85fd80310b8730684769c31ec9aa21fa8f0413
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,32 @@
|
|
|
3
3
|
All notable changes to AsciiChem are documented here.
|
|
4
4
|
This project follows [Semantic Versioning](https://semver.org/).
|
|
5
5
|
|
|
6
|
+
## [0.31.13] - 2026-11-11
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
- `Resolver.resolve_all` isolates source-level failures: a live probe
|
|
10
|
+
found the Common Chemistry API now answers 401 without an API key
|
|
11
|
+
(CAS key-gated the endpoint); with it registered by default, that
|
|
12
|
+
401 raised through the multi-source fan-out and took the other
|
|
13
|
+
sources down with it. A source-level error now skips that source —
|
|
14
|
+
callers see what answered.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- The README documents the full capability surface: structures,
|
|
18
|
+
lint, local identity, resolution with provenance, citations, and
|
|
19
|
+
the command line.
|
|
20
|
+
|
|
21
|
+
## [0.31.12] - 2026-11-11
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- CAS Common Chemistry resolves by default (maintainer decision:
|
|
25
|
+
CC BY-NC 4.0 acceptable on by default). The adapter self-registers
|
|
26
|
+
like the other sources; every result still carries the required
|
|
27
|
+
attribution. `Citation.for_molecule` accepts a `sources:`
|
|
28
|
+
allow-list so callers can keep resolution NC-free — the
|
|
29
|
+
metanorma plugin exposes it as the `asciichem-sources` document
|
|
30
|
+
attribute.
|
|
31
|
+
|
|
6
32
|
## [0.31.11] - 2026-11-10
|
|
7
33
|
|
|
8
34
|
### Added
|
data/README.adoc
CHANGED
|
@@ -45,6 +45,91 @@ formula.to_mathml # => "<math>...</math>"
|
|
|
45
45
|
formula.to_text # => "H_2O" (round trip)
|
|
46
46
|
----
|
|
47
47
|
|
|
48
|
+
The parsed model is the canonical form: MathML, HTML, LaTeX, text,
|
|
49
|
+
linear and 2D structural SVG, and CML are views over the same tree.
|
|
50
|
+
|
|
51
|
+
=== Structures: SMILES and molfile in, AsciiChem out
|
|
52
|
+
|
|
53
|
+
[source,ruby]
|
|
54
|
+
----
|
|
55
|
+
formula = AsciiChem.parse_smiles("CC(=O)Oc1ccccc1C(=O)O")
|
|
56
|
+
formula.to_structural_svg # 2D depiction, deterministic layout
|
|
57
|
+
formula.to_smiles # canonical aromatic SMILES
|
|
58
|
+
formula.to_text # AsciiChem text
|
|
59
|
+
|
|
60
|
+
formula = AsciiChem.parse_molfile(molfile_text) # V2000 in and out
|
|
61
|
+
----
|
|
62
|
+
|
|
63
|
+
=== Lint
|
|
64
|
+
|
|
65
|
+
[source,ruby]
|
|
66
|
+
----
|
|
67
|
+
AsciiChem::Linter.run(formula) # chemistry diagnostics: balance,
|
|
68
|
+
# valence, charge, isotopes, rings,
|
|
69
|
+
# identifier consistency, ...
|
|
70
|
+
AsciiChem::Linter.errors?(formula) # => true/false
|
|
71
|
+
----
|
|
72
|
+
|
|
73
|
+
=== Local identity: InChI, AuxInfo, InChIKey
|
|
74
|
+
|
|
75
|
+
[source,ruby]
|
|
76
|
+
----
|
|
77
|
+
require "asciichem/inchi"
|
|
78
|
+
|
|
79
|
+
# the native chemomer engine (byte-exact vs the IUPAC oracle;
|
|
80
|
+
# CHEMOMER_LIB points at the built cdylib) or the IUPAC binary:
|
|
81
|
+
AsciiChem::Inchi.engine = AsciiChem::Inchi::NativeEngine.new
|
|
82
|
+
|
|
83
|
+
identity = AsciiChem::Inchi.identity_for(molecule)
|
|
84
|
+
identity.inchi # "InChI=1S/C9H8O4/..."
|
|
85
|
+
identity.inchikey # "BSYNRYMUTXBXSQ-UHFFFAOYSA-N"
|
|
86
|
+
----
|
|
87
|
+
|
|
88
|
+
=== Resolution: identifiers to substance records
|
|
89
|
+
|
|
90
|
+
[source,ruby]
|
|
91
|
+
----
|
|
92
|
+
require "asciichem/resolver"
|
|
93
|
+
|
|
94
|
+
substance = AsciiChem::Resolver.resolve(value: "50-78-2", convention: "cas")
|
|
95
|
+
substance.preferred_name # => "Aspirin"
|
|
96
|
+
substance.structure.to_smiles # from the source record
|
|
97
|
+
substance.identifiers.first.provenance.source # "common_chemistry"
|
|
98
|
+
|
|
99
|
+
# all shipped sources (PubChem, OPSIN, CAS Common Chemistry) resolve
|
|
100
|
+
# by default; attribution rides every record. Limit the set when a
|
|
101
|
+
# use must stay NC-free:
|
|
102
|
+
AsciiChem::Resolver.resolve(value: "50-78-2", convention: "cas",
|
|
103
|
+
sources: [:pubchem, :opsin])
|
|
104
|
+
----
|
|
105
|
+
|
|
106
|
+
=== Citations: dataset bibitems with provenance
|
|
107
|
+
|
|
108
|
+
[source,ruby]
|
|
109
|
+
----
|
|
110
|
+
require "asciichem/citation"
|
|
111
|
+
|
|
112
|
+
molecule = AsciiChem.parse("CC(=O)OC1=CC=CC=C1C(=O)O @cas(\"50-78-2\") @cite(\"pubchem\")").nodes.first
|
|
113
|
+
AsciiChem::Citation.for_molecule(molecule)
|
|
114
|
+
# => [["pubchem", #<Relaton bibitem>]] - one dataset-type bibitem per
|
|
115
|
+
# (substance, source), anchored by InChIKey, with a provenance note
|
|
116
|
+
# (retrieved from, version, attribution).
|
|
117
|
+
----
|
|
118
|
+
|
|
119
|
+
== Command line
|
|
120
|
+
|
|
121
|
+
[source,sh]
|
|
122
|
+
----
|
|
123
|
+
asciichem convert -i "H_2O" -t mathml # also: --from smiles|molfile
|
|
124
|
+
asciichem identity -i aspirin.smiles --from smiles
|
|
125
|
+
asciichem resolve --cas 50-78-2 --source common_chemistry
|
|
126
|
+
asciichem validate -i 'H_2O @cas("50-78-2")'
|
|
127
|
+
asciichem cite --cas 50-78-2 -t model-json
|
|
128
|
+
asciichem lint -i "2H_2 + O_2 -> 2H_2O"
|
|
129
|
+
asciichem roundtrip -i "C_6H_12O_6"
|
|
130
|
+
asciichem parse-cml -i formula.xml
|
|
131
|
+
----
|
|
132
|
+
|
|
48
133
|
== Conformance
|
|
49
134
|
|
|
50
135
|
The reference implementation of https://www.asciichem.org[the AsciiChem
|
|
@@ -60,7 +145,7 @@ a level without the green corpus run behind it does not ship.
|
|
|
60
145
|
|L1 semantics |yes |wire form validates against the v1 schemas and round-trips; the perception pass (implicit hydrogens, aromatic rings) is corpus-gated
|
|
61
146
|
|L2 render |yes |MathML byte-goldens, canonical text, and the wire form for every construct
|
|
62
147
|
|L3 stereo |yes |tetrahedral chirality, directional bonds, wedge/hash; parity-correct canonical SMILES including ring closures
|
|
63
|
-
|L4 identity |
|
|
148
|
+
|L4 identity |yes |opt-in InChI engine (binary or native) with stereo layers, OPSIN adapter, identifier cross-check, resolver (PubChem, OPSIN, and Common Chemistry on by default — CC BY-NC attribution carried everywhere; limit sources with `Resolver.resolve(sources:)`), citation bibitems
|
|
64
149
|
|===
|
|
65
150
|
|
|
66
151
|
TypeScript and Python claim the same levels they pass; see their
|
data/lib/asciichem/citation.rb
CHANGED
|
@@ -84,9 +84,15 @@ module AsciiChem
|
|
|
84
84
|
#
|
|
85
85
|
# AsciiChem.parse('H_2O @name("water") @cite("pubchem")')
|
|
86
86
|
# AsciiChem::Citation.for_molecule(formula.nodes.first).map(&:last)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
#
|
|
88
|
+
# `sources:` limits the citable set (an allow-list): cited
|
|
89
|
+
# sources outside it are skipped — the document pipeline (the
|
|
90
|
+
# metanorma plugin's `asciichem-sources` attribute) warns about
|
|
91
|
+
# the exclusions; the gem stays silent and total.
|
|
92
|
+
def for_molecule(molecule, cache: nil, fetch: nil, sources: nil)
|
|
93
|
+
citable = citation_sources(molecule)
|
|
94
|
+
citable &= Array(sources).map(&:to_s) if sources
|
|
95
|
+
return [] if citable.empty?
|
|
90
96
|
|
|
91
97
|
convention, value = lookup_key(molecule)
|
|
92
98
|
unless value
|
|
@@ -95,7 +101,7 @@ module AsciiChem
|
|
|
95
101
|
'(annotate @cas/@inchikey/@smiles or @name)'
|
|
96
102
|
end
|
|
97
103
|
|
|
98
|
-
|
|
104
|
+
citable.filter_map do |source|
|
|
99
105
|
substance = AsciiChem::Resolver[source].new.resolve(
|
|
100
106
|
value: value, convention: convention, cache: cache, fetch: fetch
|
|
101
107
|
)
|
|
@@ -7,16 +7,15 @@ module AsciiChem
|
|
|
7
7
|
module Resolver
|
|
8
8
|
# CAS Common Chemistry source adapter (TODO.impl 40; TODO.v2 07).
|
|
9
9
|
#
|
|
10
|
-
# License posture (maintainer
|
|
11
|
-
# Chemistry data is CC BY-NC 4.0 —
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# Every result carries the required attribution.
|
|
10
|
+
# License posture (maintainer decision 2026-11-11): Common
|
|
11
|
+
# Chemistry data is CC BY-NC 4.0 — acceptable to enable by
|
|
12
|
+
# default; every result carries the required attribution, and the
|
|
13
|
+
# citation track carries it into rendered documents. Documents
|
|
14
|
+
# that must stay NC-free limit sources with the
|
|
15
|
+
# `asciichem-sources` attribute (metanorma-plugin-asciichem) or
|
|
16
|
+
# `Resolver.resolve(sources:)`.
|
|
18
17
|
class CommonChemistry < Adapter
|
|
19
|
-
source_name :common_chemistry
|
|
18
|
+
source_name :common_chemistry
|
|
20
19
|
supports "cas"
|
|
21
20
|
|
|
22
21
|
BASE = "https://commonchemistry.cas.org/api/detail"
|
data/lib/asciichem/resolver.rb
CHANGED
|
@@ -9,10 +9,13 @@ module AsciiChem
|
|
|
9
9
|
# citation track (TODO.v2 08) emits one bibitem per (substance,
|
|
10
10
|
# source) the user chooses to cite.
|
|
11
11
|
#
|
|
12
|
-
# Sources are
|
|
13
|
-
# as the linter checks).
|
|
14
|
-
#
|
|
15
|
-
#
|
|
12
|
+
# Sources are adapters that self-register (OCP, same pattern
|
|
13
|
+
# as the linter checks). All shipped sources are on by default;
|
|
14
|
+
# every result carries its source's required attribution (Common
|
|
15
|
+
# Chemistry: CC BY-NC 4.0 — default-on per the maintainer decision
|
|
16
|
+
# of 2026-11-11). Callers limit the set explicitly:
|
|
17
|
+
# `Resolver.resolve(sources:)` or the metanorma plugin's
|
|
18
|
+
# `asciichem-sources` attribute for NC-free documents.
|
|
16
19
|
module Resolver
|
|
17
20
|
autoload :Adapter, "asciichem/resolver/adapter"
|
|
18
21
|
autoload :Cache, "asciichem/resolver/cache"
|
|
@@ -40,16 +43,23 @@ module AsciiChem
|
|
|
40
43
|
raise Error, "unknown resolver source #{name.inspect} (registered: #{adapters.keys.sort.join(', ')})"
|
|
41
44
|
end
|
|
42
45
|
|
|
43
|
-
# Resolves against every
|
|
44
|
-
# per source that answers (nil
|
|
45
|
-
# in registration order.
|
|
46
|
+
# Resolves against every registered source (or the selected
|
|
47
|
+
# subset), returning one Substance per source that answers (nil
|
|
48
|
+
# entries dropped). Sources answer in registration order. A
|
|
49
|
+
# source-level failure (HTTP error, rejected request) skips that
|
|
50
|
+
# source — one dead or key-gated endpoint must not poison the
|
|
51
|
+
# others; callers see what answered.
|
|
46
52
|
def resolve_all(value:, convention:, sources: nil, fetch: nil, cache: nil, refresh: false)
|
|
47
53
|
selected = sources ? Array(sources).map { |s| self[s] } : adapters.values
|
|
48
54
|
selected.filter_map do |adapter_class|
|
|
49
55
|
adapter = adapter_class.new
|
|
50
56
|
next unless adapter.supports?(convention)
|
|
51
57
|
|
|
52
|
-
|
|
58
|
+
begin
|
|
59
|
+
adapter.resolve(value: value, convention: convention, fetch: fetch, cache: cache, refresh: refresh)
|
|
60
|
+
rescue AsciiChem::Error
|
|
61
|
+
nil
|
|
62
|
+
end
|
|
53
63
|
end
|
|
54
64
|
end
|
|
55
65
|
|
|
@@ -71,9 +81,10 @@ module AsciiChem
|
|
|
71
81
|
end
|
|
72
82
|
end
|
|
73
83
|
|
|
74
|
-
#
|
|
75
|
-
# CC BY-NC 4.0
|
|
76
|
-
#
|
|
84
|
+
# Every shipped source self-registers eagerly (autoload fires the
|
|
85
|
+
# class bodies). CommonChemistry is CC BY-NC 4.0 and default-on
|
|
86
|
+
# per the maintainer decision of 2026-11-11; callers that need
|
|
87
|
+
# NC-free resolution limit sources explicitly.
|
|
77
88
|
adapters
|
|
78
89
|
constants.each { |c| const_get(c) unless c == :Conflict }
|
|
79
90
|
end
|
data/lib/asciichem/version.rb
CHANGED