bibtex-ruby 2.0.7 → 2.0.8
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.
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
|