bibtex-ruby 2.1.2 → 2.2.0
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.lock +1 -1
- data/History.txt +6 -0
- data/lib/bibtex/bibliography.rb +76 -71
- data/lib/bibtex/elements.rb +12 -1
- data/lib/bibtex/entry.rb +166 -121
- data/lib/bibtex/value.rb +26 -3
- data/lib/bibtex/version.rb +2 -2
- data/test/bibtex/test_bibliography.rb +53 -38
- data/test/bibtex/test_entry.rb +90 -57
- data/test/bibtex/test_names.rb +14 -0
- data/test/bibtex/test_value.rb +22 -12
- metadata +4 -4
data/Gemfile.lock
CHANGED
data/History.txt
CHANGED
data/lib/bibtex/bibliography.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
#--
|
2
2
|
# BibTeX-Ruby
|
3
3
|
# Copyright (C) 2010-2012 Sylvester Keil <sylvester.keil.or.at>
|
4
|
-
#
|
4
|
+
#
|
5
5
|
# This program is free software: you can redistribute it and/or modify
|
6
6
|
# it under the terms of the GNU General Public License as published by
|
7
7
|
# the Free Software Foundation, either version 3 of the License, or
|
8
8
|
# (at your option) any later version.
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# This program is distributed in the hope that it will be useful,
|
11
11
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
12
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
13
|
# GNU General Public License for more details.
|
14
|
-
#
|
14
|
+
#
|
15
15
|
# You should have received a copy of the GNU General Public License
|
16
16
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
17
|
#++
|
@@ -24,16 +24,16 @@ module BibTeX
|
|
24
24
|
#
|
25
25
|
class Bibliography
|
26
26
|
extend Forwardable
|
27
|
-
|
27
|
+
|
28
28
|
include Enumerable
|
29
29
|
include Comparable
|
30
|
-
|
30
|
+
|
31
31
|
@defaults = { :parse_names => true, :parse_months => true }.freeze
|
32
|
-
|
33
|
-
class << self
|
32
|
+
|
33
|
+
class << self
|
34
34
|
|
35
35
|
attr_reader :defaults
|
36
|
-
|
36
|
+
|
37
37
|
# Opens and parses the `.bib' file at the given +path+. Returns
|
38
38
|
# a new Bibliography instance corresponding to the file, or, if a block
|
39
39
|
# is given, yields the instance to the block, ensuring that the file
|
@@ -71,7 +71,7 @@ module BibTeX
|
|
71
71
|
raise ArgumentError, "failed to parse #{input.inspect}"
|
72
72
|
end
|
73
73
|
end
|
74
|
-
|
74
|
+
|
75
75
|
# Defines a new accessor that selects elements by type.
|
76
76
|
def attr_by_type(*arguments)
|
77
77
|
arguments.each do |type|
|
@@ -80,17 +80,17 @@ module BibTeX
|
|
80
80
|
end
|
81
81
|
end
|
82
82
|
end
|
83
|
-
|
83
|
+
|
84
84
|
attr_accessor :path
|
85
85
|
|
86
86
|
attr_reader :data, :strings, :entries, :errors, :options
|
87
87
|
|
88
88
|
attr_by_type :article, :book, :journal, :collection, :preamble,
|
89
89
|
:comment, :meta_content
|
90
|
-
|
90
|
+
|
91
91
|
def_delegators :@data, :length, :size, :empty?
|
92
92
|
def_delegators :@entries, :key?, :has_key?, :values_at
|
93
|
-
|
93
|
+
|
94
94
|
alias entries_at values_at
|
95
95
|
|
96
96
|
# Creates a new bibliography.
|
@@ -101,7 +101,7 @@ module BibTeX
|
|
101
101
|
|
102
102
|
yield self if block_given?
|
103
103
|
end
|
104
|
-
|
104
|
+
|
105
105
|
def initialize_copy(other)
|
106
106
|
@options = other.options.dup
|
107
107
|
@errors = other.errors.dup
|
@@ -109,7 +109,7 @@ module BibTeX
|
|
109
109
|
@entries = Hash.new { |h,k| h.fetch(k.to_s, nil) }
|
110
110
|
add(other.data)
|
111
111
|
end
|
112
|
-
|
112
|
+
|
113
113
|
# Adds a new element, or a list of new elements to the bibliography.
|
114
114
|
# Returns the Bibliography for chainability.
|
115
115
|
def add(*arguments)
|
@@ -118,16 +118,16 @@ module BibTeX
|
|
118
118
|
end
|
119
119
|
self
|
120
120
|
end
|
121
|
-
|
121
|
+
|
122
122
|
alias << add
|
123
123
|
alias push add
|
124
|
-
|
124
|
+
|
125
125
|
|
126
126
|
# Saves the bibliography to the current path.
|
127
127
|
def save(options = {})
|
128
128
|
save_to(@path, options)
|
129
129
|
end
|
130
|
-
|
130
|
+
|
131
131
|
# Saves the bibliography to a file at the given path. Returns the bibliography.
|
132
132
|
def save_to(path, options = {})
|
133
133
|
options[:quotes] ||= %w({ })
|
@@ -135,7 +135,7 @@ module BibTeX
|
|
135
135
|
File.open(path, 'w:UTF-8') do |f|
|
136
136
|
f.write(to_s(options))
|
137
137
|
end
|
138
|
-
|
138
|
+
|
139
139
|
self
|
140
140
|
end
|
141
141
|
|
@@ -148,7 +148,7 @@ module BibTeX
|
|
148
148
|
to_enum
|
149
149
|
end
|
150
150
|
end
|
151
|
-
|
151
|
+
|
152
152
|
|
153
153
|
def parse_names
|
154
154
|
entries.each_value { |e| e.parse_names }
|
@@ -160,7 +160,7 @@ module BibTeX
|
|
160
160
|
self
|
161
161
|
end
|
162
162
|
|
163
|
-
|
163
|
+
|
164
164
|
# Converts all enties using the given filter. If an optional block is given
|
165
165
|
# the block is used as a condition (the block will be called with each
|
166
166
|
# entry). @see Entry#convert!
|
@@ -168,10 +168,10 @@ module BibTeX
|
|
168
168
|
entries.each_value do |entry|
|
169
169
|
entry.convert!(filter) if !block_given? || yield(entry)
|
170
170
|
end
|
171
|
-
|
171
|
+
|
172
172
|
self
|
173
173
|
end
|
174
|
-
|
174
|
+
|
175
175
|
|
176
176
|
# Deletes an object, or a list of objects from the bibliography.
|
177
177
|
# If a list of objects is to be deleted, you can either supply the list
|
@@ -184,7 +184,7 @@ module BibTeX
|
|
184
184
|
@data = @data - objects
|
185
185
|
objects.length == 1 ? objects[0] : objects
|
186
186
|
end
|
187
|
-
|
187
|
+
|
188
188
|
alias remove delete
|
189
189
|
alias rm delete
|
190
190
|
|
@@ -218,7 +218,7 @@ module BibTeX
|
|
218
218
|
|
219
219
|
case
|
220
220
|
when arguments[0].is_a?(Numeric) || arguments[0].is_a?(Range)
|
221
|
-
data[*arguments]
|
221
|
+
data[*arguments]
|
222
222
|
when arguments.length == 1
|
223
223
|
case
|
224
224
|
when arguments[0].nil?
|
@@ -248,12 +248,12 @@ module BibTeX
|
|
248
248
|
def valid?
|
249
249
|
!errors? && entries.values.all?(&:valid?)
|
250
250
|
end
|
251
|
-
|
251
|
+
|
252
252
|
# Returns a list of the names of all authors, editors and translators in the Bibliography.
|
253
253
|
def names
|
254
254
|
map(&:names).flatten
|
255
255
|
end
|
256
|
-
|
256
|
+
|
257
257
|
# Replaces all string symbols which are defined in the bibliography.
|
258
258
|
#
|
259
259
|
# By default symbols in @string, @preamble and entries are replaced; this
|
@@ -271,21 +271,21 @@ module BibTeX
|
|
271
271
|
q(filter) { |e| e.replace(@strings.values) }
|
272
272
|
self
|
273
273
|
end
|
274
|
-
|
274
|
+
|
275
275
|
alias :replace_strings :replace
|
276
276
|
|
277
277
|
def join(filter = '')
|
278
278
|
q(filter, &:join)
|
279
279
|
self
|
280
280
|
end
|
281
|
-
|
281
|
+
|
282
282
|
alias join_strings join
|
283
283
|
|
284
284
|
def rename(*arguments, &block)
|
285
285
|
q('@entry') { |e| e.rename(*arguments, &block) }
|
286
286
|
self
|
287
287
|
end
|
288
|
-
|
288
|
+
|
289
289
|
# call-seq:
|
290
290
|
# b.extend_initials(['Edgar Allen', 'Poe'], ['Nathaniel', 'Hawthorne'])
|
291
291
|
# #=> Extends the initials in names like 'E.A. Poe' or 'Hawethorne, N.'
|
@@ -298,10 +298,10 @@ module BibTeX
|
|
298
298
|
name.extend_initials(with_first, for_last)
|
299
299
|
end
|
300
300
|
end
|
301
|
-
|
301
|
+
|
302
302
|
self
|
303
303
|
end
|
304
|
-
|
304
|
+
|
305
305
|
# This method combines all names in the bibliography that look identical
|
306
306
|
# when using initials as first names and then tries to extend the first
|
307
307
|
# names for all names in each group to the longest available form.
|
@@ -314,24 +314,24 @@ module BibTeX
|
|
314
314
|
groups = Hash.new do |h,k|
|
315
315
|
h[k] = { :prototype => nil, :names => [] }
|
316
316
|
end
|
317
|
-
|
317
|
+
|
318
318
|
# group names together
|
319
319
|
names.each do |name|
|
320
320
|
group = groups[name.sort_order(:initials => true).gsub(/\s+/, '').downcase]
|
321
321
|
group[:names] << name
|
322
|
-
|
322
|
+
|
323
323
|
if group[:prototype].nil? || group[:prototype].first.to_s.length < name.first.to_s.length
|
324
324
|
group[:prototype] = name
|
325
325
|
end
|
326
326
|
end
|
327
|
-
|
327
|
+
|
328
328
|
# extend all names in group to prototype
|
329
329
|
groups.each_value do |group|
|
330
330
|
group[:names].each do |name|
|
331
331
|
name.set(group[:prototype])
|
332
332
|
end
|
333
333
|
end
|
334
|
-
|
334
|
+
|
335
335
|
self
|
336
336
|
end
|
337
337
|
|
@@ -350,21 +350,31 @@ module BibTeX
|
|
350
350
|
else
|
351
351
|
Proc.new { |e| e[field] = value }
|
352
352
|
end
|
353
|
-
|
353
|
+
|
354
354
|
each_entry do |entry|
|
355
355
|
if entry.field?(field) && entry[field].to_s =~ pattern
|
356
356
|
block.call(entry)
|
357
357
|
end
|
358
358
|
end
|
359
|
-
|
359
|
+
|
360
360
|
self
|
361
361
|
end
|
362
|
-
|
362
|
+
|
363
|
+
def group_by(*arguments, &block)
|
364
|
+
groups = Hash.new { |h,k| h[k] = [] }
|
365
|
+
|
366
|
+
entries.values.each do |e|
|
367
|
+
groups[e.digest(arguments, &block)] << e
|
368
|
+
end
|
369
|
+
|
370
|
+
groups
|
371
|
+
end
|
372
|
+
|
363
373
|
def sort(*arguments, &block)
|
364
374
|
data.sort(*arguments, &block)
|
365
375
|
self
|
366
376
|
end
|
367
|
-
|
377
|
+
|
368
378
|
# Returns a string representation of the bibliography.
|
369
379
|
def to_s(options = {})
|
370
380
|
map { |o| o.to_s(options) }.join
|
@@ -373,42 +383,42 @@ module BibTeX
|
|
373
383
|
def inspect
|
374
384
|
"#<#{self.class} data=[#{length}]>"
|
375
385
|
end
|
376
|
-
|
386
|
+
|
377
387
|
def to_a(options = {})
|
378
388
|
map { |o| o.to_hash(options) }
|
379
389
|
end
|
380
|
-
|
390
|
+
|
381
391
|
# Returns a Ruby hash representation of the bibliography.
|
382
392
|
def to_hash(options = {})
|
383
393
|
{ :bibliography => map { |o| o.to_hash(options) } }
|
384
394
|
end
|
385
|
-
|
395
|
+
|
386
396
|
# Returns a YAML representation of the bibliography.
|
387
397
|
def to_yaml(options = {})
|
388
398
|
to_a(options).to_yaml
|
389
399
|
end
|
390
|
-
|
400
|
+
|
391
401
|
# Returns a JSON representation of the bibliography.
|
392
402
|
def to_json(options = {})
|
393
403
|
MultiJson.dump(to_a(options))
|
394
404
|
end
|
395
|
-
|
405
|
+
|
396
406
|
# Returns a CiteProc JSON representation of the bibliography. Only BibTeX enrties are exported.
|
397
407
|
def to_citeproc(options = {})
|
398
408
|
q('@entry').map { |o| o.to_citeproc(options) }
|
399
409
|
end
|
400
|
-
|
410
|
+
|
401
411
|
# Returns a REXML::Document representation of the bibliography using the
|
402
412
|
# BibTeXML format.
|
403
413
|
def to_xml(options = {})
|
404
414
|
require 'rexml/document'
|
405
|
-
|
415
|
+
|
406
416
|
xml = REXML::Document.new
|
407
417
|
xml << REXML::XMLDecl.new('1.0','UTF-8')
|
408
418
|
|
409
419
|
root = REXML::Element.new('bibtex:file')
|
410
420
|
root.add_namespace('bibtex', 'http://bibtexml.sf.net/')
|
411
|
-
|
421
|
+
|
412
422
|
each { |e| root.add_element(e.to_xml(options)) if e.is_a?(Entry) }
|
413
423
|
|
414
424
|
xml.add_element(root)
|
@@ -419,18 +429,18 @@ module BibTeX
|
|
419
429
|
# can be serialized using any of the RDF serializer plugins.
|
420
430
|
def to_rdf(options = {})
|
421
431
|
require 'rdf'
|
422
|
-
|
432
|
+
|
423
433
|
graph = RDF::Graph.new
|
424
434
|
|
425
435
|
q('@entry').each do |entry|
|
426
436
|
graph << entry.to_rdf(options)
|
427
437
|
end
|
428
|
-
|
438
|
+
|
429
439
|
graph
|
430
440
|
rescue LoadError
|
431
441
|
BibTeX.log.error "Please gem install rdf for RDF support."
|
432
442
|
end
|
433
|
-
|
443
|
+
|
434
444
|
# call-seq:
|
435
445
|
# bib.query() #=> returns all elements
|
436
446
|
# bib.query('@book') #=> returns all books
|
@@ -470,15 +480,15 @@ module BibTeX
|
|
470
480
|
else
|
471
481
|
raise ArgumentError, "wrong number of arguments (#{arguments.length} for 0..2)"
|
472
482
|
end
|
473
|
-
|
483
|
+
|
474
484
|
filter = block ? Proc.new { |e| e.match?(q) && block.call(e) } :
|
475
485
|
Proc.new { |e| e.match?(q) }
|
476
486
|
|
477
487
|
send(query_handler(selector), &filter)
|
478
488
|
end
|
479
|
-
|
489
|
+
|
480
490
|
alias q query
|
481
|
-
|
491
|
+
|
482
492
|
def each_entry
|
483
493
|
if block_given?
|
484
494
|
q('@entry').each(&Proc.new)
|
@@ -486,39 +496,34 @@ module BibTeX
|
|
486
496
|
q('@entry').to_enum
|
487
497
|
end
|
488
498
|
end
|
489
|
-
|
499
|
+
|
490
500
|
def find_by_type(*types, &block)
|
491
501
|
q(types.flatten.compact.map { |t| "@#{t}" }.join(', '), &block)
|
492
502
|
end
|
493
|
-
|
503
|
+
|
494
504
|
alias find_by_types find_by_type
|
495
505
|
|
496
506
|
def <=>(other)
|
497
507
|
other.respond_to?(:to_a) ? to_a <=> other.to_a : nil
|
498
508
|
end
|
499
|
-
|
500
|
-
# TODO this should be faster than select_duplicates_by
|
501
|
-
# def detect_duplicates_by(*arguments)
|
502
|
-
# end
|
503
509
|
|
504
510
|
def select_duplicates_by(*arguments)
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
d.values.dup
|
511
|
+
arguments = [:year, :title] if arguments.empty?
|
512
|
+
|
513
|
+
group_by(*arguments) { |digest|
|
514
|
+
digest.gsub(/\s+/, '').downcase
|
515
|
+
}.values.select { |d| d.length > 1 }
|
511
516
|
end
|
512
|
-
|
517
|
+
|
513
518
|
alias duplicates select_duplicates_by
|
514
|
-
|
519
|
+
|
515
520
|
def duplicates?
|
516
521
|
!select_duplicates_by?.empty?
|
517
522
|
end
|
518
|
-
|
519
|
-
|
523
|
+
|
524
|
+
|
520
525
|
private
|
521
|
-
|
526
|
+
|
522
527
|
def query_handler(selector)
|
523
528
|
case selector.to_s
|
524
529
|
when /first|distinct|detect/i
|
@@ -529,6 +534,6 @@ module BibTeX
|
|
529
534
|
:select
|
530
535
|
end
|
531
536
|
end
|
532
|
-
|
537
|
+
|
533
538
|
end
|
534
539
|
end
|
data/lib/bibtex/elements.rb
CHANGED
@@ -47,8 +47,19 @@ module BibTeX
|
|
47
47
|
end
|
48
48
|
|
49
49
|
# Returns a string containing the object's content.
|
50
|
-
def content(options = {})
|
50
|
+
def content(options = {})
|
51
|
+
''
|
52
|
+
end
|
51
53
|
|
54
|
+
# Returns a string containing the object's content.
|
55
|
+
def values_at(*arguments)
|
56
|
+
[]
|
57
|
+
end
|
58
|
+
|
59
|
+
def digest(*arguments)
|
60
|
+
[type, content].join('|')
|
61
|
+
end
|
62
|
+
|
52
63
|
# Invokes BibTeX string replacement on this element.
|
53
64
|
def replace(*arguments); self; end
|
54
65
|
|