rubabel 0.2.0 → 0.2.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.
@@ -0,0 +1,189 @@
1
+ # rubabel
2
+
3
+ Ruby interface to the openbabel ruby bindings (or the openbabel gem). The
4
+ interface attempts to be a ruby-ish analogue of
5
+ [pybel](http://openbabel.org/docs/current/UseTheLibrary/Python_PybelAPI.html).
6
+
7
+ ## Examples
8
+
9
+ The [Chemistry Toolkit Rosetta Wiki](http://ctr.wikia.com/wiki/Chemistry_Toolkit_Rosetta_Wiki) has a lot of examples you can check out.
10
+
11
+ ### Creating a molecule
12
+
13
+ #### From a string
14
+
15
+ require 'rubabel'
16
+
17
+ # by default, reads in smiles strings
18
+ serine = Rubabel["C(C(C(=O)O)N)O"]
19
+ # more formally:
20
+ serine = Rubabel::Molecule.from_string("C(C(C(=O)O)N)O")
21
+
22
+ # also any other format openbabel supports, for example inchi
23
+ serine = Rubabel["InChI=1S/C3H7NO3/c4-2(1-5)3(6)7/h2,5H,1,4H2,(H,6,7)", :inchi]
24
+
25
+ # from the internet:
26
+ mol = Rubabel[some_molecule, Rubabel.format_from_mime(some_mime_type)]
27
+
28
+ Find out all the formats Rubabel supports (hash is format key pointing to the description):
29
+
30
+ hash = Rubabel.in_formats
31
+ hash = Rubabel.out_formats
32
+
33
+ #### From a file
34
+
35
+ Reading multiple entries from a file:
36
+
37
+ Rubabel.foreach("file.sdf") do |mol|
38
+ puts mol.exact_mass
39
+ end
40
+
41
+ Foreach returns an enumerator that can be chained:
42
+
43
+ # return an array of every unique atom type in the file
44
+ uniq_atom_types = Rubabel.foreach("file.mol").flat_map {|mol| mol.map(&:type) }.uniq
45
+
46
+ Read a single molecule from a file (reads only the first molecule)
47
+
48
+ mol = Rubabel::Molecule.from_file("file.sdf")
49
+ # handles gzipped files seamlessly:
50
+ mol = Rubabel::Molecule.from_file("file.sdf.gz")
51
+ mol = Rubabel.molecule_from_file("file.sdf") # alternative
52
+
53
+ # explicit format for uninformative/wrong extensions:
54
+ mol = Rubabel::Molecule.from_file("file", :sdf)
55
+
56
+ ### Writing/Drawing
57
+
58
+ #### create string output
59
+
60
+ mol = Rubabel["OCC"] # ethanol
61
+
62
+ mol.to_s # canonical smiles -> "CCO"
63
+ mol.csmiles # same thing
64
+
65
+ mol.to_s(:smi) # smiles -> "OCC"
66
+ mol.smiles # same thing
67
+
68
+ For inclusion in a file with standard smiles formatting (SMILES\tID\n):
69
+
70
+ can_smiles_string = mol.write_string # -> "CCO\t\n"
71
+ mol.title = "ethanol"
72
+ can_smiles_string = mol.write(:can) # -> "CCO\tethanol\n"
73
+
74
+ Other formats in the same manner:
75
+
76
+ pdb_string = mol.write(:pdb)
77
+
78
+ Write to a file directly (single molecule only; depends on file extension for type):
79
+
80
+ # write to a smiles file
81
+ mol.write("file.smi")
82
+ mol.write_file("file.smi")
83
+
84
+ Write multiple molecules to a file:
85
+
86
+ File.open("somefile.pdb", 'w') do |out|
87
+ molecules.each {|mol| out.print mol.write(:pdb) }
88
+ end
89
+
90
+ #### Drawing
91
+
92
+ If you write to svg or png (png uses mini_magick to convert from svg) then the
93
+ molecule is automatically drawn in 2D:
94
+
95
+ mol = Rubabel["NCC(O)C(=O)O"]
96
+ mol.write("file.svg")
97
+
98
+ # must have imagemagick ('convert' command) and mini_magick gem installed
99
+ mol.write("file.png")
100
+
101
+ ### Searching and Splitting
102
+
103
+ *each_match*, *matches*, *matches?*, *smarts_indices* all take the same input (SMARTS
104
+ string or object and optional boolean specifying uniqueness of results):
105
+
106
+ mol = Rubabel["NCC(O)C(=O)O"]
107
+ mol.each_match("CO") do |match|
108
+ # match is just an array of atoms that matched
109
+ match.first.el # => :c
110
+ match.last.el # => :o
111
+ end
112
+
113
+ # matches returns all the matches in an array
114
+ all_matches = mol.matches("CO")
115
+ # all the match routines take a boolean to alter uniqueness
116
+ all_matches = mol.matches("CO", false) # some matches may not be uniq
117
+
118
+ Have some bonds to break?, split makes new molecules split from that bond(s)
119
+
120
+ bonds = mol.matches("CO").map {|c, o| c.get_bond(o) }
121
+ mol.split(*bonds) # splits between every carbon single bonded to oxygen
122
+
123
+ ### Add & Delete atoms/bonds
124
+
125
+ #### Adding
126
+
127
+ mol = Rubabel["OCC"]
128
+ # adds a carbon, then an oxygen to last indexed atom by atomic number
129
+ mol << 6 << 8 # #<Mol "OCCCO">
130
+ mol << :c << :o # same thing
131
+
132
+ # add an ethyl group specifically to second carbon
133
+ mol = Rubabel["OCC"]
134
+ mol[1] << :c << :c
135
+
136
+ # add a vinyl group to second carbon (use method notation and parenthesis
137
+ # because we are going to specify 2 arguments (the bond order):
138
+ ( mol[1] << :c).<<(:c, 2)
139
+
140
+ #### Deleting
141
+
142
+ # delete an atom:
143
+ mol = Rubabel["NCO"]
144
+ mol.delete(mol[0]) # -> #<Mol CO>
145
+
146
+ # delete a bond:
147
+ bond = mol[0].get_bond(mol[1])
148
+ mol.delete(bond) # -> #<Mol C.O>
149
+
150
+ ## Installing
151
+
152
+ First, many thanks to Andreas Maunz for packaging openbabel as a gem which makes this install quite painless.
153
+
154
+ ### Quick Install
155
+
156
+ On a POSIX system, make sure you have openbabel (including header files), cmake, curl, tar, sed and make {see openbabel instructions}[https://github.com/amaunz/openbabel-gem]. On ubuntu/debian:
157
+
158
+ sudo apt-get install openbabel libopenbabel-dev cmake make curl
159
+
160
+ Then install the gem (which should install the openbabel gem, too):
161
+
162
+ gem install rubabel
163
+
164
+ ### Building from Source
165
+
166
+ 1. download openbabel
167
+ 2. swap out Init_OpenBabel for Init_openbabel in scripts/ruby/openbabel-ruby.cpp (see [here](http://forums.openbabel.org/Ruby-Open-Babel-in-2-1-1-td957640.html)). Some versions have this fixed already, apparently.
168
+ 3. make sure you have the right [dependencies to compile](http://openbabel.org/docs/2.3.1/Installation/install.html#compiling-open-babel)
169
+
170
+ Here's a complete example of compiling for a single user on Ubuntu 11.10 and probably will be generally forward compatible for some time. This will compile bindings on whichever ruby comes up with '/usr/bin/env ruby':
171
+
172
+ # install the dependencies:
173
+ sudo apt-get install libeigen2-dev cmake libwxgtk2.8-dev libxml2-dev libcairo2-dev
174
+ # unpack it:
175
+ tar -xzvf openbabel-2.3.1.tar.gz
176
+ # swap out buggy lines in ruby bindings:
177
+ sed -i 's/Init_OpenBabel/Init_openbabel/g' openbabel-2.3.1/scripts/ruby/openbabel-ruby.cpp
178
+ # make a separate build directory for building in:
179
+ mkdir build-rvmruby1.9.3
180
+ cd build-rvmruby1.9.3
181
+ mkdir ~/tools
182
+ cmake ../openbabel-2.3.1 -DRUBY_BINDINGS=ON -DCMAKE_INSTALL_PREFIX=~/tools/openbabel-rvmruby1.9.3
183
+ make && make install
184
+
185
+ [[Still need directions to install the gem on top of a build from source]]
186
+
187
+ ## Copyright
188
+
189
+ MIT License. See LICENSE for further details.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.2.1
@@ -14,14 +14,30 @@ module Rubabel
14
14
  include Enumerable
15
15
 
16
16
  class << self
17
- # takes an element symbol and creates that atom. If el_sym is set to
18
- # nil or 0, then an atom of atomic number 0 is used
19
- def [](el_sym=:h, id=nil)
20
- ob_atom = OpenBabel::OBAtom.new
21
- ob_atom.set_id(id) if id
22
- ob_atom.set_atomic_num(Rubabel::EL_TO_NUM[el_sym] || 0)
23
- self.new(ob_atom)
24
- end
17
+ # I cannot figure out how to get the atom attached properly into a
18
+ # molecule. Until I figure that out, no sense in having this method
19
+ # exposed:
20
+ #
21
+ # takes an atomic number or element symbol and creates that atom. If
22
+ # arg is set to nil, then an atom of atomic number 0 is used
23
+ # (represented by '*' in smiles)
24
+ #def [](arg=:c)
25
+ # ob_atom = OpenBabel::OBAtom.new
26
+ # atomic_number =
27
+ # if arg.nil? then 0
28
+ # elsif arg.is_a?(Symbol)
29
+ # Rubabel::EL_TO_NUM[arg]
30
+ # else
31
+ # arg
32
+ # end
33
+ # ob_atom.set_atomic_num(atomic_number)
34
+ # self.new(ob_atom)
35
+ #end
36
+ end
37
+
38
+ # adds an atom and returns the added atom (allows chaining)
39
+ def <<(atom, bond_order=1)
40
+ add_atom!(atom, bond_order)
25
41
  end
26
42
 
27
43
  # returns the molecule that is parent of this atom
@@ -59,14 +75,14 @@ module Rubabel
59
75
  NUM_TO_ELEMENT[atomic_num]
60
76
  end
61
77
 
62
- # creates a bond and adds it to both atoms
63
- def add_atom!(other)
64
- obbond = OpenBabel::OBBond.new
65
- obbond.set_begin(self.ob)
66
- obbond.set_end(other.ob)
67
- @ob.add_bond(obbond)
68
- other.ob.add_bond(obbond)
69
- self
78
+ # creates a bond and adds it to both atoms. Returns the passed in or
79
+ # newly created atom.
80
+ def add_atom!(arg, bond_order=1)
81
+ unless arg.is_a?(Rubabel::Atom)
82
+ arg = mol.add_atom!(arg)
83
+ end
84
+ @ob.get_parent.add_bond(self.ob.get_idx, arg.ob.get_idx, bond_order)
85
+ arg
70
86
  end
71
87
 
72
88
  def each_bond(&block)
@@ -108,14 +108,28 @@ module Rubabel
108
108
  end
109
109
  end
110
110
 
111
- # arg is an atomic number. returns the newly created atom
112
- def add_atom!(atomic_num=1)
113
- # jtp implementation:
114
- # @ob.add_atom(atom.ob)
115
- new_obatom = @ob.new_atom
116
- new_obatom.set_atomic_num(atomic_num)
117
- #@ob.add_atom(new_obatom)
118
- Rubabel::Atom.new(new_obatom)
111
+ # returns the atom passed in or that was created. arg is a pre-existing
112
+ # atom, an atomic number or an element symbol (e.g. :c). default is to
113
+ # add carbon.
114
+ def add_atom!(arg=6, attach_to=nil, bond_order=1)
115
+ if attach_to
116
+ attach_to.add_atom!(arg, bond_order)
117
+ else
118
+ if arg.is_a?(Rubabel::Atom)
119
+ @ob.add_atom(arg.ob)
120
+ arg
121
+ else
122
+ new_obatom = @ob.new_atom
123
+ arg = Rubabel::EL_TO_NUM[arg] if arg.is_a?(Symbol)
124
+ new_obatom.set_atomic_num(arg)
125
+ Rubabel::Atom.new(new_obatom)
126
+ end
127
+ end
128
+ end
129
+
130
+ # retrieves the atom by index (accepts everything an array would)
131
+ def [](*args)
132
+ atoms[*args]
119
133
  end
120
134
 
121
135
  def delete_atom(atom)
@@ -145,8 +159,8 @@ module Rubabel
145
159
  # to add_h!
146
160
  def formula() @ob.get_formula end
147
161
 
148
- def initialize(obmol)
149
- @ob = obmol
162
+ def initialize(obmol=nil)
163
+ @ob = obmol.nil? ? OpenBabel::OBMol.new : obmol
150
164
  end
151
165
 
152
166
  # returns a list of atom indices matching the patterns (corresponds to the
@@ -476,6 +490,18 @@ module Rubabel
476
490
  Rubabel::MoleculeData.new(@ob)
477
491
  end
478
492
 
493
+ # adds the atom (takes atomic number, element symbol or preexisting atom)
494
+ # and returns self
495
+ def <<(arg, bond_order=1)
496
+ last_atom = atoms[-1]
497
+ if last_atom
498
+ last_atom.add_atom!(arg, bond_order)
499
+ else
500
+ add_atom!(arg)
501
+ end
502
+ self
503
+ end
504
+
479
505
  # sensitive to add_h!
480
506
  def num_atoms() @ob.num_atoms end
481
507
  def num_bonds() @ob.num_bonds end
@@ -568,9 +594,9 @@ module Rubabel
568
594
  end
569
595
  end
570
596
 
571
- # writes to the file based on the extension given. If type is given
572
- # explicitly, then it is used. If png is the extension or format, the
573
- # png is generated from an svg.
597
+ # writes to the file based on the extension given (must be recognized by
598
+ # OpenBabel). If png is the extension or format, the png is generated
599
+ # from an svg.
574
600
  def write_file(filename, out_options={})
575
601
  type = Rubabel.filetype(filename)
576
602
  File.write(filename, write_string(type, out_options))
@@ -5,7 +5,7 @@ require 'rubabel/atom'
5
5
 
6
6
  describe Rubabel::Atom do
7
7
 
8
- it 'can be created given an element symbol' do
8
+ xit 'can be created given an element symbol' do
9
9
  hydrogen = Rubabel::Atom[:h]
10
10
  hydrogen.el.should == :h
11
11
 
@@ -17,6 +17,19 @@ describe Rubabel::Atom do
17
17
  chlorine.id.should == 3
18
18
  end
19
19
 
20
+ describe 'attaching another atom to itself (as part of a molecule)' do
21
+
22
+ it 'attaches preformed atom' do
23
+ end
24
+
25
+ it 'attaches given an atomic number' do
26
+ end
27
+
28
+ it 'attaches given an element symbol' do
29
+ end
30
+
31
+ end
32
+
20
33
  specify 'equality' do
21
34
  mol = Rubabel["CCO"]
22
35
  oxygen = mol.atoms[2]
@@ -76,13 +89,19 @@ describe Rubabel::Atom do
76
89
  end
77
90
  end
78
91
 
92
+ specify '#<<(atom) adds an atom and returns the added atom' do
93
+ mol = Rubabel["C"]
94
+ reply = (mol[0] << :n << :o)
95
+ reply.should be_a(Rubabel::Atom)
96
+ mol.csmiles.should == 'CNO'
97
+ end
79
98
 
80
99
  it '#mol retrieves the parent molecule' do
81
100
  @atom.mol.should == @mol
82
101
 
83
- # no parent molecule
84
- h = Rubabel::Atom[:h]
85
- h.mol.should be_nil
102
+ ## no parent molecule
103
+ #h = Rubabel::Atom[:h]
104
+ #h.mol.should be_nil
86
105
  end
87
106
 
88
107
  it 'can get the bonds' do
@@ -92,8 +111,7 @@ describe Rubabel::Atom do
92
111
  @atom.bonds.size.should == 4
93
112
  end
94
113
 
95
- it 'can add a bond' do
96
- end
114
+ it 'can add a bond'
97
115
 
98
116
  it 'can get the neighboring atoms' do
99
117
  @atom.id.should == 0
@@ -8,6 +8,17 @@ describe Rubabel::Molecule do
8
8
  mol = Rubabel["CC(O)O"]
9
9
  mol.csmiles.should == "CC(O)O"
10
10
  end
11
+
12
+ it 'can be made with an existing obmol object' do
13
+ mol = Rubabel["CC(O)O"]
14
+ mol_dup = Rubabel::Molecule.new(mol.ob)
15
+ end
16
+
17
+ it 'can be made fresh' do
18
+ mol = Rubabel::Molecule.new
19
+ mol.atoms.size == 0
20
+ mol.title.should == ''
21
+ end
11
22
  end
12
23
 
13
24
  #xit 'can add a hydrogen to the formula' do
@@ -58,25 +69,59 @@ describe Rubabel::Molecule do
58
69
  mol1 = Rubabel["CCO"]
59
70
  mol2 = Rubabel["OCC"]
60
71
  (mol1 == mol2).should be_true
61
- mol2.atoms[0].charge += 1
72
+ mol2[0].charge += 1
62
73
  (mol1 == mol2).should be_false
63
74
  mol3 = Rubabel["CCCO"]
64
75
  (mol1 == mol3).should be_false
65
76
  end
66
77
 
67
- specify '#add_atom! adds an atom given an atomic number and returns it' do
68
- mol = Rubabel["CCO"]
69
- before_size = mol.atoms.size
70
- atom = mol.add_atom!(6)
71
- atom.el.should == :c
72
- (mol.atoms.size - before_size).should == 1
73
- mol.csmiles.should == "CCO.C"
78
+ specify '#[] retrieves atom by index' do
79
+ mol = Rubabel["NCO"]
80
+ mol[0].el.should == :n
81
+ mol[1].el.should == :c
82
+ mol[2].el.should == :o
83
+ mol[-1].el.should == :o
84
+ mol[-2].el.should == :c
85
+ ar = mol[1..-1]
86
+ ar.first.el.should == :c
87
+ ar.last.el.should == :o
88
+ ar.size.should == 2
89
+ end
90
+
91
+ describe 'adding an atom' do
92
+ it 'can be added but not attached' do
93
+ mol = Rubabel["CCO"]
94
+ atom = mol.add_atom!(:n)
95
+ atom.el.should == :n
96
+ atom = mol.add_atom!(8)
97
+ atom.el.should == :o
98
+ mol.csmiles.should == "CCO.N.O"
99
+ end
100
+
101
+ it "can be added and attached by el symbol or atomic number" do
102
+ mol = Rubabel["CCO"]
103
+ first_carbon = mol[0]
104
+ mol.add_atom!(:n, first_carbon)
105
+ mol.csmiles.should == "NCCO"
106
+
107
+ mol.add_atom!(16, first_carbon)
108
+ mol.csmiles.should == "NC(CO)S"
109
+ end
110
+
111
+ end
112
+
113
+ specify '#<< adds atom to the last atom and returns the mol' do
114
+ mol = Rubabel::Molecule.new
115
+ # by element symbol or atomic number
116
+ reply = (mol << :n << :c)
117
+ reply.should be_a(Rubabel::Molecule)
118
+ reply.csmiles.should == 'CN'
74
119
  end
75
120
 
76
121
  specify '#dup duplicates the molecule' do
77
122
  mol = Rubabel["CCO"]
78
123
  dup_mol = mol.dup
79
- mol.atoms[0].ob.set_atomic_num(9)
124
+ mol[0].ob.set_atomic_num(9)
80
125
  mol.csmiles.should == "OCF"
81
126
  dup_mol.csmiles.should == "CCO"
82
127
  end
@@ -87,7 +132,7 @@ describe Rubabel::Molecule do
87
132
  specify 'given two atoms' do
88
133
  mol = Rubabel["CCO"]
89
134
  atom = mol.add_atom!(0)
90
- mol.add_bond!(mol.atoms[1], atom)
135
+ mol.add_bond!(mol[1], atom)
91
136
  mol.csmiles.should == '*C(O)C'
92
137
  end
93
138
  end
@@ -148,7 +193,7 @@ describe Rubabel::Molecule do
148
193
  subject { mol = Rubabel["CC"] }
149
194
  it 'can be turned into a carbocation' do
150
195
  mol = subject
151
- c = mol.atoms[0]
196
+ c = mol[0]
152
197
  c.ob.set_spin_multiplicity 2
153
198
  c.charge += 1
154
199
  mol.csmiles.should == "C[CH2+]"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubabel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -116,12 +116,12 @@ executables:
116
116
  extensions: []
117
117
  extra_rdoc_files:
118
118
  - LICENSE
119
- - README.rdoc
119
+ - README.md
120
120
  files:
121
121
  - .document
122
122
  - .rspec
123
123
  - LICENSE
124
- - README.rdoc
124
+ - README.md
125
125
  - Rakefile
126
126
  - VERSION
127
127
  - bin/fragmenter.rb
@@ -1,56 +0,0 @@
1
- = rubabel
2
-
3
- Ruby interface to the openbabel ruby bindings (or the openbabel gem). The
4
- interface attempts to be a ruby-ish analogue of
5
- {pybel}[http://openbabel.org/docs/current/UseTheLibrary/Python_PybelAPI.html].
6
-
7
- == Examples
8
-
9
- Rubabel.foreach("file.sdf") do |mol|
10
- print mol.write # -> canonical smiles: "smiles_string\tname\n"
11
- puts mol # .to_s -> canonical smiles string with no name or newline
12
- puts mol.exact_mass
13
- end
14
-
15
- uniq_atom_types = Rubabel.foreach("file.mol").map(&:type).uniq
16
-
17
- == Installing
18
-
19
- First, many thanks to Andreas Maunz for packaging openbabel as a gem which makes this install quite painless.
20
-
21
- === Quick Install
22
-
23
- On a POSIX system, make sure you have openbabel (including header files), cmake, curl, tar, sed and make {see openbabel instructions}[https://github.com/amaunz/openbabel-gem]. On ubuntu/debian:
24
-
25
- sudo apt-get install openbabel libopenbabel-dev cmake make curl
26
-
27
- Then install the gem (which should install the openbabel gem, too):
28
-
29
- gem install rubabel
30
-
31
- === Building from Source
32
-
33
- 1. download openbabel
34
- 2. swap out Init_OpenBabel for Init_openbabel in scripts/ruby/openbabel-ruby.cpp (see here[http://forums.openbabel.org/Ruby-Open-Babel-in-2-1-1-td957640.html]). Some versions have this fixed already, apparently.
35
- 3. make sure you have the right {dependencies to compile}(http://openbabel.org/docs/2.3.1/Installation/install.html#compiling-open-babel)
36
-
37
- Here's a complete example of compiling for a single user on Ubuntu 11.10 and probably will be generally forward compatible for some time. This will compile bindings on whichever ruby comes up with '/usr/bin/env ruby':
38
-
39
- # install the dependencies:
40
- sudo apt-get install libeigen2-dev cmake libwxgtk2.8-dev libxml2-dev libcairo2-dev
41
- # unpack it:
42
- tar -xzvf openbabel-2.3.1.tar.gz
43
- # swap out buggy lines in ruby bindings:
44
- sed -i 's/Init_OpenBabel/Init_openbabel/g' openbabel-2.3.1/scripts/ruby/openbabel-ruby.cpp
45
- # make a separate build directory for building in:
46
- mkdir build-rvmruby1.9.3
47
- cd build-rvmruby1.9.3
48
- mkdir ~/tools
49
- cmake ../openbabel-2.3.1 -DRUBY_BINDINGS=ON -DCMAKE_INSTALL_PREFIX=~/tools/openbabel-rvmruby1.9.3
50
- make && make install
51
-
52
- [I still need to make directions to install the gem on top of a build from source]
53
-
54
- == Copyright
55
-
56
- MIT License. See LICENSE for further details.