bibtex-ruby 1.3.12 → 2.0.0pre1

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 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)