citeproc 0.0.2 → 0.0.3
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.
- data/.rspec +1 -0
- data/README.md +2 -0
- data/auto.watchr +2 -0
- data/lib/citeproc.rb +16 -0
- data/lib/citeproc/assets.rb +66 -0
- data/lib/citeproc/attributes.rb +92 -16
- data/lib/citeproc/bibliography.rb +180 -0
- data/lib/citeproc/citation_data.rb +208 -0
- data/lib/citeproc/compatibility.rb +5 -1
- data/lib/citeproc/date.rb +225 -162
- data/lib/citeproc/engine.rb +6 -11
- data/lib/citeproc/errors.rb +5 -4
- data/lib/citeproc/extensions.rb +71 -3
- data/lib/citeproc/item.rb +113 -0
- data/lib/citeproc/names.rb +556 -0
- data/lib/citeproc/processor.rb +30 -4
- data/lib/citeproc/selector.rb +128 -0
- data/lib/citeproc/variable.rb +85 -45
- data/lib/citeproc/version.rb +1 -1
- data/spec/citeproc/assets_spec.rb +60 -0
- data/spec/citeproc/attributes_spec.rb +23 -10
- data/spec/citeproc/bibliography_spec.rb +72 -0
- data/spec/citeproc/citation_data_spec.rb +94 -0
- data/spec/citeproc/date_spec.rb +65 -9
- data/spec/citeproc/extensions_spec.rb +33 -0
- data/spec/citeproc/item_spec.rb +52 -0
- data/spec/citeproc/names_spec.rb +492 -0
- data/spec/citeproc/processor_spec.rb +39 -0
- data/spec/citeproc/selector_spec.rb +81 -0
- data/spec/citeproc/variable_spec.rb +9 -3
- metadata +133 -121
@@ -3,36 +3,49 @@ require 'spec_helper'
|
|
3
3
|
module CiteProc
|
4
4
|
describe Attributes do
|
5
5
|
|
6
|
-
before(:
|
6
|
+
before(:all) do
|
7
|
+
A = Class.new { include Attributes }
|
8
|
+
end
|
7
9
|
|
8
|
-
let(:instance)
|
9
|
-
|
10
|
+
let(:instance) do
|
11
|
+
o = A.new
|
12
|
+
o[:bar] = 'foo'
|
13
|
+
o
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:other) do
|
17
|
+
o = A.new
|
18
|
+
o[:foo] = 'bar'
|
19
|
+
o
|
20
|
+
end
|
10
21
|
|
11
22
|
it { should_not be_nil }
|
12
23
|
|
13
24
|
describe '.attr_fields' do
|
14
25
|
|
15
|
-
|
26
|
+
# before(:all) do
|
27
|
+
# A.instance_eval { attr_fields :value, %w[ is-numeric punctuation-mode ] }
|
28
|
+
# end
|
16
29
|
|
17
30
|
it 'generates setters for attr_field values' do
|
18
31
|
# pending
|
19
|
-
# lambda {
|
32
|
+
# lambda { A.new.is_numeric }.should_not raise_error
|
20
33
|
end
|
21
34
|
|
22
35
|
it 'generates no other setters' do
|
23
|
-
lambda {
|
36
|
+
lambda { A.new.some_other_value }.should raise_error
|
24
37
|
end
|
25
38
|
end
|
26
39
|
|
27
40
|
describe '#merge' do
|
28
41
|
|
29
42
|
it 'merges non-existent values from other object' do
|
30
|
-
|
43
|
+
A.new.merge(other)[:foo].should == 'bar'
|
31
44
|
end
|
32
45
|
|
33
|
-
|
34
|
-
|
35
|
-
|
46
|
+
it 'does not overwrite existing values when merging other object' do
|
47
|
+
instance.merge(other)[:bar].should == 'foo'
|
48
|
+
end
|
36
49
|
|
37
50
|
end
|
38
51
|
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module CiteProc
|
4
|
+
|
5
|
+
describe Bibliography do
|
6
|
+
|
7
|
+
it { should be_empty }
|
8
|
+
it { should_not have_errors }
|
9
|
+
|
10
|
+
describe '#to_citeproc conversion' do
|
11
|
+
|
12
|
+
it 'returns an array' do
|
13
|
+
subject.to_citeproc.should be_a(Array)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'returns exactly two elements' do
|
17
|
+
subject.to_citeproc.should have(2).elements
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'returns formatting options as the first element' do
|
21
|
+
subject.to_citeproc.first.should be_a(Hash)
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'the formatting options' do
|
25
|
+
let(:options) { subject.to_citeproc[0] }
|
26
|
+
|
27
|
+
it 'contains a the error list' do
|
28
|
+
options.should have_key('bibliography_errors')
|
29
|
+
options['bibliography_errors'].should be_empty
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'returns the list of references as the second element' do
|
35
|
+
subject.to_citeproc.last.should be_a(Array)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '.parse (citeproc parser)' do
|
41
|
+
let(:js) { <<-JS_END }
|
42
|
+
[
|
43
|
+
{
|
44
|
+
"maxoffset": 0,
|
45
|
+
"entryspacing": 0,
|
46
|
+
"linespacing": 0,
|
47
|
+
"hangingindent": 0,
|
48
|
+
"second-field-align": true,
|
49
|
+
"bibstart": "<div class=\\"csl-bib-body\\">\\n",
|
50
|
+
"bibend": "</div>",
|
51
|
+
"bibliography_errors": []
|
52
|
+
},
|
53
|
+
[
|
54
|
+
"<div class=\\"csl-entry\\">Book A</div>",
|
55
|
+
"<div class=\\"csl-entry\\">Book C</div>"
|
56
|
+
]
|
57
|
+
]
|
58
|
+
JS_END
|
59
|
+
|
60
|
+
it 'parses citeproc/json strings' do
|
61
|
+
b = Bibliography.parse(js)
|
62
|
+
b.should be_a(Bibliography)
|
63
|
+
b.should have(2).references
|
64
|
+
b.should_not have_errors
|
65
|
+
b.options[:align].should be true
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module CiteProc
|
4
|
+
|
5
|
+
describe 'citation input' do
|
6
|
+
|
7
|
+
let(:hash) {{
|
8
|
+
"citationItems" => [
|
9
|
+
{
|
10
|
+
"id" => "ITEM-1"
|
11
|
+
}
|
12
|
+
],
|
13
|
+
"properties" => {
|
14
|
+
"noteIndex" => 1
|
15
|
+
}
|
16
|
+
}}
|
17
|
+
|
18
|
+
let(:json) { MultiJson.encode(hash) }
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
describe CitationData do
|
23
|
+
|
24
|
+
it { should_not be nil }
|
25
|
+
it { should be_empty }
|
26
|
+
|
27
|
+
it 'has not been processed by default' do
|
28
|
+
CitationData.new.should_not be_processed
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '.new' do
|
32
|
+
|
33
|
+
it 'accepts a citeproc hash' do
|
34
|
+
d = CitationData.new(hash)
|
35
|
+
d.should be_footnote
|
36
|
+
d.should_not be_empty
|
37
|
+
d[0].should be_a(CitationItem)
|
38
|
+
d.index.should == 1
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'accepts an array of items' do
|
42
|
+
CitationData.new([CitationItem.new(:id => 'id')]).should have(1).items
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'accepts an array of hashes' do
|
46
|
+
CitationData.new([{:id => 'id'}])[0].should be_a(CitationItem)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#to_citeproc' do
|
52
|
+
|
53
|
+
it 'returns empty an empty/default citation data element by default' do
|
54
|
+
CitationData.new.to_citeproc.should == { 'citationItems' => [], 'properties' => { 'noteIndex' => 0}}
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
describe CitationItem do
|
63
|
+
|
64
|
+
it { should_not be nil }
|
65
|
+
it { should be_empty }
|
66
|
+
|
67
|
+
describe '.new' do
|
68
|
+
|
69
|
+
it 'accepts a hash as input' do
|
70
|
+
CitationItem.new(:label => 'chapter').should have_label
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#to_citeproc' do
|
76
|
+
|
77
|
+
it 'returns empty citation data by default' do
|
78
|
+
CitationItem.new.to_citeproc.should == {}
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'returns a hash with stringified keys' do
|
82
|
+
CitationItem.new(:type => :article).to_citeproc.should have_key('type')
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'returns a hash with stringified values' do
|
86
|
+
CitationItem.new(:type => :article).to_citeproc.should have_value('article')
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
data/spec/citeproc/date_spec.rb
CHANGED
@@ -3,6 +3,15 @@ require 'spec_helper'
|
|
3
3
|
module CiteProc
|
4
4
|
describe Date do
|
5
5
|
|
6
|
+
let(:ad2k) { Date.create('date-parts' => [[2000]])}
|
7
|
+
let(:may) { Date.create('date-parts' => [[2000, 5]])}
|
8
|
+
let(:first_of_may) { Date.create('date-parts' => [[2000, 5, 1]])}
|
9
|
+
|
10
|
+
let(:bc100) { Date.create('date-parts' => [[-100]]) }
|
11
|
+
let(:bc50) { Date.create('date-parts' => [[-50]]) }
|
12
|
+
let(:ad50) { Date.create('date-parts' => [[50]]) }
|
13
|
+
let(:ad100) { Date.create('date-parts' => [[100]]) }
|
14
|
+
|
6
15
|
it { should_not be nil }
|
7
16
|
|
8
17
|
it { should_not be_numeric }
|
@@ -11,6 +20,18 @@ module CiteProc
|
|
11
20
|
|
12
21
|
end
|
13
22
|
|
23
|
+
describe '.parse' do
|
24
|
+
|
25
|
+
it 'returns nil by default' do
|
26
|
+
Date.parse('').should be nil
|
27
|
+
Date.parse(nil).should be nil
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'parses date strings' do
|
31
|
+
Date.parse('2004-10-26').year.should == 2004
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
14
35
|
describe '.create' do
|
15
36
|
it 'should accept parameters and return a new instance' do
|
16
37
|
Date.create('date-parts' => [[2001, 1]]).year.should == 2001
|
@@ -40,15 +61,6 @@ module CiteProc
|
|
40
61
|
|
41
62
|
describe 'sorting' do
|
42
63
|
|
43
|
-
let(:ad2k) { Date.create('date-parts' => [[2000]])}
|
44
|
-
let(:may) { Date.create('date-parts' => [[2000, 5]])}
|
45
|
-
let(:first_of_may) { Date.create('date-parts' => [[2000, 5, 1]])}
|
46
|
-
|
47
|
-
let(:bc100) { Date.create('date-parts' => [[-100]]) }
|
48
|
-
let(:bc50) { Date.create('date-parts' => [[-50]]) }
|
49
|
-
let(:ad50) { Date.create('date-parts' => [[50]]) }
|
50
|
-
let(:ad100) { Date.create('date-parts' => [[100]]) }
|
51
|
-
|
52
64
|
it 'dates with more date-parts will come after those with fewer parts' do
|
53
65
|
(ad2k < may && may < first_of_may).should be true
|
54
66
|
end
|
@@ -65,6 +77,50 @@ module CiteProc
|
|
65
77
|
end
|
66
78
|
end
|
67
79
|
|
80
|
+
describe 'b.c. and a.d.' do
|
81
|
+
|
82
|
+
it 'the year 993 is a.d.' do
|
83
|
+
Date.new(993).should be_ad
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'the year 1000 is not a.d.' do
|
87
|
+
Date.new(1000).should_not be_ad
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'the year 993 is not b.c.' do
|
91
|
+
Date.new(993).should_not be_bc
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'the year 0 is a.d.' do
|
95
|
+
Date.new(0).should be_ad
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'the year 0 is not b.c.' do
|
99
|
+
Date.new(0).should_not be_bc
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'the year -33 is not a.d.' do
|
103
|
+
Date.new(-33).should_not be_ad
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'the year -33 is b.c.' do
|
107
|
+
Date.new(-33).should be_bc
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'today is not a.d.' do
|
111
|
+
Date.today.should_not be_ad
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'today is not b.c.' do
|
115
|
+
Date.today.should_not be_bc
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'the year 2000 is not a.d.' do
|
119
|
+
ad2k.should_not be_ad
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
68
124
|
describe '#to_json' do
|
69
125
|
it 'supports simple parts' do
|
70
126
|
Date.new(%w{2000 1 15}).to_json.should == '{"date-parts":[[2000,1,15]]}'
|
@@ -53,4 +53,37 @@ describe Hash do
|
|
53
53
|
end
|
54
54
|
|
55
55
|
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
describe Array do
|
60
|
+
|
61
|
+
describe '#compact_join' do
|
62
|
+
|
63
|
+
it 'is equivalent to #join when there are no blank elements' do
|
64
|
+
[1,2,3].compact_join(' ').should == [1,2,3].join(' ')
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'is equivalent to #compact and #join when there are no empty elements' do
|
68
|
+
[1,2,3,nil,nil,4].compact_join(' ').should == [1,2,3,nil,nil,4].compact.join(' ')
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'returns an empty string if the array is empty' do
|
72
|
+
[].compact_join(' ').should == ''
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'returns an empty string if there are only nil elements' do
|
76
|
+
[nil,nil,nil].compact_join(' ').should == ''
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'returns an empty string if there are only empty elements' do
|
80
|
+
['','',''].compact_join(' ').should == ''
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'returns an empty string if there are only blank elements' do
|
84
|
+
['','',nil,'',nil].compact_join(' ').should == ''
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
56
89
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module CiteProc
|
4
|
+
describe Item do
|
5
|
+
|
6
|
+
it { should_not be nil }
|
7
|
+
it { should be_empty }
|
8
|
+
|
9
|
+
describe '.new' do
|
10
|
+
|
11
|
+
it 'creates number variables for number fields' do
|
12
|
+
Item.new(:edition => 23).edition.should be_a(Number)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'creates text variable for text fields' do
|
16
|
+
Item.new(:ISBN => 23).isbn.should be_a(Text)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'creates date variables for date fields' do
|
20
|
+
Item.new(:accessed => Time.now).accessed.should be_a(CiteProc::Date)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'creates names variables for name fields' do
|
24
|
+
Item.new(:editor => { :given => 'Jane' }).editor.should be_a(Names)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'creates text variables for unknown fields' do
|
28
|
+
v = Item.new(:unknown => 42)[:unknown]
|
29
|
+
v.should be_a(Variable)
|
30
|
+
v.should == '42'
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#to_citeproc' do
|
36
|
+
|
37
|
+
it 'returns an empty hash by default' do
|
38
|
+
Item.new.to_citeproc.should == {}
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'returns a hash with stringified keys' do
|
42
|
+
Item.new(:issue => 42).to_citeproc.should have_key('issue')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'returns a hash with stringified values' do
|
46
|
+
Item.new(:issue => 42).to_citeproc.values[0].should == '42'
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,492 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
module CiteProc
|
6
|
+
|
7
|
+
describe 'CiteProc Names' do
|
8
|
+
|
9
|
+
let(:poe) { Name.new(:family => 'Poe', :given => 'Edgar Allen') }
|
10
|
+
let(:joe) { Name.new(:given => 'Joe') }
|
11
|
+
let(:plato) { Name.new(:given => 'Plato') }
|
12
|
+
let(:aristotle) { Name.new(:given => 'Ἀριστοτέλης') }
|
13
|
+
let(:dostoyevksy) { Name.new(:given => 'Фёдор Михайлович', :family => 'Достоевский') }
|
14
|
+
|
15
|
+
let(:utf) { Name.new(
|
16
|
+
:given => 'Gérard',
|
17
|
+
:'dropping-particle' => 'de',
|
18
|
+
:'non-dropping-particle' => 'la',
|
19
|
+
:family => 'Martinière',
|
20
|
+
:suffix => 'III')
|
21
|
+
}
|
22
|
+
|
23
|
+
let(:markup) { Name.new(
|
24
|
+
:given => '<b>Gérard</b>',
|
25
|
+
:'dropping-particle' => 'd<i>e</i>',
|
26
|
+
:'non-dropping-particle' => 'la',
|
27
|
+
:family => 'Mar<strong>tinière</strong>',
|
28
|
+
:suffix => 'III')
|
29
|
+
}
|
30
|
+
|
31
|
+
|
32
|
+
let(:japanese) { Name.new(
|
33
|
+
"family" => "穂積",
|
34
|
+
"given" => "陳重")
|
35
|
+
}
|
36
|
+
|
37
|
+
let(:saunders) { Name.new("family" => "Saunders", "given" => "John Bertrand de Cusance Morant") }
|
38
|
+
|
39
|
+
let(:humboldt) { Name.new(
|
40
|
+
"family" => "Humboldt",
|
41
|
+
"given" => "Alexander",
|
42
|
+
"dropping-particle" => "von")
|
43
|
+
}
|
44
|
+
|
45
|
+
let(:van_gogh) { Name.new(
|
46
|
+
"family" => "Gogh",
|
47
|
+
"given" => "Vincent",
|
48
|
+
"non-dropping-particle" => "van")
|
49
|
+
}
|
50
|
+
|
51
|
+
let(:jr) { Name.new(
|
52
|
+
"family" => "Stephens",
|
53
|
+
"given" => "James",
|
54
|
+
"suffix" => "Jr.")
|
55
|
+
}
|
56
|
+
|
57
|
+
let(:frank) { Name.new(
|
58
|
+
"family" => "Bennett",
|
59
|
+
"given" => "Frank G.",
|
60
|
+
"suffix" => "Jr.",
|
61
|
+
"comma-suffix" => "true")
|
62
|
+
}
|
63
|
+
|
64
|
+
let(:ramses) { Name.new(
|
65
|
+
:family => 'Ramses',
|
66
|
+
:given => 'Horatio',
|
67
|
+
:suffix => 'III')
|
68
|
+
}
|
69
|
+
|
70
|
+
|
71
|
+
describe Name do
|
72
|
+
|
73
|
+
|
74
|
+
it { should_not be_nil }
|
75
|
+
|
76
|
+
describe 'formatting options' do
|
77
|
+
|
78
|
+
it 'does not always demote particle by default' do
|
79
|
+
Name.new.always_demote_particle?.should be false
|
80
|
+
Name.new.always_demote_non_dropping_particle?.should be false
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'does not demote particle by default' do
|
84
|
+
Name.new.demote_particle?.should be false
|
85
|
+
Name.new.demote_non_dropping_particle?.should be false
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'does not demote particle in sort order by default' do
|
89
|
+
Name.new.sort_order!.demote_particle?.should be false
|
90
|
+
Name.new.sort_order!.demote_non_dropping_particle?.should be false
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'always demotes particle if option is set' do
|
94
|
+
Name.new({}, :'demote-non-dropping-particle' => 'display-and-sort').always_demote_particle?.should be true
|
95
|
+
Name.new({}, :'demote-non-dropping-particle' => 'display-and-sort').always_demote_non_dropping_particle?.should be true
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'demotes particle in sort order if option is set to sort-only' do
|
99
|
+
Name.new({}, :'demote-non-dropping-particle' => 'display-and-sort').sort_order!.demote_particle?.should be true
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'never demotes particle by default' do
|
103
|
+
Name.new.never_demote_particle?.should be true
|
104
|
+
Name.new.never_demote_non_dropping_particle?.should be true
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'is not in sort order by default' do
|
108
|
+
Name.new.sort_order?.should be false
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'uses the long form by default' do
|
112
|
+
Name.new.should be_long_form
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'does not use short form by default' do
|
116
|
+
Name.new.should_not be_short_form
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
describe 'constructing' do
|
122
|
+
|
123
|
+
describe '.new' do
|
124
|
+
|
125
|
+
it 'accepts a symbolized hash' do
|
126
|
+
Name.new(:family => 'Doe').to_s.should == 'Doe'
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'accepts a stringified hash' do
|
130
|
+
Name.new('family' => 'Doe').to_s.should == 'Doe'
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
describe '#dup' do
|
139
|
+
|
140
|
+
it 'returns a new name copied by value' do
|
141
|
+
poe.dup.upcase!.to_s.should_not == poe.to_s
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
describe 'script awareness' do
|
147
|
+
|
148
|
+
it 'english names are romanesque' do
|
149
|
+
frank.should be_romanesque
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'ancient greek names are romanesque' do
|
153
|
+
aristotle.should be_romanesque
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'russian names are romanesque' do
|
157
|
+
dostoyevksy.should be_romanesque
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'japanese names are not romanesque' do
|
161
|
+
japanese.should_not be_romanesque
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'german names are romanesque' do
|
165
|
+
Name.new(:given => 'Firedrich', :family => 'Hölderlin').should be_romanesque
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'french names are romanesque' do
|
169
|
+
utf.should be_romanesque
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'markup does not interfere with romanesque test' do
|
173
|
+
markup.should be_romanesque
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
describe 'literals' do
|
179
|
+
|
180
|
+
it 'is a literal if the literal attribute is set' do
|
181
|
+
Name.new(:literal => 'GNU/Linux').should be_literal
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'is not literal by default' do
|
185
|
+
Name.new.should_not be_literal
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'is literal even if other name parts are set' do
|
189
|
+
Name.new(:family => 'Tux', :literal => 'GNU/Linux').should be_literal
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
describe 'in-place manipulation (bang! methods)' do
|
195
|
+
|
196
|
+
it 'delegates to string for family name' do
|
197
|
+
plato.swapcase!.to_s.should == 'pLATO'
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'delegates to string for given name' do
|
201
|
+
humboldt.gsub!(/^Alex\w*/, 'Wilhelm').to_s.should == 'Wilhelm von Humboldt'
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'delegates to string for dropping particle' do
|
205
|
+
humboldt.upcase!.dropping_particle.should == 'VON'
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'delegates to string for non dropping particle' do
|
209
|
+
van_gogh.upcase!.non_dropping_particle.should == 'VAN'
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'delegates to string for suffix' do
|
213
|
+
frank.sub!(/jr./i, 'Sr.').to_s.should == 'Frank G. Bennett, Sr.'
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'returns the name object' do
|
217
|
+
poe.upcase!.should be_a(Name)
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
describe '#to_s' do
|
224
|
+
|
225
|
+
it 'returns an empty string by default' do
|
226
|
+
Name.new.to_s.should be_empty
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'returns the last name if only last name is set' do
|
230
|
+
Name.new(:family => 'Doe').to_s.should == 'Doe'
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'returns the first name if only the first name is set' do
|
234
|
+
Name.new(:given => 'John').to_s.should == 'John'
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'prints japanese names using static ordering' do
|
238
|
+
japanese.to_s.should == '穂積 陳重'
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'returns the literal if the name is a literal' do
|
242
|
+
Name.new(:literal => 'GNU/Linux').to_s == 'GNU/Linux'
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'returns the name in display order by default' do
|
246
|
+
Name.new(:family => 'Doe', :given => 'John').to_s.should == 'John Doe'
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'returns the name in sort order if the sort order option is active' do
|
250
|
+
Name.new(:family => 'Doe', :given => 'John').sort_order!.to_s.should == 'Doe, John'
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'returns the full given name' do
|
254
|
+
saunders.to_s.should == 'John Bertrand de Cusance Morant Saunders'
|
255
|
+
end
|
256
|
+
|
257
|
+
it 'includes dropping particles' do
|
258
|
+
humboldt.to_s.should == 'Alexander von Humboldt'
|
259
|
+
end
|
260
|
+
|
261
|
+
it 'includes non dropping particles' do
|
262
|
+
van_gogh.to_s.should == 'Vincent van Gogh'
|
263
|
+
end
|
264
|
+
|
265
|
+
it 'includes suffices' do
|
266
|
+
jr.to_s.should == 'James Stephens Jr.'
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'uses the comma suffix option' do
|
270
|
+
frank.to_s.should == 'Frank G. Bennett, Jr.'
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'prints unicode characters' do
|
274
|
+
utf.to_s.should == "Gérard de la Martinière III"
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'prints russian names normally' do
|
278
|
+
dostoyevksy.to_s.should == 'Фёдор Михайлович Достоевский'
|
279
|
+
end
|
280
|
+
|
281
|
+
describe 'when static ordering is active' do
|
282
|
+
|
283
|
+
it 'always prints the family name first' do
|
284
|
+
poe.static_order!.to_s.should == 'Poe Edgar Allen'
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
288
|
+
|
289
|
+
describe 'when the sort order option is active' do
|
290
|
+
|
291
|
+
it 'returns an empty string by default' do
|
292
|
+
Name.new.sort_order!.to_s.should be_empty
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'returns the last name if only last name is set' do
|
296
|
+
Name.new({:family => 'Doe'}, { :'name-as-sort-order' => true }).to_s.should == 'Doe'
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'returns the first name if only the first name is set' do
|
300
|
+
Name.new(:given => 'John').sort_order!.to_s.should == 'John'
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'prints japanese names using static ordering' do
|
304
|
+
japanese.sort_order!.to_s.should == '穂積 陳重'
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'returns the literal if the name is a literal' do
|
308
|
+
Name.new(:literal => 'GNU/Linux').sort_order!.to_s == 'GNU/Linux'
|
309
|
+
end
|
310
|
+
|
311
|
+
it 'uses comma for suffix if comma suffix is set' do
|
312
|
+
frank.sort_order!.to_s.should == 'Bennett, Frank G., Jr.'
|
313
|
+
end
|
314
|
+
|
315
|
+
it 'also uses comma for suffix if comma suffix is *not* set' do
|
316
|
+
jr.sort_order!.to_s.should == 'Stephens, James, Jr.'
|
317
|
+
end
|
318
|
+
|
319
|
+
it 'for normal names it prints them as "family, given"' do
|
320
|
+
poe.sort_order!.to_s.should == 'Poe, Edgar Allen'
|
321
|
+
end
|
322
|
+
|
323
|
+
it 'particles come after given name by default' do
|
324
|
+
van_gogh.sort_order!.to_s.should == 'van Gogh, Vincent'
|
325
|
+
end
|
326
|
+
|
327
|
+
it 'particles come after given name if demote option is active' do
|
328
|
+
van_gogh.sort_order!.demote_particle!.to_s.should == 'Gogh, Vincent van'
|
329
|
+
end
|
330
|
+
|
331
|
+
it 'dropping particles come after given name' do
|
332
|
+
humboldt.sort_order!.to_s.should == 'Humboldt, Alexander von'
|
333
|
+
end
|
334
|
+
|
335
|
+
it 'by default if all parts are set they are returned as "particle family, first dropping-particle, suffix"' do
|
336
|
+
utf.sort_order!.to_s.should == 'la Martinière, Gérard de, III'
|
337
|
+
end
|
338
|
+
|
339
|
+
end
|
340
|
+
|
341
|
+
end
|
342
|
+
|
343
|
+
describe '#sort_order' do
|
344
|
+
|
345
|
+
it 'returns only a single token for literal names' do
|
346
|
+
Name.new(:literal => 'ACME Corp.').sort_order.should have(1).element
|
347
|
+
end
|
348
|
+
|
349
|
+
it 'strips leading "the" off literal names' do
|
350
|
+
Name.new(:literal => 'The ACME Corp.').sort_order[0].should == 'ACME Corp.'
|
351
|
+
end
|
352
|
+
|
353
|
+
it 'strips leading "a" off literal names' do
|
354
|
+
Name.new(:literal => 'A Company').sort_order[0].should == 'Company'
|
355
|
+
end
|
356
|
+
|
357
|
+
it 'strips leading "an" off literal names' do
|
358
|
+
Name.new(:literal => 'an ACME Corp.').sort_order[0].should == 'ACME Corp.'
|
359
|
+
end
|
360
|
+
|
361
|
+
it 'strips leading "l\'" off literal names' do
|
362
|
+
Name.new(:literal => "L'Augustine").sort_order[0].should == 'Augustine'
|
363
|
+
end
|
364
|
+
|
365
|
+
it 'always returns four tokens for non literal names' do
|
366
|
+
poe.sort_order.should have(4).elements
|
367
|
+
joe.sort_order.should have(4).elements
|
368
|
+
aristotle.sort_order.should have(4).elements
|
369
|
+
utf.sort_order.should have(4).elements
|
370
|
+
frank.sort_order.should have(4).elements
|
371
|
+
japanese.sort_order.should have(4).elements
|
372
|
+
end
|
373
|
+
|
374
|
+
it 'demotes non dropping particles if option is set' do
|
375
|
+
van_gogh.demote_particle!.sort_order.should == ['Gogh', 'van', 'Vincent', '']
|
376
|
+
end
|
377
|
+
|
378
|
+
it 'does not demote non dropping particles by default' do
|
379
|
+
van_gogh.sort_order.should == ['van Gogh', '', 'Vincent', '']
|
380
|
+
end
|
381
|
+
|
382
|
+
it 'does not demote non dropping particles by default but dropping particles are demoted' do
|
383
|
+
utf.sort_order.should == ['la Martinière', 'de', 'Gérard', 'III']
|
384
|
+
end
|
385
|
+
|
386
|
+
it 'demotes dropping particles' do
|
387
|
+
humboldt.sort_order.should == ['Humboldt', 'von', 'Alexander', '']
|
388
|
+
end
|
389
|
+
|
390
|
+
it 'combines non dropping particles with family name if option demote-non-dropping-particles is not active' do
|
391
|
+
van_gogh.never_demote_particle!.sort_order.should == ['van Gogh', '', 'Vincent', '']
|
392
|
+
end
|
393
|
+
|
394
|
+
end
|
395
|
+
|
396
|
+
describe 'sorting' do
|
397
|
+
|
398
|
+
it 'sorts by sort order by default' do
|
399
|
+
[poe, utf, joe, plato].sort.should == [joe, plato, utf, poe]
|
400
|
+
end
|
401
|
+
|
402
|
+
end
|
403
|
+
|
404
|
+
|
405
|
+
end
|
406
|
+
|
407
|
+
describe Names do
|
408
|
+
|
409
|
+
it { should_not be nil }
|
410
|
+
it { should_not be_numeric }
|
411
|
+
|
412
|
+
describe 'constructing' do
|
413
|
+
|
414
|
+
it 'accepts a single name' do
|
415
|
+
lambda { Names.new(joe) }.should_not raise_error
|
416
|
+
end
|
417
|
+
|
418
|
+
it 'accepts a single name as hash' do
|
419
|
+
Names.new(:given => 'Jim').should have(1).name
|
420
|
+
end
|
421
|
+
|
422
|
+
it 'accepts two names' do
|
423
|
+
Names.new(joe, poe).should have(2).names
|
424
|
+
end
|
425
|
+
|
426
|
+
it 'accepts two names as hash' do
|
427
|
+
Names.new({:given => 'Jim'}, {:family => 'Jameson'}).should have(2).names
|
428
|
+
end
|
429
|
+
|
430
|
+
it 'accepts an array of names' do
|
431
|
+
Names.new([joe, poe]).should have(2).names
|
432
|
+
end
|
433
|
+
|
434
|
+
end
|
435
|
+
|
436
|
+
describe '#strip_markup' do
|
437
|
+
|
438
|
+
it 'strips markup off string representation' do
|
439
|
+
Names.new(markup).strip_markup.should == utf.to_s
|
440
|
+
end
|
441
|
+
|
442
|
+
it 'when using the bang! version, strips markup off each name part' do
|
443
|
+
Names.new(markup).strip_markup![0].should == utf
|
444
|
+
end
|
445
|
+
|
446
|
+
|
447
|
+
end
|
448
|
+
|
449
|
+
describe 'bang! methods' do
|
450
|
+
|
451
|
+
it 'delegate to the individual names and return self' do
|
452
|
+
Names.new(poe, plato, joe).upcase!.map(&:given).should == ['EDGAR ALLEN', 'PLATO', 'JOE']
|
453
|
+
end
|
454
|
+
|
455
|
+
end
|
456
|
+
|
457
|
+
describe '#to_bibtex' do
|
458
|
+
|
459
|
+
describe 'when there is only a single name' do
|
460
|
+
it 'prints the name in sort order' do
|
461
|
+
Names.new(poe).to_bibtex.should == 'Poe, Edgar Allen'
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
describe 'when there are two or more names' do
|
466
|
+
it 'prints the names in sort order connected with the word "and"' do
|
467
|
+
Names.new(poe, plato, humboldt).to_bibtex.should == 'Poe, Edgar Allen and Plato and Humboldt, Alexander von'
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
end
|
472
|
+
|
473
|
+
describe '#to_s' do
|
474
|
+
|
475
|
+
describe 'when the number of names exceeds the et-al-min option' do
|
476
|
+
|
477
|
+
it 'prints only the et-al-use-first names'
|
478
|
+
it 'adds et-al at the end'
|
479
|
+
it 'adds the delimiter before et-al only in the right circumstances'
|
480
|
+
|
481
|
+
end
|
482
|
+
|
483
|
+
end
|
484
|
+
|
485
|
+
describe 'sorting' do
|
486
|
+
end
|
487
|
+
|
488
|
+
end
|
489
|
+
|
490
|
+
end
|
491
|
+
|
492
|
+
end
|