msplinter 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 549e4f5c83a9065e97cc542ee98a6c69c54d0443
4
+ data.tar.gz: a93e6a4567344e57b8a6d3bdfc86bb766200118d
5
+ SHA512:
6
+ metadata.gz: 4b705b21e3db91c1f5b9862b4b02a01d09140b1b827244c98cf86e89bd1ce6558a169c6a8b3f702b553aa2f7d4094ffa81952cef83c138cb519183bc4e431dc9
7
+ data.tar.gz: 38695117916f6328b555d78338d114a8994ae859b0bbb604fba059ed08eb9498f8eadddc530a220ea6e6a56d3cca905f60ff9389737c518dc1fe09f16e2b1754
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ coverage
2
+ .coveralls.yml
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in msplinter.gemspec
4
+ gemspec
5
+
6
+ gem 'coveralls', require: false
data/LICENSE.txt ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2013 Brigham Young University
2
+ Authored by: John T. Prince
3
+
4
+ MIT License
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # msplinter
2
+
3
+ [![Gem Version][GV img]][Gem Version]
4
+ [![Dependency Status][DS img]][Dependency Status]
5
+ [![Code Climate][CC img]][Code Climate]
6
+ [![Coverage Status][CS img]][Coverage Status]
7
+
8
+ Predicts how molecules will fragment in a mass spectrometer. Currently
9
+ focused on lipid fragmentation under CID, HCD or PQD.
10
+
11
+ ## Installation
12
+
13
+ msplinter is built on rubabel, which is built on openbabel. To get the
14
+ necessary openbabel dependencies on ubuntu/debian:
15
+
16
+ sudo apt-get install openbabel libopenbabel-dev cmake make curl
17
+
18
+ Then
19
+
20
+ gem install msplinter
21
+
22
+ See [the "Installing" section](https://github.com/princelab/rubabel) for
23
+ complete instructions for installing rubabel and openbabel.
24
+
25
+ ## Commandline Usage
26
+
27
+ Type 'msplinter' with no arguments to see a help message.
28
+
29
+ ## Copyright
30
+
31
+ MIT license. See LICENSE.txt for further details.
32
+
33
+ [Gem Version]: https://rubygems.org/gems/msplinter
34
+ [Dependency Status]: https://gemnasium.com/princelab/msplinter
35
+ [Code Climate]: https://codeclimate.com/github/princelab/msplinter
36
+ [Coverage Status]: https://coveralls.io/r/princelab/msplinter
37
+
38
+ [GV img]: https://badge.fury.io/rb/msplinter.png
39
+ [DS img]: https://gemnasium.com/princelab/msplinter.png
40
+ [CC img]: https://codeclimate.com/github/princelab/msplinter.png
41
+ [CS img]: https://coveralls.io/repos/princelab/msplinter/badge.png?branch=master
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core'
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec) do |spec|
6
+ spec.pattern = FileList['spec/**/*_spec.rb']
7
+ end
8
+
9
+ task :default => :spec
10
+
11
+ require 'rdoc/task'
12
+ Rake::RDocTask.new do |rdoc|
13
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
14
+
15
+ rdoc.rdoc_dir = 'rdoc'
16
+ rdoc.title = "rubabel #{version}"
17
+ rdoc.rdoc_files.include('README*')
18
+ rdoc.rdoc_files.include('lib/**/*.rb')
19
+ end
data/bin/msplinter ADDED
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'trollop'
4
+ require 'rubabel'
5
+ require 'rubabel/molecule/fragmentable'
6
+
7
+ default_ph = 2.5
8
+
9
+ Fragment = Struct.new(:frag, :id, :title, :mz, :mass, :charge, :smiles, :pairing)
10
+
11
+ progname = File.basename(__FILE__)
12
+
13
+ parser = Trollop::Parser.new do
14
+ banner "usage: #{progname} [OPTIONS|RULES] <SMARTS> ..."
15
+ text "\noptions:"
16
+ opt :ph, "the pH to use (experimental option)", :default => default_ph
17
+ opt :images, "print out svg images of fragments"
18
+ opt :format, "format of the molecules", :default => 'smiles'
19
+ #opt :uniq, "no repeated fragments", :default => false
20
+ text "\nrules:"
21
+ Rubabel::Molecule::Fragmentable::RULES.each do |rule|
22
+ opt rule, rule.to_s.gsub("_",' ')
23
+ end
24
+ text "\nexample:"
25
+ text " #{progname} -xeh 'CCC(=O)OCCC' 'CCC(=O)OCCC(=O)O'"
26
+ end
27
+
28
+ options = parser.parse(ARGV)
29
+ opts = {rules: []}
30
+ opts[:uniq] = options.delete(:uniq)
31
+ ph = options.delete(:ph)
32
+ opts[:rules] = Rubabel::Molecule::Fragmentable::RULES.map do |rule|
33
+ rule if options["#{rule}_given".to_sym]
34
+ end.compact
35
+
36
+ if ARGV.size == 0
37
+ parser.educate && exit
38
+ end
39
+
40
+ ARGV.each do |smiles|
41
+ mol = Rubabel[smiles, options[:format].to_sym]
42
+ puts "\nmolecule: #{mol.csmiles}"
43
+ mol.correct_for_ph!(ph)
44
+ puts "at ph #{ph}: #{mol.csmiles}"
45
+ fragment_sets = mol.fragment(opts)
46
+ puts %w(mz mass charge title smiles pairing).join("\t")
47
+ frags = []
48
+ fragment_sets.each_with_index do |frag_set,i|
49
+ frag_set.each_with_index do |frag,j|
50
+ unless frag.charge == 0
51
+ mz = (frag.mass / frag.charge).round(5)
52
+ end
53
+
54
+ frag.title = "#{i}-#{j}pair_" + (mz ? "#{mz}_mz" : "#{frag.mass.round(3)}_Mass")
55
+ frag_obj = Fragment.new(frag, frag.title, frag.title, mz, frag.exact_mass, frag.charge, frag.csmiles, i)
56
+ frags << frag_obj
57
+ end
58
+ end
59
+ frags = frags.sort_by {|frag| [-frag.charge, frag.mz] }
60
+ if options[:images]
61
+ frags.each do |frag|
62
+ fn = "#{frag.title}.svg"
63
+ frag.frag.write(fn)
64
+ end
65
+ end
66
+ frags.each do |frag|
67
+ puts [:mz, :mass, :charge, :title, :smiles, :pairing].map {|cat| frag.send(cat) }.join("\t")
68
+ end
69
+ end
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,3 @@
1
+ module Msplinter
2
+ VERSION = "0.0.1"
3
+ end
data/lib/msplinter.rb ADDED
@@ -0,0 +1,4 @@
1
+ require "msplinter/version"
2
+
3
+ module Msplinter
4
+ end
@@ -0,0 +1,160 @@
1
+ require 'set'
2
+
3
+ module Rubabel
4
+ class Molecule
5
+ module Fragmentable
6
+
7
+ #RULES = Set[:cod, :codoo, :oxe, :oxepd, :oxh]
8
+ RULES = Set[:cod, :codoo, :oxe, :oxepd, :oxh, :oxhpd]
9
+
10
+ DEFAULT_OPTIONS = {
11
+ rules: RULES,
12
+ errors: :remove,
13
+ # return only the set of unique fragments
14
+ uniq: false,
15
+ }
16
+
17
+ # molecules and fragments should all have hydrogens added (add_h!)
18
+ # before calling this method
19
+ #
20
+ # For instance, water loss with double bond formation is not allowable
21
+ # for NCC(O)CC => CCC=C[NH2+], presumably because of the lone pair and
22
+ # double bond resonance.
23
+ def allowable_fragmentation?(frags)
24
+ self.num_atoms(true) == frags.reduce(0) {|cnt,fr| cnt + fr.num_atoms(true) }
25
+ end
26
+
27
+ # splits the molecule between the carbon and carbon_nbr, adds a double
28
+ # bond between the carbon and oxygen, and moves whatever was on the
29
+ # oxygen (e.g., an OH or a charge) to the carbon_nbr. Returns two new
30
+ # molecules.
31
+ def carbonyl_oxygen_dump(carbon, oxygen, carbon_nbr)
32
+ appendage = oxygen.atoms.find {|a| a.el != :C }
33
+ if oxygen.charge != 0
34
+ ocharge = oxygen.charge
35
+ end
36
+ nmol = self.dup
37
+ new_oxygen = nmol.atom(oxygen.id)
38
+ new_carbon = nmol.atom(carbon.id)
39
+ new_carbon_nbr = nmol.atom(carbon_nbr.id)
40
+ new_appendage = nmol.atom(appendage.id) if appendage
41
+ nmol.delete_bond(new_carbon.get_bond(new_carbon_nbr))
42
+ if new_appendage
43
+ nmol.delete_bond(new_oxygen.get_bond(new_appendage))
44
+ nmol.add_bond!(new_carbon_nbr, new_appendage)
45
+ end
46
+ if ocharge
47
+ new_carbon_nbr.charge += ocharge
48
+ new_oxygen.charge -= ocharge
49
+ end
50
+ new_carbon.get_bond(new_oxygen).bond_order = 2
51
+ nmol.split
52
+ end
53
+
54
+ # breaks the bond and gives the electrons to the oxygen
55
+ def carbon_oxygen_esteal(carbon, oxygen)
56
+ nmol = self.dup
57
+ ncarbon = nmol.atom(carbon.id)
58
+ noxygen = nmol.atom(oxygen.id)
59
+
60
+ is_carboxyl = noxygen.carboxyl_oxygen?
61
+
62
+ nmol.delete_bond(ncarbon, noxygen)
63
+ ncarbon.remove_a_hydride!
64
+ noxygen.remove_a_proton!
65
+ nmol.split
66
+ end
67
+
68
+ # returns the duplicated molecule and the equivalent atoms
69
+ def dup_molecule(atoms=[])
70
+ nmol = self.dup
71
+ [nmol, atoms.map {|old_atom| nmol.atom(old_atom.id) }]
72
+ end
73
+
74
+ # returns molecules created from splitting between the electrophile and
75
+ # the center and where the bond order is increased between the center
76
+ # and center_nbr
77
+ def break_with_double_bond(electrophile, center, center_nbr)
78
+ (nmol, (nele, ncarb, ncarb_nbr)) = self.dup_molecule([electrophile, center, center_nbr])
79
+ nmol.delete_bond(nele, ncarb)
80
+ ncarb_nbr.get_bond(ncarb) + 1
81
+ nmol.split
82
+ end
83
+
84
+ # an empty array is returned if there are no fragments generated.
85
+ # Hydrogens are added at a pH of 7.4, unless they have already been
86
+ # added.
87
+ #
88
+ # :rules => queryable by :include? set of rules
89
+ # :uniq => false
90
+ # :errors => :remove | :fix | :ignore (default is :remove)
91
+ def fragment(opts={})
92
+ only_uniqs = true
93
+ opts = DEFAULT_OPTIONS.merge(opts)
94
+ opts[:rules].each do |rule|
95
+ raise ArgumentError, "bad rule: #{rule}" unless RULES.include?(rule)
96
+ end
97
+
98
+ had_hydrogens = self.h_added?
99
+ self.correct_for_ph!(7.4) unless had_hydrogens
100
+ self.remove_h!
101
+
102
+ fragment_sets = []
103
+
104
+ if opts[:rules].any? {|r| [:cod, :codoo].include?(r) }
105
+ self.each_match("C[O;h1,O]", only_uniqs) do |carbon, oxygen|
106
+ carbon.atoms.select {|a| a.el == :C }.each do |carbon_nbr|
107
+ fragment_sets << carbonyl_oxygen_dump(carbon, oxygen, carbon_nbr)
108
+ end
109
+ end
110
+ end
111
+ if opts[:rules].any? {|r| [:oxe].include?(r) }
112
+ self.each_match("C-O", only_uniqs) do |carbon, oxygen|
113
+ fragment_sets << carbon_oxygen_esteal(carbon, oxygen)
114
+ end
115
+ end
116
+ # right now implemented so that a beta hydrogen has to be availabe for
117
+ # extraction
118
+ if opts[:rules].any? {|r| [:oxh].include?(r) }
119
+ self.each_match("C[C,O]-O", only_uniqs) do |beta_c, center, oxygen|
120
+ next unless beta_c.hydrogen_count > 0
121
+ fragment_sets << break_with_double_bond(oxygen, center, beta_c)
122
+ end
123
+ end
124
+ if opts[:rules].any? {|r| [:oxhpd].include?(r) }
125
+ self.each_match("C-O-P-O", only_uniqs) do |carbon, alc_oxy, phosphate, beta_carb_oxy|
126
+ next unless beta_carb_oxy.hydrogen_count > 0
127
+ frag_set = break_with_double_bond(alc_oxy, phosphate, beta_carb_oxy)
128
+ frag_set.map! &:convert_dative_bonds!
129
+ fragment_sets << frag_set
130
+ end
131
+ end
132
+ if opts[:rules].any? {|r| [:oxepd].include?(r) }
133
+ self.each_match("P-O-C", only_uniqs) do |phosphate, oxygen, carbon|
134
+ frag_set = carbon_oxygen_esteal(phosphate, oxygen)
135
+ frag_set.map! &:convert_dative_bonds!
136
+ fragment_sets << frag_set
137
+ end
138
+ end
139
+
140
+ case opts[:errors]
141
+ when :remove
142
+ fragment_sets.select! {|set| allowable_fragmentation?(set) }
143
+ when :fix
144
+ raise NotImplementedError
145
+ when :ignore # do nothing
146
+ end
147
+
148
+ self.remove_h!
149
+ if opts[:uniq]
150
+ # TODO: impelent properly
151
+ raise NotImplementedError
152
+ #fragment_sets = fragment_sets.uniq_by(&:csmiles)
153
+ end
154
+
155
+ fragment_sets
156
+ end
157
+ end
158
+ include Fragmentable
159
+ end
160
+ end # Rubabel
data/msplinter.gemspec ADDED
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'msplinter/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "msplinter"
8
+ spec.version = Msplinter::VERSION
9
+ spec.authors = ["John T. Prince"]
10
+ spec.email = ["jtprince@gmail.com"]
11
+ spec.description = %q{Predicts how molecules will fragment in a mass spectrometer. Currently focused on lipid fragmentation under CID, HCD or PQD.}
12
+ spec.summary = %q{Predicts how molecules will fragment in a mass spectrometer}
13
+ spec.homepage = "https://github.com/princelab/msplinter"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ [
22
+ ["rubabel", "~> 0.4.1"],
23
+ ].each do |args|
24
+ spec.add_dependency(*args)
25
+ end
26
+ [
27
+ ["bundler", "~> 1.3"],
28
+ ["rake"],
29
+ ["rspec", "~> 2.13.0"],
30
+ ["rdoc", "~> 3.12"],
31
+ ["simplecov"],
32
+ ].each do |args|
33
+ spec.add_development_dependency(*args)
34
+ end
35
+
36
+ end
@@ -0,0 +1,200 @@
1
+ require 'spec_helper'
2
+
3
+ require 'rubabel'
4
+ require 'rubabel/molecule/fragmentable'
5
+
6
+ $VERBOSE = nil
7
+
8
+ describe Rubabel::Molecule::Fragmentable do
9
+
10
+ # :peroxy_to_carboxy
11
+ # :oxygen_asymmetric_sp3, :nitrogen_asymmetric_sp3,
12
+ # :internal_phosphoester
13
+
14
+ describe 'fragmentation rules' do
15
+ # coenzyme: CC1=CC(=O)C=CC1=O
16
+ # 2-methylcyclohexa-2,5-diene-1,4-dione
17
+
18
+ #let(:test_mol) { "COP(=O)(O)OCNCOCC(OO)C(=O)O" }
19
+
20
+ it 'raises an error for a bad rule' do
21
+ mol = Rubabel["CCNC"]
22
+ expect { mol.fragment(rules: [:wackiness]) }.to raise_error
23
+ end
24
+
25
+ describe 'cod: carbonyl appendage dump' do
26
+ # a primary oxygen or peroxide => C=O appendage dump
27
+
28
+ specify 'cod: primary alcohol' do
29
+ mol = Rubabel["NCC(O)CC"]
30
+ frags = mol.fragment(rules: [:cod])
31
+ frags.flatten(1).map(&:csmiles).should == ["C[NH3+]", "CCC=O", "C([NH3+])C=O", "CC"]
32
+ end
33
+
34
+ specify 'peroxide' do
35
+ mol = Rubabel["NCC(OO)CC"]
36
+ frags = mol.fragment(rules: [:codoo])
37
+ frags.flatten(1).map(&:csmiles).should == ["OC[NH3+]", "CCC=O", "C([NH3+])C=O", "CCO"]
38
+ end
39
+
40
+ specify 'cod: carboxylate' do
41
+ mol = Rubabel["CCC(=O)O"]
42
+ pieces = mol.fragment(rules: [:cod])
43
+ pieces.flatten(1).map(&:csmiles).should == ["[CH2-]C", "O=C=O"]
44
+ end
45
+
46
+ specify 'cod: carboxylic acid' do
47
+ mol = Rubabel["CCC(=O)O"]
48
+ mol.add_h!(1.5)
49
+ pieces = mol.fragment(rules: [:cod])
50
+ pieces.flatten(1).map(&:csmiles).should == ["CC", "O=C=O"]
51
+ end
52
+ end
53
+
54
+ describe 'oxe: oxygen electron stealing' do
55
+ # oxygen just steals the electron pair it is attached to. This
56
+ # typically results in a negatively charged oxygen and a positively
57
+ # charged carbo-cation.
58
+ specify 'ether to ions' do
59
+ mol = Rubabel["CCOCCN"]
60
+ frag_set = mol.fragment(rules: [:oxe])
61
+ frags = frag_set.first
62
+ frags.first.csmiles.should == "C[CH2+]"
63
+ frags.last.csmiles.should == '[O-]CC[NH3+]'
64
+ frags.first.formula.should == 'C2H5+'
65
+ frags.last.formula.should == 'C2H7NO'
66
+ frags.first.exact_mass.should be_within(1e-6).of(29.03912516)
67
+ frags.last.exact_mass.should be_within(1e-6).of(61.052763849)
68
+ end
69
+
70
+ specify 'ester to ions' do
71
+ mol = Rubabel["CCOC(=O)CCN"]
72
+ frag_set = mol.fragment(rules: [:oxe])
73
+ ff = frag_set.first
74
+ ff.first.csmiles.should == 'C[CH2+]'
75
+ ff.last.csmiles.should == '[O-]C(=O)CC[NH3+]'
76
+ ff.first.formula.should == "C2H5+"
77
+
78
+ ff.last.formula.should == "C3H7NO2"
79
+ ff.first.exact_mass.should be_within(1e-6).of(29.03912516035)
80
+ ff.last.exact_mass.should be_within(1e-6).of(89.04767846841)
81
+ end
82
+
83
+ specify 'carboxyl group' do
84
+ mol = Rubabel["CCC(=O)O"]
85
+ frag_set = mol.fragment(rules: [:oxe])
86
+ ff = frag_set.first
87
+ ff.first.csmiles.should == 'CC[C+]=O'
88
+ # this is a carboxylate oxygen that falls off
89
+ ff.last.csmiles.should == '[O-2]'
90
+ #ff.first.formula.should == "C3H5O"
91
+ ff.first.formula.should == "C3H5O+"
92
+ ff.first.exact_mass.should be_within(1e-6).of(57.034039779909996)
93
+ #ff.last.formula.should == "O"
94
+ ff.last.formula.should == "O--"
95
+ end
96
+
97
+ specify 'phosphodiester' do
98
+ mol = Rubabel["CC(COP(=O)([O-])OCCN"]
99
+ frag_set = mol.fragment(rules: [:oxepd])
100
+ ff = frag_set.first
101
+ ff.first.csmiles.should == '[O-]CCC'
102
+ #ff.last.csmiles.should == '[NH3+]CCO[P](=O)=O'
103
+ ff.last.csmiles.should == "[NH3+]CCOP(=O)=O"
104
+ #ff.first.formula.should == 'C3H7O'
105
+ ff.first.formula.should == 'C3H7O-'
106
+ ff.first.exact_mass.should be_within(1e-6).of(59.049689844)
107
+ #ff.last.formula.should == 'C2H7NO3P'
108
+ ff.last.formula.should == 'C2H7NO3P+'
109
+ ff.last.exact_mass.should be_within(1e-6).of(124.016354719)
110
+
111
+ mol = Rubabel["CCCOP(=O)(OCC[N+](C)(C)C)[O-]"]
112
+ frag_set = mol.fragment(rules: [:oxepd, :oxe])
113
+ # some of these don't like right on first inspection, but that is
114
+ # because we 'converted dative bonds' meaning + and - next to each
115
+ # other are allowed to cancel one another out!
116
+ frag_set.size.should == 4
117
+ mols = frag_set.flatten
118
+ mols.map(&:csmiles).should == ["CC[CH2+]", "[O-]P(=O)(OCC[N+](C)(C)C)[O-]", "CCCOP(=O)([O-])[O-]", "[CH2+]C[N+](C)(C)C", "[O-]CCC", "O=P(=O)OCC[N+](C)(C)C", "CCCOP(=O)=O", "[O-]CC[N+](C)(C)C"]
119
+ mols.map(&:formula).should == ["C3H7+", "C5H13NO4P-", "C3H7O4P--", "C5H13N++", "C3H7O-", "C5H13NO3P+", "C3H7O3P", "C5H13NO"]
120
+ mols.map(&:exact_mass).zip([43.05477522449, 182.05821952995, 138.00819533273, 87.10479942171, 59.04968984405, 166.06330491039, 122.01328071317, 103.09971404127]) do |act, exp|
121
+ act.should be_within(1e-6).of(exp)
122
+ end
123
+
124
+ end
125
+ end
126
+
127
+ # this is really a subset of oxygen bond stealing: if the negatively
128
+ # charged oxygen can rip off a nearby proton, it will.
129
+ describe 'oxh: oxygen alpha/beta/gamma hydrogen stealing' do
130
+ specify 'primary alcohol giving water loss' do
131
+ mol = Rubabel["CC(O)CCN"]
132
+ frags = mol.fragment(rules: [:oxh])
133
+ ff = frags.first
134
+ ff.first.csmiles.should == 'C=CCC[NH3+]'
135
+ ff.last.csmiles.should == 'O'
136
+ ll = frags.last
137
+ ll.first.csmiles.should == 'CC=CC[NH3+]'
138
+ ll.last.csmiles.should == 'O'
139
+ #ff.first.formula.should == 'C4H10N'
140
+ ff.first.formula.should == 'C4H10N+'
141
+ ff.first.exact_mass.should be_within(1e-6).of(72.0813243255)
142
+ end
143
+
144
+ specify 'peroxide carbonyl formation (or peroxide formation [that what we want??])' do
145
+ # do we really see peroxide formation? Tamil didn't include this in
146
+ # the rules but it follows from the broad way for creating these
147
+ # rules. Can prohibit peroxide formation in future if necessary...
148
+ mol = Rubabel["CC(OO)CCN"]
149
+ frags = mol.fragment(rules: [:oxh])
150
+ mols = frags.flatten
151
+ mols.map(&:csmiles).should == ["C=CCC[NH3+]", "OO", "CC(=O)CC[NH3+]", "O", "CC=CC[NH3+]", "OO"]
152
+ mols.map(&:formula).should == ["C4H10N+", "H2O2", "C4H10NO+", "H2O", "C4H10N+", "H2O2"]
153
+ #mols.map(&:formula).should == ["C4H10N", "H2O2", "C4H10NO", "H2O", "C4H10N", "H2O2"]
154
+ mols.map(&:exact_mass).zip([72.081324325, 34.005479304, 88.076238945, 18.010564684, 72.081324325, 34.005479304]) do |act, exp|
155
+ act.should be_within(1e-6).of(exp)
156
+ end
157
+ end
158
+
159
+ specify 'ether to alcohol, ignoring errors' do
160
+ # this is a good example of a 'disallowed structure' where the
161
+ # formula's do not match up to the original formulas
162
+ mol = Rubabel["CCOCCN"]
163
+ frags = mol.fragment(rules: [:oxh], errors: :ignore)
164
+ mols = frags.flatten
165
+ mols.map(&:csmiles).should == ["C=C", "OCC[NH3+]", "CCO", "C=C[NH2+]"]
166
+ end
167
+
168
+ specify 'ether to alcohol, removing errors' do
169
+ mol = Rubabel["CCOCCN"]
170
+ frags = mol.fragment(rules: [:oxh])
171
+ mols = frags.flatten
172
+ mols.map(&:csmiles).should == ["C=C", "OCC[NH3+]"]
173
+ end
174
+
175
+ specify 'ester to alcohol' do
176
+ mol = Rubabel["CC(=O)OCCCN"]
177
+ frags = mol.fragment(rules: [:oxh])
178
+ mols = frags.flatten
179
+ mols.map(&:csmiles).should == ["C=C=O", "OCCC[NH3+]", "CC(=O)O", "C=CC[NH3+]"]
180
+ #mols.map(&:formula).should == ["C2H2O", "C3H10NO", "C2H4O2", "C3H8N"]
181
+ mols.map(&:formula).should == ["C2H2O", "C3H10NO+", "C2H4O2", "C3H8N+"]
182
+ mols.map(&:exact_mass).zip([42.010564684, 76.076238945, 60.021129368000004, 58.065674261]) do |act,exp|
183
+ act.should be_within(1e-6).of(exp)
184
+ end
185
+ end
186
+
187
+ specify 'phosphodiester (right now needs very low pH and NOT SURE WORKING PROPERLY)' do
188
+ mol = Rubabel["CC(COP(=O)(O)OCCCN"]
189
+ mol.add_h!(1.0)
190
+ frags = mol.fragment(rules: [:oxhpd])
191
+ frags = frags.flatten
192
+ frags.map(&:csmiles).should == ["CCCO", "[NH3+]CCCOP(=O)=O", "CCCOP(=O)=O", "OCCC[NH3+]"]
193
+ end
194
+ end
195
+
196
+ end
197
+ end
198
+
199
+
200
+
@@ -0,0 +1,28 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
4
+ require 'rspec'
5
+ require 'stringio'
6
+
7
+ require 'rspec/core/formatters/progress_formatter'
8
+ # doesn't say so much about pending guys
9
+ class QuietPendingFormatter < RSpec::Core::Formatters::ProgressFormatter
10
+ def example_pending(example)
11
+ output.print pending_color('*')
12
+ end
13
+ end
14
+
15
+ require 'rspec/core/formatters/documentation_formatter'
16
+ class QuietPendingDocFormatter < RSpec::Core::Formatters::DocumentationFormatter
17
+ def example_pending(example)
18
+ output.puts pending_color( "<pending>: #{example.execution_result[:pending_message]}" )
19
+ end
20
+ end
21
+
22
+ RSpec.configure do |config|
23
+ config.treat_symbols_as_metadata_keys_with_true_values = true
24
+ config.formatter = QuietPendingDocFormatter
25
+ config.color = true
26
+ end
27
+
28
+ TESTFILES = File.dirname(__FILE__) + "/testfiles"
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: msplinter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - John T. Prince
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-06-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rubabel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.4.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.4.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 2.13.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 2.13.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: rdoc
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '3.12'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '3.12'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Predicts how molecules will fragment in a mass spectrometer. Currently
98
+ focused on lipid fragmentation under CID, HCD or PQD.
99
+ email:
100
+ - jtprince@gmail.com
101
+ executables:
102
+ - msplinter
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - .gitignore
107
+ - Gemfile
108
+ - LICENSE.txt
109
+ - README.md
110
+ - Rakefile
111
+ - bin/msplinter
112
+ - lib/msplinter.rb
113
+ - lib/msplinter/commandline.rb
114
+ - lib/msplinter/version.rb
115
+ - lib/rubabel/molecule/fragmentable.rb
116
+ - msplinter.gemspec
117
+ - spec/rubabel/molecule/fragmentable_spec.rb
118
+ - spec/spec_helper.rb
119
+ homepage: https://github.com/princelab/msplinter
120
+ licenses:
121
+ - MIT
122
+ metadata: {}
123
+ post_install_message:
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ requirements: []
138
+ rubyforge_project:
139
+ rubygems_version: 2.0.2
140
+ signing_key:
141
+ specification_version: 4
142
+ summary: Predicts how molecules will fragment in a mass spectrometer
143
+ test_files:
144
+ - spec/rubabel/molecule/fragmentable_spec.rb
145
+ - spec/spec_helper.rb
146
+ has_rdoc: