bibtex-ruby 1.3.12 → 2.0.0pre1

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 CHANGED
@@ -7,6 +7,13 @@ group :debug do
7
7
  gem 'rbx-trepanning', :platforms => [:rbx]
8
8
  end
9
9
 
10
+ group :test do
11
+ gem 'minitest', :platforms => [:ruby_18, :jruby, :rbx]
12
+ gem 'mynyml-redgreen', ['~>0.7']
13
+ gem 'autowatchr', ['~>0.1']
14
+ gem 'cucumber', ['~>1.0']
15
+ end
16
+
10
17
  group :profile do
11
18
  gem 'ruby-prof', ['~>0.10']
12
19
  gem 'gnuplot', ['~>2.3']
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bibtex-ruby (1.3.12)
4
+ bibtex-ruby (2.0.0pre1)
5
5
  latex-decode (>= 0.0.3)
6
+ multi_json (~> 1.0)
6
7
 
7
8
  GEM
8
9
  remote: http://rubygems.org/
@@ -12,29 +13,28 @@ GEM
12
13
  watchr
13
14
  builder (3.0.0)
14
15
  columnize (0.3.4)
15
- cucumber (0.10.7)
16
+ cucumber (1.0.2)
16
17
  builder (>= 2.1.2)
17
18
  diff-lcs (>= 1.1.2)
18
- gherkin (~> 2.4.0)
19
+ gherkin (~> 2.4.5)
19
20
  json (>= 1.4.6)
20
21
  term-ansicolor (>= 1.0.5)
21
22
  diff-lcs (1.1.2)
22
- gherkin (2.4.5)
23
+ gherkin (2.4.18)
23
24
  json (>= 1.4.6)
24
- gherkin (2.4.5-java)
25
+ gherkin (2.4.18-java)
25
26
  json (>= 1.4.6)
26
27
  gnuplot (2.3.6)
27
- json (1.5.3)
28
- json (1.5.3-java)
28
+ json (1.5.4)
29
+ json (1.5.4-java)
29
30
  latex-decode (0.0.3)
30
31
  unicode (>= 0.4)
31
32
  linecache (0.46)
32
33
  rbx-require-relative (> 0.0.4)
33
34
  linecache19 (0.5.12)
34
35
  ruby_core_source (>= 0.1.4)
35
- mini_shoulda (0.3.0)
36
- minitest (~> 2.1.0)
37
- minitest (2.1.0)
36
+ minitest (2.3.1)
37
+ multi_json (1.0.3)
38
38
  mynyml-redgreen (0.7.1)
39
39
  term-ansicolor (>= 1.0.4)
40
40
  racc (1.4.6)
@@ -75,10 +75,9 @@ PLATFORMS
75
75
  DEPENDENCIES
76
76
  autowatchr (~> 0.1)
77
77
  bibtex-ruby!
78
- cucumber (~> 0.10)
78
+ cucumber (~> 1.0)
79
79
  gnuplot (~> 2.3)
80
- json (~> 1.5)
81
- mini_shoulda (~> 0.3)
80
+ minitest
82
81
  mynyml-redgreen (~> 0.7)
83
82
  racc (~> 1.4)
84
83
  rake (~> 0.9)
data/Manifest CHANGED
@@ -17,6 +17,7 @@ features/bibtex.feature
17
17
  features/entries.feature
18
18
  features/issues
19
19
  features/issues/braced_strings.feature
20
+ features/issues/crossref.feature
20
21
  features/issues/latex_filter.feature
21
22
  features/issues/number_keys.feature
22
23
  features/issues/parse_months.feature
@@ -75,7 +76,6 @@ lib/bibtex/version.rb
75
76
  lib/bibtex/version.rbc
76
77
  lib/bibtex.rb
77
78
  lib/bibtex.rbc
78
- profile.png
79
79
  test
80
80
  test/benchmark.rb
81
81
  test/bibtex
data/README.md CHANGED
@@ -1,14 +1,17 @@
1
1
  BibTeX-Ruby
2
2
  ===========
3
3
 
4
- BibTeX-Ruby is a fairly complete library and parser for BibTeX bibliography
5
- files; it offers a rich interface to manage, search, or convert BibTeX objects in
6
- Ruby. It is designed to support all BibTeX objects (including @comment,
7
- string-replacements via @string, as well as string concatenation using '#')
8
- and optionally handles all content outside of BibTeX objects as 'meta content'
9
- which may or may not be included in post-processing. BibTeX-Ruby also includes
10
- a name parser to support comfortable access to the individual tokens of name
11
- values.
4
+ BibTeX-Ruby is the Rubyist's swiss-army-knife for all things BibTeX. It
5
+ includes a parser for all common BibTeX objects (@string, @preamble,
6
+ @comment and regular entries) and a sophisticated name parser that
7
+ tokenizes correctly formatted names; BibTeX-Ruby recognizes BibTeX string
8
+ replacements, joins values containing multiple strings or variables,
9
+ supports cross-references, and decodes common LaTeX formatting
10
+ instructions to unicode; if you are in a hurry, it also allows for easy
11
+ export/conversion to formats such as YAML, JSON, CiteProc, and XML (BibTeXML).
12
+
13
+ For a list of projects using BibTeX-Ruby, see
14
+ [the project wiki](https://github.com/inukshuk/bibtex-ruby/wiki/Projects-Using-BibTeX-Ruby).
12
15
 
13
16
 
14
17
  Quickstart
@@ -19,9 +22,9 @@ Quickstart
19
22
  >> require 'bibtex'
20
23
  => true
21
24
  >> b = BibTeX.open('./ruby.bib')
22
- >> b[:pickaxe]
25
+ >> b['pickaxe']
23
26
  => "2009"
24
- >> b[:pickaxe].title
27
+ >> b['pickaxe'].title
25
28
  => "Programming Ruby 1.9: The Pragmatic Programmer's Guide"
26
29
  >> b[:pickaxe].author.length
27
30
  => 3
@@ -39,7 +42,8 @@ Quickstart
39
42
  BibTeX-Ruby helps you convert your bibliography to JSON, XML, or YAML;
40
43
  alternatively, you can export to the JSON format used by
41
44
  [CSL](http://citationstyles.org) processors and render the bibliography in
42
- many styles:
45
+ one of [many different styles](https://github.com/citation-style-language/styles)
46
+ ([previews](http://www.zotero.org/styles/)):
43
47
 
44
48
  >> require 'citeproc' # requires the citeproc-ruby gem
45
49
  => true
@@ -80,10 +84,9 @@ Requirements
80
84
 
81
85
  * The parser generator [racc](http://i.loveruby.net/en/projects/racc/) is
82
86
  required to generate the BibTeX parser and the name parser.
83
- * The **json** gem is required on older Ruby versions for JSON export.
84
87
 
85
- The bibtex-ruby gem has been tested on Ruby versions 1.8.7 and 1.9.2; it has
86
- been confirmed to work with REE 1.8.7 x86_64 and JRuby 1.5.6 x86_64-java;
88
+ The BibTeX-Ruby gem has been tested on Ruby versions 1.8.7 and 1.9.2; it has
89
+ been confirmed to work with REE, JRuby, and Rubinius;
87
90
  however, there have been some [issues](https://github.com/inukshuk/bibtex-ruby/issues)
88
91
  with MacRuby implementations.
89
92
 
@@ -139,6 +142,27 @@ Instead of parsing strings you can also create BibTeX elements directly in Ruby:
139
142
  > book.key = :mybook
140
143
  > bib << book
141
144
 
145
+ ### Cross References
146
+
147
+ From version 2.0, BibTeX-Ruby correctly resolves entry cross-references, which are commonly used for entries with type `inbook`, `incollection`, and `inproceedings`. When an entry has a valid citation key in field `crossref`, BibTeX-Ruby will return any fields inherited from the parent entry:
148
+
149
+ > b = BibTeX.parse <<-END
150
+ @inbook{fraassen_1989b,
151
+ Crossref = {fraassen_1989},
152
+ Pages = {40-64},
153
+ Title = {Ideal Science: David Lewis's Account of Laws},
154
+ }
155
+
156
+ @book{fraassen_1989,
157
+ Address = {Oxford},
158
+ Author = {Bas C. van Fraassen},
159
+ Publisher = {Oxford University Press},
160
+ Title = {Laws and Symmetry},
161
+ Year = 1989
162
+ }
163
+ END
164
+ > b['fraassen_1989b'].booktitle
165
+ => <"Laws and Symmetry">
142
166
 
143
167
  ### Queries
144
168
 
@@ -274,21 +298,22 @@ Conditional conversions are also supported:
274
298
  >> faust1 = '@book{faust1, title = {Faust: Der Trag\"odie Erster Teil}}'
275
299
  >> faust2 = '@book{faust2, title = {Faust: Der Trag\"odie Zweiter Teil}}'
276
300
  >> p BibTeX.parse(faust1 + faust2).convert(:latex) { |e| e.key == :faust2 }.to_s
277
- @book{faust1,
278
- title = {Faust: Der Trag\"odie Erster Teil}
279
- }
280
- @book{faust2,
281
- title = {Faust: Der Tragödie Zweiter Teil}
282
- }
301
+ @book{faust1,
302
+ title = {Faust: Der Trag\"odie Erster Teil}
303
+ }
304
+ @book{faust2,
305
+ title = {Faust: Der Tragödie Zweiter Teil}
306
+ }
283
307
 
284
308
  If you need to express a condition on the basis of individual fields, use the
285
309
  conversion methods of BibTeX::Entry with a block instead (the block will be
286
310
  passed the key and value of each field prior to conversion).
287
311
 
288
- ### Conversions
312
+ ### Exports
289
313
 
290
314
  Furthermore, BibTeX-Ruby allows you to export your bibliography for processing
291
315
  by other tools. Currently supported formats include YAML, JSON, and XML.
316
+
292
317
  Of course, you can also export your bibliography back to BibTeX; if you include
293
318
  `:meta_content', your export should be identical to the original '.bib' file,
294
319
  except for whitespace, blank lines and letter case (BibTeX-Ruby will downcase
@@ -300,6 +325,52 @@ BibTeX to YAML converter:
300
325
 
301
326
  >> BibTeX.open('example.bib').to_yaml
302
327
 
328
+ Starting with version 2.0, BibTeX-Ruby's `#to_xml` exports your bibliography
329
+ to the [BibTeXML](http//bibtexml.sf.net/) format. By passing the option
330
+ `:extended => true` you can make use of the BibTeXML's extended format which
331
+ will return individual person elements and name tokens (provided you have
332
+ successfully parsed the names of your bibliography).
333
+
334
+ > BibTeX.parse(<<-END).to_xml(:extended => true).write($stdout, 2)
335
+ " @book{pickaxe,
336
+ " Address = {Raleigh, North Carolina},
337
+ " Author = {Thomas, Dave, and Fowler, Chad, and Hunt, Andy},
338
+ " Publisher = {The Pragmatic Bookshelf},
339
+ " Title = {Programming Ruby 1.9: The Pragmatic Programmer's Guide},
340
+ " Year = {2009}
341
+ " }
342
+ " END
343
+
344
+ This example parse a BibTeX entry, formats it as extended BibTeXML,
345
+ and writes the following XML to standard out:
346
+
347
+ <?xml version='1.0' encoding='UTF-8'?>
348
+ <bibtex:file xmlns:bibtex='http://bibtexml.sf.net/'>
349
+ <bibtex:entry id='pickaxe'>
350
+ <bibtex:book>
351
+ <bibtex:address>Raleigh, North Carolina</bibtex:address>
352
+ <bibtex:person>
353
+ <bibtex:first>Dave</bibtex:first>
354
+ <bibtex:last>Thomas</bibtex:last>
355
+ </bibtex:person>
356
+ <bibtex:person>
357
+ <bibtex:first>Chad</bibtex:first>
358
+ <bibtex:last>Fowler</bibtex:last>
359
+ </bibtex:person>
360
+ <bibtex:person>
361
+ <bibtex:first>Andy</bibtex:first>
362
+ <bibtex:last>Hunt</bibtex:last>
363
+ </bibtex:person>
364
+ <bibtex:author/>
365
+ <bibtex:publisher>The Pragmatic Bookshelf</bibtex:publisher>
366
+ <bibtex:title>
367
+ Programming Ruby 1.9: The Pragmatic Programmer&apos;s Guide
368
+ </bibtex:title>
369
+ <bibtex:year>2009</bibtex:year>
370
+ </bibtex:book>
371
+ </bibtex:entry>
372
+ </bibtex:file>
373
+
303
374
  Look at the 'examples' directory for more elaborate examples of a BibTeX to YAML
304
375
  and a BibTeX to HTML converter using **#to_citeproc** to format a bibliography
305
376
  using [CSL](http://citationstyles.org/).
@@ -345,20 +416,41 @@ quotes; therefore you can simply add the :quotes option with an empty string:
345
416
  :title=>"Programming Ruby 1.9: The Pragmatic Programmer's Guide",
346
417
  :year=>"2009"}]
347
418
 
419
+
348
420
  The Parser
349
421
  ----------
350
422
 
351
423
  The BibTeX-Ruby parser is generated using the awesome
352
424
  [racc](http://i.loveruby.net/en/projects/racc/) parser generator. You can take
353
- look at the grammar definition in the file
425
+ look at the LALR grammar in the file
354
426
  [lib/bibtex/bibtex.y](https://github.com/inukshuk/bibtex-ruby/blob/master/lib/bibtex/bibtex.y).
355
427
 
356
428
  For more information about the BibTeX format and the parser's idiosyncrasies
357
429
  [refer to the project wiki](https://github.com/inukshuk/bibtex-ruby/wiki/The-BibTeX-Format).
358
430
 
431
+ Contributing
432
+ ------------
433
+
434
+ The BibTeX-Ruby source code is
435
+ [hosted on GitHub](http://github.com/inukshuk/bibtex-ruby/).
436
+ You can check out a copy of the latest code using Git:
437
+
438
+ $ git clone https://github.com/inukshuk/bibtex-ruby.git
439
+
440
+ If you've found a bug or have a question, please open an issue on the
441
+ [BibTeX-Ruby issue tracker](http://github.com/inukshuk/bibtex-ruby/issues).
442
+ Or, for extra credit, clone the BibTeX-Ruby repository, write a failing
443
+ example, or cucumber feature, fix the bug and submit a pull request (for
444
+ useful examples, take a look at the cucumber features in the
445
+ [features/issues/](https://github.com/inukshuk/bibtex-ruby/blob/master/features/issues)
446
+ directory).
447
+
359
448
 
360
449
  Credits
361
450
  -------
362
451
 
363
452
  The BibTeX-Ruby package was written by [Sylvester Keil](http://sylvester.keil.or.at/);
364
453
  kudos and thanks to all [contributors](https://github.com/inukshuk/bibtex-ruby/contributors)!
454
+
455
+ BibTeX-Ruby is distributed under the terms and conditions of the GPL. See
456
+ LICENSE for details.
@@ -11,19 +11,25 @@ Gem::Specification.new do |s|
11
11
  s.authors = ['Sylvester Keil']
12
12
  s.email = ['http://sylvester.keil.or.at']
13
13
  s.homepage = 'http://inukshuk.github.com/bibtex-ruby'
14
- s.summary = 'A BibTeX parser and converter written in Ruby.'
15
- s.description = 'A (fairly complete) BibTeX library and parser written in Ruby. Includes a name parser and supports regular BibTeX entries, @comments, string replacement via @string. Allows for easy export/conversion to formats such as YAML, JSON, and XML.'
16
14
  s.license = 'GPL-3'
17
15
 
16
+ s.summary = 'A BibTeX parser, converter and API for Ruby.'
17
+ s.description = <<-END_DESCRIPTION
18
+ BibTeX-Ruby is the Rubyist's swiss-army-knife for all things BibTeX. It
19
+ includes a parser for all common BibTeX objects (@string, @preamble,
20
+ @comment and regular entries) and a sophisticated name parser that
21
+ tokenizes correctly formatted names; BibTeX-Ruby recognizes BibTeX string
22
+ replacements, joins values containing multiple strings or variables,
23
+ supports cross-references, and decodes common LaTeX formatting
24
+ instructions to unicode; if you are in a hurry, it also allows for easy
25
+ export/conversion to formats such as YAML, JSON, CSL, and XML (BibTeXML).
26
+ END_DESCRIPTION
27
+
18
28
  s.add_runtime_dependency('latex-decode', ['>=0.0.3'])
29
+ s.add_runtime_dependency('multi_json', ['~>1.0'])
19
30
 
20
31
  s.add_development_dependency('rake', ['~>0.9'])
21
32
  s.add_development_dependency('racc', ['~>1.4'])
22
- s.add_development_dependency('mini_shoulda', ['~>0.3'])
23
- s.add_development_dependency('mynyml-redgreen', ['~>0.7'])
24
- s.add_development_dependency('autowatchr', ['~>0.1'])
25
- s.add_development_dependency('cucumber', ['~>0.10'])
26
- s.add_development_dependency('json', ['~>1.5'])
27
33
  s.add_development_dependency('rdoc', ['~>3.9'])
28
34
 
29
35
  s.files = File.open('Manifest').readlines.map(&:chomp)
@@ -0,0 +1,62 @@
1
+ Feature: Parse BibTeX cross-references
2
+ As a hacker who works with bibliographies
3
+ I want to be able to parse BibTeX files with cross-referenced entries
4
+
5
+ @wip
6
+ Scenario: A BibTeX file with six entries and three cross-references
7
+ When I parse the following file:
8
+ """
9
+ @book{zimmerman_2004,
10
+ Address = {Oxford},
11
+ Author = {Dean W. Zimmerman},
12
+ Editor = {Dean W. Zimmerman},
13
+ Keywords = {Metaphysics},
14
+ Publisher = {Oxford University Press},
15
+ Title = {Oxford Studies in Metaphysics},
16
+ Volume = {1},
17
+ Year = {2004}
18
+ }
19
+
20
+ @incollection{hall_2004b,
21
+ Author = {Ned Hall},
22
+ Crossref = {zimmerman_2004},
23
+ Keywords = {Causation},
24
+ Pages = {255-299},
25
+ Title = {The Intrinsic Character of Causation}
26
+ }
27
+
28
+ @proceedings{weingartner_1989,
29
+ Address = {Vienna},
30
+ Editor = {Paul Weingartner and Gerhard Schurz},
31
+ Keywords = {Anthology},
32
+ Publisher = {Verlag H{\"o}lder-Pichter Tempsky},
33
+ Title = {Philosophy of the Natural Sciences: Proceedings of the 13th International Wittgenstein Symposium},
34
+ Year = {1989}}
35
+
36
+ @inproceedings{cartwright_1989a,
37
+ Author = {Nancy Cartwright},
38
+ Crossref = {weingartner_1989},
39
+ Keywords = {Causation; Quantum Mechanics},
40
+ Pages = {120-127},
41
+ Title = {Quantum Causes: The Lesson of the Bell Inequalities}}
42
+
43
+ @inbook{fraassen_1989b,
44
+ Crossref = {fraassen_1989},
45
+ Pages = {40-64},
46
+ Title = {Ideal Science: David Lewis's Account of Laws},
47
+ Url = {http://dx.doi.org/10.1093/0198248601.003.0003}
48
+ }
49
+
50
+ @book{fraassen_1989,
51
+ Address = {Oxford},
52
+ Author = {Bas C. van Fraassen},
53
+ Keywords = {Laws; Lewis},
54
+ Publisher = {Oxford University Press},
55
+ Title = {Laws and Symmetry},
56
+ Url = {http://dx.doi.org/10.1093/0198248601.001.0001},
57
+ Year = 1989
58
+ }
59
+ """
60
+ Then the entry with key "fraassen_1989b" should have a field "Booktitle" with the value "Laws and Symmetry"
61
+ And the entry with key "hall_2004b" should have a field "Editor" with the value "Zimmerman, Dean W."
62
+ And the entry with key "cartwright_1989a" should have a field "Year" with the value "1989"
@@ -46,7 +46,7 @@ Feature: BibTeX Names
46
46
  | CC dd BB, AA | AA | CC dd | BB | |
47
47
  | BB, AA | AA | | BB | |
48
48
 
49
- @sort @wip
49
+ @sort
50
50
  Scenarios: Long von parts
51
51
  | name | first | von | last | jr |
52
52
  | bb cc dd CC, AA | AA | bb cc dd | CC | |
@@ -61,7 +61,7 @@ end
61
61
 
62
62
  Then /^my bibliography should contain an entry with (?:key|id) "([^"]*)" and a?n? (\w+) value of "([^"]*)"$/ do |key,field,value|
63
63
  refute_nil @bibliography[key.to_s]
64
- assert_equal value, @bibliography[key.to_s][field].to_s
64
+ assert_equal value, @bibliography[key.to_s][field.downcase.to_sym].to_s
65
65
  end
66
66
 
67
67
 
@@ -91,5 +91,5 @@ Then /^the string "([^"]*)" should be "([^"]*)"$/ do |key, value|
91
91
  end
92
92
 
93
93
  Then /^the entry with key "([^"]*)" should have a field "([^"]*)" with the value "([^"]*)"$/ do |key, field, value|
94
- assert_equal value, @bibliography[key.to_sym][field.to_sym].to_s
94
+ assert_equal value, @bibliography[key][field.downcase.to_sym].to_s
95
95
  end
@@ -19,11 +19,13 @@
19
19
  $:.unshift(File.dirname(__FILE__)) unless
20
20
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
21
21
 
22
- require 'rubygems'
22
+ require 'digest/md5'
23
23
 
24
24
  require 'bibtex/version'
25
25
  require 'logger'
26
26
 
27
+ require 'multi_json'
28
+
27
29
  # = BibTeX
28
30
  #
29
31
  # This module encompasses a parser for BibTeX files and
@@ -31,10 +31,12 @@ module BibTeX
31
31
  include Enumerable
32
32
  include Comparable
33
33
 
34
- DEFAULTS = { :parse_names => true, :parse_months => true }.freeze
34
+ @defaults = { :parse_names => true, :parse_months => true }.freeze
35
35
 
36
36
  class << self
37
37
 
38
+ attr_reader :defaults
39
+
38
40
  # Opens and parses the `.bib' file at the given +path+. Returns
39
41
  # a new Bibliography instance corresponding to the file, or, if a block
40
42
  # is given, yields the instance to the block, ensuring that the file
@@ -50,7 +52,8 @@ module BibTeX
50
52
  # -:filter: convert all entries using the sepcified filter (not set by default)
51
53
  #
52
54
  def open(path, options = {})
53
- b = parse(Kernel.open(path).read, options)
55
+ b = parse(Kernel.open(path, 'r:UTF-8').read, options)
56
+ b.path = path
54
57
  return b unless block_given?
55
58
 
56
59
  begin
@@ -61,13 +64,16 @@ module BibTeX
61
64
  end
62
65
 
63
66
  # Parses the given string and returns a corresponding Bibliography instance.
64
- def parse(bibtex, options = {})
65
- Parser.new(options).parse(bibtex) || Bibliography.new(options)
67
+ def parse(input, options = {})
68
+ case input
69
+ when Array, Hash, Element
70
+ Bibliography.new(options).add(input)
71
+ else
72
+ Parser.new(options).parse(input) || Bibliography.new(options)
73
+ end
66
74
  end
67
75
 
68
- #
69
76
  # Defines a new accessor that selects elements by type.
70
- #
71
77
  def attr_by_type(*arguments)
72
78
  arguments.each do |type|
73
79
  method_id = "#{type}s"
@@ -77,19 +83,21 @@ module BibTeX
77
83
  end
78
84
 
79
85
  attr_accessor :path
86
+
80
87
  attr_reader :data, :strings, :entries, :errors, :options
81
88
 
82
- attr_by_type :article, :book, :journal, :collection, :preamble, :comment, :meta_content
89
+ attr_by_type :article, :book, :journal, :collection, :preamble, :comment,
90
+ :meta_content
83
91
 
84
- def_delegators :@data, :length, :size, :each, :empty?
85
-
92
+ def_delegators :@data, :length, :size, :each, :empty?, :last
93
+ def_delegators :@entries, :has_key?
86
94
 
87
- #
95
+
88
96
  # Creates a new bibliography.
89
- #
90
97
  def initialize(options = {})
91
- @options = DEFAULTS.merge(options)
92
- @data, @strings, @entries = [], {}, {}
98
+ @options = Bibliography.defaults.merge(options)
99
+ @data, @strings = [], {}
100
+ @entries = Hash.new { |h,k| h.fetch(k.to_s, nil) }
93
101
 
94
102
  yield self if block_given?
95
103
  end
@@ -97,9 +105,8 @@ module BibTeX
97
105
  # Adds a new element, or a list of new elements to the bibliography.
98
106
  # Returns the Bibliography for chainability.
99
107
  def add(*arguments)
100
- arguments.flatten.each do |element|
101
- raise(ArgumentError, "Failed to add #{ element.inspect } to Bibliography; instance of BibTeX::Element expected.") unless element.is_a?(Element)
102
- @data << element.added_to_bibliography(self)
108
+ Element.parse(arguments.flatten).each do |element|
109
+ data << element.added_to_bibliography(self)
103
110
  end
104
111
  self
105
112
  end
@@ -107,6 +114,7 @@ module BibTeX
107
114
  alias << add
108
115
  alias push add
109
116
 
117
+
110
118
  # Saves the bibliography to the current path.
111
119
  def save(options = {})
112
120
  save_to(@path, options)
@@ -115,19 +123,21 @@ module BibTeX
115
123
  # Saves the bibliography to a file at the given path. Returns the bibliography.
116
124
  def save_to(path, options = {})
117
125
  options[:quotes] ||= %w({ })
118
- File.open(path, "w") { |f| f.write(to_s(options)) }
126
+ File.open(path, 'w:UTF-8') { |f| f.write(to_s(options)) }
119
127
  self
120
128
  end
121
129
 
130
+
122
131
  def parse_names
123
132
  @entries.each_value { |e| e.parse_names }
124
133
  self
125
134
  end
126
-
135
+
127
136
  def parse_months
128
137
  @entries.each_value { |e| e.parse_month }
129
138
  self
130
139
  end
140
+
131
141
 
132
142
  # Converts all enties using the given filter. If an optional block is given
133
143
  # the block is used as a condition (the block will be called with each
@@ -137,7 +147,7 @@ module BibTeX
137
147
  self
138
148
  end
139
149
 
140
- #
150
+
141
151
  # Deletes an object, or a list of objects from the bibliography.
142
152
  # If a list of objects is to be deleted, you can either supply the list
143
153
  # of objects or use a query or block to define the list.
@@ -169,7 +179,7 @@ module BibTeX
169
179
  # >> bib[:key]
170
180
  # => Returns the first entry with key 'key' or nil
171
181
  # >> bib['key']
172
- # => Returns all entries with key 'key' or []
182
+ # => Same as above
173
183
  # >> bib['@article']
174
184
  # => Returns all entries of type 'article' or []
175
185
  # >> bib['@preamble']
@@ -183,10 +193,21 @@ module BibTeX
183
193
  raise(ArgumentError, "wrong number of arguments (#{arguments.length} for 1..2)") unless arguments.length.between?(1,2)
184
194
 
185
195
  case
186
- when !([Range, Numeric] & arguments[0].class.ancestors).empty?
187
- @data[*arguments]
188
- when arguments.length == 1 && arguments[0].is_a?(Symbol)
189
- @entries[arguments[0]]
196
+ when arguments[0].is_a?(Numeric) || arguments[0].is_a?(Range)
197
+ data[*arguments]
198
+ when arguments.length == 1
199
+ case
200
+ when arguments[0].nil?
201
+ nil
202
+ when arguments[0].respond_to?(:empty?) && arguments[0].empty?
203
+ nil
204
+ when arguments[0].is_a?(Symbol)
205
+ entries[arguments[0]]
206
+ when arguments[0].respond_to?(:start_with?) && !arguments[0].start_with?('@')
207
+ entries[arguments[0]]
208
+ else
209
+ query(*arguments)
210
+ end
190
211
  else
191
212
  query(*arguments)
192
213
  end
@@ -208,6 +229,11 @@ module BibTeX
208
229
  !errors? && @entries.values.all?(&:valid?)
209
230
  end
210
231
 
232
+ # Returns a list of the names of all authors, editors and translators in the Bibliography.
233
+ def names
234
+ map(&:names).flatten
235
+ end
236
+
211
237
  # Replaces all string symbols which are defined in the bibliography.
212
238
  #
213
239
  # By default symbols in @string, @preamble and entries are replaced; this
@@ -266,7 +292,7 @@ module BibTeX
266
292
 
267
293
  # Returns a JSON representation of the bibliography.
268
294
  def to_json(options = {})
269
- to_a(options).to_json
295
+ MultiJson.encode(to_a(options))
270
296
  end
271
297
 
272
298
  # Returns a CiteProc JSON representation of the bibliography. Only BibTeX enrties are exported.
@@ -274,15 +300,20 @@ module BibTeX
274
300
  q('@entry').map { |o| o.to_citeproc(options) }
275
301
  end
276
302
 
277
- # Returns an XML representation of the bibliography. Only BibTeX entries are exported.
278
- def to_xml
303
+ # Returns a REXML::Document representation of the bibliography using the
304
+ # BibTeXML format.
305
+ def to_xml(options = {})
279
306
  require 'rexml/document'
280
307
 
281
- xml = REXML::Document.new
308
+ xml = REXML::Document.new
282
309
  xml << REXML::XMLDecl.new('1.0','UTF-8')
283
- root = REXML::Element.new('bibliography')
284
- each { |e| root.add_element(e.to_xml) }
285
- xml << root
310
+
311
+ root = REXML::Element.new('bibtex:file')
312
+ root.add_namespace('bibtex', 'http://bibtexml.sf.net/')
313
+
314
+ each { |e| root.add_element(e.to_xml(options)) }
315
+
316
+ xml.add_element(root)
286
317
  xml
287
318
  end
288
319
 
@@ -307,7 +338,8 @@ module BibTeX
307
338
  raise(ArgumentError, "wrong number of arguments (#{arguments.length} for 0..2)") unless arguments.length.between?(0,2)
308
339
 
309
340
  q, selector = arguments.reverse
310
- filter = block ? Proc.new { |e| e.match?(q) && block.call(e) } : Proc.new { |e| e.match?(q) }
341
+ filter = block ? Proc.new { |e| e.match?(q) && block.call(e) } :
342
+ Proc.new { |e| e.match?(q) }
311
343
 
312
344
  send(query_handler(selector), &filter)
313
345
  end
@@ -324,6 +356,26 @@ module BibTeX
324
356
  other.respond_to?(:to_a) ? to_a <=> other.to_a : nil
325
357
  end
326
358
 
359
+ # TODO this should be faster than select_duplicates_by
360
+ # def detect_duplicates_by(*arguments)
361
+ # end
362
+
363
+ def select_duplicates_by(*arguments)
364
+ d, fs = Hash.new([]), arguments.flatten.map(&:to_sym)
365
+ q('@entry') do |e|
366
+ d[e.generate_hash(fs)] << e
367
+ end
368
+
369
+ d.values.dup
370
+ end
371
+
372
+ alias duplicates select_duplicates_by
373
+
374
+ def duplicates?
375
+ !select_duplicates_by?.empty?
376
+ end
377
+
378
+
327
379
  private
328
380
 
329
381
  def query_handler(selector)