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 +7 -0
- data/Gemfile.lock +12 -13
- data/Manifest +1 -1
- data/README.md +114 -22
- data/bibtex-ruby.gemspec +13 -7
- data/features/issues/crossref.feature +62 -0
- data/features/names.feature +1 -1
- data/features/step_definitions/bibtex_steps.rb +2 -2
- data/lib/bibtex.rb +3 -1
- data/lib/bibtex/bibliography.rb +84 -32
- data/lib/bibtex/elements.rb +49 -30
- data/lib/bibtex/entry.rb +243 -72
- data/lib/bibtex/error.rb +11 -2
- data/lib/bibtex/lexer.rb +8 -8
- data/lib/bibtex/names.rb +29 -4
- data/lib/bibtex/value.rb +9 -9
- data/lib/bibtex/version.rb +1 -1
- data/lib/bibtex/version.rbc +2 -2
- data/test/bibtex/test_bibliography.rb +59 -31
- data/test/bibtex/test_elements.rb +31 -7
- data/test/bibtex/test_entry.rb +220 -41
- data/test/bibtex/test_filters.rb +6 -6
- data/test/bibtex/test_lexer.rb +1 -1
- data/test/bibtex/test_name_parser.rb +4 -4
- data/test/bibtex/test_names.rb +5 -5
- data/test/bibtex/test_parser.rb +23 -23
- data/test/bibtex/test_string.rb +6 -6
- data/test/bibtex/test_value.rb +32 -32
- data/test/helper.rb +0 -1
- data/test/test_bibtex.rb +5 -6
- data/test/test_export.rb +2 -3
- metadata +32 -72
- data/profile.png +0 -0
data/lib/bibtex/error.rb
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
module BibTeX
|
2
2
|
|
3
|
-
|
3
|
+
class BibTeXError < StandardError
|
4
|
+
attr_reader :orginal
|
5
|
+
|
6
|
+
def initialize(message = nil, original = $!)
|
7
|
+
super(message)
|
8
|
+
@original = original
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class ParseError < BibTeXError; end
|
4
13
|
|
5
14
|
#
|
6
15
|
# Represents a lexical or syntactical error.
|
@@ -12,7 +21,7 @@ module BibTeX
|
|
12
21
|
def initialize(trace=[])
|
13
22
|
@trace = trace
|
14
23
|
end
|
15
|
-
|
24
|
+
|
16
25
|
def trace=(trace)
|
17
26
|
raise(ArgumentError, "BibTeX::Error trace must be of type Array; was: #{trace.class.name}.") unless trace.kind_of?(Array)
|
18
27
|
@trace = trace
|
data/lib/bibtex/lexer.rb
CHANGED
@@ -95,10 +95,10 @@ module BibTeX
|
|
95
95
|
def push(value)
|
96
96
|
case value[0]
|
97
97
|
when :CONTENT, :STRING_LITERAL
|
98
|
-
|
99
|
-
|
98
|
+
if !@stack.empty? && value[0] == @stack[-1][0]
|
99
|
+
@stack[-1][1] << value[1]
|
100
100
|
else
|
101
|
-
|
101
|
+
@stack.push(value)
|
102
102
|
end
|
103
103
|
when :ERROR
|
104
104
|
@stack.push(value) if @include_errors
|
@@ -119,10 +119,10 @@ module BibTeX
|
|
119
119
|
|
120
120
|
self.data = string || @scanner.string
|
121
121
|
|
122
|
-
|
123
|
-
|
122
|
+
until @scanner.eos?
|
123
|
+
send("parse_#{MODE[@mode]}")
|
124
124
|
end
|
125
|
-
|
125
|
+
|
126
126
|
push([false, '$end'])
|
127
127
|
end
|
128
128
|
|
@@ -130,7 +130,7 @@ module BibTeX
|
|
130
130
|
|
131
131
|
def parse_bibtex
|
132
132
|
case
|
133
|
-
when @scanner.scan(/[\
|
133
|
+
when @scanner.scan(/[\s]+/o)
|
134
134
|
when @scanner.scan(/\{/o)
|
135
135
|
@brace_level += 1
|
136
136
|
push([:LBRACE,'{'])
|
@@ -146,7 +146,7 @@ module BibTeX
|
|
146
146
|
push([:COMMA,','])
|
147
147
|
when @scanner.scan(/\d+/o)
|
148
148
|
push([:NUMBER,@scanner.matched])
|
149
|
-
when @scanner.scan(/[
|
149
|
+
when @scanner.scan(/[[:alpha:]\d \/:_!$\.%&*-]+/io)
|
150
150
|
push([:NAME,@scanner.matched.rstrip])
|
151
151
|
when @scanner.scan(/"/o)
|
152
152
|
@mode = :literal
|
data/lib/bibtex/names.rb
CHANGED
@@ -26,7 +26,10 @@ module BibTeX
|
|
26
26
|
def_delegators :@tokens, :each, :sort
|
27
27
|
|
28
28
|
def self.parse(string)
|
29
|
-
|
29
|
+
new(NameParser.new.parse(string))
|
30
|
+
rescue => e
|
31
|
+
BibTeX.log.info(e.message)
|
32
|
+
nil
|
30
33
|
end
|
31
34
|
|
32
35
|
def initialize(*arguments)
|
@@ -49,7 +52,7 @@ module BibTeX
|
|
49
52
|
q = [options[:quotes]].flatten
|
50
53
|
[q[0], value, q[-1]].compact.join
|
51
54
|
end
|
52
|
-
|
55
|
+
|
53
56
|
def name?; true; end
|
54
57
|
def numeric?; false; end
|
55
58
|
def atomic?; true; end
|
@@ -91,8 +94,15 @@ module BibTeX
|
|
91
94
|
class Name < Struct.new(:first, :last, :prefix, :suffix)
|
92
95
|
extend Forwardable
|
93
96
|
include Comparable
|
94
|
-
|
95
|
-
|
97
|
+
|
98
|
+
BibTeXML = {
|
99
|
+
:first => :first,
|
100
|
+
:last => :last,
|
101
|
+
:prefix => :prelast,
|
102
|
+
:suffix => :lineage
|
103
|
+
}.freeze
|
104
|
+
|
105
|
+
def_delegators :to_s, :=~, :===, *(String.instance_methods(false).reject { |m| m =~ /^\W|each|!$/ })
|
96
106
|
|
97
107
|
class << self
|
98
108
|
def parse(string)
|
@@ -140,6 +150,21 @@ module BibTeX
|
|
140
150
|
Hash[each_pair.to_a]
|
141
151
|
end
|
142
152
|
|
153
|
+
def to_xml
|
154
|
+
require 'rexml/document'
|
155
|
+
xml = REXML::Element.new('bibtex:person')
|
156
|
+
|
157
|
+
each_pair do |part, text|
|
158
|
+
unless text.nil?
|
159
|
+
element = REXML::Element.new("bibtex:#{BibTeXML[part]}")
|
160
|
+
element.text = text
|
161
|
+
xml.add_element(element)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
xml
|
166
|
+
end
|
167
|
+
|
143
168
|
[:strip!, :upcase!, :downcase!, :sub!, :gsub!, :chop!, :chomp!, :rstrip!].each do |method_id|
|
144
169
|
define_method(method_id) do |*arguments, &block|
|
145
170
|
each do |part|
|
data/lib/bibtex/value.rb
CHANGED
@@ -52,7 +52,7 @@ module BibTeX
|
|
52
52
|
attr_reader :tokens
|
53
53
|
alias to_a tokens
|
54
54
|
|
55
|
-
def_delegators :to_s,
|
55
|
+
def_delegators :to_s, :=~, :===, *String.instance_methods(false).reject { |m| m =~ /^\W|^length$|!$/ }
|
56
56
|
def_delegators :@tokens, :[], :length
|
57
57
|
|
58
58
|
def initialize(*arguments)
|
@@ -236,19 +236,19 @@ module BibTeX
|
|
236
236
|
self
|
237
237
|
end
|
238
238
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
239
|
+
def method_missing (name, *args)
|
240
|
+
case
|
241
|
+
when name.to_s =~ /^(?:convert|from)_([a-z]+)(!)?$/
|
242
|
+
$2 ? convert!($1) : convert($1)
|
243
|
+
else
|
244
|
+
super
|
245
|
+
end
|
245
246
|
end
|
246
247
|
|
247
248
|
def respond_to? (method)
|
248
249
|
method =~ /^(?:convert|from)_([a-z]+)(!)?$/ || super
|
249
250
|
end
|
250
|
-
|
251
|
-
|
251
|
+
|
252
252
|
def <=> (other)
|
253
253
|
to_s <=> other.to_s
|
254
254
|
end
|
data/lib/bibtex/version.rb
CHANGED
data/lib/bibtex/version.rbc
CHANGED
@@ -1,20 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
1
3
|
require 'helper'
|
2
4
|
|
3
5
|
module BibTeX
|
4
6
|
|
5
7
|
class BibliographyTest < MiniTest::Spec
|
6
8
|
|
7
|
-
|
8
|
-
should
|
9
|
+
describe 'when newly created' do
|
10
|
+
it 'should not be nil' do
|
9
11
|
assert Bibliography.new
|
10
12
|
end
|
11
|
-
should
|
13
|
+
it 'should be empty' do
|
12
14
|
assert Bibliography.new.empty?
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
16
|
-
|
17
|
-
should
|
18
|
+
describe '.open' do
|
19
|
+
it 'should accept a block and save the file after execution' do
|
18
20
|
tmp = Tempfile.new('bibtex')
|
19
21
|
tmp.close
|
20
22
|
b = BibTeX.open(Test.fixtures(:bibdesk)).save_to(tmp.path)
|
@@ -25,10 +27,17 @@ module BibTeX
|
|
25
27
|
|
26
28
|
assert_equal b.length - 1, BibTeX.open(tmp.path).length
|
27
29
|
end
|
30
|
+
|
28
31
|
end
|
32
|
+
|
33
|
+
describe '.parse' do
|
34
|
+
it 'should accept filters' do
|
35
|
+
Bibliography.parse("@misc{k, title = {\\''u}}", :filter => 'latex')[0].title.must_be :==, 'ü'
|
36
|
+
end
|
37
|
+
end
|
29
38
|
|
30
|
-
|
31
|
-
|
39
|
+
describe 'given a populated biliography' do
|
40
|
+
before do
|
32
41
|
@bib = BibTeX.parse <<-END
|
33
42
|
@book{rails,
|
34
43
|
address = {Raleigh, North Carolina},
|
@@ -57,41 +66,41 @@ module BibTeX
|
|
57
66
|
END
|
58
67
|
end
|
59
68
|
|
60
|
-
should
|
69
|
+
it 'should support access by index' do
|
61
70
|
assert_equal 'ruby', @bib[1].keywords
|
62
71
|
end
|
63
72
|
|
64
|
-
should
|
73
|
+
it 'should support access by range' do
|
65
74
|
assert_equal %w{2008 2007}, @bib[1..2].map(&:year)
|
66
75
|
end
|
67
76
|
|
68
|
-
should
|
77
|
+
it 'should support access by index and offset' do
|
69
78
|
assert_equal %w{2008 2007}, @bib[1,2].map(&:year)
|
70
79
|
end
|
71
80
|
|
72
|
-
should
|
81
|
+
it 'should support queries by symbol key' do
|
73
82
|
refute_nil @bib[:rails]
|
74
83
|
assert_nil @bib[:ruby]
|
75
84
|
end
|
76
85
|
|
77
|
-
should
|
86
|
+
it 'should support queries by symbol key and selector' do
|
78
87
|
assert_equal 1, @bib.q(:all, :rails).length
|
79
88
|
refute_nil @bib.q(:first, :rails)
|
80
89
|
assert_nil @bib.q(:first, :railss)
|
81
90
|
end
|
82
91
|
|
83
|
-
should
|
84
|
-
|
85
|
-
|
92
|
+
it 'should support queries by string key' do
|
93
|
+
refute_nil @bib['rails']
|
94
|
+
assert_nil @bib['ruby']
|
86
95
|
end
|
87
96
|
|
88
|
-
should
|
97
|
+
it 'should support queries by type string' do
|
89
98
|
assert_equal 2, @bib['@book'].length
|
90
99
|
assert_equal 1, @bib['@article'].length
|
91
100
|
assert_equal 0, @bib['@collection'].length
|
92
101
|
end
|
93
102
|
|
94
|
-
should
|
103
|
+
it 'should support queries by type string and selector' do
|
95
104
|
assert_equal 2, @bib.q(:all, '@book').length
|
96
105
|
refute_nil @bib.q(:first, '@book')
|
97
106
|
assert_equal 1, @bib.q(:all, '@article').length
|
@@ -101,16 +110,16 @@ module BibTeX
|
|
101
110
|
end
|
102
111
|
|
103
112
|
|
104
|
-
should
|
113
|
+
it 'should support queries by pattern' do
|
105
114
|
assert_equal 0, @bib[/reilly/].length
|
106
115
|
assert_equal 2, @bib[/reilly/i].length
|
107
116
|
end
|
108
117
|
|
109
|
-
should
|
118
|
+
it 'should support queries by type string and conditions' do
|
110
119
|
assert_equal 1, @bib['@book[keywords=ruby]'].length
|
111
120
|
end
|
112
121
|
|
113
|
-
should
|
122
|
+
it 'should support queries by bibtex element' do
|
114
123
|
entry = Entry.parse(<<-END).first
|
115
124
|
@article{segaran2007,
|
116
125
|
title = {{Programming collective intelligence}},
|
@@ -124,40 +133,59 @@ module BibTeX
|
|
124
133
|
assert_equal 0, @bib[entry].length
|
125
134
|
end
|
126
135
|
|
127
|
-
should
|
136
|
+
it 'should support query and additional block' do
|
128
137
|
assert_equal 1, @bib.q('@book') { |e| e.keywords.split(/,/).length > 1 }.length
|
129
138
|
end
|
130
139
|
|
131
|
-
should
|
140
|
+
it 'should support saving the bibliography to a file' do
|
132
141
|
tmp = Tempfile.new('bibtex')
|
133
142
|
tmp.close
|
134
143
|
@bib.save_to(tmp.path)
|
135
|
-
assert_equal @bib.
|
144
|
+
assert_equal @bib.length, BibTeX.open(tmp.path).length
|
136
145
|
end
|
137
146
|
|
138
|
-
|
139
|
-
|
147
|
+
describe 'given a filter' do
|
148
|
+
before do
|
140
149
|
@filter = Object
|
141
150
|
def @filter.apply (value); value.is_a?(::String) ? value.upcase : value; end
|
142
151
|
end
|
143
152
|
|
144
|
-
should
|
153
|
+
it 'should support arbitrary conversions' do
|
145
154
|
@bib.convert(@filter)
|
146
155
|
assert_equal 'RUBY, RAILS', @bib[:rails].keywords
|
147
156
|
end
|
148
157
|
|
149
|
-
should
|
150
|
-
@bib.convert(@filter) { |e| e.key !=
|
158
|
+
it 'should support conditional arbitrary conversions' do
|
159
|
+
@bib.convert(@filter) { |e| e.key != 'rails' }
|
151
160
|
assert_equal 'ruby, rails', @bib[:rails].keywords
|
152
161
|
assert_equal 'RUBY', @bib[:flanagan2008].keywords
|
153
162
|
end
|
154
163
|
|
155
164
|
end
|
156
165
|
|
166
|
+
describe 'BibTeXML export' do
|
167
|
+
before { @bibtexml = Tempfile.new('bibtexml') }
|
168
|
+
after { @bibtexml.unlink }
|
169
|
+
|
170
|
+
it 'should support exporting to BibTeXML' do
|
171
|
+
@bib.to_xml.write(@bibtexml, 2)
|
172
|
+
@bibtexml.rewind
|
173
|
+
xml = REXML::Document.new(@bibtexml)
|
174
|
+
xml.root.namespace.must_be :==, 'http://bibtexml.sf.net/'
|
175
|
+
xml.root.get_elements('//bibtex:entry').wont_be_empty
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'should support exporting to extended BibTeXML' do
|
179
|
+
@bib.to_xml(:extended => true).write(@bibtexml, 2)
|
180
|
+
@bibtexml.rewind
|
181
|
+
xml = REXML::Document.new(@bibtexml)
|
182
|
+
xml.root.namespace.must_be :==, 'http://bibtexml.sf.net/'
|
183
|
+
xml.root.get_elements('//bibtex:person').wont_be_empty
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
157
187
|
end
|
158
|
-
|
159
|
-
|
160
|
-
|
188
|
+
|
161
189
|
|
162
190
|
end
|
163
191
|
end
|
@@ -2,31 +2,55 @@ require 'helper'
|
|
2
2
|
|
3
3
|
module BibTeX
|
4
4
|
|
5
|
+
class ElementTest < MiniTest::Spec
|
6
|
+
|
7
|
+
describe '.parse' do
|
8
|
+
|
9
|
+
it 'accepts a BibTeX string' do
|
10
|
+
Element.parse('@misc{x,},@misc{y,}').length.must_be :==, 2
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'accepts an Element' do
|
14
|
+
Element.parse(Comment.new('blah')).length.must_be :==, 1
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'accepts a Hash and returns an Entry' do
|
18
|
+
Element.parse({ :type => :book })[0].type.must_be :==, :book
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'accepts an array of hashes' do
|
22
|
+
Element.parse([{ :type => :book }, { :type => :misc }])[1].type.must_be :==, :misc
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
5
29
|
class PreambleTest < MiniTest::Spec
|
6
30
|
|
7
|
-
|
8
|
-
|
31
|
+
describe 'a new preamble instance' do
|
32
|
+
before do
|
9
33
|
@preamble = Preamble.new
|
10
34
|
end
|
11
35
|
|
12
|
-
should
|
36
|
+
it 'should not be nil' do
|
13
37
|
assert @preamble
|
14
38
|
end
|
15
39
|
end
|
16
40
|
|
17
|
-
|
18
|
-
|
41
|
+
describe 'given a set of @preambles' do
|
42
|
+
before do
|
19
43
|
@bib = BibTeX.open(Test.fixtures(:preamble))
|
20
44
|
@preambles = @bib.preambles
|
21
45
|
end
|
22
46
|
|
23
|
-
should
|
47
|
+
it 'should support round-trips of all parsed preambles' do
|
24
48
|
assert_equal %q[@preamble{ "This bibliography was created \today" }], @preambles[0].to_s
|
25
49
|
assert_equal %q[@preamble{ "Bib\TeX" }], @preambles[1].to_s
|
26
50
|
assert_equal %q[@preamble{ "Maintained by " # maintainer }], @preambles[2].to_s
|
27
51
|
end
|
28
52
|
|
29
|
-
should
|
53
|
+
it 'should support string replacement of preamble contents' do
|
30
54
|
assert_equal %q["Maintained by " # maintainer], @preambles[2].value.to_s
|
31
55
|
@bib.replace_strings
|
32
56
|
assert_equal %q["Maintained by " # "Myself"], @preambles[2].value.to_s
|
data/test/bibtex/test_entry.rb
CHANGED
@@ -3,37 +3,171 @@ require 'helper.rb'
|
|
3
3
|
module BibTeX
|
4
4
|
class EntryTest < MiniTest::Spec
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
describe 'a new entry' do
|
7
|
+
it "won't be nil" do
|
8
|
+
Entry.new.wont_be_nil
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
describe 'cross-references' do
|
13
|
+
it 'has no cross-reference by default' do
|
14
|
+
assert_equal false, Entry.new.has_cross_reference?
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'is not cross-referenced by default' do
|
18
|
+
assert_equal false, Entry.new.cross_referenced?
|
19
|
+
Entry.new.cross_referenced_by.must_be_empty
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'given a bibliography with cross referenced entries' do
|
23
|
+
before do
|
24
|
+
@bib = Bibliography.parse <<-END
|
25
|
+
@book{a, editor = "A", title = "A"}
|
26
|
+
@incollection{a1, crossref = "a"}
|
27
|
+
@incollection{b1, crossref = "b"}
|
28
|
+
END
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#has_cross_reference?' do
|
32
|
+
it 'returns true if the entry has a valid cross-reference' do
|
33
|
+
assert_equal true, @bib['a1'].has_cross_reference?
|
34
|
+
end
|
35
|
+
it 'returns false if the entry has no valid cross-reference' do
|
36
|
+
assert_equal false, @bib['a'].has_cross_reference?
|
37
|
+
assert_equal false, @bib['b1'].has_cross_reference?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#cross_referemced?' do
|
42
|
+
it 'returns true if the entry is cross-referenced by another entry' do
|
43
|
+
assert_equal true, @bib['a'].cross_referenced?
|
44
|
+
end
|
45
|
+
it 'returns false if the entry is not cross-referenced' do
|
46
|
+
assert_equal false, @bib['a1'].cross_referenced?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#cross_referenced_by' do
|
51
|
+
it 'returns a list of all entries that cross-reference this entry' do
|
52
|
+
@bib['a'].cross_referenced_by.must_include(@bib['a1'])
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'returns an empty list if there are no cross-references to this entry' do
|
56
|
+
@bib['a1'].cross_referenced_by.must_be_empty
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'resolve field values using array accessors #[]' do
|
61
|
+
describe 'when a "title" is set in the entry itself' do
|
62
|
+
before { @bib['a1'].title = 'A1' }
|
63
|
+
it 'returns the title' do
|
64
|
+
@bib['a1'].title.must_be :==, 'A1'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'when "title" is undefined for the entry but defined in the reference' do
|
69
|
+
it 'returns the referenced title' do
|
70
|
+
@bib['a1'].title.must_be :==, @bib['a'].title
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe 'when "booktitle" is undefined for the entry but defined in the reference' do
|
75
|
+
before { @bib['a'].booktitle = "A Booktitle" }
|
76
|
+
it 'returns the referenced booktitle' do
|
77
|
+
@bib['a1'].booktitle.must_be :==, @bib['a'].booktitle
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe 'when "booktitle" is undefined for the entry and the reference but the reference has a "title"' do
|
82
|
+
it "returns the reference's title" do
|
83
|
+
@bib['a1'].booktitle.must_be :==, @bib['a'].title
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'does not store referenced values permanently' do
|
88
|
+
refute_nil @bib['a1'].booktitle
|
89
|
+
assert_nil @bib['a1'].fields[:booktitle]
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#inherited_fields' do
|
93
|
+
it 'returns an empty list by default' do
|
94
|
+
Entry.new.inherited_fields.must_be_empty
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'returns an empty list if this entry has no cross-reference' do
|
98
|
+
@bib['a'].inherited_fields.must_be_empty
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'returns an empty list if this entry has a cross-reference but the reference does not exist in the bibliography' do
|
102
|
+
@bib['b1'].inherited_fields.must_be_empty
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'returns a list of all fields not set in the field but in the reference' do
|
106
|
+
@bib['a1'].inherited_fields.must_be :==, [:booktitle, :editor, :title]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '#save_inherited_fields' do
|
111
|
+
it 'copies referenced values to the entry' do
|
112
|
+
@bib['a1'].title = 'a1'
|
113
|
+
@bib['a1'].save_inherited_fields
|
114
|
+
@bib['a1'].fields[:booktitle].must_be :==, @bib['a'].title
|
115
|
+
@bib['a1'].fields[:title].wont_be :==, @bib['a'].title
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe '#names' do
|
124
|
+
it 'returns an empty list by default' do
|
125
|
+
Entry.new.names.must_be :==, []
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'returns the author (if set)' do
|
129
|
+
Entry.new(:author => 'A').names.must_be :==, %w{ A }
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'returns all authors (if set)' do
|
133
|
+
Entry.new(:author => 'A B and C D').parse_names.names.length.must_be :==, 2
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'returns the editor (if set)' do
|
137
|
+
Entry.new(:editor => 'A').names.must_be :==, %w{ A }
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'returns the translator (if set)' do
|
141
|
+
Entry.new(:translator => 'A').names.must_be :==, %w{ A }
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
describe 'month conversion' do
|
147
|
+
before do
|
14
148
|
@entry = Entry.new
|
15
149
|
end
|
16
150
|
|
17
151
|
[[:jan,'January'], [:feb,'February'], [:sep,'September']].each do |m|
|
18
|
-
should
|
152
|
+
it 'should convert english months' do
|
19
153
|
@entry.month = m[1]
|
20
154
|
assert_equal m[0], @entry.month.v
|
21
155
|
end
|
22
156
|
end
|
23
157
|
|
24
158
|
[[:jan,:jan], [:feb,:feb], [:sep,:sep]].each do |m|
|
25
|
-
should
|
159
|
+
it 'should convert bibtex abbreviations' do
|
26
160
|
@entry.month = m[1]
|
27
161
|
assert_equal m[0], @entry.month.v
|
28
162
|
end
|
29
163
|
end
|
30
164
|
|
31
165
|
[[:jan,1], [:feb,2], [:sep,9]].each do |m|
|
32
|
-
should
|
166
|
+
it 'should convert numbers' do
|
33
167
|
@entry.month = m[1]
|
34
168
|
assert_equal m[0], @entry.month.v
|
35
169
|
end
|
36
|
-
should
|
170
|
+
it 'should convert numbers when parsing' do
|
37
171
|
@entry = Entry.parse("@misc{id, month = #{m[1]}}")[0]
|
38
172
|
assert_equal m[0], @entry.month.v
|
39
173
|
end
|
@@ -41,8 +175,8 @@ module BibTeX
|
|
41
175
|
|
42
176
|
end
|
43
177
|
|
44
|
-
|
45
|
-
|
178
|
+
describe 'given an entry' do
|
179
|
+
before do
|
46
180
|
@entry = Entry.new do |e|
|
47
181
|
e.type = :book
|
48
182
|
e.key = :key
|
@@ -56,13 +190,13 @@ module BibTeX
|
|
56
190
|
end
|
57
191
|
end
|
58
192
|
|
59
|
-
should
|
193
|
+
it 'should support renaming! of field attributes' do
|
60
194
|
@entry.rename!(:title => :foo)
|
61
195
|
refute @entry.has_field?(:title)
|
62
196
|
assert_equal 'Moby Dick', @entry[:foo]
|
63
197
|
end
|
64
198
|
|
65
|
-
should
|
199
|
+
it 'should support renaming of field attributes' do
|
66
200
|
e = @entry.rename(:title => :foo)
|
67
201
|
|
68
202
|
assert @entry.has_field?(:title)
|
@@ -76,7 +210,7 @@ module BibTeX
|
|
76
210
|
end
|
77
211
|
|
78
212
|
|
79
|
-
should
|
213
|
+
it 'should support citeproc export' do
|
80
214
|
e = @entry.to_citeproc
|
81
215
|
assert_equal 'book', e['type']
|
82
216
|
assert_equal 'New York', e['publisher-place']
|
@@ -86,30 +220,30 @@ module BibTeX
|
|
86
220
|
assert_equal 'Melville', e['author'][0]['family']
|
87
221
|
end
|
88
222
|
|
89
|
-
|
90
|
-
|
223
|
+
describe 'given a filter' do
|
224
|
+
before do
|
91
225
|
@filter = Object.new
|
92
226
|
def @filter.apply (value); value.is_a?(::String) ? value.upcase : value; end
|
93
227
|
end
|
94
228
|
|
95
|
-
should
|
229
|
+
it 'should support arbitrary conversion' do
|
96
230
|
e = @entry.convert(@filter)
|
97
231
|
assert_equal 'MOBY DICK', e.title
|
98
232
|
assert_equal 'Moby Dick', @entry.title
|
99
233
|
end
|
100
234
|
|
101
|
-
should
|
235
|
+
it 'should support arbitrary in-place conversion' do
|
102
236
|
@entry.convert!(@filter)
|
103
237
|
assert_equal 'MOBY DICK', @entry.title
|
104
238
|
end
|
105
239
|
|
106
|
-
should
|
240
|
+
it 'should support conditional arbitrary in-place conversion' do
|
107
241
|
@entry.convert!(@filter) { |k,v| k.to_s =~ /publisher/i }
|
108
242
|
assert_equal 'Moby Dick', @entry.title
|
109
243
|
assert_equal 'PENGUIN', @entry.publisher
|
110
244
|
end
|
111
245
|
|
112
|
-
should
|
246
|
+
it 'should support conditional arbitrary conversion' do
|
113
247
|
e = @entry.convert(@filter) { |k,v| k.to_s =~ /publisher/i }
|
114
248
|
assert_equal 'Moby Dick', e.title
|
115
249
|
assert_equal 'PENGUIN', e.publisher
|
@@ -120,8 +254,8 @@ module BibTeX
|
|
120
254
|
|
121
255
|
end
|
122
256
|
|
123
|
-
|
124
|
-
|
257
|
+
describe 'citeproc export' do
|
258
|
+
before do
|
125
259
|
@entry = Entry.new do |e|
|
126
260
|
e.type = :book
|
127
261
|
e.key = :key
|
@@ -130,11 +264,11 @@ module BibTeX
|
|
130
264
|
end
|
131
265
|
end
|
132
266
|
|
133
|
-
should
|
267
|
+
it 'should use dropping-particle by default' do
|
134
268
|
assert_equal 'van', @entry.to_citeproc['author'][0]['dropping-particle']
|
135
269
|
end
|
136
270
|
|
137
|
-
should
|
271
|
+
it 'should accept option to use non-dropping-particle' do
|
138
272
|
assert_equal 'van', @entry.to_citeproc(:particle => 'non-dropping-particle')['author'][0]['non-dropping-particle']
|
139
273
|
end
|
140
274
|
end
|
@@ -145,9 +279,9 @@ module BibTeX
|
|
145
279
|
assert_equal(BibTeX::Bibliography, bib.class)
|
146
280
|
assert_equal(3, bib.data.length)
|
147
281
|
assert_equal([BibTeX::Entry], bib.data.map(&:class).uniq)
|
148
|
-
assert_equal(
|
149
|
-
assert_equal(
|
150
|
-
assert_equal(
|
282
|
+
assert_equal('key:0', bib.data[0].key)
|
283
|
+
assert_equal('key:1', bib.data[1].key)
|
284
|
+
assert_equal('foo', bib.data[2].key)
|
151
285
|
assert_equal(:book, bib.data[0].type)
|
152
286
|
assert_equal(:article, bib.data[1].type)
|
153
287
|
assert_equal(:article, bib.data[2].type)
|
@@ -180,7 +314,7 @@ module BibTeX
|
|
180
314
|
entry.title = 'The Raven'
|
181
315
|
|
182
316
|
assert_equal :book, entry.type
|
183
|
-
assert_equal
|
317
|
+
assert_equal 'raven', entry.key
|
184
318
|
assert_equal 'Poe, Edgar A.', entry.author
|
185
319
|
assert_equal 'The Raven', entry.title
|
186
320
|
end
|
@@ -194,7 +328,7 @@ module BibTeX
|
|
194
328
|
})
|
195
329
|
|
196
330
|
assert_equal :book, entry.type
|
197
|
-
assert_equal
|
331
|
+
assert_equal 'raven', entry.key
|
198
332
|
assert_equal 'Poe, Edgar A.', entry.author
|
199
333
|
assert_equal 'The Raven', entry.title
|
200
334
|
end
|
@@ -202,36 +336,81 @@ module BibTeX
|
|
202
336
|
def test_creation_from_block
|
203
337
|
entry = BibTeX::Entry.new do |e|
|
204
338
|
e.type = :book
|
205
|
-
e.key =
|
339
|
+
e.key = 'raven'
|
206
340
|
e.author = 'Poe, Edgar A.'
|
207
341
|
e.title = 'The Raven'
|
208
342
|
end
|
209
343
|
|
210
344
|
assert_equal :book, entry.type
|
211
|
-
assert_equal
|
345
|
+
assert_equal 'raven', entry.key
|
212
346
|
assert_equal 'Poe, Edgar A.', entry.author
|
213
347
|
assert_equal 'The Raven', entry.title
|
214
348
|
end
|
215
349
|
|
216
350
|
def test_sorting
|
217
351
|
entries = []
|
218
|
-
entries <<
|
219
|
-
entries <<
|
220
|
-
entries <<
|
221
|
-
entries <<
|
352
|
+
entries << Entry.new({ :type => 'book', :key => 'raven3', :author => 'Poe, Edgar A.', :title => 'The Raven'})
|
353
|
+
entries << Entry.new({ :type => 'book', :key => 'raven2', :author => 'Poe, Edgar A.', :title => 'The Raven'})
|
354
|
+
entries << Entry.new({ :type => 'book', :key => 'raven1', :author => 'Poe, Edgar A.', :title => 'The Raven'})
|
355
|
+
entries << Entry.new({ :type => 'book', :key => 'raven1', :author => 'Poe, Edgar A.', :title => 'The Aven'})
|
222
356
|
|
223
357
|
entries.sort!
|
224
358
|
|
225
|
-
assert_equal [
|
359
|
+
assert_equal ['raven1', 'raven1', 'raven2', 'raven3'], entries.map(&:key)
|
226
360
|
assert_equal ['The Aven', 'The Raven'], entries.map(&:title)[0,2]
|
227
361
|
|
228
362
|
end
|
229
363
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
364
|
+
describe 'default keys' do
|
365
|
+
before {
|
366
|
+
@e1 = Entry.new(:type => 'book', :author => 'Poe, Edgar A.', :title => 'The Raven', :editor => 'John Hopkins', :year => 1996)
|
367
|
+
@e2 = Entry.new(:type => 'book', :title => 'The Raven', :editor => 'John Hopkins', :year => 1996)
|
368
|
+
@e3 = Entry.new(:type => 'book', :author => 'Poe, Edgar A.', :title => 'The Raven', :editor => 'John Hopkins')
|
369
|
+
}
|
370
|
+
|
371
|
+
it 'should return "unknown-a" for an empty Entry' do
|
372
|
+
Entry.new.key.must_be :==, 'unknown-a'
|
373
|
+
end
|
374
|
+
|
375
|
+
it 'should return a key made up of author-year-a if all fields are present' do
|
376
|
+
@e1.key.must_be :==, 'poe1996a'
|
377
|
+
end
|
378
|
+
|
379
|
+
it 'should return a key made up of editor-year-a if there is no author' do
|
380
|
+
@e2.key.must_be :==, 'john1996a'
|
381
|
+
end
|
382
|
+
|
383
|
+
it 'should return use the last name if the author/editor names have been parsed' do
|
384
|
+
@e2.parse_names.key.must_be :==, 'hopkins1996a'
|
385
|
+
end
|
386
|
+
|
387
|
+
it 'skips the year if not present' do
|
388
|
+
@e3.key.must_be :==, 'poe-a'
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
describe 'when the entry is added to a Bibliography' do
|
393
|
+
before {
|
394
|
+
@e = Entry.new
|
395
|
+
@bib = Bibliography.new
|
396
|
+
}
|
397
|
+
|
398
|
+
it 'should register itself with its key' do
|
399
|
+
@bib << @e
|
400
|
+
@bib.entries.keys.must_include @e.key
|
401
|
+
end
|
402
|
+
|
403
|
+
describe "when there is already an element registered with the entry's key" do
|
404
|
+
before { @bib << Entry.new }
|
405
|
+
|
406
|
+
it "should find a suitable key" do
|
407
|
+
k = @e.key
|
408
|
+
@bib << @e
|
409
|
+
@bib.entries.keys.must_include @e.key
|
410
|
+
k.wont_be :==, @e.key
|
411
|
+
end
|
412
|
+
|
413
|
+
end
|
235
414
|
end
|
236
415
|
|
237
416
|
end
|