bibtex-ruby 1.2.1 → 1.3.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 +6 -1
- data/Gemfile.lock +48 -5
- data/History.txt +16 -1
- data/Manifest +43 -19
- data/README.md +178 -167
- data/Rakefile +26 -5
- data/auto.watchr +6 -0
- data/bibtex-ruby.gemspec +8 -5
- data/examples/bib2html.rb +28 -18
- data/features/bibtex.feature +96 -0
- data/features/entries.feature +67 -0
- data/features/issues/slash_keys.feature +21 -0
- data/features/names.feature +72 -0
- data/features/preambles.feature +27 -0
- data/features/query.feature +56 -0
- data/features/replacement.feature +68 -0
- data/features/step_definitions/bibtex_steps.rb +74 -0
- data/features/step_definitions/name_steps.rb +13 -0
- data/features/strings.feature +52 -0
- data/features/support/env.rb +7 -0
- data/lib/bibtex.rb +5 -1
- data/lib/bibtex/bibliography.rb +218 -95
- data/lib/bibtex/bibtex.y +18 -15
- data/lib/bibtex/elements.rb +130 -136
- data/lib/bibtex/entry.rb +133 -69
- data/lib/bibtex/extensions.rb +0 -35
- data/lib/bibtex/lexer.rb +9 -9
- data/lib/bibtex/name_parser.output +464 -0
- data/lib/bibtex/name_parser.rb +490 -0
- data/lib/bibtex/names.rb +162 -0
- data/lib/bibtex/names.y +196 -0
- data/lib/bibtex/parser.output +5 -5
- data/lib/bibtex/parser.rb +19 -16
- data/lib/bibtex/replaceable.rb +52 -0
- data/lib/bibtex/utilities.rb +23 -5
- data/lib/bibtex/value.rb +201 -0
- data/lib/bibtex/version.rb +1 -1
- data/test/benchmark.rb +52 -0
- data/test/bibtex/test_bibliography.rb +141 -0
- data/test/bibtex/test_elements.rb +40 -0
- data/test/bibtex/test_entry.rb +99 -0
- data/test/bibtex/test_names.rb +23 -0
- data/test/bibtex/test_parser.rb +79 -0
- data/test/bibtex/test_string.rb +83 -0
- data/test/bibtex/test_utilities.rb +34 -0
- data/test/bibtex/test_value.rb +70 -0
- data/test/{bib/10_bibdesk.bib → fixtures/bibdesk.bib} +1 -1
- data/test/{bib/05_comment.bib → fixtures/comment.bib} +0 -0
- data/test/{bib/08_decoret.bib → fixtures/decoret.bib} +0 -0
- data/test/{bib/00_empty.bib → fixtures/empty.bib} +0 -0
- data/test/{bib/07_entry.bib → fixtures/entry.bib} +0 -0
- data/test/{bib/09_errors.bib → fixtures/errors.bib} +0 -0
- data/test/{bib/01_no_bibtex.bib → fixtures/no_bibtex.bib} +0 -0
- data/test/{bib/06_preamble.bib → fixtures/preamble.bib} +1 -1
- data/test/{bib/11_roundtrip.bib → fixtures/roundtrip.bib} +1 -1
- data/test/helper.rb +17 -2
- data/test/test_bibtex.rb +87 -93
- data/test/test_export.rb +18 -22
- metadata +85 -30
- data/test/bib/02_string.bib +0 -1
- data/test/bib/03_string.bib +0 -25
- data/test/bib/04_string_replacement.bib +0 -16
- data/test/test_comment.rb +0 -21
- data/test/test_entry.rb +0 -98
- data/test/test_preamble.rb +0 -39
- data/test/test_string.rb +0 -97
- data/test/test_utilities.rb +0 -36
@@ -0,0 +1,68 @@
|
|
1
|
+
Feature: BibTeX String Replacement
|
2
|
+
As a hacker who works with bibliographies
|
3
|
+
I want to be able to parse BibTeX files with string assignments
|
4
|
+
And replace string symbols in other values
|
5
|
+
Because that is a cool BibTeX feature
|
6
|
+
|
7
|
+
@string @replacement
|
8
|
+
Scenario: A BibTeX file with string assignments and symbols
|
9
|
+
When I parse the following file:
|
10
|
+
"""
|
11
|
+
%%
|
12
|
+
%% A valid BibTeX file
|
13
|
+
%% String assignment and replacement
|
14
|
+
%%
|
15
|
+
|
16
|
+
@string{ foo = "foo" }
|
17
|
+
@string{ bar = "bar" }
|
18
|
+
@string{ foobar = foo # "bar" }
|
19
|
+
@string{ foobarfoo = foobar # foo }
|
20
|
+
@string{ barfoobar = bar # "foo" # bar }
|
21
|
+
|
22
|
+
@preamble { "foo" # foo # foobarfoo # "bar" }
|
23
|
+
|
24
|
+
@manual {manual:1,
|
25
|
+
title = "foo" # barfoobar
|
26
|
+
}
|
27
|
+
"""
|
28
|
+
Then my bibliography should contain these strings:
|
29
|
+
| value |
|
30
|
+
| foo |
|
31
|
+
| bar |
|
32
|
+
| foo # "bar" |
|
33
|
+
| foobar # foo |
|
34
|
+
| bar # "foo" # bar |
|
35
|
+
And my bibliography should contain these preambles:
|
36
|
+
| content |
|
37
|
+
| "foo" # foo # foobarfoo # "bar" |
|
38
|
+
And my bibliography should contain these manuals:
|
39
|
+
| title |
|
40
|
+
| "foo" # barfoobar |
|
41
|
+
When I replace all strings in my bibliography
|
42
|
+
Then my bibliography should contain these strings:
|
43
|
+
| content |
|
44
|
+
| foo = "foo" |
|
45
|
+
| bar = "bar" |
|
46
|
+
| foobar = "foo" # "bar" |
|
47
|
+
| foobarfoo = "foo" # "bar" # "foo" |
|
48
|
+
| barfoobar = "bar" # "foo" # "bar" |
|
49
|
+
And my bibliography should contain these preambles:
|
50
|
+
| content |
|
51
|
+
| "foo" # "foo" # "foo" # "bar" # "foo" # "bar" |
|
52
|
+
And my bibliography should contain these manuals:
|
53
|
+
| title |
|
54
|
+
| "foo" # "bar" # "foo" # "bar" |
|
55
|
+
When I join all strings in my bibliography
|
56
|
+
Then my bibliography should contain these strings:
|
57
|
+
| content |
|
58
|
+
| foo = "foo" |
|
59
|
+
| bar = "bar" |
|
60
|
+
| foobar = "foobar" |
|
61
|
+
| foobarfoo = "foobarfoo" |
|
62
|
+
| barfoobar = "barfoobar" |
|
63
|
+
And my bibliography should contain these preambles:
|
64
|
+
| content |
|
65
|
+
| "foofoofoobarfoobar" |
|
66
|
+
And my bibliography should contain these manuals:
|
67
|
+
| title |
|
68
|
+
| foobarfoobar |
|
@@ -0,0 +1,74 @@
|
|
1
|
+
Given /^the bibliography:$/ do |string|
|
2
|
+
@bibliography = BibTeX.parse(string)
|
3
|
+
end
|
4
|
+
|
5
|
+
When /^I parse the following file:$/ do |string|
|
6
|
+
@bibliography = BibTeX.parse(string)
|
7
|
+
end
|
8
|
+
|
9
|
+
When /^I search for "([^"]*)"$/ do |query|
|
10
|
+
@result = @bibliography.query(query)
|
11
|
+
end
|
12
|
+
|
13
|
+
When /^I search for :(.+)$/ do |query|
|
14
|
+
@result = @bibliography.query(query.to_sym)
|
15
|
+
end
|
16
|
+
|
17
|
+
When /^I search for \/(.+)\/$/ do |query|
|
18
|
+
@result = @bibliography.query(Regexp.new(query))
|
19
|
+
end
|
20
|
+
|
21
|
+
When /^I (replace|join) all strings(?: in my bibliography)$/ do |method|
|
22
|
+
@bibliography.send("#{method}_strings")
|
23
|
+
end
|
24
|
+
|
25
|
+
When /^I replace and join all strings(?: in my bibliography)$/ do
|
26
|
+
@bibliography.replace_strings.join_strings
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
Then /^my bibliography should contain the following objects:$/ do |table|
|
32
|
+
@bibliography.each_with_index do |object, index|
|
33
|
+
table.hashes[index].each_pair do |key, value|
|
34
|
+
assert_equal value, object.send(key).to_s
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
Then /^my bibliography should contain th(?:ese|is) (\w+):$/ do |type, table|
|
40
|
+
@bibliography.q("@#{type.chomp!('s')}").zip(table.hashes).each do |object, expected|
|
41
|
+
expected.each_pair do |key, value|
|
42
|
+
assert_equal value, object.send(key).to_s
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
Then /^my bibliography should contain the following numbers of elements:$/ do |table|
|
48
|
+
counts = table.hashes.first
|
49
|
+
counts[[]] = counts.delete('total') if counts.has_key?('total')
|
50
|
+
counts.each_pair do |type, length|
|
51
|
+
assert_equal length.to_i, @bibliography.find_by_type(type).length
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
Then /^my bibliography should contain an entry with key "([^"]*)"$/ do |key|
|
56
|
+
refute_nil @bibliography[key.to_s]
|
57
|
+
end
|
58
|
+
|
59
|
+
Then /^my bibliography should not contain an entry with key "([^"]*)"$/ do |key|
|
60
|
+
assert_nil @bibliography[key.to_sym]
|
61
|
+
end
|
62
|
+
|
63
|
+
Then /^there should be exactly (\d+) match(?:es)?$/ do |matches|
|
64
|
+
assert_equal matches.to_i, @result.length
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
Then /^my bibliography should contain (\d+) (\w+) published in (\d+)$/ do |count, type, year|
|
69
|
+
assert_equal @bibliography.q("@#{type.chomp!('s')}[year=#{year}]").length, count.to_i
|
70
|
+
end
|
71
|
+
|
72
|
+
Then /^my bibliography should contain an? (\w+) with id "([^"]*)"$/ do |type, id|
|
73
|
+
assert_equal @bibliography[id.to_sym].type, type.to_sym
|
74
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
When /^I parse the name "(.*)"$/ do |string|
|
2
|
+
@name = BibTeX::Name.parse(string)
|
3
|
+
end
|
4
|
+
|
5
|
+
Then /^the parts should be:$/ do |table|
|
6
|
+
table.hashes.each do |row|
|
7
|
+
assert_equal [row['first'], row['von'], row['last'], row['jr']],
|
8
|
+
[@name.first, @name.von, @name.last, @name.jr].map(&:to_s)
|
9
|
+
# row.each do |k,v|
|
10
|
+
# assert_equal v, @name.send(k).to_s
|
11
|
+
# end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
Feature: BibTeX Strings
|
2
|
+
As a hacker who works with bibliographies
|
3
|
+
I want to be able to parse BibTeX files containing string assignments
|
4
|
+
|
5
|
+
Scenario: A BibTeX file with string assignments
|
6
|
+
When I parse the following file:
|
7
|
+
"""
|
8
|
+
%%
|
9
|
+
%% This is a valid BibTeX file
|
10
|
+
%% String assignments Test
|
11
|
+
%%
|
12
|
+
|
13
|
+
@string{foo="bar"}
|
14
|
+
@ string{foo="bar"}
|
15
|
+
@string {foo="bar"}
|
16
|
+
@string{ foo="bar"}
|
17
|
+
@string{foo ="bar"}
|
18
|
+
@string{foo= "bar"}
|
19
|
+
@string{foo="bar" }
|
20
|
+
@ string { foo = "bar" }
|
21
|
+
@string { foo= "bar"}
|
22
|
+
@string{ foo = "bar" }
|
23
|
+
|
24
|
+
@string{foo="bar"}
|
25
|
+
@string{foo="'bar'"}
|
26
|
+
@string{foo="{"}bar{"}"}
|
27
|
+
|
28
|
+
Using some interesting symbols
|
29
|
+
@string{foo="@bar@"}
|
30
|
+
@string{foo="'bar'"}
|
31
|
+
@string{foo="{"}bar{"}"}
|
32
|
+
@string{foo="{bar}"}
|
33
|
+
"""
|
34
|
+
Then my bibliography should contain the following objects:
|
35
|
+
| type | value |
|
36
|
+
| string | bar |
|
37
|
+
| string | bar |
|
38
|
+
| string | bar |
|
39
|
+
| string | bar |
|
40
|
+
| string | bar |
|
41
|
+
| string | bar |
|
42
|
+
| string | bar |
|
43
|
+
| string | bar |
|
44
|
+
| string | bar |
|
45
|
+
| string | bar |
|
46
|
+
| string | bar |
|
47
|
+
| string | 'bar' |
|
48
|
+
| string | {"}bar{"} |
|
49
|
+
| string | @bar@ |
|
50
|
+
| string | 'bar' |
|
51
|
+
| string | {"}bar{"} |
|
52
|
+
| string | {bar} |
|
data/lib/bibtex.rb
CHANGED
@@ -31,7 +31,7 @@ require 'logger'
|
|
31
31
|
# +Entry+.
|
32
32
|
#
|
33
33
|
# Author:: {Sylvester Keil}[http://sylvester.keil.or.at]
|
34
|
-
# Copyright:: Copyright (c) 2010 Sylvester Keil
|
34
|
+
# Copyright:: Copyright (c) 2010-2011 Sylvester Keil
|
35
35
|
# License:: GNU GPL 3.0
|
36
36
|
#
|
37
37
|
module BibTeX
|
@@ -52,6 +52,10 @@ end
|
|
52
52
|
# Debugger.start
|
53
53
|
|
54
54
|
require 'bibtex/extensions'
|
55
|
+
require 'bibtex/value'
|
56
|
+
require 'bibtex/name_parser'
|
57
|
+
require 'bibtex/names'
|
58
|
+
require 'bibtex/replaceable'
|
55
59
|
require 'bibtex/elements'
|
56
60
|
require 'bibtex/entry'
|
57
61
|
require 'bibtex/error'
|
data/lib/bibtex/bibliography.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#--
|
2
2
|
# BibTeX-Ruby
|
3
|
-
# Copyright (C) 2010 Sylvester Keil <sylvester.keil.or.at>
|
3
|
+
# Copyright (C) 2010-2011 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
|
@@ -16,6 +16,9 @@
|
|
16
16
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
17
|
#++
|
18
18
|
|
19
|
+
require 'forwardable'
|
20
|
+
require 'open-uri'
|
21
|
+
|
19
22
|
module BibTeX
|
20
23
|
|
21
24
|
#
|
@@ -23,94 +26,157 @@ module BibTeX
|
|
23
26
|
# typically, it corresponds to a `.bib' file.
|
24
27
|
#
|
25
28
|
class Bibliography
|
29
|
+
extend Forwardable
|
30
|
+
|
31
|
+
include Enumerable
|
32
|
+
include Comparable
|
33
|
+
|
34
|
+
class << self
|
35
|
+
|
36
|
+
# Opens and parses the `.bib' file at the given +path+. Returns
|
37
|
+
# a new Bibliography instance corresponding to the file, or, if a block
|
38
|
+
# is given, yields the instance to the block, ensuring that the file
|
39
|
+
# is saved after the block's execution (use the :out option if you want
|
40
|
+
# to specify a save path other than the path from where the file is
|
41
|
+
# loaded).
|
42
|
+
#
|
43
|
+
# The options argument is passed on to BibTeX::Parser.new.
|
44
|
+
#
|
45
|
+
def open(path, options = {})
|
46
|
+
b = parse(Kernel.open(path).read, options)
|
47
|
+
return b unless block_given?
|
48
|
+
|
49
|
+
begin
|
50
|
+
yield b
|
51
|
+
ensure
|
52
|
+
b.save_to(options[:out] || path)
|
53
|
+
end
|
54
|
+
end
|
26
55
|
|
56
|
+
# Parses the given string and returns a corresponding Bibliography instance.
|
57
|
+
def parse(bibtex, options = {})
|
58
|
+
b = Parser.new(options).parse(bibtex)
|
59
|
+
b.parse_names unless options[:parse_names] == false
|
60
|
+
b
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Defines a new accessor that selects elements by type.
|
65
|
+
#
|
66
|
+
def attr_by_type(*arguments)
|
67
|
+
arguments.each do |type|
|
68
|
+
method_id = "#{type}s"
|
69
|
+
define_method(method_id) { find_by_type(type) } unless respond_to?(method_id)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
27
74
|
attr_accessor :path
|
28
75
|
attr_reader :data, :strings, :entries, :errors
|
29
76
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
# The options argument is passed on to BibTeX::Parser.new.
|
35
|
-
#
|
36
|
-
def self.open(path, options={})
|
37
|
-
Log.debug('Opening file ' + path.to_s)
|
38
|
-
BibTeX::Parser.new(options).parse(File.read(path))
|
39
|
-
end
|
77
|
+
attr_by_type :article, :book, :journal, :collection, :preamble, :comment, :meta_content
|
78
|
+
|
79
|
+
def_delegators :@data, :length, :size, :each, :empty?
|
80
|
+
|
40
81
|
|
41
82
|
#
|
42
|
-
# Creates a new bibliography; empty if no data attribute is specified
|
43
|
-
# by parsing the file at the given path.
|
83
|
+
# Creates a new bibliography; empty if no data attribute is specified.
|
44
84
|
#
|
45
|
-
def initialize(data=[])
|
46
|
-
@path = path
|
85
|
+
def initialize(data = [])
|
47
86
|
@data = []
|
48
87
|
@strings = {}
|
49
88
|
@entries = {}
|
50
|
-
@errors = []
|
51
89
|
add(data)
|
52
90
|
end
|
53
91
|
|
54
92
|
# Adds a new element, or a list of new elements to the bibliography.
|
55
|
-
|
56
|
-
|
57
|
-
|
93
|
+
# Returns the Bibliography for chainability.
|
94
|
+
def add(*arguments)
|
95
|
+
arguments.flatten.each do |element|
|
96
|
+
raise(ArgumentError, "Failed to add #{ element.inspect } to Bibliography; instance of BibTeX::Element expected.") unless element.is_a?(Element)
|
97
|
+
@data << element.added_to_bibliography(self)
|
98
|
+
end
|
58
99
|
self
|
59
100
|
end
|
60
101
|
|
102
|
+
alias :<< :add
|
103
|
+
alias :push :add
|
104
|
+
|
61
105
|
# Saves the bibliography to the current path.
|
62
|
-
def save
|
63
|
-
save_to(@path)
|
106
|
+
def save(options = {})
|
107
|
+
save_to(@path, options)
|
64
108
|
end
|
65
109
|
|
66
|
-
# Saves the bibliography to a file at the given path.
|
67
|
-
def save_to(path)
|
68
|
-
|
69
|
-
|
70
|
-
|
110
|
+
# Saves the bibliography to a file at the given path. Returns the bibliography.
|
111
|
+
def save_to(path, options = {})
|
112
|
+
options[:quotes] ||= %w({ })
|
113
|
+
File.open(path, "w") { |f| f.write(to_s(options)) }
|
114
|
+
self
|
71
115
|
end
|
72
116
|
|
73
|
-
|
74
|
-
|
75
|
-
raise(ArgumentError, 'A BibTeX::Bibliography can contain only BibTeX::Elements; was: ' + obj.class.name) unless obj.is_a?(Element)
|
76
|
-
@data << obj.added_to_bibliography(self)
|
117
|
+
def parse_names
|
118
|
+
q('@entry') { |e| e.parse_names }
|
77
119
|
self
|
78
120
|
end
|
79
121
|
|
80
|
-
#
|
122
|
+
#
|
123
|
+
# Deletes an object, or a list of objects from the bibliography.
|
124
|
+
# If a list of objects is to be deleted, you can either supply the list
|
125
|
+
# of objects or use a query or block to define the list.
|
126
|
+
#
|
127
|
+
# Returns the object (or the list of objects) that were deleted; nil
|
81
128
|
# if the object was not part of the bibliography.
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
@data.each { |obj| obj.removed_from_bibliography(self) }
|
88
|
-
@data = []
|
89
|
-
end
|
90
|
-
|
91
|
-
# Returns all @preamble objects.
|
92
|
-
def preamble
|
93
|
-
find_by_type(BibTeX::Preamble)
|
129
|
+
#
|
130
|
+
def delete(*arguments, &block)
|
131
|
+
objects = q(*arguments, &block).map { |o| o.removed_from_bibliography(self) }
|
132
|
+
@data = @data - objects
|
133
|
+
objects.length == 1 ? objects[0] : objects
|
94
134
|
end
|
95
135
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
#
|
102
|
-
|
103
|
-
|
104
|
-
|
136
|
+
alias :remove :delete
|
137
|
+
alias :rm :delete
|
138
|
+
|
139
|
+
#
|
140
|
+
# Returns an element or a list of elements according to the given index,
|
141
|
+
# range, or query. Contrary to the Bibliography#query this method does
|
142
|
+
# not yield to a block for additional refinement of the query.
|
143
|
+
#
|
144
|
+
# call-seq:
|
145
|
+
# >> bib[-1]
|
146
|
+
# => Returns the last element of the Bibliography or nil
|
147
|
+
# >> bib[1,2]
|
148
|
+
# => Returns the second and third elements or nil
|
149
|
+
# >> bib[1..2]
|
150
|
+
# >> Same as above
|
151
|
+
# >> bib[:key]
|
152
|
+
# => Returns the first entry with key 'key' or nil
|
153
|
+
# >> bib['key']
|
154
|
+
# => Returns all entries with key 'key' or []
|
155
|
+
# >> bib['@article']
|
156
|
+
# => Returns all entries of type 'article' or []
|
157
|
+
# >> bib['@preamble']
|
158
|
+
# => Returns all preamble objects (this is the same as Bibliography#preambles) or []
|
159
|
+
# >> bib[/ruby/]
|
160
|
+
# => Returns all objects that match 'ruby' anywhere or []
|
161
|
+
# >> bib['@book[keywords=ruby]']
|
162
|
+
# => Returns all books whose keywords attribute equals 'ruby' or []
|
163
|
+
#
|
164
|
+
def [](*arguments)
|
165
|
+
raise(ArgumentError, "wrong number of arguments (#{arguments.length} for 1..2)") unless arguments.length.between?(1,2)
|
105
166
|
|
106
|
-
|
107
|
-
|
108
|
-
|
167
|
+
case
|
168
|
+
when !([Range, Numeric] & arguments[0].class.ancestors).empty?
|
169
|
+
@data[*arguments]
|
170
|
+
when arguments.length == 1 && arguments[0].is_a?(Symbol)
|
171
|
+
@entries[arguments[0]]
|
172
|
+
else
|
173
|
+
query(*arguments)
|
174
|
+
end
|
109
175
|
end
|
110
176
|
|
111
177
|
# Returns all objects which could not be parsed successfully.
|
112
178
|
def errors
|
113
|
-
@errors
|
179
|
+
@errors ||= []
|
114
180
|
end
|
115
181
|
|
116
182
|
# Returns true if there are object which could not be parsed.
|
@@ -119,82 +185,139 @@ module BibTeX
|
|
119
185
|
end
|
120
186
|
|
121
187
|
# Returns true if the +Bibliography+ contains no errors and only
|
122
|
-
# valid BibTeX objects (meta
|
188
|
+
# valid BibTeX objects (meta content is ignored).
|
123
189
|
def valid?
|
124
|
-
!errors? &&
|
190
|
+
!errors? && @entries.values.all?(&:valid?)
|
125
191
|
end
|
126
192
|
|
127
|
-
# Replaces all string
|
193
|
+
# Replaces all string symbols which are defined in the bibliography.
|
128
194
|
#
|
129
|
-
# By default
|
130
|
-
# behaviour can be changed using the
|
131
|
-
# the :include option to a list of types.
|
195
|
+
# By default symbols in @string, @preamble and entries are replaced; this
|
196
|
+
# behaviour can be changed using the optional query parameter.
|
132
197
|
#
|
133
198
|
# Note that strings are replaced in the order in which they occur in the
|
134
199
|
# bibliography.
|
135
200
|
#
|
136
201
|
# call-seq:
|
137
|
-
#
|
138
|
-
#
|
202
|
+
# bib.replace #=> replaces all symbols
|
203
|
+
# bib.replace('@string, @preamble')
|
204
|
+
# #=> replaces only symbols in @string and @preamble objects
|
139
205
|
#
|
140
|
-
def
|
141
|
-
|
142
|
-
|
206
|
+
def replace(filter = '')
|
207
|
+
q(filter) { |e| e.replace(@strings.values) }
|
208
|
+
self
|
209
|
+
end
|
210
|
+
|
211
|
+
alias :replace_strings :replace
|
212
|
+
|
213
|
+
def join(filter = '')
|
214
|
+
q(filter, &:join)
|
215
|
+
self
|
143
216
|
end
|
217
|
+
|
218
|
+
alias :join_strings :join
|
144
219
|
|
145
|
-
def
|
146
|
-
|
147
|
-
|
220
|
+
def rename(*arguments, &block)
|
221
|
+
q('@entry') { |e| e.rename(*arguments, &block) }
|
222
|
+
self
|
148
223
|
end
|
149
224
|
|
150
|
-
|
151
|
-
|
152
|
-
|
225
|
+
def sort(*arguments, &block)
|
226
|
+
@data.sort(*arguments, &block)
|
227
|
+
self
|
153
228
|
end
|
154
229
|
|
155
|
-
# Returns
|
156
|
-
def
|
157
|
-
|
230
|
+
# Returns a string representation of the bibliography.
|
231
|
+
def to_s(options = {})
|
232
|
+
map { |o| o.to_s(options) }.join
|
158
233
|
end
|
159
234
|
|
160
|
-
|
161
|
-
|
162
|
-
@data
|
235
|
+
def to_a(options = {})
|
236
|
+
map { |o| o.to_hash(options) }
|
163
237
|
end
|
164
238
|
|
165
|
-
# Returns a
|
166
|
-
def
|
167
|
-
|
239
|
+
# Returns a Ruby hash representation of the bibliography.
|
240
|
+
def to_hash(options = {})
|
241
|
+
{ :bibliography => map { |o| o.to_hash(options) } }
|
242
|
+
end
|
243
|
+
|
244
|
+
# Returns a YAML representation of the bibliography.
|
245
|
+
def to_yaml(options = {})
|
246
|
+
to_a(options).to_yaml
|
168
247
|
end
|
169
248
|
|
170
|
-
# Returns a
|
171
|
-
def
|
172
|
-
|
249
|
+
# Returns a JSON representation of the bibliography.
|
250
|
+
def to_json(options = {})
|
251
|
+
to_a(options).to_json
|
173
252
|
end
|
174
253
|
|
175
|
-
# Returns a JSON representation of the bibliography. Only BibTeX
|
176
|
-
def
|
177
|
-
@
|
254
|
+
# Returns a CiteProc JSON representation of the bibliography. Only BibTeX enrties are exported.
|
255
|
+
def to_citeproc(options = {})
|
256
|
+
q('@entry').map { |o| o.to_citeproc(options) }
|
178
257
|
end
|
179
258
|
|
180
259
|
# Returns an XML representation of the bibliography. Only BibTeX entries are exported.
|
181
260
|
def to_xml
|
261
|
+
require 'rexml/document'
|
262
|
+
|
182
263
|
xml = REXML::Document.new
|
183
264
|
xml << REXML::XMLDecl.new('1.0','UTF-8')
|
184
265
|
root = REXML::Element.new('bibliography')
|
185
|
-
|
266
|
+
each { |e| root.add_element(e.to_xml) }
|
186
267
|
xml << root
|
187
268
|
xml
|
188
269
|
end
|
189
|
-
|
190
|
-
private
|
191
270
|
|
192
|
-
|
193
|
-
|
271
|
+
# Returns objects in the Bibliography which match the given selector and,
|
272
|
+
# optionally, the conditions specified in the given block.
|
273
|
+
#
|
274
|
+
# call-seq:
|
275
|
+
# bib.query() #=> returns all objects
|
276
|
+
# bib.query(:all) #=> returns all objects
|
277
|
+
# bib.query(:first) #=> returns the first object
|
278
|
+
# bib.query('@book') #=> returns all books
|
279
|
+
# bib.query(:first, '@book, @article')
|
280
|
+
# #=> returns the first book or article
|
281
|
+
# bib.query('@book[year=2011], @article)
|
282
|
+
# #=> returns all books published in 2011 and all articles
|
283
|
+
# bib.query('@book, @article) { |o| o.year == '2011' }
|
284
|
+
# #=> returns all books and articles published in 2011
|
285
|
+
# bib.query('@book[year=2011], @article[year=2011])
|
286
|
+
# #=> same as above without using a block
|
287
|
+
#
|
288
|
+
def query(*arguments, &block)
|
289
|
+
raise(ArgumentError, "wrong number of arguments (#{arguments.length} for 0..2)") unless arguments.length.between?(0,2)
|
290
|
+
|
291
|
+
q, selector = arguments.reverse
|
292
|
+
filter = block ? Proc.new { |e| e.match?(q) && block.call(e) } : Proc.new { |e| e.match?(q) }
|
293
|
+
|
294
|
+
send(query_handler(selector), &filter)
|
295
|
+
end
|
296
|
+
|
297
|
+
alias :q :query
|
298
|
+
|
299
|
+
def find_by_type(*types, &block)
|
300
|
+
q(types.flatten.compact.map { |t| "@#{t}" }.join(', '), &block)
|
194
301
|
end
|
302
|
+
|
303
|
+
alias :find_by_types :find_by_type
|
195
304
|
|
196
|
-
def
|
197
|
-
|
305
|
+
def <=>(other)
|
306
|
+
other.respond_to?(:to_a) ? to_a <=> other.to_a : nil
|
198
307
|
end
|
308
|
+
|
309
|
+
private
|
310
|
+
|
311
|
+
def query_handler(selector)
|
312
|
+
case selector
|
313
|
+
when /first|distinct|detect/i
|
314
|
+
:detect
|
315
|
+
when /none|reject|not/i
|
316
|
+
:reject
|
317
|
+
else
|
318
|
+
:select
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
199
322
|
end
|
200
323
|
end
|