File: reference.rb

package info (click to toggle)
ruby-bio 1.5.0-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 7,480 kB
  • ctags: 9,428
  • sloc: ruby: 74,117; xml: 3,383; makefile: 17; perl: 13; sh: 1
file content (625 lines) | stat: -rw-r--r-- 19,129 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
#
# = bio/reference.rb - Journal reference classes
#
# Copyright::   Copyright (C) 2001, 2006, 2008
#               Toshiaki Katayama <k@bioruby.org>,
#               Ryan Raaum <ryan@raaum.org>,
#               Jan Aerts <jandot@bioruby.org>
# License::     The Ruby License
#
# $Id:$
#

require 'enumerator'

module Bio

  # = DESCRIPTION
  #
  # A class for journal reference information.
  #
  # = USAGE
  # 
  #    hash = {'authors' => [ "Hoge, J.P.", "Fuga, F.B." ], 
  #            'title' => "Title of the study.",
  #            'journal' => "Theor. J. Hoge", 
  #            'volume' => 12, 
  #            'issue' => 3, 
  #            'pages' => "123-145",
  #            'year' => 2001, 
  #            'pubmed' => 12345678, 
  #            'medline' => 98765432, 
  #            'abstract' => "Hoge fuga. ...",
  #            'url' => "http://example.com", 
  #            'mesh' => [], 
  #            'affiliations' => []}
  #    ref = Bio::Reference.new(hash)
  #
  #    # Formats in the BiBTeX style.
  #    ref.format("bibtex")
  #    
  #    # Short-cut for Bio::Reference#format("bibtex")
  #    ref.bibtex
  #
  class Reference

    # Author names in an Array, [ "Hoge, J.P.", "Fuga, F.B." ].
    attr_reader :authors

    # String with title of the study
    attr_reader :title

    # String with journal name
    attr_reader :journal

    # volume number (typically Fixnum)
    attr_reader :volume
    
    # issue number (typically Fixnum)
    attr_reader :issue

    # page range (typically String, e.g. "123-145")
    attr_reader :pages

    # year of publication (typically Fixnum)
    attr_reader :year

    # pubmed identifier (typically Fixnum)
    attr_reader :pubmed

    # medline identifier (typically Fixnum)
    attr_reader :medline

    # DOI identifier (typically String, e.g. "10.1126/science.1110418")
    attr_reader :doi
    
    # Abstract text in String.
    attr_reader :abstract

    # An URL String.
    attr_reader :url

    # MeSH terms in an Array.
    attr_reader :mesh

    # Affiliations in an Array.
    attr_reader :affiliations
    
    # Sequence number in EMBL/GenBank records
    attr_reader :embl_gb_record_number
    
    # Position in a sequence that this reference refers to
    attr_reader :sequence_position

    # Comments for the reference (typically Array of String, or nil)
    attr_reader :comments

    # Create a new Bio::Reference object from a Hash of values. 
    # Data is extracted from the values for keys:
    #
    # * authors - expected value: Array of Strings
    # * title - expected value: String
    # * journal - expected value: String
    # * volume - expected value: Fixnum or String
    # * issue - expected value: Fixnum or String
    # * pages - expected value: String
    # * year - expected value: Fixnum or String
    # * pubmed - expected value: Fixnum or String
    # * medline - expected value: Fixnum or String
    # * abstract - expected value: String
    # * url - expected value: String
    # * mesh - expected value: Array of Strings
    # * affiliations - expected value: Array of Strings
    #
    #
    #    hash = {'authors' => [ "Hoge, J.P.", "Fuga, F.B." ], 
    #            'title' => "Title of the study.",
    #            'journal' => "Theor. J. Hoge", 
    #            'volume' => 12, 
    #            'issue' => 3, 
    #            'pages' => "123-145",
    #            'year' => 2001, 
    #            'pubmed' => 12345678, 
    #            'medline' => 98765432, 
    #            'abstract' => "Hoge fuga. ...",
    #            'url' => "http://example.com", 
    #            'mesh' => [], 
    #            'affiliations' => []}
    #    ref = Bio::Reference.new(hash)
    # ---
    # *Arguments*:
    # * (required) _hash_: Hash
    # *Returns*:: Bio::Reference object
    def initialize(hash)
      @authors  = hash['authors'] || [] # [ "Hoge, J.P.", "Fuga, F.B." ]
      @title    = hash['title']   || '' # "Title of the study."
      @journal  = hash['journal'] || '' # "Theor. J. Hoge"
      @volume   = hash['volume']  || '' # 12
      @issue    = hash['issue']   || '' # 3
      @pages    = hash['pages']   || '' # 123-145
      @year     = hash['year']    || '' # 2001
      @pubmed   = hash['pubmed']  || '' # 12345678
      @medline  = hash['medline'] || '' # 98765432
      @doi      = hash['doi']
      @abstract = hash['abstract'] || '' 
      @url      = hash['url']
      @mesh     = hash['mesh'] || []
      @embl_gb_record_number = hash['embl_gb_record_number'] || nil
      @sequence_position = hash['sequence_position'] || nil
      @comments  = hash['comments']
      @affiliations = hash['affiliations'] || []
    end

    # If _other_ is equal with the self, returns true.
    # Otherwise, returns false.
    # ---
    # *Arguments*:
    # * (required) _other_: any object
    # *Returns*:: true or false
    def ==(other)
      return true if super(other)
      return false unless other.instance_of?(self.class)
      flag = false
      [ :authors, :title, :journal, :volume, :issue, :pages,
        :year, :pubmed, :medline, :doi, :abstract,
        :url, :mesh, :embl_gb_record_number,
        :sequence_position, :comments, :affiliations ].each do |m|
        begin
          flag = (self.__send__(m) == other.__send__(m))
        rescue NoMethodError, ArgumentError, NameError
          flag = false
        end
        break unless flag
      end
      flag
    end

    # Formats the reference in a given style.
    #
    # Styles:
    # 0. nil - general
    # 1. endnote - Endnote
    # 2. bibitem - Bibitem (option available)
    # 3. bibtex - BiBTeX (option available)
    # 4. rd - rd (option available)
    # 5. nature - Nature (option available)
    # 6. science - Science
    # 7. genome_biol - Genome Biology
    # 8. genome_res - Genome Research
    # 9. nar - Nucleic Acids Research
    # 10. current - Current Biology
    # 11. trends - Trends in *
    # 12. cell - Cell Press
    #
    # See individual methods for details. Basic usage is:
    #
    #   # ref is Bio::Reference object
    #   # using simplest possible call (for general style)
    #   puts ref.format
    #   
    #   # output in Nature style
    #   puts ref.format("nature")      # alternatively, puts ref.nature
    #
    #   # output in Nature short style (see Bio::Reference#nature)
    #   puts ref.format("nature",true) # alternatively, puts ref.nature(true)
    # ---
    # *Arguments*:
    # * (optional) _style_: String with style identifier
    # * (optional) _options_: Options for styles accepting one
    # *Returns*:: String
    def format(style = nil, *options)
      case style
      when 'embl'
        return embl
      when 'endnote'
        return endnote
      when 'bibitem'
        return bibitem(*options)
      when 'bibtex'
        return bibtex(*options)
      when 'rd'
        return rd(*options)
      when /^nature$/i
        return nature(*options)
      when /^science$/i
        return science
      when /^genome\s*_*biol/i
        return genome_biol
      when /^genome\s*_*res/i
        return genome_res
      when /^nar$/i
        return nar
      when /^current/i
        return current
      when /^trends/i
        return trends
      when /^cell$/i
        return cell
      else
        return general
      end
    end

    # Returns reference formatted in the Endnote style.
    #
    #   # ref is a Bio::Reference object
    #   puts ref.endnote
    #
    #     %0 Journal Article
    #     %A Hoge, J.P.
    #     %A Fuga, F.B.
    #     %D 2001
    #     %T Title of the study.
    #     %J Theor. J. Hoge
    #     %V 12
    #     %N 3
    #     %P 123-145
    #     %M 12345678
    #     %U http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=12345678
    #     %X Hoge fuga. ...
    # ---
    # *Returns*:: String
    def endnote
      lines = []
      lines << "%0 Journal Article"
      @authors.each do |author|
        lines << "%A #{author}"
      end
      lines << "%D #{@year}" unless @year.to_s.empty?
      lines << "%T #{@title}" unless @title.empty?
      lines << "%J #{@journal}" unless @journal.empty?
      lines << "%V #{@volume}" unless @volume.to_s.empty?
      lines << "%N #{@issue}" unless @issue.to_s.empty?
      lines << "%P #{@pages}" unless @pages.empty?
      lines << "%M #{@pubmed}" unless @pubmed.to_s.empty?
      u = @url.to_s.empty? ? pubmed_url : @url
      lines << "%U #{u}" unless u.empty?
      lines << "%X #{@abstract}" unless @abstract.empty?
      @mesh.each do |term|
        lines << "%K #{term}"
      end
      lines << "%+ #{@affiliations.join(' ')}" unless @affiliations.empty?
      return lines.join("\n")
    end

    # Returns reference formatted in the EMBL style.
    #
    #   # ref is a Bio::Reference object
    #   puts ref.embl
    #
    #     RP   1-1859
    #     RX   PUBMED; 1907511.
    #     RA   Oxtoby E., Dunn M.A., Pancoro A., Hughes M.A.;
    #     RT   "Nucleotide and derived amino acid sequence of the cyanogenic
    #     RT   beta-glucosidase (linamarase) from white clover (Trifolium repens L.)";
    #     RL   Plant Mol. Biol. 17(2):209-219(1991).
    def embl
      r = self
      Bio::Sequence::Format::NucFormatter::Embl.new('').instance_eval {
        reference_format_embl(r)
      }
    end

    # Returns reference formatted in the bibitem style
    #
    #   # ref is a Bio::Reference object
    #   puts ref.bibitem
    #
    #     \bibitem{PMID:12345678}
    #     Hoge, J.P., Fuga, F.B.
    #     Title of the study.,
    #     {\em Theor. J. Hoge}, 12(3):123--145, 2001.
    # ---
    # *Arguments*:
    # * (optional) _item_: label string (default: <tt>"PMID:#{pubmed}"</tt>).
    # *Returns*:: String
    def bibitem(item = nil)
      item  = "PMID:#{@pubmed}" unless item
      pages = @pages.sub('-', '--')
      return <<-"END".enum_for(:each_line).collect {|line| line.strip}.join("\n")
        \\bibitem{#{item}}
        #{@authors.join(', ')}
        #{@title},
        {\\em #{@journal}}, #{@volume}(#{@issue}):#{pages}, #{@year}.
      END
    end

    # Returns reference formatted in the BiBTeX style.
    #
    #   # ref is a Bio::Reference object
    #   puts ref.bibtex
    #
    #     @article{PMID:12345678,
    #       author  = {Hoge, J.P. and Fuga, F.B.},
    #       title   = {Title of the study.},
    #       journal = {Theor. J. Hoge},
    #       year    = {2001},
    #       volume  = {12},
    #       number  = {3},
    #       pages   = {123--145},
    #     }
    #
    #   # using a different section (e.g. "book")
    #   # (but not really configured for anything other than articles)
    #   puts ref.bibtex("book")
    #
    #     @book{PMID:12345678,
    #       author  = {Hoge, J.P. and Fuga, F.B.},
    #       title   = {Title of the study.},
    #       journal = {Theor. J. Hoge},
    #       year    = {2001},
    #       volume  = {12},
    #       number  = {3},
    #       pages   = {123--145},
    #     }    
    # ---
    # *Arguments*:
    # * (optional) _section_: BiBTeX section as String
    # * (optional) _label_: Label string cited by LaTeX documents.
    #                       Default is <tt>"PMID:#{pubmed}"</tt>.
    # * (optional) _keywords_: Hash of additional keywords,
    #                          e.g. { 'abstract' => 'This is abstract.' }.
    #                          You can also override default keywords.
    #                          To disable default keywords, specify false as
    #                          value, e.g. { 'url' => false, 'year' => false }.
    # *Returns*:: String
    def bibtex(section = nil, label = nil, keywords = {})
      section = "article" unless section
      authors = authors_join(' and ', ' and ')
      thepages = pages.to_s.empty? ? nil : pages.sub(/\-/, '--')
      unless label then
        label = "PMID:#{pubmed}"
      end
      theurl = if !(url.to_s.empty?) then
                 url
               elsif pmurl = pubmed_url and !(pmurl.to_s.empty?) then
                 pmurl
               else
                 nil
               end
      hash = {
        'author'  => authors.empty?    ? nil : authors,
        'title'   => title.to_s.empty? ? nil : title,
        'number'  => issue.to_s.empty? ? nil : issue,
        'pages'   => thepages,
        'url'     => theurl
      }
      keys = %w( author title journal year volume number pages url )
      keys.each do |k|
        hash[k] = self.__send__(k.intern) unless hash.has_key?(k)
      end
      hash.merge!(keywords) { |k, v1, v2| v2.nil? ? v1 : v2 }
      bib = [ "@#{section}{#{label}," ]
      keys.concat((hash.keys - keys).sort)
      keys.each do |kw|
        ref = hash[kw]
        bib.push "  #{kw.ljust(12)} = {#{ref}}," if ref
      end
      bib.push "}\n"
      return bib.join("\n")
    end

    # Returns reference formatted in a general/generic style.
    #
    #   # ref is a Bio::Reference object
    #   puts ref.general
    #
    #     Hoge, J.P., Fuga, F.B. (2001). "Title of the study." Theor. J. Hoge 12:123-145.
    # ---
    # *Returns*:: String
    def general
      authors = @authors.join(', ')
      "#{authors} (#{@year}). \"#{@title}\" #{@journal} #{@volume}:#{@pages}."
    end

    # Return reference formatted in the RD style.
    #
    #   # ref is a Bio::Reference object
    #   puts ref.rd
    #
    #     == Title of the study.
    #     
    #     * Hoge, J.P. and Fuga, F.B.
    #     
    #     * Theor. J. Hoge 2001 12:123-145 [PMID:12345678]
    #     
    #     Hoge fuga. ...
    #
    # An optional string argument can be supplied, but does nothing.
    # ---
    # *Arguments*:
    # * (optional) str: String (default nil)
    # *Returns*:: String
    def rd(str = nil)
      @abstract ||= str
      lines = []
      lines << "== " + @title
      lines << "* " + authors_join(' and ')
      lines << "* #{@journal} #{@year} #{@volume}:#{@pages} [PMID:#{@pubmed}]"
      lines << @abstract
      return lines.join("\n\n")
    end

    # Formats in the Nature Publishing Group 
    # (http://www.nature.com) style.
    #
    #   # ref is a Bio::Reference object
    #   puts ref.nature
    #
    #     Hoge, J.P. & Fuga, F.B. Title of the study. Theor. J. Hoge 12, 123-145 (2001).
    #
    #   # optionally, output short version
    #   puts ref.nature(true)  # or puts ref.nature(short=true)
    #
    #     Hoge, J.P. & Fuga, F.B. Theor. J. Hoge 12, 123-145 (2001).
    # ---
    # *Arguments*:
    # * (optional) _short_: Boolean (default false)
    # *Returns*:: String
    def nature(short = false)
      if short
        if @authors.size > 4
          authors = "#{@authors[0]} et al."
        elsif @authors.size == 1
          authors = "#{@authors[0]}"
        else
          authors = authors_join(' & ')
        end
        "#{authors} #{@journal} #{@volume}, #{@pages} (#{@year})."
      else
        authors = authors_join(' & ')
        "#{authors} #{@title} #{@journal} #{@volume}, #{@pages} (#{@year})."
      end
    end

    # Returns reference formatted in the 
    # Science[http://www.sciencemag.org] style.
    #
    #   # ref is a Bio::Reference object
    #   puts ref.science
    #
    #     J.P. Hoge, F.B. Fuga, Theor. J. Hoge 12 123 (2001).
    # ---
    # *Returns*:: String
    def science
      if @authors.size > 4
        authors = rev_name(@authors[0]) + " et al."
      else
        authors = @authors.collect {|name| rev_name(name)}.join(', ')
      end
      page_from, = @pages.split('-')
      "#{authors}, #{@journal} #{@volume} #{page_from} (#{@year})."
    end

    # Returns reference formatted in the Genome Biology 
    # (http://genomebiology.com) style.
    #
    #   # ref is a Bio::Reference object
    #   puts ref.genome_biol
    #
    #     Hoge JP, Fuga FB: Title of the study. Theor J Hoge 2001, 12:123-145.
    # ---
    # *Returns*:: String
    def genome_biol
      authors = @authors.collect {|name| strip_dots(name)}.join(', ')
      journal = strip_dots(@journal)
      "#{authors}: #{@title} #{journal} #{@year}, #{@volume}:#{@pages}."
    end
    
    # Returns reference formatted in the Current Biology 
    # (http://current-biology.com) style. (Same as the Genome Biology style)
    #
    #   # ref is a Bio::Reference object
    #   puts ref.current
    #
    #     Hoge JP, Fuga FB: Title of the study. Theor J Hoge 2001, 12:123-145.
    # ---
    # *Returns*:: String
    def current 
      self.genome_biol
    end

    # Returns reference formatted in the Genome Research 
    # (http://genome.org) style.
    #
    #   # ref is a Bio::Reference object
    #   puts ref.genome_res
    #
    #     Hoge, J.P. and Fuga, F.B. 2001.
    #       Title of the study. Theor. J. Hoge 12: 123-145.
    # ---
    # *Returns*:: String
    def genome_res
      authors = authors_join(' and ')
      "#{authors} #{@year}.\n  #{@title} #{@journal} #{@volume}: #{@pages}."
    end

    # Returns reference formatted in the Nucleic Acids Reseach 
    # (http://nar.oxfordjournals.org) style.
    #
    #   # ref is a Bio::Reference object
    #   puts ref.nar
    #
    #     Hoge, J.P. and Fuga, F.B. (2001) Title of the study. Theor. J. Hoge, 12, 123-145.
    # ---
    # *Returns*:: String
    def nar
      authors = authors_join(' and ')
      "#{authors} (#{@year}) #{@title} #{@journal}, #{@volume}, #{@pages}."
    end

    # Returns reference formatted in the 
    # CELL[http://www.cell.com] Press style.
    #
    #   # ref is a Bio::Reference object
    #   puts ref.cell
    #
    #     Hoge, J.P. and Fuga, F.B. (2001). Title of the study. Theor. J. Hoge 12, 123-145.
    # ---
    # *Returns*:: String
    def cell
      authors = authors_join(' and ')
      "#{authors} (#{@year}). #{@title} #{@journal} #{@volume}, #{pages}."
    end
    
    # Returns reference formatted in the 
    # TRENDS[http://www.trends.com] style.
    #
    #   # ref is a Bio::Reference object
    #   puts ref.trends
    #
    #     Hoge, J.P. and Fuga, F.B. (2001) Title of the study. Theor. J. Hoge 12, 123-145
    # ---
    # *Returns*:: String
    def trends
      if @authors.size > 2
        authors = "#{@authors[0]} et al."
      elsif @authors.size == 1
        authors = "#{@authors[0]}"
      else
        authors = authors_join(' and ')
      end
      "#{authors} (#{@year}) #{@title} #{@journal} #{@volume}, #{@pages}"
    end

    # Returns a valid URL for pubmed records
    #
    # *Returns*:: String
    def pubmed_url
      unless @pubmed.to_s.empty?
        head = "http://www.ncbi.nlm.nih.gov/pubmed"
        return "#{head}/#{@pubmed}"
      end
      ''
    end

    private

    def strip_dots(data)
      data.tr(',.', '') if data
    end

    def authors_join(amp, sep = ', ')
      authors = @authors.clone
      if authors.length > 1
        last = authors.pop
        authors = authors.join(sep) + "#{amp}" + last
      elsif authors.length == 1
        authors = authors.pop
      else
        authors = ""
      end
    end

    def rev_name(name)
      if name =~ /,/
        name, initial = name.split(/,\s+/)
        name = "#{initial} #{name}"
      end
      return name
    end

  end

end