rubabel 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.6
1
+ 0.2.0
@@ -79,7 +79,6 @@ module Rubabel
79
79
  end
80
80
  end
81
81
 
82
- alias_method :each, :each_bond
83
82
 
84
83
  # retrieves the bond
85
84
  def get_bond(atom)
@@ -101,6 +100,7 @@ module Rubabel
101
100
  _atom = @ob.next_nbr_atom(iter)
102
101
  end
103
102
  end
103
+ alias_method :each, :each_atom
104
104
 
105
105
  # returns the neighboring atoms. Consider using each_atom.
106
106
  def atoms
@@ -153,6 +153,79 @@ module Rubabel
153
153
  @ob.get_partial_charge
154
154
  end
155
155
 
156
+ # permanently removes a hydrogen by properly incrementing the
157
+ # spin_multiplicity (and deleting a hydrogen if one is explicitly attached
158
+ # to the atom). If called with cnt=2 a carbene or nitrene can be made
159
+ # (giving a spin_multiplicity of 3). Makes no effort to ensure that the
160
+ # proper number of hydrogens already exist to be deleted, just alters the
161
+ # spin_multiplicity and deletes the right number of hydrogens if they are
162
+ # available to be deleted. Adds one charge to the atom.
163
+ def remove_an_h!(add_charge=true)
164
+ new_spin =
165
+ case @ob.get_spin_multiplicity
166
+ when 0 then 2
167
+ when 2 then 3
168
+ end
169
+ @ob.set_spin_multiplicity(new_spin)
170
+ atoms.each do |atom|
171
+ if atom.atomic_num == 1
172
+ self.mol.delete_atom(atom)
173
+ break
174
+ end
175
+ end
176
+ # add the charge
177
+ (self.charge = charge + 1) if add_charge
178
+ self
179
+ end
180
+
181
+ # philosophy on equality: there are *so* many ways for two atoms to be
182
+ # different that we can never really ensure that "equivalence" is met
183
+ # without calling ~20 methods. We narrowly define equivalence so it is
184
+ # useful for that case and let the user make more complicated
185
+ # equivalency/equality definitions themselves.
186
+
187
+ # the exact same atom in the same molecule. The equivalency test for
188
+ # molecules is a little pricey, so better to use something like atom.id ==
189
+ # other.id if you know you are working within the same molecule.
190
+ def equal?(other)
191
+ other.respond_to?(:mol) && mol.equal?(other.mol) && id == other.id
192
+ end
193
+
194
+ alias_method :==, :equal?
195
+ alias_method :eql?, :equal?
196
+
197
+ ## opposite of remove_an_h!
198
+ ## THIS IS STILL BROKEN!!!
199
+ # maybe need to change the type?? C+ -> C2 or C3, but this gets really
200
+ # invasive... Why is this so flippin hard to do!!
201
+ #def add_an_h!(remove_charge=true)
202
+ #new_spin =
203
+ #case @ob.get_spin_multiplicity
204
+ #when 2 then 0
205
+ #when [1,3] then 2
206
+ #end
207
+ #@ob.set_spin_multiplicity(new_spin)
208
+
209
+ #puts self.inspect_internals
210
+ #puts "EXAMIN B:"
211
+ #p self
212
+ #p self.charge
213
+ #(self.charge = self.charge - 1) if remove_charge
214
+ #puts "EXAMIN A:"
215
+ #puts self.inspect_internals
216
+ #p self
217
+ #p self.charge
218
+ #puts "BEFORE:"
219
+ #p mol.formula
220
+ #p mol.atoms
221
+ #mol.add_bond!(self, mol.add_atom!(1))
222
+ #puts "AFTER:"
223
+ #p mol.formula
224
+ #p mol.atoms
225
+ #abort 'here'
226
+ #self
227
+ #end
228
+
156
229
  def spin
157
230
  @ob.get_spin_multiplicity
158
231
  end
@@ -203,8 +276,25 @@ module Rubabel
203
276
  def hbond_donor?() @ob.is_hbond_donor end
204
277
  def hbond_donor_h?() @ob.is_hbond_donor_h end
205
278
 
279
+ def double_bond?
280
+ each_bond.any? {|bond| bond.bond_order == 2 }
281
+ end
282
+
283
+ def single_bond?
284
+ each_bond.any? {|bond| bond.bond_order == 1 }
285
+ end
286
+
206
287
  def carboxyl_carbon?
207
- atoms.any?(&:carboxyl_oxygen?)
288
+ each_atom.any?(&:carboxyl_oxygen?)
289
+ end
290
+
291
+ def carbonyl_oxygen?
292
+ ats = atoms
293
+ ats.size == 1 && ats.first.el == :c && double_bond?
294
+ end
295
+
296
+ def carbonyl_carbon?
297
+ each_atom.any?(&:carbonyl_oxygen?)
208
298
  end
209
299
 
210
300
  # # does this carbon hold a primary alcohol
@@ -218,5 +308,17 @@ module Rubabel
218
308
  def inspect
219
309
  "<#{type} id:#{id}>"
220
310
  end
311
+
312
+ def inspect_internals
313
+ "<" << @ob.methods.grep(/get_/).map do |mthd|
314
+ begin
315
+ "#{mthd.to_s.sub(/get_/,'')}=#{@ob.send(mthd)}"
316
+ rescue ArgumentError
317
+ nil
318
+ end
319
+ end.compact.join(" ") << ">"
320
+ end
321
+
322
+
221
323
  end
222
324
  end
@@ -63,6 +63,25 @@ module Rubabel
63
63
  @ob.set_bond_order(val)
64
64
  end
65
65
 
66
+ # returns self
67
+ def set_atoms!(beg_atom, end_atom)
68
+ @ob.set_begin(beg_atom.ob)
69
+ @ob.set_end(end_atom.ob)
70
+ self
71
+ end
72
+
73
+ # Sets the beginning atom of the bond to atom. returns self
74
+ def set_begin!(atom)
75
+ @ob.set_begin(atom.ob)
76
+ self
77
+ end
78
+
79
+ # Sets the end atom of the bond to the given atom. returns self
80
+ def set_end!(atom)
81
+ @ob.set_end(atom.ob)
82
+ self
83
+ end
84
+
66
85
  # returns an array of Rubabel::Atoms
67
86
  def atoms
68
87
  [@ob.get_begin_atom.upcast, @ob.get_end_atom.upcast]
@@ -112,9 +112,10 @@ module Rubabel
112
112
  def add_atom!(atomic_num=1)
113
113
  # jtp implementation:
114
114
  # @ob.add_atom(atom.ob)
115
- new_a = @ob.new_atom
116
- new_a.set_atomic_num(atomic_num)
117
- Rubabel::Atom.new(new_a)
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)
118
119
  end
119
120
 
120
121
  def delete_atom(atom)
@@ -144,7 +145,6 @@ module Rubabel
144
145
  # to add_h!
145
146
  def formula() @ob.get_formula end
146
147
 
147
-
148
148
  def initialize(obmol)
149
149
  @ob = obmol
150
150
  end
@@ -254,11 +254,6 @@ module Rubabel
254
254
  self
