bibtex-ruby 2.0.7 → 2.0.8
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bibtex-ruby might be problematic. Click here for more details.
- data/Gemfile +3 -0
- data/Gemfile.lock +6 -2
- data/History.txt +6 -0
- data/README.md +7 -1
- data/Rakefile +2 -0
- data/lib/bibtex/bibliography.rb +29 -1
- data/lib/bibtex/entry.rb +211 -23
- data/lib/bibtex/names.rb +143 -65
- data/lib/bibtex/version.rb +1 -1
- data/test/bibtex/test_bibliography.rb +14 -0
- data/test/bibtex/test_names.rb +83 -1
- metadata +18 -12
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
bibtex-ruby (2.0.
|
4
|
+
bibtex-ruby (2.0.8)
|
5
5
|
latex-decode (>= 0.0.6)
|
6
6
|
multi_json (~> 1.3)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: http://rubygems.org/
|
10
10
|
specs:
|
11
|
+
addressable (2.2.7)
|
11
12
|
autowatchr (0.1.5)
|
12
13
|
watchr
|
13
14
|
builder (3.0.0)
|
@@ -39,7 +40,7 @@ GEM
|
|
39
40
|
linecache (0.46)
|
40
41
|
rbx-require-relative (> 0.0.4)
|
41
42
|
minitest (2.11.3)
|
42
|
-
multi_json (1.3.
|
43
|
+
multi_json (1.3.4)
|
43
44
|
mynyml-redgreen (0.7.1)
|
44
45
|
term-ansicolor (>= 1.0.4)
|
45
46
|
racc (1.4.8)
|
@@ -52,6 +53,8 @@ GEM
|
|
52
53
|
columnize
|
53
54
|
rbx-linecache (~> 1.3)
|
54
55
|
rbx-require-relative (>= 0.0.4)
|
56
|
+
rdf (0.3.5.2)
|
57
|
+
addressable (>= 2.2.6)
|
55
58
|
rdoc (3.12)
|
56
59
|
json (~> 1.4)
|
57
60
|
ruby-debug (0.10.4)
|
@@ -79,6 +82,7 @@ DEPENDENCIES
|
|
79
82
|
racc (~> 1.4)
|
80
83
|
rake (~> 0.9)
|
81
84
|
rbx-trepanning
|
85
|
+
rdf (~> 0.3)
|
82
86
|
rdoc (~> 3.9)
|
83
87
|
ruby-debug
|
84
88
|
ruby-prof (~> 0.10)
|
data/History.txt
CHANGED
data/README.md
CHANGED
@@ -35,7 +35,7 @@ Select a BibTeX entry and access individual fields:
|
|
35
35
|
b[:pickaxe].author.length
|
36
36
|
#=> 3
|
37
37
|
b[:pickaxe].author.to_s
|
38
|
-
#=> "Thomas,
|
38
|
+
#=> "Thomas, D. and Fowler, Chad and Hunt, Andy"
|
39
39
|
b[:pickaxe].author[2].first
|
40
40
|
#=> "Andy"
|
41
41
|
|
@@ -48,6 +48,12 @@ Query a bibliography:
|
|
48
48
|
b['@book[year=2009]'].length
|
49
49
|
#=> 1 - the number of books published in 2009
|
50
50
|
|
51
|
+
Extend first name initials throughout your bibliography:
|
52
|
+
|
53
|
+
b.extend_initials ['Dave', 'Thomas']
|
54
|
+
b[:pickaxe].author.to_s
|
55
|
+
#=> "Thomas, Dave and Fowler, Chad and Hunt, Andy"
|
56
|
+
|
51
57
|
Render your bibliography in one of
|
52
58
|
[many different citation styles](https://github.com/citation-style-language/styles)
|
53
59
|
(requires the **citeproc-ruby** gem):
|
data/Rakefile
CHANGED
data/lib/bibtex/bibliography.rb
CHANGED
@@ -89,8 +89,9 @@ module BibTeX
|
|
89
89
|
:comment, :meta_content
|
90
90
|
|
91
91
|
def_delegators :@data, :length, :size, :empty?
|
92
|
-
def_delegators :@entries, :key?, :has_key
|
92
|
+
def_delegators :@entries, :key?, :has_key?, :values_at
|
93
93
|
|
94
|
+
alias entries_at values_at
|
94
95
|
|
95
96
|
# Creates a new bibliography.
|
96
97
|
def initialize(options = {})
|
@@ -285,6 +286,17 @@ module BibTeX
|
|
285
286
|
self
|
286
287
|
end
|
287
288
|
|
289
|
+
# call-seq:
|
290
|
+
# b.extend_initials(['Edgar Allen', 'Poe'], ['Nathaniel', 'Hawthorne'])
|
291
|
+
# #=> Extends the initials in names like 'E.A. Poe' or 'Hawethorne, N.'
|
292
|
+
# in the bibliography.
|
293
|
+
def extend_initials(*arguments)
|
294
|
+
arguments.each do |with_first, for_last|
|
295
|
+
names.each do |name|
|
296
|
+
name.extend_initials(with_first, for_last)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
288
300
|
|
289
301
|
def sort(*arguments, &block)
|
290
302
|
data.sort(*arguments, &block)
|
@@ -341,6 +353,22 @@ module BibTeX
|
|
341
353
|
xml
|
342
354
|
end
|
343
355
|
|
356
|
+
# Returns an RDF::Graph representation of the bibliography. The graph
|
357
|
+
# can be serialized using any of the RDF serializer plugins.
|
358
|
+
def to_rdf(options = {})
|
359
|
+
require 'rdf'
|
360
|
+
|
361
|
+
graph = RDF::Graph.new
|
362
|
+
|
363
|
+
q('@entry').each do |entry|
|
364
|
+
graph << entry.to_rdf(options)
|
365
|
+
end
|
366
|
+
|
367
|
+
graph
|
368
|
+
rescue LoadError
|
369
|
+
BibTeX.log.error "Please gem install rdf for RDF support."
|
370
|
+
end
|
371
|
+
|
344
372
|
# call-seq:
|
345
373
|
# bib.query() #=> returns all elements
|
346
374
|
# bib.query('@book') #=> returns all books
|
data/lib/bibtex/entry.rb
CHANGED
@@ -107,6 +107,35 @@ module BibTeX
|
|
107
107
|
article article-journal
|
108
108
|
}.map(&:intern)]).freeze
|
109
109
|
|
110
|
+
BIBO_FIELDS = Hash[*%w{
|
111
|
+
pages pages
|
112
|
+
number issue
|
113
|
+
isbn isbn
|
114
|
+
issn issn
|
115
|
+
doi doi
|
116
|
+
edition edition
|
117
|
+
abstract abstract
|
118
|
+
volume volume
|
119
|
+
}.map(&:intern)].freeze
|
120
|
+
|
121
|
+
BIBO_TYPES = Hash.new(:Document).merge(Hash[*%w{
|
122
|
+
booklet Book
|
123
|
+
book Book
|
124
|
+
conference Conference
|
125
|
+
inbook Article
|
126
|
+
incollection Article
|
127
|
+
inproceedings Article
|
128
|
+
manual Manual
|
129
|
+
mastersthesis Thesis
|
130
|
+
phdthesis Thesis
|
131
|
+
proceedings Proceedings
|
132
|
+
techreport Report
|
133
|
+
journal Journal
|
134
|
+
periodical Periodical
|
135
|
+
unpublished Manuscript
|
136
|
+
article Article
|
137
|
+
}.map(&:intern)]).freeze
|
138
|
+
|
110
139
|
|
111
140
|
attr_reader :fields, :type
|
112
141
|
|
@@ -198,20 +227,33 @@ module BibTeX
|
|
198
227
|
alias type? has_type?
|
199
228
|
|
200
229
|
|
201
|
-
def has_field?(
|
202
|
-
|
230
|
+
def has_field?(*names)
|
231
|
+
names.flatten.any? do |name|
|
232
|
+
name.respond_to?(:to_sym) ? fields.has_key?(name.to_sym) : false
|
233
|
+
end
|
203
234
|
end
|
204
235
|
|
205
236
|
alias field? has_field?
|
206
237
|
|
207
|
-
def inherits?(
|
208
|
-
|
238
|
+
def inherits?(*names)
|
239
|
+
names.flatten.any? do |name|
|
240
|
+
!has_field(name) && has_parent? && parent.provides?(name)
|
241
|
+
end
|
209
242
|
end
|
210
243
|
|
211
|
-
# Returns true if the Entry has a field (or alias) for the passed-in
|
212
|
-
def provides?(
|
213
|
-
|
214
|
-
|
244
|
+
# Returns true if the Entry has a field (or alias) for any of the passed-in names.
|
245
|
+
def provides?(*names)
|
246
|
+
names.flatten.any? do |name|
|
247
|
+
if name.respond_to?(:to_sym)
|
248
|
+
has_field?(name) || has_field?(aliases[name.to_sym])
|
249
|
+
else
|
250
|
+
false
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def provides_or_inherits?(*names)
|
256
|
+
provides?(names) || inherits?(names)
|
215
257
|
end
|
216
258
|
|
217
259
|
# Returns the field value referenced by the passed-in name.
|
@@ -327,6 +369,16 @@ module BibTeX
|
|
327
369
|
add(name.to_sym, value)
|
328
370
|
end
|
329
371
|
|
372
|
+
# Author, Editor and Translator readers
|
373
|
+
NAME_FIELDS.each do |contributor|
|
374
|
+
define_method(contributor) do
|
375
|
+
get(contributor)
|
376
|
+
end
|
377
|
+
|
378
|
+
alias_method "#{contributor}s", contributor
|
379
|
+
end
|
380
|
+
|
381
|
+
|
330
382
|
# Adds a new field (name-value pair) or multiple fields to the entry.
|
331
383
|
# Returns the entry for chainability.
|
332
384
|
#
|
@@ -364,6 +416,19 @@ module BibTeX
|
|
364
416
|
Digest::MD5.hexdigest(field_names(filter).map { |k| [k, fields[k]] }.flatten.join)
|
365
417
|
end
|
366
418
|
|
419
|
+
def identifier
|
420
|
+
case
|
421
|
+
when provides?(:doi)
|
422
|
+
"info:doi/#{get(:doi)}"
|
423
|
+
when provides?(:isbn)
|
424
|
+
"urn:isbn:#{get(:isbn)}"
|
425
|
+
when provides?(:issn)
|
426
|
+
"urn:issn:#{get(:issn)}"
|
427
|
+
else
|
428
|
+
"urn:bibtex:#{key}"
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
367
432
|
# Called when the element was added to a bibliography.
|
368
433
|
def added_to_bibliography(bibliography)
|
369
434
|
super
|
@@ -431,6 +496,9 @@ module BibTeX
|
|
431
496
|
|
432
497
|
alias parse_months parse_month
|
433
498
|
|
499
|
+
def date
|
500
|
+
get(:date) || get(:year)
|
501
|
+
end
|
434
502
|
|
435
503
|
# Parses all name values of the entry. Tries to replace and join the
|
436
504
|
# value prior to parsing.
|
@@ -494,6 +562,43 @@ module BibTeX
|
|
494
562
|
|
495
563
|
alias cross_referenced_by children
|
496
564
|
|
565
|
+
def container_title
|
566
|
+
get(:booktitle) || get(:journal) || get(:container)
|
567
|
+
end
|
568
|
+
|
569
|
+
def pages_from
|
570
|
+
fetch(:pages, '').split(/\D+/)[0]
|
571
|
+
end
|
572
|
+
|
573
|
+
def pages_to
|
574
|
+
fetch(:pages, '').split(/\D+/)[-1]
|
575
|
+
end
|
576
|
+
|
577
|
+
# Returns true if this entry is published inside a book, collection or journal
|
578
|
+
def contained?
|
579
|
+
has_field?(:booktitle, :container, :journal)
|
580
|
+
end
|
581
|
+
|
582
|
+
|
583
|
+
# Returns a duplicate entry with all values converted using the filter.
|
584
|
+
# If an optional block is given, only those values will be converted where
|
585
|
+
# the block returns true (the block will be called with each key-value pair).
|
586
|
+
#
|
587
|
+
# @see #convert!
|
588
|
+
def convert(filter)
|
589
|
+
block_given? ? dup.convert!(filter, &Proc.new) : dup.convert!(filter)
|
590
|
+
end
|
591
|
+
|
592
|
+
# In-place variant of @see #convert
|
593
|
+
def convert!(filter)
|
594
|
+
fields.each_pair { |k,v| !block_given? || yield(k,v) ? v.convert!(filter) : v }
|
595
|
+
self
|
596
|
+
end
|
597
|
+
|
598
|
+
def <=>(other)
|
599
|
+
type != other.type ? type <=> other.type : key != other.key ? key <=> other.key : to_s <=> other.to_s
|
600
|
+
end
|
601
|
+
|
497
602
|
|
498
603
|
# Returns a string of all the entry's fields.
|
499
604
|
def content(options = {})
|
@@ -562,24 +667,106 @@ module BibTeX
|
|
562
667
|
xml
|
563
668
|
end
|
564
669
|
|
565
|
-
# Returns a
|
566
|
-
#
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
670
|
+
# Returns a RDF::Graph representation of the entry using the BIBO ontology.
|
671
|
+
# TODO: improve level of detail captured by export
|
672
|
+
def to_rdf(options = {})
|
673
|
+
require 'rdf'
|
674
|
+
|
675
|
+
bibo = RDF::Vocabulary.new('http://purl.org/ontology/bibo/')
|
676
|
+
|
677
|
+
graph = RDF::Graph.new
|
678
|
+
entry = RDF::URI.new(identifier)
|
679
|
+
|
680
|
+
graph << [entry, RDF.type, bibo[BIBO_TYPES[type]]]
|
681
|
+
|
682
|
+
[:title, :language].each do |key|
|
683
|
+
graph << [entry, RDF::DC[key], get(key).to_s] if field?(key)
|
684
|
+
end
|
685
|
+
|
686
|
+
graph << [entry, RDF::DC.date, get(:year).to_s] if field?(:year)
|
687
|
+
|
688
|
+
if field?(:publisher)
|
689
|
+
address = RDF::Vocabulary.new('http://schemas.talis.com/2005/address/schema#')
|
690
|
+
pub = RDF::Node.new
|
691
|
+
|
692
|
+
graph << [pub, RDF.type, RDF::FOAF[:Organization]]
|
693
|
+
graph << [pub, RDF::FOAF.name, get(:publisher)]
|
694
|
+
|
695
|
+
graph << [pub, address[:localityName], get(:address)] if field?(:address)
|
696
|
+
|
697
|
+
graph << [entry, RDF::DC.published, pub]
|
698
|
+
end
|
699
|
+
|
700
|
+
[:doi, :edition, :volume].each do |key|
|
701
|
+
graph << [entry, bibo[key], get(key).to_s] if field?(key)
|
702
|
+
end
|
703
|
+
|
704
|
+
if has_field?(:pages)
|
705
|
+
if get(:pages).to_s =~ /^\s*(\d+)\s*-+\s*(\d+)\s*$/
|
706
|
+
graph << [entry, bibo[:pageStart], $1]
|
707
|
+
graph << [entry, bibo[:pageEnd], $2]
|
708
|
+
else
|
709
|
+
graph << [entry, bibo[:pages], get(:pages).to_s]
|
710
|
+
end
|
711
|
+
end
|
712
|
+
|
713
|
+
if has_field?(:author)
|
714
|
+
seq = RDF::Node.new
|
715
|
+
|
716
|
+
graph << [seq, RDF.type, RDF[:Seq]]
|
717
|
+
graph << [entry, bibo[:authorList], seq]
|
718
|
+
|
719
|
+
authors.each do |author|
|
720
|
+
a = RDF::Node.new
|
721
|
+
|
722
|
+
graph << [a, RDF.type, RDF::FOAF[:Person]]
|
723
|
+
|
724
|
+
if author.is_a?(Name)
|
725
|
+
[:given, :family, :prefix, :suffix].each do |part|
|
726
|
+
graph << [a, bibo["#{part}Name"], author.send(part).to_s]
|
727
|
+
end
|
728
|
+
else
|
729
|
+
graph << [a, RDF::FOAF.name, author.to_s]
|
730
|
+
end
|
731
|
+
|
732
|
+
graph << [entry, RDF::DC.creator, a]
|
733
|
+
graph << [seq, RDF.li, a]
|
734
|
+
end
|
735
|
+
end
|
736
|
+
|
737
|
+
if has_field?(:editor)
|
738
|
+
seq = RDF::Node.new
|
739
|
+
|
740
|
+
graph << [seq, RDF.type, RDF[:Seq]]
|
741
|
+
graph << [entry, bibo[:editorList], seq]
|
742
|
+
|
743
|
+
editors.each do |editor|
|
744
|
+
e = RDF::Node.new
|
745
|
+
|
746
|
+
graph << [e, RDF.type, RDF::FOAF[:Person]]
|
747
|
+
|
748
|
+
if editor.is_a?(Name)
|
749
|
+
[:given, :family, :prefix, :suffix].each do |part|
|
750
|
+
graph << [e, bibo["#{part}Name"], editor.send(part).to_s]
|
751
|
+
end
|
752
|
+
else
|
753
|
+
graph << [e, RDF::FOAF.name, editor.to_s]
|
754
|
+
end
|
755
|
+
|
756
|
+
graph << [entry, bibo.editor, a]
|
757
|
+
graph << [seq, RDF.li, e]
|
758
|
+
end
|
759
|
+
end
|
760
|
+
|
761
|
+
graph
|
762
|
+
rescue LoadError
|
763
|
+
BibTeX.log.error "Please gem install rdf for RDF support."
|
572
764
|
end
|
573
765
|
|
574
|
-
|
575
|
-
def convert!(filter)
|
576
|
-
fields.each_pair { |k,v| !block_given? || yield(k,v) ? v.convert!(filter) : v }
|
577
|
-
self
|
578
|
-
end
|
766
|
+
alias to_bibo to_rdf
|
579
767
|
|
580
|
-
|
581
|
-
|
582
|
-
end
|
768
|
+
|
769
|
+
|
583
770
|
|
584
771
|
private
|
585
772
|
|
@@ -594,5 +781,6 @@ module BibTeX
|
|
594
781
|
k
|
595
782
|
end
|
596
783
|
|
784
|
+
|
597
785
|
end
|
598
786
|
end
|
data/lib/bibtex/names.rb
CHANGED
@@ -20,6 +20,7 @@ require 'forwardable'
|
|
20
20
|
|
21
21
|
module BibTeX
|
22
22
|
|
23
|
+
# A BibTeX Names value is an ordered list of name values.
|
23
24
|
class Names < Value
|
24
25
|
include Enumerable
|
25
26
|
|
@@ -27,9 +28,9 @@ module BibTeX
|
|
27
28
|
|
28
29
|
def self.parse(string)
|
29
30
|
new(NameParser.new.parse(string))
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
rescue => e
|
32
|
+
BibTeX.log.info(e.message)
|
33
|
+
nil
|
33
34
|
end
|
34
35
|
|
35
36
|
def initialize(*arguments)
|
@@ -39,12 +40,16 @@ module BibTeX
|
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
42
|
-
def replace(*arguments)
|
43
|
+
def replace(*arguments)
|
44
|
+
self
|
45
|
+
end
|
43
46
|
|
44
|
-
def join
|
47
|
+
def join
|
48
|
+
self
|
49
|
+
end
|
45
50
|
|
46
|
-
def value
|
47
|
-
@tokens.join(' and ')
|
51
|
+
def value(options = {})
|
52
|
+
@tokens.map { |n| n.to_s(options) }.join(' and ')
|
48
53
|
end
|
49
54
|
|
50
55
|
def to_s(options = {})
|
@@ -53,14 +58,24 @@ module BibTeX
|
|
53
58
|
[q[0], value, q[-1]].compact.join
|
54
59
|
end
|
55
60
|
|
56
|
-
def name
|
57
|
-
|
58
|
-
|
61
|
+
def name?
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
def numeric?
|
66
|
+
false
|
67
|
+
end
|
59
68
|
|
60
|
-
|
61
|
-
|
69
|
+
def atomic?
|
70
|
+
true
|
71
|
+
end
|
62
72
|
|
63
|
-
|
73
|
+
alias names? name?
|
74
|
+
alias symbol? numeric?
|
75
|
+
|
76
|
+
def to_name
|
77
|
+
self
|
78
|
+
end
|
64
79
|
|
65
80
|
def to_citeproc(options = {})
|
66
81
|
map { |n| n.to_citeproc(options) }
|
@@ -82,13 +97,14 @@ module BibTeX
|
|
82
97
|
self
|
83
98
|
end
|
84
99
|
|
85
|
-
alias
|
86
|
-
alias
|
100
|
+
alias << add
|
101
|
+
alias push add
|
87
102
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
103
|
+
[:convert!, :rename_if, :rename_unless, :extend_initials].each do |method_id|
|
104
|
+
define_method(method_id) do |*arguments|
|
105
|
+
tokens.each { |t| t.send(method_id, *arguments) }
|
106
|
+
self
|
107
|
+
end
|
92
108
|
end
|
93
109
|
|
94
110
|
def <=>(other)
|
@@ -97,20 +113,21 @@ module BibTeX
|
|
97
113
|
|
98
114
|
end
|
99
115
|
|
116
|
+
# A Name comprises individual name parts (first, last, prefix and suffix),
|
117
|
+
# but behaves almost like an atomic string value.
|
100
118
|
class Name < Struct.new(:first, :last, :prefix, :suffix)
|
101
119
|
extend Forwardable
|
102
|
-
|
103
120
|
include Comparable
|
104
|
-
|
121
|
+
|
105
122
|
BibTeXML = {
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
123
|
+
:first => :first,
|
124
|
+
:last => :last,
|
125
|
+
:prefix => :prelast,
|
126
|
+
:suffix => :lineage
|
127
|
+
}.freeze
|
128
|
+
|
112
129
|
def_delegators :to_s, :=~, :===,
|
113
|
-
|
130
|
+
*String.instance_methods(false).reject { |m| m =~ /^\W|to_s|replace|each|first|last|!$/ }
|
114
131
|
|
115
132
|
class << self
|
116
133
|
def parse(string)
|
@@ -125,53 +142,114 @@ module BibTeX
|
|
125
142
|
end
|
126
143
|
|
127
144
|
def initialize(attributes = {})
|
128
|
-
attributes
|
129
|
-
send("#{key}=", value) if respond_to?(key)
|
130
|
-
end
|
145
|
+
set(attributes)
|
131
146
|
end
|
132
147
|
|
133
148
|
def initalize_copy(other)
|
134
|
-
each_pair { |k,v| self[k] = v }
|
149
|
+
each_pair { |k,v| self[k] = v.dup }
|
150
|
+
end
|
151
|
+
|
152
|
+
# Set the name tokens to the values defined in the passed-in hash.
|
153
|
+
def set(attributes = {})
|
154
|
+
attributes.each do |key, value|
|
155
|
+
send("#{key}=", value) if respond_to?(key)
|
156
|
+
end
|
157
|
+
|
158
|
+
self
|
135
159
|
end
|
136
160
|
|
137
161
|
def blank?
|
138
162
|
to_a.compact.empty?
|
139
163
|
end
|
164
|
+
|
165
|
+
# Returns the first name (or the passed-in string) as initials.
|
166
|
+
def initials(token = first)
|
167
|
+
token.to_s.gsub(/([[:upper:]])[[:lower:]]+\s*/, '\1.')
|
168
|
+
end
|
140
169
|
|
141
|
-
|
142
|
-
|
170
|
+
# Returns true if the first name consists solely of initials.
|
171
|
+
def initials?
|
172
|
+
!(first.nil? || first.empty? || first.to_s =~ /[[:alpha:]]{2,}[^\.]/)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Sets the name's first name to the passed-in name if the last name equals
|
176
|
+
# for_last and the current first name has the same initials as with_first.
|
177
|
+
def extend_initials(with_first, for_last)
|
178
|
+
rename_if :first => with_first do |name|
|
179
|
+
name.last == for_last && name.initials.gsub(/\s+/, '') == initials(with_first).gsub(/\s+/, '')
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# Renames the tokens according to the passed-in attributes if all of the
|
184
|
+
# conditions match or if the given block returns true.
|
185
|
+
def rename_if(attributes, conditions = {})
|
186
|
+
if block_given?
|
187
|
+
set(attributes) if yield self
|
188
|
+
else
|
189
|
+
set(attributes) if conditions.all? do |key, value|
|
190
|
+
respond_to?(key) && send(key) == value
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
self
|
195
|
+
end
|
196
|
+
|
197
|
+
def rename_unless(attributes, conditions = {})
|
198
|
+
if block_given?
|
199
|
+
set(attributes) unless yield self
|
200
|
+
else
|
201
|
+
set(attributes) unless conditions.all? do |key, value|
|
202
|
+
respond_to?(key) && send(key) == value
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
self
|
207
|
+
end
|
208
|
+
|
209
|
+
# call-seq:
|
210
|
+
# name.display_order #=> 'Edgar Allen Poe'
|
211
|
+
# name.display_order :initials => true #=> 'E.A. Poe'
|
212
|
+
#
|
213
|
+
# Returns the name as a string in display order.
|
214
|
+
def display_order(options = {})
|
215
|
+
[options[:initials] ? initials : first, prefix, last, suffix].compact.join(' ')
|
143
216
|
end
|
144
217
|
|
145
218
|
alias display display_order
|
146
219
|
|
147
|
-
|
148
|
-
|
220
|
+
# call-seq:
|
221
|
+
# name.sort_order #=> 'Poe, Edgar Allen'
|
222
|
+
# name.sort_order :initials => true #=> 'Poe, E.A.'
|
223
|
+
#
|
224
|
+
# Returns the name as a string in sort order.
|
225
|
+
def sort_order(options = {})
|
226
|
+
[[prefix, last].compact.join(' '), suffix, options[:initials] ? initials : first].compact.join(', ')
|
149
227
|
end
|
150
228
|
|
151
229
|
alias to_s sort_order
|
152
|
-
|
230
|
+
|
153
231
|
def <=>(other)
|
154
|
-
other.is_a?(Name) ?
|
232
|
+
other.is_a?(Name) ? sort_order <=> other.sort_order : super
|
155
233
|
end
|
156
234
|
|
157
235
|
def to_hash
|
158
236
|
Hash[each_pair.to_a]
|
159
237
|
end
|
160
238
|
|
161
|
-
|
162
|
-
|
163
|
-
|
239
|
+
def to_xml
|
240
|
+
require 'rexml/document'
|
241
|
+
xml = REXML::Element.new('bibtex:person')
|
164
242
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
243
|
+
each_pair do |part, text|
|
244
|
+
unless text.nil?
|
245
|
+
element = REXML::Element.new("bibtex:#{BibTeXML[part]}")
|
246
|
+
element.text = text
|
247
|
+
xml.add_element(element)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
xml
|
252
|
+
end
|
175
253
|
|
176
254
|
[:strip!, :upcase!, :downcase!, :sub!, :gsub!, :chop!, :chomp!, :rstrip!].each do |method_id|
|
177
255
|
define_method(method_id) do |*arguments, &block|
|
@@ -182,20 +260,20 @@ module BibTeX
|
|
182
260
|
end
|
183
261
|
end
|
184
262
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
263
|
+
def convert(filter)
|
264
|
+
dup.convert!(filter)
|
265
|
+
end
|
266
|
+
|
267
|
+
def convert!(filter)
|
268
|
+
if f = Filters.resolve(filter)
|
269
|
+
each_pair { |k,v| self[k] = f.apply(v) unless v.nil? }
|
270
|
+
else
|
271
|
+
raise ArgumentError, "Failed to load filter #{filter.inspect}"
|
272
|
+
end
|
195
273
|
|
196
|
-
|
197
|
-
|
198
|
-
|
274
|
+
self
|
275
|
+
end
|
276
|
+
|
199
277
|
def to_citeproc(options = {})
|
200
278
|
hash = {}
|
201
279
|
hash['family'] = family unless family.nil?
|
@@ -213,6 +291,6 @@ module BibTeX
|
|
213
291
|
alias jr= suffix=
|
214
292
|
alias von prefix
|
215
293
|
alias von= prefix=
|
216
|
-
|
294
|
+
|
217
295
|
end
|
218
296
|
end
|
data/lib/bibtex/version.rb
CHANGED
@@ -71,6 +71,20 @@ module BibTeX
|
|
71
71
|
END
|
72
72
|
end
|
73
73
|
|
74
|
+
describe '#entries_at' do
|
75
|
+
it 'returns a list of all entries identified by the passed-in keys' do
|
76
|
+
assert_equal [@bib['segaran2007'], @bib['rails']], @bib.entries_at('segaran2007', :rails)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '#extend_initials' do
|
81
|
+
it 'extends the initials in matching names' do
|
82
|
+
@bib.names.map(&:to_s).wont_include 'Flanagan, Dave'
|
83
|
+
@bib.extend_initials(['Dave', 'Flanagan'])
|
84
|
+
@bib.names.map(&:to_s).must_include 'Flanagan, Dave'
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
74
88
|
it 'supports access by index' do
|
75
89
|
assert_equal 'ruby', @bib[1].keywords
|
76
90
|
end
|
data/test/bibtex/test_names.rb
CHANGED
@@ -5,6 +5,10 @@ require 'helper'
|
|
5
5
|
module BibTeX
|
6
6
|
class NamesTest < MiniTest::Spec
|
7
7
|
|
8
|
+
before do
|
9
|
+
@poe = Name.new(:first => 'Edgar Allen', :last => 'Poe')
|
10
|
+
end
|
11
|
+
|
8
12
|
describe 'string behaviour' do
|
9
13
|
before do
|
10
14
|
@name = Name.new(:first => 'Charles Louis Xavier Joseph', :prefix => 'de la', :last => 'Vallee Poussin', :suffix => 'Jr.')
|
@@ -18,7 +22,85 @@ module BibTeX
|
|
18
22
|
it 'should implement gsub!' do
|
19
23
|
assert_equal 'dX la VallXX PoussXn, Jr., CharlXs LouXs XavXXr JosXph', @name.gsub!(/[ei]/, 'X').to_s
|
20
24
|
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#display_order' do
|
28
|
+
it 'returns the name as "first last"' do
|
29
|
+
@poe.display_order.must_be :==, 'Edgar Allen Poe'
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'accepts the :initials option' do
|
33
|
+
@poe.display_order(:initials => true).must_be :==, 'E.A. Poe'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#sort_order' do
|
38
|
+
it 'returns the name as "last, first"' do
|
39
|
+
@poe.sort_order.must_be :==, 'Poe, Edgar Allen'
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'accepts the :initials option' do
|
43
|
+
@poe.sort_order(:initials => true).must_be :==, 'Poe, E.A.'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#initials?' do
|
48
|
+
it 'returns true if the name contains a solely initials as a first name' do
|
49
|
+
@poe.initials?.must_equal false
|
50
|
+
|
51
|
+
@poe.first = 'Edgar A.'
|
52
|
+
@poe.initials?.must_equal false
|
53
|
+
|
54
|
+
@poe.first = 'E.A.'
|
55
|
+
@poe.initials?.must_equal true
|
56
|
+
|
57
|
+
@poe.first = ''
|
58
|
+
@poe.initials?.must_equal false
|
59
|
+
|
60
|
+
@poe.first = nil
|
61
|
+
@poe.initials?.must_equal false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#rename_if' do
|
66
|
+
it 'renames the name to the given attributes if no condition is given' do
|
67
|
+
@poe.rename_if({ :first => 'E.A.' }).first.must_equal 'E.A.'
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'renames the name to the given attributes if all conditions match' do
|
71
|
+
@poe.rename_if({ :first => 'E.A.' }, { :last => @poe.last }).first.must_equal 'E.A.'
|
72
|
+
@poe.rename_if({ :first => 'E.A.' }, { :last => @poe.last, :first => @poe.first }).first.must_equal 'E.A.'
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'renames the name to the given attributes if the block returns true' do
|
76
|
+
@poe.rename_if({ :first => 'E.A.' }) {|n| true}.first.must_equal 'E.A.'
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'does not rename the name to the given attributes if at least one condition does not match' do
|
80
|
+
@poe.rename_if({ :first => 'E.A.' }, { :last => 'foo' }).first.wont_equal 'E.A.'
|
81
|
+
@poe.rename_if({ :first => 'E.A.' }, { :last => 'foo', :first => @poe.first }).first.wont_equal 'E.A.'
|
82
|
+
@poe.rename_if({ :first => 'E.A.' }, { :last => @poe.last, :first => 'foo' }).first.wont_equal 'E.A.'
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'does not rename the name to the given attributes if the block returns false' do
|
86
|
+
@poe.rename_if({ :first => 'E.A.' }) {|n| false}.first.wont_equal 'E.A.'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe '#extend_initials' do
|
91
|
+
it 'extends the first name if the last name and initials match' do
|
92
|
+
Name.new(:first => 'E.A.', :last => 'Poe').extend_initials('Edgar Allen', 'Poe').first.must_equal 'Edgar Allen'
|
93
|
+
Name.new(:first => 'Edgar A.', :last => 'Poe').extend_initials('Edgar Allen', 'Poe').first.must_equal 'Edgar Allen'
|
94
|
+
Name.new(:first => 'E. A.', :last => 'Poe').extend_initials('Edgar Allen', 'Poe').first.must_equal 'Edgar Allen'
|
95
|
+
Name.new(:first => 'E. Allen', :last => 'Poe').extend_initials('Edgar Allen', 'Poe').first.must_equal 'Edgar Allen'
|
96
|
+
Name.new(:first => 'E.A.', :last => 'Poe').extend_initials('Edgar A.', 'Poe').first.must_equal 'Edgar A.'
|
97
|
+
end
|
21
98
|
|
99
|
+
it 'does not extend the first name if the last name or initials do not match' do
|
100
|
+
Name.new(:first => 'E.A.', :last => 'Poe').extend_initials('Edgar Allen', 'Poser').first.wont_equal 'Edgar Allen'
|
101
|
+
Name.new(:first => 'E.A.', :last => 'Poe').extend_initials('Edgar Ellen', 'Poe').first.wont_equal 'Edgar Ellen'
|
102
|
+
Name.new(:first => 'E.R.', :last => 'Poe').extend_initials('Edgar Allen', 'Poe').first.wont_equal 'Edgar Allen'
|
103
|
+
end
|
22
104
|
end
|
23
105
|
|
24
106
|
describe "conversions" do
|
@@ -39,8 +121,8 @@ module BibTeX
|
|
39
121
|
Names.parse("S{\\o}ren Kirkegaard and Emmanuel L\\'evinas").convert(:latex).to_s.must_be :==, 'Kirkegaard, Søren and Lévinas, Emmanuel'
|
40
122
|
end
|
41
123
|
end
|
42
|
-
|
43
124
|
end
|
44
125
|
|
45
126
|
end
|
127
|
+
|
46
128
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bibtex-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.8
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-05-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: latex-decode
|
16
|
-
requirement: &
|
16
|
+
requirement: &70229986220740 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 0.0.6
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70229986220740
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: multi_json
|
27
|
-
requirement: &
|
27
|
+
requirement: &70229986220220 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '1.3'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70229986220220
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rake
|
38
|
-
requirement: &
|
38
|
+
requirement: &70229986219680 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0.9'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70229986219680
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: racc
|
49
|
-
requirement: &
|
49
|
+
requirement: &70229986219200 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '1.4'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70229986219200
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: rdoc
|
60
|
-
requirement: &
|
60
|
+
requirement: &70229986218720 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,7 +65,7 @@ dependencies:
|
|
65
65
|
version: '3.9'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70229986218720
|
69
69
|
description: ! "\t\tBibTeX-Ruby is the Rubyist's swiss-army-knife for all things BibTeX.
|
70
70
|
It\n includes a parser for all common BibTeX objects (@string, @preamble,\n @comment
|
71
71
|
and regular entries) and a sophisticated name parser that\n tokenizes correctly
|
@@ -180,12 +180,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
180
180
|
- - ! '>='
|
181
181
|
- !ruby/object:Gem::Version
|
182
182
|
version: '0'
|
183
|
+
segments:
|
184
|
+
- 0
|
185
|
+
hash: -4363834808819370688
|
183
186
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
184
187
|
none: false
|
185
188
|
requirements:
|
186
189
|
- - ! '>='
|
187
190
|
- !ruby/object:Gem::Version
|
188
191
|
version: '0'
|
192
|
+
segments:
|
193
|
+
- 0
|
194
|
+
hash: -4363834808819370688
|
189
195
|
requirements: []
|
190
196
|
rubyforge_project:
|
191
197
|
rubygems_version: 1.8.10
|