asciichem 0.31.8 → 0.31.10

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: 040c17f14243ee3dfa4ecb2c5db7f961aa91a10768118977cd01d48a209650a7
4
- data.tar.gz: 9320019d60542569b130dd57e332b5e9afe4ff6641254d680e73f2b965977ecf
3
+ metadata.gz: 11c430e97b570a9f7297e831904c6701db083757a092646536a29d4452a7b302
4
+ data.tar.gz: 21cb557a14d1914adec15dbbf8c9820160e7bf4a93bf3a853e6a81c61b4d595c
5
5
  SHA512:
6
- metadata.gz: 7a923467ce4e6e857f4175c5c335de6bb1116c5426c78828b328fccc6621a6c9313809ee93d82f2de1a35fc14114f1d77a1220f453e4c59af2fa3eac6e5c4aa2
7
- data.tar.gz: 63b55738186c1f4b2e98bd38b71b2af97b01946ee9715d44f934c364df26b1f1b79e29d9e072c34cd264e384f475fa0b4e8ce64719a2d7844a3e9db5f8da489f
6
+ metadata.gz: 7b38cae89fed5fff85d89c9dd870915506cb9500b684281801ec2f4000c3da1c9bde273747b83438f7f85ed0a09eac5377499c977a05dd261360b7de57f038be
7
+ data.tar.gz: 1a9e53346bdc0d74f99ecc34f525e2a3358054fc8ef97849f23451370ec03841c4adc61c2813c5abb9cdd59b9190a3de2185708c92841c203815fda2000e336b
data/CHANGELOG.md CHANGED
@@ -3,6 +3,29 @@
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.10] - 2026-11-05
7
+
8
+ ### Changed
9
+ - The native engine binding points at the renamed **chemomer** crate
10
+ (chemomer/chemomer): the dylib is `libchemomer.dylib` with
11
+ `chemomer_*` symbols and `CHEMOMER_LIB` replaces `INCHI_RS_LIB`.
12
+ Same contract, new spelling.
13
+ - moxml is pinned to the 0.6 line from 0.6.1 (resolves to 0.6.2);
14
+ the full suite runs green against it.
15
+
16
+ ## [0.31.9] - 2026-10-24
17
+
18
+ ### Added
19
+ - `AsciiChem::Inchi::NativeEngine`: the identity engine drives the
20
+ native inchi-rs Rust engine through its C ABI (model → molfile →
21
+ identifier + InChIKey). The library is an external artifact built
22
+ from the inchi-rs checkout and located via `INCHI_RS_LIB`; the
23
+ `ffi` gem loads lazily, so the core gem stays dependency-free.
24
+ Opt-in registration matches the BinaryEngine:
25
+ `AsciiChem::Inchi.engine = AsciiChem::Inchi::NativeEngine.new`.
26
+ - The install guide mentions the native path alongside the IUPAC
27
+ binary.
28
+
6
29
  ## [0.31.8] - 2026-10-22
7
30
 
8
31
  ### Fixed
data/asciichem.gemspec CHANGED
@@ -39,7 +39,7 @@ Gem::Specification.new do |spec|
39
39
  spec.add_dependency "elkrb", "~> 2.0"
40
40
  spec.add_dependency "lutaml-model", "~> 0.8"
41
41
  spec.add_dependency "mml", "~> 2.3"
42
- spec.add_dependency "moxml", "~> 0.5"
42
+ spec.add_dependency "moxml", "~> 0.6.1"
43
43
  spec.add_dependency "parslet", "~> 2.0"
44
44
  spec.add_dependency "relaton-bib", "~> 2.1"
45
45
  spec.add_dependency "plurimath", "~> 0.11"
@@ -33,7 +33,7 @@ module AsciiChem
33
33
  # the pipe: atom coordinates, charges and isotopes cross into
34
34
  # the engine through the V2000 block.
35
35
  def identity(molecule)
