bibtex-ruby 2.1.2 → 2.2.0
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.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
|
|