255
255
  end
256
256
 
257
- # calls separate on the OBMol object
258
- def separate!
259
- @ob.separate
260
- end
261
-
262
257
  # returns just the smiles string :smi (not the id)
263
258
  def smiles
264
259
  to_s(:smi)
@@ -269,10 +264,38 @@ module Rubabel
269
264
  to_s(:can)
270
265
  end
271
266
 
272
- # equal if their canonical SMILES strings (including ID) are equal. This
273
- # may become more rigorous in the future
267
+ # checks to see if the molecules are the same OBMol object underneath by
268
+ # modifying one and seeing if the other changes. This is because
269
+ # openbabel routinely creates new objects that point to the same
270
+ # underlying data store, so even checking for OBMol equivalency is not
271
+ # enough.
272
+ def equal?(other)
273
+ return false unless other.is_a?(self.class)
274
+ are_identical = false
275
+ if self.title == other.title
276
+ begin
277
+ obj_id = self.object_id.to_s
278
+ self.title += obj_id
279
+ are_identical = (self.title == other.title)
280
+ ensure
281
+ self.title.sub(/#{obj_id}$/,'')
282
+ end
283
+ are_identical
284
+ else
285
+ false
286
+ end
287
+ end
288
+
289
+ alias_method :eql?, :equal?
290
+
291
+ # defined as whether the csmiles strings are identical. This incorporates
292
+ # more information than the FP2 fingerprint, for instance (try changing
293
+ # the charge and see how it does not influence the fingerprint).
294
+ # Obviously, things like title or data will not be evaluated with ==. See
295
+ # equal? if you are looking for identity. More stringent comparisons will
296
+ # have to be done by hand!
274
297
  def ==(other)
275
- self.write_string(:can) == other.write_string(:can)
298
+ other.respond_to?(:csmiles) && (csmiles == other.csmiles)
276
299
  end
277
300
 
278
301
  # iterates over the molecule's Rubabel::Atom objects
@@ -308,11 +331,21 @@ module Rubabel
308
331
  self
309
332
  end
310
333
 
334
+ # gets the bond by id
335
+ def bond(id)
336
+ @ob.get_bond_by_id(id).upcast
337
+ end
338
+
311
339
  # returns the array of bonds. Consider using #each_bond
312
340
  def bonds
313
341
  each_bond.map.to_a
314
342
  end
315
343
 
344
+ # gets the atom by id
345
+ def atom(id)
346
+ @ob.get_atom_by_id(id).upcast
347
+ end
348
+
316
349
  # returns the array of atoms. Consider using #each
317
350
  def atoms
318
351
  each_atom.map.to_a
@@ -358,8 +391,15 @@ module Rubabel
358
391
  end
359
392
  end
360
393
 
361
- def delete_bond(bond)
362
- @ob.delete_bond(bond.ob, false)
394
+ # if given a bond, deletes it (doesn't garbage collect). If given two
395
+ # atoms, deletes the bond between them.
396
+ def delete_bond(*args)
397
+ case args.size
398
+ when 1
399
+ @ob.delete_bond(args[0].ob, false)
400
+ when 2
401
+ @ob.delete_bond(args[0].get_bond(args[1]).ob, false)
402
+ end
363
403
  end
364
404
 
365
405
  def delete_atom(atom)
@@ -373,21 +413,15 @@ module Rubabel
373
413
  self
374
414
  end
375
415
 
376
- # takes a Rubabel::Bond object or a pair of Rubabel::Atom objects
377
- def add_bond!(*args)
378
- case args.size
379
- when 1
380
- ob_bond = args.first.ob
381
- @ob.add_bond(ob_bond)
382
- ob_bond.get_begin_atom.add_bond(ob_bond)
383
- ob_bond.get_end_atom.add_bond(ob_bond)
384
- when 2
385
- @ob.add_bond(args[0].idx, args[1].idx, args[2] || 1)
386
- #ob_bond = Rubabel::Bond[ *args ].ob
387
- #ob_bond.get_begin_atom.add_bond(ob_bond)
388
- #ob_bond.get_end_atom.add_bond(ob_bond)
389
- #@ob.add_bond(ob_bond)
390
- end
416
+ # creates a new (as yet unspecified) bond associated with the molecule and gives it a unique id
417
+ def new_bond
418
+ @ob.new_bond.upcast
419
+ end
420
+
421
+ # takes a pair of Rubabel::Atom objects and adds a bond to the molecule
422
+ # returns whether the bond creation was successful.
423
+ def add_bond!(atom1, atom2, order=1)
424
+ @ob.add_bond(atom1.idx, atom2.idx, order)
391
425
  end
392
426
 
393
427
  # yields self after deleting the specified bonds. When the block is
@@ -405,14 +439,24 @@ module Rubabel
405
439
  end
406
440
 
407
441
  # splits the molecules at the given bonds and returns the fragments. Does
408
- # not alter the caller.
442
+ # not alter the caller. If the molecule is already fragmented, then
443
+ # returns the separate fragments.
409
444
  def split(*bonds)
410
- delete_and_restore_bonds(*bonds) do |mol|
411
- mol.ob.separate.map(&:upcast)
445
+ if bonds.size > 0
446
+ delete_and_restore_bonds(*bonds) do |mol|
447
+ mol.ob.separate.map(&:upcast)
448
+ end
449
+ else
450
+ self.ob.separate.map(&:upcast)
412
451
  end
413
452
  end
414
453
 
415
- alias_method :separate, :split
454
+ def each_fragment(&block)
455
+ block or return enum_for(__method__)
456
+ @ob.separate.each do |ob_mol|
457
+ block.call( ob_mol.upcast )
458
+ end
459
+ end
416
460
 
417
461
  # emits smiles without the trailing tab, newline, or id. Use write_string
418
462
  # to get the default OpenBabel behavior (ie., tabs and newlines).
@@ -593,6 +637,21 @@ module Rubabel
593
637
  distance_matrix.max
594
638
  end
595
639
 
640
+ # adds 1 hydrogen to the formula and returns self
641
+ def add_hydrogen_to_formula!
642
+ string = @ob.get_formula
643
+ substituted = false
644
+ new_string = string.sub(/H(\d*)/) { substituted=true; "H#{$1.to_i+1}" }
645
+ unless substituted
646
+ new_string = string.sub("^(C?\d*)") { $1 + 'H' }
647
+ end
648
+ puts 'HERE'
649
+ p string
650
+ p new_string
651
+ #@ob.set_formula(new_string)
652
+ self
653
+ end
654
+
596
655
  end
597
656
  end
598
657
 
@@ -7,18 +7,20 @@ module Rubabel
7
7
  module Fragmentable
8
8
 
9
9
  #:sp3c_oxygen_asymmetric_far_sp3, :sp3c_nitrogen_asymmetric_far_sp3,
10
- RULES = Set[ :alcohol_to_aldehyde, :peroxy_to_carboxy, :co2_loss,
11
- :sp3c_oxygen_double_bond_far_side_sp3, :sp3c_oxygen_double_bond_far_side_sp2, :sp3c_oxygen_double_bond_water_loss, :sp3c_nitrogen_double_bond,
12
- ]
10
+ #RULES = Set[ :alcohol_to_aldehyde, :peroxy_to_carboxy, :co2_loss,
11
+ # :sp3c_oxygen_double_bond_far_side_sp3, :sp3c_oxygen_double_bond_far_side_sp2, :sp3c_oxygen_double_bond_water_loss, :sp3c_nitrogen_double_bond,
12
+ #]
13
13
  #ADDUCTS = [:lioh, :nh4cl, :nh4oh]
14
- CO_RULES = Set[:alcohol_to_aldehyde, :peroxy_to_carboxy, :co2_loss,
15
- :sp3c_oxygen_double_bond_water_loss, :sp3c_oxygen_double_bond_far_side_sp2, :sp3c_oxygen_double_bond_far_side_sp3, :sp3c_oxygen_asymmetric_far_sp3
16
- ]
14
+ #CO_RULES = Set[:alcohol_to_aldehyde, :peroxy_to_carboxy, :co2_loss,
15
+ # :sp3c_oxygen_double_bond_water_loss, :sp3c_oxygen_double_bond_far_side_sp2, :sp3c_oxygen_double_bond_far_side_sp3, :sp3c_oxygen_asymmetric_far_sp3
16
+ #]
17
+
18
+ RULES = Set[:cad_o, :cad_oo, :oxed_ether]
17
19
 
18
20
  DEFAULT_OPTIONS = {
19
21
  rules: RULES,
20
22
  #adduct: nil,
21
- ph: 7.4,
23
+ #ph: 7.4,
22
24
  # return only the set of unique fragments
23
25
  uniq: false,
24
26
  }
@@ -142,98 +144,90 @@ module Rubabel
142
144
 
143
145
  end
144
146
 
145
- # to ensure proper fragmentation, will add_h!(ph) first at the given ph
147
+ # splits the molecule between the carbon and carbon_nbr, adds a double
148
+ # bond between the carbon and oxygen, and moves whatever was on the
149
+ # oxygen (e.g., an OH or a charge) to the carbon_nbr. Returns two new
150
+ # molecules.
151
+ def carbonyl_oxygen_dump(carbon, oxygen, carbon_nbr)
152
+ appendage = oxygen.atoms.find {|a| a.el != :c }
153
+ if oxygen.charge != 0
154
+ ocharge = oxygen.charge
155
+ end
156
+ nmol = self.dup
157
+ new_oxygen = nmol.atom(oxygen.id)
158
+ new_carbon = nmol.atom(carbon.id)
159
+ new_carbon_nbr = nmol.atom(carbon_nbr.id)
160
+ new_appendage = nmol.atom(appendage.id) if appendage
161
+ nmol.delete_bond(new_carbon.get_bond(new_carbon_nbr))
162
+ if new_appendage
163
+ nmol.delete_bond(new_oxygen.get_bond(new_appendage))
164
+ nmol.add_bond!(new_carbon_nbr, new_appendage)
165
+ end
166
+ if ocharge
167
+ new_carbon_nbr.charge += ocharge
168
+ new_oxygen.charge -= ocharge
169
+ end
170
+ new_carbon.get_bond(new_oxygen).bond_order = 2
171
+ nmol.split
172
+ end
173
+
174
+ # breaks the bond and gives the electrons to the oxygen
175
+ def carbon_oxygen_esteal(carbon, oxygen)
176
+ nmol = self.dup
177
+ nmol.ob.add_hydrogens
178
+ ncarbon = nmol.atom(carbon.id)
179
+ noxygen = nmol.atom(oxygen.id)
180
+ nmol.delete_bond(ncarbon, noxygen)
181
+ ncarbon.charge += 1
182
+ noxygen.charge -= 1
183
+ ncarbon.remove_an_h!
184
+ #p ncarbon.ob.implicit_hydrogen_count
185
+ #p ncarbon
186
+ #ncarbon.ob.decrement_implicit_valence
187
+ #p ncarbon.ob.implicit_hydrogen_count
188
+ #p ncarbon
189
+ #ncarbon.ob.increment_implicit_valence
190
+
191
+ nmol.title = nmol.to_s
192
+ p nmol.write("tmp.svg")
193
+ parts = nmol.split
194
+ p z=parts.first
195
+ p z.formula
196
+ p z.mass
197
+ p z.exact_mass
198
+
199
+ puts "HIAY"
200
+ end
201
+
146
202
  # an empty array is returned if there are no fragments generated.
203
+ # Hydrogens are added at a pH of 7.4, unless they have already been
204
+ # added.
147
205
  #
148
- # :ph => 7.4
149
- # :uniq => false
206
+ # :rules => queryable by :include? set of rules
207
+ # :uniq => false
150
208
  def fragment(opts={})
209
+ only_uniqs = true
151
210
  opts = DEFAULT_OPTIONS.merge(opts)
152
211
  opts[:rules].each do |rule|
153
212
  raise ArgumentError, "bad rule: #{rule}" unless RULES.include?(rule)
154
213
  end
155
214
 
156
215
  had_hydrogens = self.h_added?
157
-
158
- self.correct_for_ph!(opts[:ph])
216
+ self.correct_for_ph!(7.4) unless had_hydrogens
159
217
  self.remove_h!
160
218
 
161
- rules = opts[:rules]
162
219
  fragment_sets = []
163
- if rules.any? {|rule| CO_RULES.include?(rule) }
164
- putsv "matching C-O"
165
- self.each_match("CO").each do |_atoms|
166
- # note: this will *not* match C=O
167
- (carbon, oxygen) = _atoms
168
- carbon_nbrs = carbon.atoms.reject {|atom| atom == oxygen }
169
- c3_nbrs = carbon_nbrs.select {|atm| atm.type == 'C3' }
170
- # pulling this out here causes it to work incorrectly internally
171
- # (not sure why)
172
- #co_bond = carbon.get_bond(oxygen)
173
-
174
- case oxygen.bonds.size # non-hydrogen bonds
175
- when 1 # *must* be an alcohol or a carboxylic acid
176
- putsv "#{csmiles} oxygen has no other bonds besides C-O (alcohol or carboxylic acid)"
177
- if carbon.type == 'C3'
178
- if rules.include?(:sp3c_oxygen_double_bond_water_loss)
179
- putsv "rule :sp3c_oxygen_double_bond_water_loss"
180
- fragment_sets.push *near_side_double_bond_break(carbon, oxygen)
181
- end
182
- if rules.include?(:alcohol_to_aldehyde)
183
- putsv "rule :alcohol_to_aldehyde"
184
- fragment_sets.push *alcohol_to_aldehyde(carbon, oxygen, carbon_nbrs)
185
- end
186
- elsif carbon.carboxyl_carbon?
187
- if rules.include?(:co2_loss)
188
- putsv "rule :co2_loss"
189
- if c3_nbr = c3_nbrs.first
190
- fragment_sets.push *co2_loss(carbon, oxygen, c3_nbr)
191
- end
192
- end
193
- end
194
- when 2
195
- putsv "#{csmiles} c-o & oxygen has 2 non-hydrogen bonds"
196
- oxygen_nbr = oxygen.atoms.reject {|atom| atom.idx == carbon.idx }.first
197
- if carbon.type == 'C3'
198
- if rules.include?(:peroxy_to_carboxy)
199
- fragment_sets.push *peroxy_to_carboxy(carbon, oxygen, carbon_nbrs, oxygen_nbr)
200
- end
201
- # ester and ethers (look *only* on close side for places to make
202
- # double bond)
203
-
204
- if oxygen_nbr.type == 'C3'
205
- putsv "oxygen nbr is C3"
206
- if rules.include?(:sp3c_oxygen_double_bond_far_side_sp3)
207
- putsv "rule :sp3c_oxygen_double_bond_far_side_sp3"
208
- fragment_sets.push *near_side_double_bond_break(carbon, oxygen)
209
- end
210
- if rules.include?(:sp3c_oxygen_asymmetric_far_sp3)
211
- putsv "rule :sp3c_oxygen_asymmetric_far_sp3"
212
- # only returns a single frag set
213
- fragment_sets.push electrophile_snatches_electrons(carbon, oxygen)
214
- end
215
- end
216
- if oxygen_nbr.type == 'C2'
217
- if rules.include?(:sp3c_oxygen_double_bond_far_side_sp2)
218
- putsv "rule :sp3c_oxygen_double_bond_far_side_sp2"
219
- fragment_sets.push *near_side_double_bond_break(carbon, oxygen)
220
- end
221
- end
222
- # note: the case of a carboxy is found with CO search
223
- end
220
+
221
+ if opts[:rules].any? {|r| [:cad_o, :cad_oo].include?(r) }
222
+ self.each_match("C[O;h1,O]", only_uniqs) do |carbon, oxygen|
223
+ carbon.atoms.select {|a| a.el == :c }.each do |carbon_nbr|
224
+ fragment_sets << carbonyl_oxygen_dump(carbon, oxygen, carbon_nbr)
224
225
  end
225
226
  end
226
227
  end
227
- if rules.include?(:sp3c_nitrogen_double_bond)
228
- self.each_match("CN") do |_atoms|
229
- (carbon, nitrogen) = _atoms
230
- num_nitrogen_bonds = nitrogen.bonds.size
231
- case num_nitrogen_bonds
232
- when 2
233
- if carbon.type == 'C3'
234
- fragment_sets.push *near_side_double_bond_break(carbon, nitrogen)
235
- end
236
- end
228
+ if opts[:rules].any? {|r| [:oxed_ether].include?(r) }
229
+ self.each_match("C[O&X2]", only_uniqs) do |carbon, oxygen|
230
+ fragment_sets << carbon_oxygen_esteal(carbon, oxygen)
237
231
  end
238
232
  end
239
233
 
@@ -243,12 +237,110 @@ module Rubabel
243
237
  end
244
238
  if opts[:uniq]
245
239
  # TODO: impelent properly
246
- #fragment_sets = fragment_sets.uniq_by(&:csmiles)
247
240
  raise NotImplementedError
241
+ #fragment_sets = fragment_sets.uniq_by(&:csmiles)
248
242
  end
243
+
249
244
  fragment_sets
250
245
  end
251
246
 
247
+
248
+ # had_hydrogens = self.h_added?
249
+
250
+ #self.correct_for_ph!(opts[:ph])
251
+ #self.remove_h!
252
+
253
+ #rules = opts[:rules]
254
+ #fragment_sets = []
255
+ #if rules.any? {|rule| CO_RULES.include?(rule) }
256
+ #putsv "matching C-O"
257
+ #self.each_match("CO").each do |_atoms|
258
+ ## note: this will *not* match C=O
259
+ #(carbon, oxygen) = _atoms
260
+ #carbon_nbrs = carbon.atoms.reject {|atom| atom == oxygen }
261
+ #c3_nbrs = carbon_nbrs.select {|atm| atm.type == 'C3' }
262
+ ## pulling this out here causes it to work incorrectly internally
263
+ ## (not sure why)
264
+ ##co_bond = carbon.get_bond(oxygen)
265
+
266
+ #case oxygen.bonds.size # non-hydrogen bonds
267
+ #when 1 # *must* be an alcohol or a carboxylic acid
268
+ #putsv "#{csmiles} oxygen has no other bonds besides C-O (alcohol or carboxylic acid)"
269
+ #if carbon.type == 'C3'
270
+ #if rules.include?(:sp3c_oxygen_double_bond_water_loss)
271
+ #putsv "rule :sp3c_oxygen_double_bond_water_loss"
272
+ #fragment_sets.push *near_side_double_bond_break(carbon, oxygen)
273
+ #end
274
+ #if rules.include?(:alcohol_to_aldehyde)
275
+ #putsv "rule :alcohol_to_aldehyde"
276
+ #fragment_sets.push *alcohol_to_aldehyde(carbon, oxygen, carbon_nbrs)
277
+ #end
278
+ #elsif carbon.carboxyl_carbon?
279
+ #if rules.include?(:co2_loss)
280
+ #putsv "rule :co2_loss"
281
+ #if c3_nbr = c3_nbrs.first
282
+ #fragment_sets.push *co2_loss(carbon, oxygen, c3_nbr)
283
+ #end
284
+ #end
285
+ #end
286
+ #when 2
287
+ #putsv "#{csmiles} c-o & oxygen has 2 non-hydrogen bonds"
288
+ #oxygen_nbr = oxygen.atoms.reject {|atom| atom.idx == carbon.idx }.first
289
+ #if carbon.type == 'C3'
290
+ #if rules.include?(:peroxy_to_carboxy)
291
+ #fragment_sets.push *peroxy_to_carboxy(carbon, oxygen, carbon_nbrs, oxygen_nbr)
292
+ #end
293
+ ## ester and ethers (look *only* on close side for places to make
294
+ ## double bond)
295
+
296
+ #if oxygen_nbr.type == 'C3'
297
+ #putsv "oxygen nbr is C3"
298
+ #if rules.include?(:sp3c_oxygen_double_bond_far_side_sp3)
299
+ #putsv "rule :sp3c_oxygen_double_bond_far_side_sp3"
300
+ #fragment_sets.push *near_side_double_bond_break(carbon, oxygen)
301
+ #end
302
+ #if rules.include?(:sp3c_oxygen_asymmetric_far_sp3)
303
+ #putsv "rule :sp3c_oxygen_asymmetric_far_sp3"
304
+ ## only returns a single frag set
305
+ #fragment_sets.push electrophile_snatches_electrons(carbon, oxygen)
306
+ #end
307
+ #end
308
+ #if oxygen_nbr.type == 'C2'
309
+ #if rules.include?(:sp3c_oxygen_double_bond_far_side_sp2)
310
+ #putsv "rule :sp3c_oxygen_double_bond_far_side_sp2"
311
+ #fragment_sets.push *near_side_double_bond_break(carbon, oxygen)
312
+ #end
313
+ #end
314
+ ## note: the case of a carboxy is found with CO search
315
+ #end
316
+ #end
317
+ #end
318
+ #end
319
+ #if rules.include?(:sp3c_nitrogen_double_bond)
320
+ #self.each_match("CN") do |_atoms|
321
+ #(carbon, nitrogen) = _atoms
322
+ #num_nitrogen_bonds = nitrogen.bonds.size
323
+ #case num_nitrogen_bonds
324
+ #when 2
325
+ #if carbon.type == 'C3'
326
+ #fragment_sets.push *near_side_double_bond_break(carbon, nitrogen)
327
+ #end
328
+ #end
329
+ #end
330
+ #end
331
+
332
+ #unless had_hydrogens
333
+ #fragment_sets.each {|set| set.each(&:remove_h!) }
334
+ #self.remove_h!
335
+ #end
336
+ #if opts[:uniq]
337
+ ## TODO: impelent properly
338
+ ##fragment_sets = fragment_sets.uniq_by(&:csmiles)
339
+ #raise NotImplementedError
340
+ #end
341
+ #fragment_sets
342
+ #end
343
+
252
344
  end
253
345
  include Fragmentable
254
346
  end
@@ -17,6 +17,45 @@ describe Rubabel::Atom do
17
17
  chlorine.id.should == 3
18
18
  end
19
19
 
20
+ specify 'equality' do
21
+ mol = Rubabel["CCO"]
22
+ oxygen = mol.atoms[2]
23
+ oxygen_from_match = mol.matches("CO").first.last
24
+ (oxygen == oxygen_from_match).should be_true
25
+ (oxygen.equal?(oxygen_from_match)).should be_true
26
+ (oxygen.eql?(oxygen_from_match)).should be_true
27
+
28
+ mol2 = Rubabel["CCO"]
29
+ (mol.atoms[0] == mol2.atoms[0]).should_not be_true
30
+ (mol.atoms[0].equal?(mol2.atoms[0])).should_not be_true
31
+ (mol.atoms[0].eql?(mol2.atoms[0])).should_not be_true
32
+ end
33
+
34
+ it 'removes hydrogens with proper charge accounting' do
35
+ mol = Rubabel["CC"]
36
+ mol.add_h!
37
+ mol.atoms[0].remove_an_h!
38
+ mol.formula.should == "C2H5"
39
+ mol.csmiles.should == 'C[CH2+]'
40
+ mol.exact_mass.round(5).should == 29.03913
41
+ mol.charge.should == 1
42
+
43
+ # can't seem to get working properly!!!
44
+ #mol.atoms[0].add_an_h!
45
+ #mol.formula.should == 'C2H6'
46
+ #mol.csmiles.should == 'CC'
47
+ #mol.charge.should == 0
48
+ ##fmol.atoms[0].charge -= 1
49
+ #mol.exact_mass.should == 323
50
+ end
51
+
52
+ it 'can find atom identities with simple questions' do
53
+ mol = Rubabel["NCC(O)CC(=O)"]
54
+ (c_exp, o_exp) = mol.matches("C=O").first
55
+ mol.find(&:carbonyl_carbon?).id.should == c_exp.id
56
+ mol.find(&:carbonyl_oxygen?).id.should == o_exp.id
57
+ end
58
+
20
59
  describe 'working with a complex molecule' do
21
60
 
22
61
  before do
@@ -4,16 +4,19 @@ require 'rubabel/molecule'
4
4
  require 'rubabel/bond'
5
5
 
6
6
  describe Rubabel::Bond do
7
- subject { Rubabel::Molecule.from_file( TESTFILES + '/cholesterol.sdf' ).bonds.first }
7
+ describe 'cholesterol from sdf' do
8
+ subject { Rubabel::Molecule.from_file( TESTFILES + '/cholesterol.sdf' ).bonds.first }
8
9
 
9
- it 'is a Rubabel::Bond' do
10
- subject.should be_a(Rubabel::Bond)
11
- end
10
+ it 'is a Rubabel::Bond' do
11
+ subject.should be_a(Rubabel::Bond)
12
+ end
12
13
 
13
- it 'knows what atoms it includes' do
14
- subject.each_atom do |atom|
15
- atom.should be_a(Rubabel::Atom)
14
+ it 'knows what atoms it includes' do
15
+ subject.each_atom do |atom|
16
+ atom.should be_a(Rubabel::Atom)
17
+ end
18
+ subject.atoms.size.should == 2
16
19
  end
17
- subject.atoms.size.should == 2
18
20
  end
21
+
19
22
  end
@@ -4,139 +4,226 @@ require 'rubabel'
4
4
 
5
5
  $VERBOSE = nil
6
6
 
7
- describe Rubabel::Molecule::Fragmentable do
8
-
9
- # :peroxy_to_carboxy
10
- # :oxygen_asymmetric_sp3, :nitrogen_asymmetric_sp3,
11
- # :internal_phosphoester
12
-
13
- describe 'fragmentation rules' do
14
- # coenzyme: CC1=CC(=O)C=CC1=O
15
- # 2-methylcyclohexa-2,5-diene-1,4-dione
16
-
17
- let(:test_mol) { "COP(=O)(O)OCNCOCC(OO)C(=O)O" }
18
-
19
- it 'raises an error for a bad rule' do
20
- mol = Rubabel["CCNC"]
21
- expect { mol.fragment(rules: [:wackiness]) }.to raise_error
22
- end
23
-
24
- describe ':sp3c_nitrogen_double_bond' do
25
-
26
- it 'cleaves like an ether a secondary NH group if possible' do
27
- mol = Rubabel["CCNC"]
28
- frag_sets = mol.fragment(rules: [:sp3c_nitrogen_double_bond])
29
- frag_sets.size.should == 1
30
- csmiles = frag_sets.first.map(&:csmiles)
31
- csmiles.should include("C=C")
32
- csmiles.should include("C[NH3+]")
33
- end
34
-
35
- it 'will not cleave if not possible' do
36
- mol = Rubabel["CNC"]
37
- frag_sets = mol.fragment(rules: [:sp3c_nitrogen_double_bond])
38
- frag_sets.should be_empty
39
- end
40
-
41
- end
42
-
43
- describe ':co2_loss' do
44
- it 'loss of CO2 from carboxy group with charge transfer' do
45
- mol = Rubabel["NCC(=O)O"]
46
- frag_sets = mol.fragment( rules: [:co2_loss] )
47
- frag_sets.size.should == 1
48
- csmiles = frag_sets.first.map(&:csmiles)
49
-
50
- csmiles.should include("[CH2-][NH3+]")
51
- csmiles.should include("O=C=O")
52
- end
53
-
54
- it "doesn't remove CO2 if adjacent is not c3" do
55
- mol = Rubabel["C=CC(=O)O"]
56
- fragments = mol.fragment( rules: [:co2_loss] )
57
- fragments.should be_empty
58
- end
59
-
60
- end
61
-
62
- describe ':peroxy_to_carboxy' do
63
- it 'works' do
64
- mol = Rubabel["NCCC(OO)CC"]
65
- frag_sets = mol.fragment( rules: [:peroxy_to_carboxy] )
66
- frag_sets.size.should == 2
67
- frag_sets.flatten(1).map(&:csmiles).sort.should == ["CC", "CCC(=O)O", "CC[NH3+]", "OC(=O)CC[NH3+]"]
68
- end
69
- end
70
-
71
- describe ':sp3c_oxygen_asymmetric_far_sp3', :pending do
72
- it 'splits like sp3c_oxygen_double_bond except oxygen takes the electrons' do
73
- $VERBOSE = 3
74
- mol = Rubabel["NCCCOCC"]
75
- frag_sets = mol.fragment( rules: [:sp3c_oxygen_asymmetric_far_sp3] )
76
- $VERBOSE = nil
77
- frag_sets.size.should == 2
7
+ #describe Rubabel::Molecule::Fragmentable do
8
+
9
+ ## :peroxy_to_carboxy
10
+ ## :oxygen_asymmetric_sp3, :nitrogen_asymmetric_sp3,
11
+ ## :internal_phosphoester
12
+
13
+ #describe 'fragmentation rules' do
14
+ ## coenzyme: CC1=CC(=O)C=CC1=O
15
+ ## 2-methylcyclohexa-2,5-diene-1,4-dione
16
+
17
+ #let(:test_mol) { "COP(=O)(O)OCNCOCC(OO)C(=O)O" }
18
+
19
+ #it 'raises an error for a bad rule' do
20
+ #mol = Rubabel["CCNC"]
21
+ #expect { mol.fragment(rules: [:wackiness]) }.to raise_error
22
+ #end
23
+
24
+ #describe 'cad_o: carbonyl appendage dump ' do
25
+ ## a primary oxygen or peroxide => C=O appendage dump
26
+
27
+ #describe 'cad_o: primary alcohol' do
28
+ #mol = Rubabel["NCC(O)CC"]
29
+ #frags = mol.fragment(rules: [:cad_o])
30
+ #frags.flatten(1).map(&:csmiles).should == ["C[NH3+]", "CCC=O", "C([NH3+])C=O", "CC"]
31
+ #end
32
+
33
+ #describe 'peroxide' do
34
+ #mol = Rubabel["NCC(OO)CC"]
35
+ #frags = mol.fragment(rules: [:cad_oo])
36
+ #frags.flatten(1).each_with_index do |f,i|
37
+ #f.write("mol#{i}.svg")
38
+ #end
39
+ #frags.flatten(1).map(&:csmiles).should == ["OC[NH3+]", "CCC=O", "C([NH3+])C=O", "CCO"]
40
+ #end
41
+
42
+ #describe 'cad_o: carboxylate' do
43
+ #mol = Rubabel["CCC(=O)O"]
44
+ #pieces = mol.fragment(rules: [:cad_o])
45
+ #pieces.flatten(1).map(&:csmiles).should == ["[CH2-]C", "O=C=O"]
46
+ #end
47
+
48
+ #describe 'cad_o: carboxylic acid' do
49
+ #mol = Rubabel["CCC(=O)O"]
50
+ #mol.add_h!(1.5)
51
+ #pieces = mol.fragment(rules: [:cad_o])
52
+ #pieces.flatten(1).map(&:csmiles).should == ["CC", "O=C=O"]
53
+ #end
54
+ #end
55
+
56
+ #describe 'oxe: oxygen electron stealing' do
57
+ ## oxygen just steals the electron pair it is attached to. This
58
+ ## typically results in a negatively charged oxygen and a positively
59
+ ## charged carbo-cation.
60
+ #describe 'ether to ions' do
78
61
  #mol = Rubabel["NCCOCC"]
79
- #p mol.fragment( rules: [:sp3c_oxygen_asymmetric_far_sp3] )
80
- #mol = Rubabel["NCOC"]
81
- #p mol.fragment( rules: [:sp3c_oxygen_asymmetric_far_sp3] )
82
- end
83
- end
84
-
85
- describe ':sp3c_oxygen_double_bond_water_loss' do
86
-
87
- it 'does h2o loss of alcohol' do
88
- mol = Rubabel["NCCC(O)CC"]
89
- fragments = mol.fragment( rules: [:sp3c_oxygen_double_bond_water_loss] )
90
- fragments.flatten(1).map(&:csmiles).sort.should == ["CC=CCC[NH3+]", "CCC=CC[NH3+]", "O", "O"]
91
- end
92
-
93
- it 'h2o loss does not allow bad chemistry' do
94
- # lone pair and double bond resonance ?
95
- mol = Rubabel["NCC(O)CC"]
96
- fragments = mol.fragment( rules: [:sp3c_oxygen_double_bond_water_loss] )
97
- fragments.flatten(1).map(&:csmiles).sort.should == ["CC=CC[NH3+]", "O"]
98
-
99
- mol = Rubabel["NC(O)CC"]
100
- fragments = mol.fragment( rules: [:sp3c_oxygen_double_bond_water_loss] )
101
- fragments.flatten(1).map(&:csmiles).sort.should == []
102
- end
103
- end
104
-
105
- describe 'sp3c_oxygen_double_bond_far_side_sp2' do
106
-
107
- it 'does not cleave esters without sp3 carbons available for double bond' do
108
- mol = Rubabel["NCCC(=O)OC"]
109
- pieces = mol.fragment( rules: [:sp3c_oxygen_double_bond_far_side_sp2] )
110
- pieces.should be_empty
111
- end
112
-
113
- it 'cleaves esters on far side of singly bonded oxygen' do
114
- mol = Rubabel["NCCC(=O)OCC"]
115
- pieces = mol.fragment( rules: [:sp3c_oxygen_double_bond_far_side_sp2] )
116
- pieces.size.should == 1 # one set
117
- the_pair = pieces.first
118
- csmiles = the_pair.map(&:csmiles)
119
- csmiles.should include("OC(=O)CC[NH3+]")
120
- csmiles.should include("C=C")
121
- end
122
-
123
- end
124
-
125
- describe ':alcohol_to_aldehyde' do
126
- it 'cleaves beside alcohols to generate an aldehyde' do
127
- mol = Rubabel["NCCC(O)CC"]
128
- mol.correct_for_ph!
129
- total_mass = mol.add_h!.mass
130
-
131
- pieces = mol.fragment(rules: [:alcohol_to_aldehyde])
132
- pieces.size.should == 2
133
- pieces.map(&:size).should == [2,2]
134
- pieces.flatten(1).map(&:csmiles).should == ["CC[NH3+]", "CCC=O", "C(C=O)C[NH3+]", "CC"]
135
- pieces.each do |pair|
136
- pair.map(&:mass).reduce(:+).should == total_mass
137
- end
138
- end
139
- end
140
-
141
- end
142
- end
62
+ ##mol.add_h!
63
+ #pieces = mol.fragment(rules: [:oxed_ether])
64
+ #m = pieces.first.first
65
+ ##[CH2+]CH2NH3+
66
+ ## C2H7N
67
+
68
+ #p m
69
+ #m.add_h!
70
+ #h = m.atoms[4].atoms.find {|a| a.el == :h }
71
+ #m.delete_atom(h)
72
+ #p m
73
+ #p m.atoms
74
+ ##mol = Rubabel["NCO"]
75
+ ##pieces = mol.fragment(rules: [:oxed_ether])
76
+ ##pieces.size.should == 0
77
+ #end
78
+
79
+ #describe 'ester to ions' do
80
+ #end
81
+
82
+ #describe 'carboxyl group' do
83
+ #end
84
+
85
+ #describe 'phosphodiester' do
86
+ #end
87
+ #end
88
+
89
+ ## this is really a subset of oxygen bond stealing: if the negatively
90
+ ## charged oxygen can rip off a nearby proton, it will.
91
+ #describe 'oxygen alpha/beta/gamma hydrogen stealing' do
92
+ #describe 'primary alcohol giving water loss' do
93
+ #end
94
+
95
+ #describe 'peroxide carbonyl formation' do
96
+ #end
97
+
98
+ #describe 'ether to alcohol' do
99
+ #end
100
+
101
+ #describe 'ester to alcohol' do
102
+ #end
103
+
104
+ #describe 'phosphodiester' do
105
+ #end
106
+ #end
107
+
108
+ #end
109
+ #end
110
+
111
+
112
+
113
+
114
+ ##describe ':sp3c_nitrogen_double_bond' do
115
+
116
+ ##it 'cleaves like an ether a secondary NH group if possible' do
117
+ ##mol = Rubabel["CCNC"]
118
+ ##frag_sets = mol.fragment(rules: [:sp3c_nitrogen_double_bond])
119
+ ##frag_sets.size.should == 1
120
+ ##csmiles = frag_sets.first.map(&:csmiles)
121
+ ##csmiles.should include("C=C")
122
+ ##csmiles.should include("C[NH3+]")
123
+ ##end
124
+
125
+ ##it 'will not cleave if not possible' do
126
+ ##mol = Rubabel["CNC"]
127
+ ##frag_sets = mol.fragment(rules: [:sp3c_nitrogen_double_bond])
128
+ ##frag_sets.should be_empty
129
+ ##end
130
+
131
+ ##end
132
+
133
+ ##describe ':co2_loss' do
134
+ ##it 'loss of CO2 from carboxy group with charge transfer' do
135
+ ##mol = Rubabel["NCC(=O)O"]
136
+ ##frag_sets = mol.fragment( rules: [:co2_loss] )
137
+ ##frag_sets.size.should == 1
138
+ ##csmiles = frag_sets.first.map(&:csmiles)
139
+
140
+ ##csmiles.should include("[CH2-][NH3+]")
141
+ ##csmiles.should include("O=C=O")
142
+ ##end
143
+
144
+ ##it "doesn't remove CO2 if adjacent is not c3" do
145
+ ##mol = Rubabel["C=CC(=O)O"]
146
+ ##fragments = mol.fragment( rules: [:co2_loss] )
147
+ ##fragments.should be_empty
148
+ ##end
149
+
150
+ ##end
151
+
152
+ ##describe ':peroxy_to_carboxy' do
153
+ ##it 'works' do
154
+ ##mol = Rubabel["NCCC(OO)CC"]
155
+ ##frag_sets = mol.fragment( rules: [:peroxy_to_carboxy] )
156
+ ##frag_sets.size.should == 2
157
+ ##frag_sets.flatten(1).map(&:csmiles).sort.should == ["CC", "CCC(=O)O", "CC[NH3+]", "OC(=O)CC[NH3+]"]
158
+ ##end
159
+ ##end
160
+
161
+ ##describe ':sp3c_oxygen_asymmetric_far_sp3', :pending do
162
+ ##it 'splits like sp3c_oxygen_double_bond except oxygen takes the electrons' do
163
+ ##$VERBOSE = 3
164
+ ##mol = Rubabel["NCCCOCC"]
165
+ ##frag_sets = mol.fragment( rules: [:sp3c_oxygen_asymmetric_far_sp3] )
166
+ ##$VERBOSE = nil
167
+ ##frag_sets.size.should == 2
168
+ ###mol = Rubabel["NCCOCC"]
169
+ ###p mol.fragment( rules: [:sp3c_oxygen_asymmetric_far_sp3] )
170
+ ###mol = Rubabel["NCOC"]
171
+ ###p mol.fragment( rules: [:sp3c_oxygen_asymmetric_far_sp3] )
172
+ ##end
173
+ ##end
174
+
175
+ ##describe ':sp3c_oxygen_double_bond_water_loss' do
176
+
177
+ ##it 'does h2o loss of alcohol' do
178
+ ##mol = Rubabel["NCCC(O)CC"]
179
+ ##fragments = mol.fragment( rules: [:sp3c_oxygen_double_bond_water_loss] )
180
+ ##fragments.flatten(1).map(&:csmiles).sort.should == ["CC=CCC[NH3+]", "CCC=CC[NH3+]", "O", "O"]
181
+ ##end
182
+
183
+ ##it 'h2o loss does not allow bad chemistry' do
184
+ ### lone pair and double bond resonance ?
185
+ ##mol = Rubabel["NCC(O)CC"]
186
+ ##fragments = mol.fragment( rules: [:sp3c_oxygen_double_bond_water_loss] )
187
+ ##fragments.flatten(1).map(&:csmiles).sort.should == ["CC=CC[NH3+]", "O"]
188
+
189
+ ##mol = Rubabel["NC(O)CC"]
190
+ ##fragments = mol.fragment( rules: [:sp3c_oxygen_double_bond_water_loss] )
191
+ ##fragments.flatten(1).map(&:csmiles).sort.should == []
192
+ ##end
193
+ ##end
194
+
195
+ ##describe 'sp3c_oxygen_double_bond_far_side_sp2' do
196
+
197
+ ##it 'does not cleave esters without sp3 carbons available for double bond' do
198
+ ##mol = Rubabel["NCCC(=O)OC"]
199
+ ##pieces = mol.fragment( rules: [:sp3c_oxygen_double_bond_far_side_sp2] )
200
+ ##pieces.should be_empty
201
+ ##end
202
+
203
+ ##it 'cleaves esters on far side of singly bonded oxygen' do
204
+ ##mol = Rubabel["NCCC(=O)OCC"]
205
+ ##pieces = mol.fragment( rules: [:sp3c_oxygen_double_bond_far_side_sp2] )
206
+ ##pieces.size.should == 1 # one set
207
+ ##the_pair = pieces.first
208
+ ##csmiles = the_pair.map(&:csmiles)
209
+ ##csmiles.should include("OC(=O)CC[NH3+]")
210
+ ##csmiles.should include("C=C")
211
+ ##end
212
+
213
+ ##end
214
+
215
+ ##describe ':alcohol_to_aldehyde' do
216
+ ##it 'cleaves beside alcohols to generate an aldehyde' do
217
+ ##mol = Rubabel["NCCC(O)CC"]
218
+ ##mol.correct_for_ph!
219
+ ##total_mass = mol.add_h!.mass
220
+
221
+ ##pieces = mol.fragment(rules: [:alcohol_to_aldehyde])
222
+ ##pieces.size.should == 2
223
+ ##pieces.map(&:size).should == [2,2]
224
+ ##pieces.flatten(1).map(&:csmiles).should == ["CC[NH3+]", "CCC=O", "C(C=O)C[NH3+]", "CC"]
225
+ ##pieces.each do |pair|
226
+ ##pair.map(&:mass).reduce(:+).should == total_mass
227
+ ##end
228
+ ##end
229
+ ##end
@@ -10,6 +10,13 @@ describe Rubabel::Molecule do
10
10
  end
11
11
  end
12
12
 
13
+ #xit 'can add a hydrogen to the formula' do
14
+ #mol = Rubabel["CCC"]
15
+ #p mol.formula
16
+ #mol.add_hydrogen_to_formula!
17
+ #p mol.formula
18
+ #end
19
+
13
20
  describe 'png output' do
14
21
  it 'creates a png image (corresponds to the svg)' do
15
22
  mol = Rubabel["NCC(=O)O"]
@@ -39,6 +46,24 @@ describe Rubabel::Molecule do
39
46
  end
40
47
  end
41
48
 
49
+ specify 'eql? and equal? mean the objects modify the same underlying openbabel molecule data' do
50
+ mol = Rubabel["C"]
51
+ eq_mol = mol.atoms.first.mol
52
+ mol.equal?(eq_mol).should be_true
53
+ another = Rubabel["C"]
54
+ mol.equal?(another).should be_false
55
+ end
56
+
57
+ specify '== means the canonical smiles strings (:csmiles) are equal' do
58
+ mol1 = Rubabel["CCO"]
59
+ mol2 = Rubabel["OCC"]
60
+ (mol1 == mol2).should be_true
61
+ mol2.atoms[0].charge += 1
62
+ (mol1 == mol2).should be_false
63
+ mol3 = Rubabel["CCCO"]
64
+ (mol1 == mol3).should be_false
65
+ end
66
+
42
67
  specify '#add_atom! adds an atom given an atomic number and returns it' do
43
68
  mol = Rubabel["CCO"]
44
69
  before_size = mol.atoms.size
@@ -48,29 +73,30 @@ describe Rubabel::Molecule do
48
73
  mol.csmiles.should == "CCO.C"
49
74
  end
50
75
 
76
+ specify '#dup duplicates the molecule' do
77
+ mol = Rubabel["CCO"]
78
+ dup_mol = mol.dup
79
+ mol.atoms[0].ob.set_atomic_num(9)
80
+ mol.csmiles.should == "OCF"
81
+ dup_mol.csmiles.should == "CCO"
82
+ end
83
+
51
84
  specify '#add_atom! with a Rubabel::Atom'
52
85
 
53
- specify '#add_bond! adds a bond (and updates atoms)' do
54
- mol = Rubabel["CCO"]
55
- atom = mol.add_atom!(0)
56
- mol.add_bond!(mol.atoms[1], atom)
57
- mol.csmiles.should == '*C(O)C'
86
+ describe '#add_bond! adds a bond (and updates atoms)' do
87
+ specify 'given two atoms' do
88
+ mol = Rubabel["CCO"]
89
+ atom = mol.add_atom!(0)
90
+ mol.add_bond!(mol.atoms[1], atom)
91
+ mol.csmiles.should == '*C(O)C'
92
+ end
58
93
  end
59
94
 
60
- # specify '#add_bond adds a bond (and updates atoms)' do
61
- # c = Rubabel::Atom[:c]
62
- # o = Rubabel::Atom[:o]
63
- # mol = Rubabel::Molecule.from_atoms_and_bonds([c,o])
64
- # mol.add_bond(c,o)
65
- # puts "BONDS:"
66
- # p mol.bonds
67
- # puts "ATOMS:"
68
- # p mol.atoms
69
- # puts "C ATOMS:"
70
- # p c.atoms
71
- # puts "O ATOMS:"
72
- # p o.atoms
73
- # end
95
+ specify '#atom(id) retrieves atom by id num' do
96
+ mol = Rubabel["CCO"]
97
+ o = mol.find {|a| a.el == :o }
98
+ mol.atom(o.id).id.should == o.id
99
+ end
74
100
 
75
101
  specify '#swap! can swap atoms around' do
76
102
  mol = Rubabel["NCC(=O)O"]
@@ -117,8 +143,20 @@ describe Rubabel::Molecule do
117
143
  ar.first.should be_a(OpenBabel::OBRing)
118
144
  end
119
145
 
146
+ describe 'making carbo-cations: spin_multiplicity and charges' do
147
+ # http://openbabel.org/docs/2.3.1/Features/Radicals.html
148
+ subject { mol = Rubabel["CC"] }
149
+ it 'can be turned into a carbocation' do
150
+ mol = subject
151
+ c = mol.atoms[0]
152
+ c.ob.set_spin_multiplicity 2
153
+ c.charge += 1
154
+ mol.csmiles.should == "C[CH2+]"
155
+ end
156
+ end
157
+
120
158
  describe 'masses' do
121
- subject { Rubabel::Molecule.from_string("C(=O)COC(=O)C[NH3+]") }
159
+ subject { Rubabel["C(=O)COC(=O)C[NH3+]"] }
122
160
  it '#mol_wt (or #avg_mass)' do
123
161
  subject.mol_wt.should be_within(0.000001).of(118.11121999999999)
124
162
  end
@@ -138,7 +176,7 @@ describe Rubabel::Molecule do
138
176
 
139
177
  describe 'pH' do
140
178
 
141
- subject { Rubabel::Molecule.from_string("NCC(=O)OCC(=O)O") }
179
+ subject { Rubabel["NCC(=O)OCC(=O)O"] }
142
180
 
143
181
  it '#correct_for_ph! neutral' do
144
182
  subject.correct_for_ph!.to_s.should == '[O-]C(=O)COC(=O)C[NH3+]'
@@ -220,6 +258,7 @@ describe Rubabel::Molecule do
220
258
  describe 'breaking a molecule' do
221
259
  before(:each) do
222
260
  @mol = Rubabel::Molecule.from_string("NC(=O)CO")
261
+ @n = @mol.find {|a| a.el == :n }
223
262
  end
224
263
 
225
264
  it 'num_atoms, atoms and each_atom are sensitive to #add_h!' do
@@ -256,6 +295,20 @@ describe Rubabel::Molecule do
256
295
  csmiles.sort.should == %w(N CC=O O).sort
257
296
  end
258
297
 
298
+ it 'can split fragments (akin to separate)' do
299
+ @mol.delete_bond(@n, @n.atoms.first)
300
+ pieces = @mol.split
301
+ pieces.map(&:csmiles).sort.should == ["N", "OCC=O"]
302
+ end
303
+
304
+ it 'can iterate through fragments' do
305
+ expected = %w(N OCC=O)
306
+ @mol.delete_bond(@n, @n.atoms.first)
307
+ @mol.each_fragment do |frag|
308
+ frag.csmiles.should == expected.shift
309
+ end
310
+ end
311
+
259
312
  end
260
313
 
261
314
  describe 'matching patterns (SMARTS)' do
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.1.6
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-06 00:00:00.000000000 Z
12
+ date: 2012-10-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: openbabel