36
- molfile = AsciiChem::Molfile.write(molecule)
36
+ molfile = AsciiChem::Molfile.write(molecule, layout: false)
37
37
  out = execute(molfile)
38
38
  inchi = out[/^InChI=\S+/]
39
39
  raise AsciiChem::Error, "#{bin} produced no InChI:\n#{out}" unless inchi
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AsciiChem
4
+ module Inchi
5
+ # Engine that drives the native chemomer engine (the Rust port)
6
+ # through its C ABI. The library is a documented external
7
+ # artifact — nothing is vendored: build it once from the chemomer
8
+ # checkout (`cargo build --release`) and point CHEMOMER_LIB at
9
+ # the cdylib, the same opt-in shape as the BinaryEngine's
10
+ # ASCIICHEM_INCHI_BIN. The `ffi` gem loads lazily — the core gem
11
+ # stays dependency-free.
12
+ #
13
+ # AsciiChem::Inchi.engine = AsciiChem::Inchi::NativeEngine.new
14
+ # AsciiChem::Inchi.engine = AsciiChem::Inchi::NativeEngine.new(lib: "/path/libchemomer.dylib")
15
+ class NativeEngine < Engine
16
+ DEFAULT_LIB = ENV.fetch('CHEMOMER_LIB', nil)
17
+
18
+ attr_reader :lib
19
+
20
+ def initialize(lib: DEFAULT_LIB)
21
+ super()
22
+ @lib = lib
23
+ unless @lib
24
+ raise AsciiChem::Error,
25
+ 'chemomer native engine: set CHEMOMER_LIB to the built cdylib ' \
26
+ '(cargo build --release in the chemomer checkout)'
27
+ end
28
+ return if File.readable?(@lib)
29
+
30
+ raise AsciiChem::Error, "chemomer native engine: #{@lib} is not readable"
31
+ end
32
+
33
+ # model → molfile → chemomer → Identity. The molfile emission is
34
+ # the pipe, exactly as in the BinaryEngine.
35
+ def identity(molecule)
36
+ molfile = AsciiChem::Molfile.write(molecule, layout: false)
37
+ inchi = call(molfile, :chemomer_from_molfile)
38
+ inchikey = call(molfile, :chemomer_inchikey)
39
+ Identity.new(inchi: inchi, inchikey: inchikey)
40
+ end
41
+
42
+ private
43
+
44
+ def call(molfile, function)
45
+ binding_class = self.class.binding_class(@lib)
46
+ pointer = binding_class.public_send(function, molfile)
47
+ unless pointer
48
+ message = binding_class.last_error
49
+ raise AsciiChem::Error, "chemomer native engine: #{message || 'call failed'}"
50
+ end
51
+ value = pointer.read_string
52
+ binding_class.chemomer_string_free(pointer)
53
+ value
54
+ end
55
+
56
+ # One FFI module per library path, cached: attach_function
57
+ # defines the symbol bindings once.
58
+ def self.binding_class(path)
59
+ @binding_classes ||= {}
60
+ @binding_classes[path] ||= begin
61
+ require 'ffi'
62
+ Module.new do
63
+ extend FFI::Library
64
+
65
+ ffi_lib path
66
+ attach_function :chemomer_from_molfile, %i[string], :pointer
67
+ attach_function :chemomer_inchikey, %i[string], :pointer
68
+ attach_function :chemomer_string_free, %i[pointer], :void
69
+ attach_function :chemomer_last_error, [], :pointer
70
+
71
+ def self.last_error
72
+ pointer = chemomer_last_error
73
+ pointer.null? ? nil : pointer.read_string
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -14,11 +14,15 @@ module AsciiChem
14
14
  autoload :BinaryEngine, 'asciichem/inchi/binary_engine'
15
15
  autoload :Engine, 'asciichem/inchi/engine'
16
16
  autoload :Identity, 'asciichem/inchi/identity'
17
+ autoload :NativeEngine, 'asciichem/inchi/native_engine'
17
18
 
