asciichem 0.30.0 → 0.30.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf3a968f977add504a84afdbeb3d5d231b959688f4d9c9ac70e424a251ea2f75
4
- data.tar.gz: 8b8c12366ce9f05f44058b897b474f226dea561568ee6c3e17820dda4c5bd4f7
3
+ metadata.gz: 2f8a0ef9952b9507c35ded04295298a3a62e86a7efce1f92d258a1aaf6d6125c
4
+ data.tar.gz: 0d11ccfb16383194e8a1fd3bf0816ce8b8465b548bc6dac43535d2633a0cf2ba
5
5
  SHA512:
6
- metadata.gz: 6ae161dc4a0eef5ec494ee42a5c7e1280cddc02c0cfb3fa5701377bc6fcf82ddc32a37039b24ccaa2666959f39179d0fa7f7a48d0c91fca14829679bdbdc5e03
7
- data.tar.gz: 47124baeb7589216a48176c718a7556536334203f1d01f941de501a186750cfe00dc7151589295790a16fb37efa5f688709eef28b9d88ad7f7da9e6191140207
6
+ metadata.gz: 270015bf0f88f0bc2f06452a472759970b39ab75ae9aef233ea3c9f94f7b3abc2d20752098151aedd7ab51251aaad9414cfdb349167c059ac036e8bbdaff04d5
7
+ data.tar.gz: a4211637fc504c7db50994d14b151bb5e45357b48d870b4f4aff7eb49c79c269ba7014a755fd00c6e8dd22678e1f044011b9b4123ecc24530b9d693464a11357
data/CHANGELOG.md CHANGED
@@ -3,6 +3,19 @@
3
3
  All notable changes to AsciiChem are documented here.
4
4
  This project follows [Semantic Versioning](https://semver.org/).
5
5
 
6
+ ## [0.30.1] - 2026-09-23
7
+
8
+ ### Fixed
9
+ - Groups attached by an explicit bond no longer re-spell through
10
+ CML round-trip: `CC(=O)O` came back as `CC=(O)O`. The CML
11
+ rebuilder left the attachment bond outside the reconstructed
12
+ group; the grammar keeps it inside (the inner molecule starts
13
+ with the bond), so the rebuilder now pulls an adjacent preceding
14
+ Bond into the group. Affects text syntax and SMILES ingestion
15
+ alike; regression specs cover single/sibling bonded groups,
16
+ multi-atom groups, and aspirin stability across double
17
+ round-trips.
18
+
6
19
  ## [0.30.0] - 2026-09-22
7
20
 
8
21
  ### Changed
@@ -213,18 +213,23 @@ module AsciiChem
213
213
  def self.splice_group_into(nodes, target_nodes, record)
214
214
  return if target_nodes.empty?
215
215
 
216
- positions = target_nodes.map do |target|
217
- found = nodes.each_with_index.find { |node, _| node.equal?(target) }
218
- found&.last
219
- end.compact
216
+ inner_nodes = group_nodes(nodes, target_nodes)
217
+
218
+ positions = target_nodes
219
+ .map { |target| position_of(nodes, target) }
220
+ .compact
220
221
  return if positions.empty?
221
222
 
222
223
  group = AsciiChem::Model::Group.new(
223
- nodes: target_nodes,
224
+ nodes: inner_nodes,
224
225
  multiplicity: record[:multiplicity],
225
226
  bracket: record[:bracket]
226
227
  )
228
+ replace_positions_with_group(nodes, group, positions)
229
+ end
230
+ private_class_method :splice_group_into
227
231
 
232
+ def self.replace_positions_with_group(nodes, group, positions)
228
233
  first_pos = positions.min
229
234
  nodes[first_pos] = group
230
235
  # Remove remaining positions in descending order so earlier
@@ -233,7 +238,31 @@ module AsciiChem
233
238
  nodes.delete_at(pos)
234
239
  end
235
240
  end
236
- private_class_method :splice_group_into
241
+ private_class_method :replace_positions_with_group
242
+
243
+ # A Bond sitting immediately before the group's first target node
244
+ # is the group's attachment bond — the `=` of `C(=O)`. The
245
+ # grammar keeps that bond INSIDE the group (its inner molecule
246
+ # starts with the bond), so the rebuilder moves it in instead of
247
+ # leaving a stray prefix bond outside, which would re-spell as
248
+ # `C=(O)`. By construction of the bond insertion (each bond sits
249
+ # just before its later endpoint), a bond adjacent to the group's
250
+ # first atom always has that atom as its later endpoint.
251
+ def self.group_nodes(nodes, target_nodes)
252
+ idx = position_of(nodes, target_nodes.first)
253
+ bond = idx&.positive? ? nodes[idx - 1] : nil
254
+ return target_nodes unless bond.is_a?(AsciiChem::Model::Bond)
255
+
256
+ [nodes.delete_at(idx - 1), *target_nodes]
257
+ end
258
+ private_class_method :group_nodes
259
+
260
+ # Identity-based position lookup so duplicate Bonds (same kind
261
+ # → `==` equal) aren't confused.
262
+ def self.position_of(nodes, node)
263
+ nodes.each_with_index.find { |candidate, _| candidate.equal?(node) }&.last
264
+ end
265
+ private_class_method :position_of
237
266
 
238
267
  # Flatten the canonical document's molecules (top-level +
239
268
  # reaction reactants/products + cascade reactions) into a list,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AsciiChem
4
- VERSION = "0.30.0"
4
+ VERSION = "0.30.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asciichem
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.30.0
4
+ version: 0.30.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.