bibtex-ruby 1.2.1 → 1.3.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 +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
|