18
19
  INSTALL_GUIDE = 'install the IUPAC InChI software ' \
19
20
  '(https://www.inchi-trust.org/downloads/) and set ' \
20
21
  'AsciiChem::Inchi.engine = AsciiChem::Inchi::BinaryEngine.new ' \
21
- '(or pass bin: "path/to/inchi-1")'
22
+ '(or pass bin: "path/to/inchi-1") — or, for the native ' \
23
+ 'Rust engine, build chemomer and set ' \
24
+ 'AsciiChem::Inchi.engine = AsciiChem::Inchi::NativeEngine.new ' \
25
+ '(pointing CHEMOMER_LIB at the built cdylib)'
22
26
 
23
27
  class << self
24
28
  attr_accessor :engine
@@ -24,13 +24,16 @@ module AsciiChem
24
24
  BOND_STEREO = { wedge: 1, hash: 6 }.freeze
25
25
  CHIRALITY_PARITY = { 'TH1' => 1, 'TH2' => 2 }.freeze
26
26
 
27
- def initialize(molecule, name: nil)
27
+ def initialize(molecule, name: nil, layout: true)
28
28
  @molecule = molecule
29
29
  @name = name
30
+ @layout = layout
30
31
  end
31
32
 
32
33
  def write
33
- atoms, edges = Structure::Graph.build!(@molecule)
34
+ @atoms, @edges = Structure::Graph.build!(@molecule)
35
+ atoms = @atoms
36
+ edges = @edges
34
37
  raise ParseError, "molecule has no bonds — a formula is not a structure" if edges.empty? && atoms.length > 1
35
38
 
36
39
  lines = []
@@ -100,11 +103,11 @@ module AsciiChem
100
103
 
101
104
  # Authored coordinates win; otherwise positions from the
102
105
  # deterministic 2D layout (same walk order as the graph).
103
- def coordinates(atom, index, atoms)
104
- return [atom.x2, atom.y2] if atom.x2 && atom.y2
106
+ def coordinates(atom, index, _atoms)
107
+ return [0.0, 0.0] unless @layout
105
108
 
106
- @layout ||= AsciiChem::Layout.layout(@molecule)
107
- placed = @layout.atoms[index]
109
+ @layout_positions ||= AsciiChem::Layout.layout(@molecule).atoms
110
+ placed = @layout_positions[index]
108
111
  placed ? [placed.x, placed.y] : [0.0, 0.0]
109
112
  end
110
113
 
@@ -18,8 +18,8 @@ module AsciiChem
18
18
 
19
19
  # Emits a V2000 molfile. Uses authored x2/y2 coordinates;
20
20
  # computes a 2D layout otherwise.
21
- def write(molecule, name: nil)
22
- Writer.new(molecule, name: name).write
21
+ def write(molecule, name: nil, layout: true)
22
+ Writer.new(molecule, name: name, layout: layout).write
23
23
  end
24
24
  end
25
25
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AsciiChem
4
- VERSION = "0.31.8"
4
+ VERSION = "0.31.10"
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.31.8
4
+ version: 0.31.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -71,14 +71,14 @@ dependencies:
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: '0.5'
74
+ version: 0.6.1
75
75
  type: :runtime
76
76
  prerelease: false
77
77
  version_requirements: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
- version: '0.5'
81
+ version: 0.6.1
82
82
  - !ruby/object:Gem::Dependency
83
83
  name: parslet
84
84
  requirement: !ruby/object:Gem::Requirement
@@ -220,6 +220,7 @@ files:
220
220
  - lib/asciichem/inchi/binary_engine.rb
221
221
  - lib/asciichem/inchi/engine.rb
222
222
  - lib/asciichem/inchi/identity.rb
223
+ - lib/asciichem/inchi/native_engine.rb
223
224
  - lib/asciichem/layout.rb
224
225
  - lib/asciichem/linter.rb
225
226
  - lib/asciichem/linter/balance_check.rb