bibtex-ruby 2.0.1 → 2.0.2
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.lock +2 -2
- data/History.txt +5 -0
- data/lib/bibtex.rb +12 -12
- data/lib/bibtex/bibliography.rb +98 -81
- data/lib/bibtex/elements.rb +191 -191
- data/lib/bibtex/entry.rb +470 -445
- data/lib/bibtex/lexer.rb +5 -5
- data/lib/bibtex/names.rb +2 -1
- data/lib/bibtex/value.rb +40 -43
- data/lib/bibtex/version.rb +1 -1
- data/test/bibtex/test_bibliography.rb +49 -49
- data/test/bibtex/test_entry.rb +214 -208
- data/test/bibtex/test_name_parser.rb +2 -2
- metadata +12 -12
data/lib/bibtex/lexer.rb
CHANGED
@@ -260,27 +260,27 @@ module BibTeX
|
|
260
260
|
end
|
261
261
|
|
262
262
|
def error_unbalanced_braces
|
263
|
-
|
263
|
+
BibTeX.log.warn("Lexer: unbalanced braces at #{@scanner.pos}; brace level #{@brace_level}; mode #{@mode.inspect}.")
|
264
264
|
backtrace [:E_UNBALANCED, @scanner.matched]
|
265
265
|
end
|
266
266
|
|
267
267
|
def error_unterminated_string
|
268
|
-
|
268
|
+
BibTeX.log.warn("Lexer: unterminated string at #{@scanner.pos}; brace level #{@brace_level}; mode #{@mode.inspect}.")
|
269
269
|
backtrace [:E_UNTERMINATED_STRING, @scanner.matched]
|
270
270
|
end
|
271
271
|
|
272
272
|
def error_unterminated_content
|
273
|
-
|
273
|
+
BibTeX.log.warn("Lexer: unterminated content at #{@scanner.pos}; brace level #{@brace_level}; mode #{@mode.inspect}.")
|
274
274
|
backtrace [:E_UNTERMINATED_CONTENT, @scanner.matched]
|
275
275
|
end
|
276
276
|
|
277
277
|
def error_unexpected_token
|
278
|
-
|
278
|
+
BibTeX.log.warn("Lexer: unexpected token `#{@scanner.matched}' at #{@scanner.pos}; brace level #{@brace_level}; mode #{@mode.inspect}.")
|
279
279
|
backtrace [:E_UNEXPECTED_TOKEN, @scanner.matched]
|
280
280
|
end
|
281
281
|
|
282
282
|
def error_unexpected_object
|
283
|
-
|
283
|
+
BibTeX.log.warn("Lexer: unexpected object at #{@scanner.pos}; brace level #{@brace_level}; mode #{@mode.inspect}.")
|
284
284
|
backtrace [:E_UNEXPECTED_OBJECT, '@']
|
285
285
|
end
|
286
286
|
|
data/lib/bibtex/names.rb
CHANGED
@@ -109,7 +109,8 @@ module BibTeX
|
|
109
109
|
:suffix => :lineage
|
110
110
|
}.freeze
|
111
111
|
|
112
|
-
def_delegators :to_s, :=~, :===,
|
112
|
+
def_delegators :to_s, :=~, :===,
|
113
|
+
*String.instance_methods(false).reject { |m| m =~ /^\W|to_s|replace|each|first|last|!$/ }
|
113
114
|
|
114
115
|
class << self
|
115
116
|
def parse(string)
|
data/lib/bibtex/value.rb
CHANGED
@@ -103,7 +103,7 @@ module BibTeX
|
|
103
103
|
when ::String # simulates Ruby's String#replace
|
104
104
|
@tokens = [argument]
|
105
105
|
when String
|
106
|
-
|
106
|
+
@tokens = @tokens.map { |v| argument.key == v ? argument.value.tokens : v }.flatten
|
107
107
|
when Hash
|
108
108
|
@tokens = @tokens.map { |v| argument[v] || v }
|
109
109
|
end
|
@@ -112,12 +112,11 @@ module BibTeX
|
|
112
112
|
end
|
113
113
|
|
114
114
|
|
115
|
-
# Returns the Value instance with all consecutive String tokens joined.
|
116
|
-
#
|
117
115
|
# call-seq:
|
118
|
-
#
|
119
|
-
#
|
116
|
+
# Value.new('foo', 'bar').join #=> <'foobar'>
|
117
|
+
# Value.new(:foo, 'bar').join #=> <:foo, 'bar'>
|
120
118
|
#
|
119
|
+
# Returns the Value instance with all consecutive String tokens joined.
|
121
120
|
def join
|
122
121
|
@tokens = @tokens.inject([]) do |a,b|
|
123
122
|
a[-1].is_a?(::String) && b.is_a?(::String) ? a[-1] += b : a << b; a
|
@@ -125,6 +124,16 @@ module BibTeX
|
|
125
124
|
self
|
126
125
|
end
|
127
126
|
|
127
|
+
# call-seq:
|
128
|
+
# Value.new('foo').to_s #=> "foo"
|
129
|
+
# Value.new(:foo).to_s #=> "foo"
|
130
|
+
# Value.new('foo').to_s(:quotes => '"') #=> "\"foo\""
|
131
|
+
# Value.new('foo').to_s(:quotes => ['"','"']) #=> "\"foo\""
|
132
|
+
# Value.new('foo').to_s(:quotes => ['{','}']) #=> "{foo}"
|
133
|
+
# Value.new(:foo, 'bar').to_s #=> "foo # \"bar\""
|
134
|
+
# Value.new('foo', 'bar').to_s #=> "\"foo\" # \"bar\""
|
135
|
+
# Value.new('\"u').to_s(:filter => :latex) #=> "ü"
|
136
|
+
#
|
128
137
|
# Returns a the Value as a string. @see #value; the only difference is
|
129
138
|
# that single symbols are returned as String, too.
|
130
139
|
# If the Value is atomic and the option :quotes is given, the string
|
@@ -132,17 +141,6 @@ module BibTeX
|
|
132
141
|
#
|
133
142
|
# If the option :filter is given, the Value will be converted using
|
134
143
|
# the filter(s) specified.
|
135
|
-
#
|
136
|
-
# call-seq:
|
137
|
-
# Value.new('foo').to_s #=> "foo"
|
138
|
-
# Value.new(:foo).to_s #=> "foo"
|
139
|
-
# Value.new('foo').to_s(:quotes => '"') #=> "\"foo\""
|
140
|
-
# Value.new('foo').to_s(:quotes => ['"','"']) #=> "\"foo\""
|
141
|
-
# Value.new('foo').to_s(:quotes => ['{','}']) #=> "{foo}"
|
142
|
-
# Value.new(:foo, 'bar').to_s #=> "foo # \"bar\""
|
143
|
-
# Value.new('foo', 'bar').to_s #=> "\"foo\" # \"bar\""
|
144
|
-
# Value.new('\"u').to_s(:filter => :latex) #=> "ü"
|
145
|
-
#
|
146
144
|
def to_s(options = {})
|
147
145
|
return convert(options.delete(:filter)).to_s(options) if options.has_key?(:filter)
|
148
146
|
return value.to_s unless options.has_key?(:quotes) && atomic?
|
@@ -161,7 +159,7 @@ module BibTeX
|
|
161
159
|
alias :v :value
|
162
160
|
|
163
161
|
def inspect
|
164
|
-
|
162
|
+
"#<#{self.class} #{tokens.map(&:inspect).join(', ')}>"
|
165
163
|
end
|
166
164
|
|
167
165
|
# Returns true if the Value is empty or consists of a single token.
|
@@ -173,7 +171,6 @@ module BibTeX
|
|
173
171
|
def name?; false; end
|
174
172
|
|
175
173
|
alias :names? :name?
|
176
|
-
alias :is_name? :name?
|
177
174
|
|
178
175
|
def to_name
|
179
176
|
Names.parse(to_s)
|
@@ -181,24 +178,24 @@ module BibTeX
|
|
181
178
|
|
182
179
|
alias to_names to_name
|
183
180
|
|
184
|
-
# Returns true if the Value's content
|
181
|
+
# Returns true if the Value's content is a date.
|
185
182
|
def date?
|
183
|
+
!to_date.nil?
|
186
184
|
end
|
187
|
-
|
188
|
-
alias is_date? date?
|
189
185
|
|
190
|
-
# Returns the string as a
|
186
|
+
# Returns the string as a date.
|
191
187
|
def to_date
|
192
|
-
|
188
|
+
require 'date'
|
189
|
+
Date.parse(to_s)
|
190
|
+
rescue
|
191
|
+
nil
|
193
192
|
end
|
194
|
-
|
193
|
+
|
195
194
|
# Returns true if the Value's content is numeric.
|
196
195
|
def numeric?
|
197
196
|
to_s =~ /^\s*[+-]?\d+[\/\.]?\d*\s*$/
|
198
197
|
end
|
199
|
-
|
200
|
-
alias is_numeric? numeric?
|
201
|
-
|
198
|
+
|
202
199
|
def to_citeproc (options = {})
|
203
200
|
to_s(options)
|
204
201
|
end
|
@@ -225,27 +222,27 @@ module BibTeX
|
|
225
222
|
# Converts all string values according to the given filter.
|
226
223
|
def convert! (filter)
|
227
224
|
if f = Filters.resolve(filter)
|
228
|
-
|
229
|
-
|
230
|
-
|
225
|
+
tokens.map! { |t| f.apply(t) }
|
226
|
+
else
|
227
|
+
raise ArgumentError, "Failed to load filter #{filter.inspect}"
|
231
228
|
end
|
232
229
|
|
233
230
|
self
|
234
231
|
end
|
235
232
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
233
|
+
def method_missing (name, *args)
|
234
|
+
case
|
235
|
+
when name.to_s =~ /^(?:convert|from)_([a-z]+)(!)?$/
|
236
|
+
$2 ? convert!($1) : convert($1)
|
237
|
+
else
|
238
|
+
super
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def respond_to? (method)
|
243
|
+
method =~ /^(?:convert|from)_([a-z]+)(!)?$/ || super
|
244
|
+
end
|
245
|
+
|
249
246
|
def <=> (other)
|
250
247
|
to_s <=> other.to_s
|
251
248
|
end
|
data/lib/bibtex/version.rb
CHANGED
@@ -30,11 +30,11 @@ module BibTeX
|
|
30
30
|
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
33
|
+
describe '.parse' do
|
34
|
+
it 'accepts filters' do
|
35
|
+
Bibliography.parse("@misc{k, title = {\\''u}}", :filter => 'latex')[0].title.must_be :==, 'ü'
|
36
|
+
end
|
37
|
+
end
|
38
38
|
|
39
39
|
describe 'given a populated biliography' do
|
40
40
|
before do
|
@@ -66,41 +66,41 @@ module BibTeX
|
|
66
66
|
END
|
67
67
|
end
|
68
68
|
|
69
|
-
it '
|
69
|
+
it 'supports access by index' do
|
70
70
|
assert_equal 'ruby', @bib[1].keywords
|
71
71
|
end
|
72
72
|
|
73
|
-
it '
|
73
|
+
it 'supports access by range' do
|
74
74
|
assert_equal %w{2008 2007}, @bib[1..2].map(&:year)
|
75
75
|
end
|
76
76
|
|
77
|
-
it '
|
77
|
+
it 'supports access by index and offset' do
|
78
78
|
assert_equal %w{2008 2007}, @bib[1,2].map(&:year)
|
79
79
|
end
|
80
80
|
|
81
|
-
it '
|
81
|
+
it 'supports queries by symbol key' do
|
82
82
|
refute_nil @bib[:rails]
|
83
83
|
assert_nil @bib[:ruby]
|
84
84
|
end
|
85
85
|
|
86
|
-
it '
|
86
|
+
it 'supports queries by symbol key and selector' do
|
87
87
|
assert_equal 1, @bib.q(:all, :rails).length
|
88
88
|
refute_nil @bib.q(:first, :rails)
|
89
89
|
assert_nil @bib.q(:first, :railss)
|
90
90
|
end
|
91
91
|
|
92
|
-
it '
|
92
|
+
it 'supports queries by string key' do
|
93
93
|
refute_nil @bib['rails']
|
94
94
|
assert_nil @bib['ruby']
|
95
95
|
end
|
96
96
|
|
97
|
-
it '
|
97
|
+
it 'supports queries by type string' do
|
98
98
|
assert_equal 2, @bib['@book'].length
|
99
99
|
assert_equal 1, @bib['@article'].length
|
100
100
|
assert_equal 0, @bib['@collection'].length
|
101
101
|
end
|
102
102
|
|
103
|
-
it '
|
103
|
+
it 'supports queries by type string and selector' do
|
104
104
|
assert_equal 2, @bib.q(:all, '@book').length
|
105
105
|
refute_nil @bib.q(:first, '@book')
|
106
106
|
assert_equal 1, @bib.q(:all, '@article').length
|
@@ -110,16 +110,16 @@ module BibTeX
|
|
110
110
|
end
|
111
111
|
|
112
112
|
|
113
|
-
it '
|
113
|
+
it 'supports queries by pattern' do
|
114
114
|
assert_equal 0, @bib[/reilly/].length
|
115
115
|
assert_equal 2, @bib[/reilly/i].length
|
116
116
|
end
|
117
117
|
|
118
|
-
it '
|
118
|
+
it 'supports queries by type string and conditions' do
|
119
119
|
assert_equal 1, @bib['@book[keywords=ruby]'].length
|
120
120
|
end
|
121
121
|
|
122
|
-
it '
|
122
|
+
it 'supports queries by bibtex element' do
|
123
123
|
entry = Entry.parse(<<-END).first
|
124
124
|
@article{segaran2007,
|
125
125
|
title = {{Programming collective intelligence}},
|
@@ -133,11 +133,11 @@ module BibTeX
|
|
133
133
|
assert_equal 0, @bib[entry].length
|
134
134
|
end
|
135
135
|
|
136
|
-
it '
|
136
|
+
it 'supports query and additional block' do
|
137
137
|
assert_equal 1, @bib.q('@book') { |e| e.keywords.split(/,/).length > 1 }.length
|
138
138
|
end
|
139
139
|
|
140
|
-
it '
|
140
|
+
it 'supports saving the bibliography to a file' do
|
141
141
|
tmp = Tempfile.new('bibtex')
|
142
142
|
tmp.close
|
143
143
|
@bib.save_to(tmp.path)
|
@@ -163,38 +163,38 @@ module BibTeX
|
|
163
163
|
|
164
164
|
end
|
165
165
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
166
|
+
describe 'LaTeX filter' do
|
167
|
+
before do
|
168
|
+
@bib['rails'].keywords = 'r\\"uby'
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'converts LaTeX umlauts' do
|
172
|
+
@bib.convert(:latex)['rails'].keywords.must_be :==, 'rüby'
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
describe 'BibTeXML export' do
|
178
|
+
before { @bibtexml = Tempfile.new('bibtexml') }
|
179
|
+
after { @bibtexml.unlink }
|
180
|
+
|
181
|
+
it 'supports exporting to BibTeXML' do
|
182
|
+
@bib.to_xml.write(@bibtexml, 2)
|
183
|
+
@bibtexml.rewind
|
184
|
+
xml = REXML::Document.new(@bibtexml)
|
185
|
+
xml.root.namespace.must_be :==, 'http://bibtexml.sf.net/'
|
186
|
+
xml.root.get_elements('//bibtex:entry').wont_be_empty
|
187
|
+
end
|
188
188
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
189
|
+
it 'supports exporting to extended BibTeXML' do
|
190
|
+
@bib.to_xml(:extended => true).write(@bibtexml, 2)
|
191
|
+
@bibtexml.rewind
|
192
|
+
xml = REXML::Document.new(@bibtexml)
|
193
|
+
xml.root.namespace.must_be :==, 'http://bibtexml.sf.net/'
|
194
|
+
xml.root.get_elements('//bibtex:person').wont_be_empty
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
198
198
|
end
|
199
199
|
|
200
200
|
|
data/test/bibtex/test_entry.rb
CHANGED
@@ -11,140 +11,146 @@ module BibTeX
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
14
|
+
describe 'cross-references' do
|
15
|
+
it 'has no cross-reference by default' do
|
16
|
+
assert_equal false, Entry.new.has_cross_reference?
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'is not cross-referenced by default' do
|
20
|
+
assert_equal false, Entry.new.cross_referenced?
|
21
|
+
Entry.new.cross_referenced_by.must_be_empty
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'given a bibliography with cross referenced entries' do
|
25
|
+
before do
|
26
|
+
@bib = Bibliography.parse <<-END
|
27
|
+
@book{a, editor = "A", title = "A"}
|
28
|
+
@incollection{a1, crossref = "a"}
|
29
|
+
@incollection{b1, crossref = "b"}
|
30
|
+
END
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#has_cross_reference?' do
|
34
|
+
it 'returns true if the entry has a valid cross-reference' do
|
35
|
+
assert_equal true, @bib['a1'].has_cross_reference?
|
36
|
+
end
|
37
|
+
it 'returns false if the entry has no valid cross-reference' do
|
38
|
+
assert_equal false, @bib['a'].has_cross_reference?
|
39
|
+
assert_equal false, @bib['b1'].has_cross_reference?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#cross_referenced?' do
|
44
|
+
it 'returns true if the entry is cross-referenced by another entry' do
|
45
|
+
assert_equal true, @bib['a'].cross_referenced?
|
46
|
+
end
|
47
|
+
it 'returns false if the entry is not cross-referenced' do
|
48
|
+
assert_equal false, @bib['a1'].cross_referenced?
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#cross_referenced_by' do
|
53
|
+
it 'returns a list of all entries that cross-reference this entry' do
|
54
|
+
@bib['a'].cross_referenced_by.must_include(@bib['a1'])
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'returns an empty list if there are no cross-references to this entry' do
|
58
|
+
@bib['a1'].cross_referenced_by.must_be_empty
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#respond_to?' do
|
63
|
+
it 'takes into account the inherited attributes' do
|
64
|
+
@bib['a1'].respond_to?(:title)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'resolve field values using array accessors #[]' do
|
69
|
+
describe 'when a "title" is set in the entry itself' do
|
70
|
+
before { @bib['a1'].title = 'A1' }
|
71
|
+
it 'returns the title' do
|
72
|
+
@bib['a1'].title.must_be :==, 'A1'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe 'when "title" is undefined for the entry but defined in the reference' do
|
77
|
+
it 'returns the referenced title' do
|
78
|
+
@bib['a1'].title.must_be :==, @bib['a'].title
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe 'when "booktitle" is undefined for the entry but defined in the reference' do
|
83
|
+
before { @bib['a'].booktitle = "A Booktitle" }
|
84
|
+
it 'returns the referenced booktitle' do
|
85
|
+
@bib['a1'].booktitle.must_be :==, @bib['a'].booktitle
|
86
|
+
end
|
87
|
+
end
|
82
88
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
89
|
+
describe 'when "booktitle" is undefined for the entry and the reference but the reference has a "title"' do
|
90
|
+
it "returns the reference's title" do
|
91
|
+
@bib['a1'].booktitle.must_be :==, @bib['a'].title
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'does not store referenced values permanently' do
|
96
|
+
refute_nil @bib['a1'].booktitle
|
97
|
+
assert_nil @bib['a1'].fields[:booktitle]
|
98
|
+
end
|
93
99
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
100
|
+
describe '#inherited_fields' do
|
101
|
+
it 'returns an empty list by default' do
|
102
|
+
Entry.new.inherited_fields.must_be_empty
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'returns an empty list if this entry has no cross-reference' do
|
106
|
+
@bib['a'].inherited_fields.must_be_empty
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'returns an empty list if this entry has a cross-reference but the reference does not exist in the bibliography' do
|
110
|
+
@bib['b1'].inherited_fields.must_be_empty
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'returns a list of all fields not set in the field but in the reference' do
|
114
|
+
@bib['a1'].inherited_fields.must_be :==, [:booktitle, :editor, :title]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe '#save_inherited_fields' do
|
119
|
+
it 'copies referenced values to the entry' do
|
120
|
+
@bib['a1'].title = 'a1'
|
121
|
+
@bib['a1'].save_inherited_fields
|
122
|
+
@bib['a1'].fields[:booktitle].must_be :==, @bib['a'].title
|
123
|
+
@bib['a1'].fields[:title].wont_be :==, @bib['a'].title
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe '#names' do
|
132
|
+
it 'returns an empty list by default' do
|
133
|
+
Entry.new.names.must_be :==, []
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'returns the author (if set)' do
|
137
|
+
Entry.new(:author => 'A').names.must_be :==, %w{ A }
|
138
|
+
end
|
133
139
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
140
|
+
it 'returns all authors (if set)' do
|
141
|
+
Entry.new(:author => 'A B and C D').parse_names.names.length.must_be :==, 2
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'returns the editor (if set)' do
|
145
|
+
Entry.new(:editor => 'A').names.must_be :==, %w{ A }
|
146
|
+
end
|
141
147
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
+
it 'returns the translator (if set)' do
|
149
|
+
Entry.new(:translator => 'A').names.must_be :==, %w{ A }
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
148
154
|
describe 'month conversion' do
|
149
155
|
before do
|
150
156
|
@entry = Entry.new
|
@@ -254,36 +260,36 @@ module BibTeX
|
|
254
260
|
|
255
261
|
end
|
256
262
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
263
|
+
describe 'LaTeX filter' do
|
264
|
+
before do
|
265
|
+
@entry.title = 'M\\"{o}by Dick'
|
266
|
+
end
|
267
|
+
|
268
|
+
describe '#convert' do
|
269
|
+
it 'converts LaTeX umlauts' do
|
270
|
+
@entry.convert(:latex).title.must_be :==, 'Möby Dick'
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'does not change the original entry' do
|
274
|
+
e = @entry.convert(:latex)
|
275
|
+
e.wont_be :==, @entry
|
276
|
+
e.title.to_s.length.must_be :<, @entry.title.to_s.length
|
277
|
+
end
|
278
|
+
end
|
273
279
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
280
|
+
describe '#convert!' do
|
281
|
+
it 'converts LaTeX umlauts' do
|
282
|
+
@entry.convert!(:latex).title.must_be :==, 'Möby Dick'
|
283
|
+
end
|
284
|
+
|
285
|
+
it 'changes the original entry in-place' do
|
286
|
+
e = @entry.convert!(:latex)
|
287
|
+
e.must_be :equal?, @entry
|
288
|
+
e.title.to_s.length.must_be :==, @entry.title.to_s.length
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
end
|
287
293
|
end
|
288
294
|
|
289
295
|
describe 'citeproc export' do
|
@@ -393,57 +399,57 @@ module BibTeX
|
|
393
399
|
|
394
400
|
end
|
395
401
|
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
402
|
+
describe 'default keys' do
|
403
|
+
before {
|
404
|
+
@e1 = Entry.new(:type => 'book', :author => 'Poe, Edgar A.', :title => 'The Raven', :editor => 'John Hopkins', :year => 1996)
|
405
|
+
@e2 = Entry.new(:type => 'book', :title => 'The Raven', :editor => 'John Hopkins', :year => 1996)
|
406
|
+
@e3 = Entry.new(:type => 'book', :author => 'Poe, Edgar A.', :title => 'The Raven', :editor => 'John Hopkins')
|
407
|
+
}
|
408
|
+
|
409
|
+
it 'should return "unknown-a" for an empty Entry' do
|
410
|
+
Entry.new.key.must_be :==, 'unknown-a'
|
411
|
+
end
|
412
|
+
|
413
|
+
it 'should return a key made up of author-year-a if all fields are present' do
|
414
|
+
@e1.key.must_be :==, 'poe1996a'
|
415
|
+
end
|
410
416
|
|
411
|
-
|
412
|
-
|
413
|
-
|
417
|
+
it 'should return a key made up of editor-year-a if there is no author' do
|
418
|
+
@e2.key.must_be :==, 'john1996a'
|
419
|
+
end
|
414
420
|
|
415
|
-
|
416
|
-
|
417
|
-
|
421
|
+
it 'should return use the last name if the author/editor names have been parsed' do
|
422
|
+
@e2.parse_names.key.must_be :==, 'hopkins1996a'
|
423
|
+
end
|
418
424
|
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
425
|
+
it 'skips the year if not present' do
|
426
|
+
@e3.key.must_be :==, 'poe-a'
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
describe 'when the entry is added to a Bibliography' do
|
431
|
+
before {
|
432
|
+
@e = Entry.new
|
433
|
+
@bib = Bibliography.new
|
434
|
+
}
|
435
|
+
|
436
|
+
it 'should register itself with its key' do
|
437
|
+
@bib << @e
|
438
|
+
@bib.entries.keys.must_include @e.key
|
439
|
+
end
|
440
|
+
|
441
|
+
describe "when there is already an element registered with the entry's key" do
|
442
|
+
before { @bib << Entry.new }
|
443
|
+
|
444
|
+
it "should find a suitable key" do
|
445
|
+
k = @e.key
|
446
|
+
@bib << @e
|
447
|
+
@bib.entries.keys.must_include @e.key
|
448
|
+
k.wont_be :==, @e.key
|
449
|
+
end
|
450
|
+
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
448
454
|
end
|
449
455
|
end
|