biodiversity 0.5.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,211 @@
1
+ # encoding: UTF-8
2
+ grammar ScientificNameDirty
3
+ include ScientificNameClean
4
+
5
+ rule root
6
+ super
7
+ end
8
+
9
+ rule scientific_name_5
10
+ a:scientific_name_4 garbage {
11
+ def value
12
+ a.value
13
+ end
14
+
15
+ def canonical
16
+ a.canonical
17
+ end
18
+
19
+ def pos
20
+ a.pos
21
+ end
22
+
23
+ def details
24
+ a.details
25
+ end
26
+ }
27
+ /
28
+ super
29
+ end
30
+
31
+ rule infraspecies
32
+ a:infraspecies_epitheton space b:year {
33
+ def value
34
+ a.value + " " + b.value
35
+ end
36
+
37
+ def canonical
38
+ a.canonical
39
+ end
40
+
41
+ def pos
42
+ a.pos.merge(b.pos)
43
+ end
44
+
45
+ def details
46
+ {:infraspecies => a.details[:infraspecies].merge(b.details)}
47
+ end
48
+ }
49
+ /
50
+ a:infraspecies_epitheton space epitheton_authorship_inconsistencies space b:authorship {
51
+ def value
52
+ a.value + " " + b.value
53
+ end
54
+
55
+ def canonical
56
+ a.canonical
57
+ end
58
+
59
+ def pos
60
+ a.pos.merge(b.pos)
61
+ end
62
+
63
+ def details
64
+ {:infraspecies => a.details[:infraspecies].merge(b.details)}
65
+ end
66
+ }
67
+ /
68
+ super
69
+ end
70
+
71
+ rule species
72
+ a:species_epitheton space b:year {
73
+ def value
74
+ a.value + " " + b.value
75
+ end
76
+
77
+ def canonical
78
+ a.canonical
79
+ end
80
+
81
+ def pos
82
+ a.pos.merge(b.pos)
83
+ end
84
+
85
+ def details
86
+ {:species => a.details[:species].merge(b.details)}
87
+ end
88
+ }
89
+ /
90
+ super
91
+ end
92
+
93
+ rule right_paren
94
+ ")" space ")"
95
+ /
96
+ super
97
+ end
98
+
99
+ rule left_paren
100
+ "(" space "("
101
+ /
102
+ super
103
+ end
104
+
105
+ rule year
106
+ a:year_number space b:approximate_year {
107
+ def value
108
+ a.value + " " + b.value
109
+ end
110
+
111
+ def pos
112
+ a.pos.merge(b.pos)
113
+ end
114
+
115
+ def details
116
+ {:year => a.value, :approximate_year => b.value}
117
+ end
118
+ }
119
+ /
120
+ a:year_number space page_number {
121
+ def value
122
+ a.text_value
123
+ end
124
+
125
+ def pos
126
+ {a.interval.begin => ['year', a.interval.end]}
127
+ end
128
+
129
+ def details
130
+ {:year => value}
131
+ end
132
+ }
133
+ /
134
+ year_number_with_punctuation
135
+ /
136
+ approximate_year
137
+ /
138
+ double_year
139
+ /
140
+ super
141
+ end
142
+
143
+ rule approximate_year
144
+ "[" space a:year_number space "]"+ {
145
+ def value
146
+ "(" + a.text_value + ")"
147
+ end
148
+
149
+ def pos
150
+ {a.interval.begin => ['year', a.interval.end]}
151
+ end
152
+
153
+ def details
154
+ {:approximate_year => value}
155
+ end
156
+ }
157
+ end
158
+
159
+
160
+ rule double_year
161
+ year_number "-" [0-9]+ [A-Za-z]? [\?]? {
162
+ def value
163
+ text_value
164
+ end
165
+
166
+ def pos
167
+ {interval.begin => ['year', interval.end]}
168
+ end
169
+
170
+ def details
171
+ {:year => value}
172
+ end
173
+ }
174
+ end
175
+
176
+ rule year_number_with_punctuation
177
+ a:year_number "." {
178
+ def value
179
+ a.text_value
180
+ end
181
+
182
+ def pos
183
+ {interval.begin => ['year', interval.end]}
184
+ end
185
+
186
+ def details
187
+ {:year => value}
188
+ end
189
+ }
190
+ end
191
+
192
+
193
+ rule page_number
194
+ ":" space [\d]+
195
+ {
196
+ def value
197
+ end
198
+ }
199
+ end
200
+
201
+ rule epitheton_authorship_inconsistencies
202
+ ("corrig.")
203
+ end
204
+
205
+ rule garbage
206
+ space (["',.]) space [^щ]*
207
+ /
208
+ space_hard [^ш]+
209
+ end
210
+
211
+ end
File without changes
@@ -0,0 +1,12 @@
1
+ dir = File.dirname("__FILE__")
2
+ require 'rubygems'
3
+ require 'spec'
4
+ require File.expand_path(dir + "../../conf/environment")
5
+ require File.expand_path(dir + "../../lib/biodiversity/guid")
6
+
7
+ describe LsidResolver do
8
+ it "should return RFD document from lsid" do
9
+ lsid = "urn:lsid:ubio.org:classificationbank:2232671"
10
+ LsidResolver.resolve(lsid).class.should == "".class
11
+ end
12
+ end
@@ -0,0 +1,35 @@
1
+ #NOTE: this spec needs compiled treetop files.
2
+ dir = File.dirname("__FILE__")
3
+ require File.expand_path(dir + '../../spec/parser/spec_helper')
4
+ require File.expand_path(dir + '../../lib/biodiversity/parser')
5
+
6
+ describe ScientificNameClean do
7
+ before(:all) do
8
+ set_parser(ScientificNameParser.new)
9
+ end
10
+
11
+ it 'should generate standardized json' do
12
+ read_test_file do |y|
13
+ JSON.load(json(y[:name])).should == JSON.load(y[:jsn]) unless y[:comment]
14
+ end
15
+ end
16
+
17
+ # it 'should generate new test_file' do
18
+ # new_test = open(File.expand_path(dir + "../../spec/parser/test_data_new.txt"),'w')
19
+ # read_test_file do |y|
20
+ # if y[:comment]
21
+ # new_test.write y[:comment]
22
+ # else
23
+ # name = y[:name]
24
+ # jsn = json(y[:name])# rescue puts(y[:name])
25
+ # new_test.write("#{name}|#{jsn}\n")
26
+ # end
27
+ # end
28
+ # end
29
+
30
+ it 'should generate reasonable output if parser failed' do
31
+ sn = 'ddd sljlkj 3223452432'
32
+ json(sn).should == '{"scientificName":{"parsed":false,"verbatim":"ddd sljlkj 3223452432"}}'
33
+ end
34
+
35
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: UTF-8
2
+ dir = File.dirname("__FILE__")
3
+ require File.expand_path(dir + '../../spec/parser/spec_helper')
4
+
5
+ describe ScientificNameCanonical do
6
+ before(:all) do
7
+ set_parser(ScientificNameCanonicalParser.new)
8
+ end
9
+
10
+
11
+ it 'should parse names with valid name part and unparseable rest' do
12
+ [
13
+ ['Morea ssjjlajajaj324$33 234243242','Morea', [{:uninomial=>{:epitheton=>"Morea"}}], {0=>["uninomial", 5]}],
14
+ ['Morea (Morea) Burt 2342343242 23424322342 23424234', 'Morea (Morea)', [{:genus=>{:epitheton=>"Morea"}, :subgenus=>{:epitheton=>"Morea"}}], {0=>["genus", 5], 7=>["subgenus", 12]}],
15
+ ['Morea (Morea) burtius 2342343242 23424322342 23424234', 'Morea (Morea) burtius', [{:genus=>{:epitheton=>"Morea"}, :subgenus=>{:epitheton=>"Morea"}, :species=>{:epitheton=>"burtius"}}], {0=>["genus", 5], 7=>["subgenus", 12], 14=>["species", 21]}],
16
+ ['Moraea spathulata ( (L. f. Klatt','Moraea spathulata',[{:genus=>{:epitheton=>"Moraea"}, :species=>{:epitheton=>"spathulata"}}], {0=>["genus", 6], 7=>["species", 17]} ],
17
+ ['Verpericola megasoma ""Dall" Pils.','Verpericola megasoma',[{:genus=>{:epitheton=>"Verpericola"}, :species=>{:epitheton=>"megasoma"}}], {0=>["genus", 11], 12=>["species", 20]}]
18
+ ].each do |n|
19
+ parse(n[0]).should_not be_nil
20
+ value(n[0]).should == n[1]
21
+ details(n[0]).should == n[2]
22
+ pos(n[0]).should == n[3]
23
+ parse(n[0]).hybrid.should be_false
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,504 @@
1
+ # encoding: UTF-8
2
+ dir = File.dirname("__FILE__")
3
+ require File.expand_path(dir + '../../spec/parser/spec_helper')
4
+
5
+
6
+ describe ScientificNameClean do
7
+ before(:all) do
8
+ set_parser(ScientificNameCleanParser.new)
9
+ end
10
+
11
+ it 'should parse uninomial' do
12
+ sn = 'Pseudocercospora'
13
+ parse(sn).should_not be_nil
14
+ value(sn).should == 'Pseudocercospora'
15
+ canonical(sn).should == 'Pseudocercospora'
16
+ details(sn).should == [{:uninomial=>{:epitheton=>"Pseudocercospora"}}]
17
+ pos(sn).should == {0=>["uninomial", 16]}
18
+ end
19
+
20
+ it 'should parse uninomial with author and year' do
21
+ sn = 'Pseudocercospora Speg.'
22
+ parse(sn).should_not be_nil
23
+ details(sn).should == [{:uninomial=>{:epitheton=>"Pseudocercospora", :authorship=>"Speg.", :basionymAuthorTeam=>{:authorTeam=>"Speg.", :author=>["Speg."]}}}]
24
+ pos(sn).should == {0=>["uninomial", 16], 17=>["author_word", 22]}
25
+ sn = 'Pseudocercospora Spegazzini, 1910'
26
+ parse(sn).should_not be_nil
27
+ value(sn).should == 'Pseudocercospora Spegazzini 1910'
28
+ details(sn).should == [{:uninomial=>{:epitheton=>"Pseudocercospora", :authorship=>"Spegazzini, 1910", :basionymAuthorTeam=>{:authorTeam=>"Spegazzini", :author=>["Spegazzini"], :year=>"1910"}}}]
29
+ pos(sn).should == {0=>["uninomial", 16], 17=>["author_word", 27], 29=>["year", 33]}
30
+ end
31
+
32
+ it 'should parse names with a valid 2 letter genus' do
33
+ ["Ca Dyar 1914",
34
+ "Ea Distant 1911",
35
+ "Ge Nicéville 1895",
36
+ "Ia Thomas 1902",
37
+ "Io Lea 1831",
38
+ "Io Blanchard 1852",
39
+ "Ix Bergroth 1916",
40
+ "Lo Seale 1906",
41
+ "Oa Girault 1929",
42
+ "Ra Whitley 1931",
43
+ "Ty Bory de St. Vincent 1827",
44
+ "Ua Girault 1929",
45
+ "Aa Baker 1940",
46
+ "Ja Uéno 1955",
47
+ "Zu Walters & Fitch 1960",
48
+ "La Bleszynski 1966",
49
+ "Qu Durkoop",
50
+ "As Slipinski 1982",
51
+ "Ba Solem 1983"].each do |name|
52
+ parse(name).should_not be_nil
53
+ end
54
+ canonical('Quoyula').should == 'Quoyula'
55
+ end
56
+
57
+ it 'should parse canonical' do
58
+ sn = 'Pseudocercospora dendrobii'
59
+ parse(sn).should_not be_nil
60
+ value(sn).should == 'Pseudocercospora dendrobii'
61
+ canonical(sn).should == 'Pseudocercospora dendrobii'
62
+ details(sn).should == [{:genus=>{:epitheton=>"Pseudocercospora"}, :species=>{:epitheton=>"dendrobii"}}]
63
+ pos(sn).should == {0=>["genus", 16], 21=>["species", 30]}
64
+ end
65
+
66
+
67
+ it 'should parse species name with author and year' do
68
+ sn = "Platypus bicaudatulus Schedl 1935"
69
+ parse(sn).should_not be_nil
70
+ value(sn).should == "Platypus bicaudatulus Schedl 1935"
71
+ sn = "Platypus bicaudatulus Schedl, 1935h"
72
+ parse(sn).should_not be_nil
73
+ value(sn).should == "Platypus bicaudatulus Schedl 1935"
74
+ details(sn).should == [{:genus=>{:epitheton=>"Platypus"}, :species=>{:epitheton=>"bicaudatulus", :authorship=>"Schedl, 1935h", :basionymAuthorTeam=>{:authorTeam=>"Schedl", :author=>["Schedl"], :year=>"1935"}}}]
75
+ pos(sn).should == {0=>["genus", 8], 9=>["species", 21], 22=>["author_word", 28], 30=>["year", 35]}
76
+ parse("Platypus bicaudatulus Schedl, 1935B").should_not be_nil
77
+ sn = "Platypus bicaudatulus Schedl (1935h)"
78
+ parse(sn).should_not be_nil
79
+ details(sn).should == [{:genus=>{:epitheton=>"Platypus"}, :species=>{:epitheton=>"bicaudatulus", :authorship=>"Schedl (1935h)", :basionymAuthorTeam=>{:authorTeam=>"Schedl", :author=>["Schedl"], :year=>"1935"}}}]
80
+ parse("Platypus bicaudatulus Schedl 1935").should_not be_nil
81
+ end
82
+
83
+ it 'should parse genus with "?"' do
84
+ sn = "Ferganoconcha? oblonga"
85
+ parse(sn).should_not be_nil
86
+ value(sn).should == "Ferganoconcha oblonga"
87
+ details(sn).should == [{:genus=>{:epitheton=>"Ferganoconcha"}, :species=>{:epitheton=>"oblonga"}}]
88
+ pos(sn).should == {0=>["genus", 14], 15=>["species", 22]}
89
+ end
90
+
91
+ it 'should parse æ in the name' do
92
+ names = [
93
+ ["Læptura laetifica Dow, 1913", "Laeptura laetifica Dow 1913"],
94
+ ["Leptura lætifica Dow, 1913", "Leptura laetifica Dow 1913"],
95
+ ["Leptura leætifica Dow, 1913", "Leptura leaetifica Dow 1913"],
96
+ ["Leæptura laetifica Dow, 1913", "Leaeptura laetifica Dow 1913"],
97
+ ["Leœptura laetifica Dow, 1913", "Leoeptura laetifica Dow 1913"],
98
+ ['Ærenea cognata Lacordaire, 1872', 'Aerenea cognata Lacordaire 1872'],
99
+ ['Œdicnemus capensis', 'Oedicnemus capensis'],
100
+ ['Œnanthe œnanthe','Oenanthe oenanthe']
101
+ ]
102
+ names.each do |name_pair|
103
+ parse(name_pair[0]).should_not be_nil
104
+ value(name_pair[0]).should == name_pair[1]
105
+ end
106
+ end
107
+
108
+ it 'should parse names with "common" utf-8 charactes' do
109
+ names = ["Rühlella","Sténométope laevissimus Bibron 1855"].each do |name|
110
+ parse(name).should_not be_nil
111
+ end
112
+ sn = "Trematosphaeria phaeospora (E. Müll.) L. Holm 1957"
113
+ parse(sn).should_not be_nil
114
+ value(sn).should == "Trematosphaeria phaeospora (E. Müll.) L. Holm 1957"
115
+ canonical(sn).should == "Trematosphaeria phaeospora"
116
+ details(sn).should == [{:genus=>{:epitheton=>"Trematosphaeria"}, :species=>{:epitheton=>"phaeospora", :authorship=>"(E. Müll.) L. Holm 1957", :combinationAuthorTeam=>{:authorTeam=>"L. Holm", :author=>["L. Holm"], :year=>"1957"}, :basionymAuthorTeam=>{:authorTeam=>"E. Müll.", :author=>["E. Müll."]}}}]
117
+ pos(sn).should == {0=>["genus", 15], 16=>["species", 26], 28=>["author_word", 30], 31=>["author_word", 36], 46=>["author_word", 48], 61=>["author_word", 65], 66=>["year", 70]}
118
+
119
+ end
120
+
121
+ it 'should parse subgenus (ICZN code)' do
122
+ sn = "Hegeter (Hegeter) intercedens Lindberg H 1950"
123
+ parse(sn).should_not be_nil
124
+ value(sn).should == "Hegeter (Hegeter) intercedens Lindberg H 1950"
125
+ canonical(sn).should == "Hegeter intercedens"
126
+ details(sn).should == [{:genus=>{:epitheton=>"Hegeter"}, :subgenus=>{:epitheton=>"Hegeter"}, :species=>{:epitheton=>"intercedens", :authorship=>"Lindberg H 1950", :basionymAuthorTeam=>{:authorTeam=>"Lindberg H", :author=>["Lindberg H"], :year=>"1950"}}}]
127
+ pos(sn).should == {0=>["genus", 7], 9=>["subgenus", 16], 18=>["species", 29], 30=>["author_word", 38], 39=>["author_word", 40], 41=>["year", 45]}
128
+ end
129
+
130
+ it 'should parse several authors without a year' do
131
+ sn = "Pseudocercospora dendrobii U. Braun & Crous"
132
+ parse(sn).should_not be_nil
133
+ value(sn).should == "Pseudocercospora dendrobii U. Braun et Crous"
134
+ canonical(sn).should == "Pseudocercospora dendrobii"
135
+ details(sn).should == [{:genus=>{:epitheton=>"Pseudocercospora"}, :species=>{:epitheton=>"dendrobii", :authorship=>"U. Braun & Crous", :basionymAuthorTeam=>{:authorTeam=>"U. Braun & Crous", :author=>["U. Braun", "Crous"]}}}]
136
+ pos(sn).should == {0=>["genus", 16], 17=>["species", 26], 27=>["author_word", 29], 30=>["author_word", 35], 38=>["author_word", 43]}
137
+ sn = "Pseudocercospora dendrobii U. Braun and Crous"
138
+ parse(sn).should_not be_nil
139
+ value(sn).should == "Pseudocercospora dendrobii U. Braun et Crous"
140
+ pos(sn).should == {0=>["genus", 16], 17=>["species", 26], 27=>["author_word", 29], 30=>["author_word", 35], 40=>["author_word", 45]}
141
+ sn = "Pseudocercospora dendrobii U. Braun et Crous"
142
+ parse(sn).should_not be_nil
143
+ value(sn).should == "Pseudocercospora dendrobii U. Braun et Crous"
144
+ sn = "Arthopyrenia hyalospora(Nyl.)R.C. Harris"
145
+ parse(sn).should_not be_nil
146
+ value(sn).should == "Arthopyrenia hyalospora (Nyl.) R.C. Harris"
147
+ canonical(sn).should == "Arthopyrenia hyalospora"
148
+ details(sn).should == [{:genus=>{:epitheton=>"Arthopyrenia"}, :species=>{:epitheton=>"hyalospora", :authorship=>"(Nyl.)R.C. Harris", :combinationAuthorTeam=>{:authorTeam=>"R.C. Harris", :author=>["R.C. Harris"]}, :basionymAuthorTeam=>{:authorTeam=>"Nyl.", :author=>["Nyl."]}}}]
149
+ end
150
+
151
+
152
+
153
+ it 'should parse several authors with a year' do
154
+ sn = "Pseudocercospora dendrobii U. Braun & Crous 2003"
155
+ parse(sn).should_not be_nil
156
+ value(sn).should == "Pseudocercospora dendrobii U. Braun et Crous 2003"
157
+ canonical(sn).should == "Pseudocercospora dendrobii"
158
+ details(sn).should == [{:genus=>{:epitheton=>"Pseudocercospora"}, :species=>{:epitheton=>"dendrobii", :authorship=>"U. Braun & Crous 2003", :basionymAuthorTeam=>{:authorTeam=>"U. Braun & Crous", :author=>["U. Braun", "Crous"], :year=>"2003"}}}]
159
+ pos(sn).should == {0=>["genus", 16], 17=>["species", 26], 27=>["author_word", 29], 30=>["author_word", 35], 38=>["author_word", 43], 44=>["year", 48]}
160
+ sn = "Pseudocercospora dendrobii Crous, 2003"
161
+ parse(sn).should_not be_nil
162
+ end
163
+
164
+ it 'should parse basionym authors in parenthesis' do
165
+ sn = "Zophosis persis (Chatanay, 1914)"
166
+ parse(sn).should_not be_nil
167
+ details(sn).should == [{:genus=>{:epitheton=>"Zophosis"}, :species=>{:epitheton=>"persis", :authorship=>"(Chatanay, 1914)", :basionymAuthorTeam=>{:authorTeam=>"Chatanay", :author=>["Chatanay"], :year=>"1914"}}}]
168
+ sn = "Zophosis persis (Chatanay 1914)"
169
+ parse(sn).should_not be_nil
170
+ details(sn).should == [{:genus=>{:epitheton=>"Zophosis"}, :species=>{:epitheton=>"persis", :authorship=>"(Chatanay 1914)", :basionymAuthorTeam=>{:authorTeam=>"Chatanay", :author=>["Chatanay"], :year=>"1914"}}}]
171
+ sn = "Zophosis persis (Chatanay), 1914"
172
+ parse(sn).should_not be_nil
173
+ value(sn).should == "Zophosis persis (Chatanay 1914)"
174
+ details(sn).should == [{:genus=>{:epitheton=>"Zophosis"}, :species=>{:epitheton=>"persis", :authorship=>"(Chatanay), 1914", :basionymAuthorTeam=>{:author_team=>"(Chatanay), 1914", :author=>["Chatanay"], :year=>"1914"}}}]
175
+ pos(sn).should == {0=>["genus", 8], 9=>["species", 15], 17=>["author_word", 25], 28=>["year", 32]}
176
+ parse("Zophosis persis (Chatanay) 1914").should_not be_nil
177
+ #parse("Zophosis persis Chatanay (1914)").should_not be_nil
178
+ end
179
+
180
+ it 'should parse scientific name' do
181
+ sn = "Pseudocercospora dendrobii(H.C. Burnett)U. Braun & Crous 2003"
182
+ parse(sn).should_not be_nil
183
+ value(sn).should == "Pseudocercospora dendrobii (H.C. Burnett) U. Braun et Crous 2003"
184
+ canonical(sn).should == "Pseudocercospora dendrobii"
185
+ details(sn).should == [{:genus=>{:epitheton=>"Pseudocercospora"}, :species=>{:epitheton=>"dendrobii", :authorship=>"(H.C. Burnett)U. Braun & Crous 2003", :combinationAuthorTeam=>{:authorTeam=>"U. Braun & Crous", :author=>["U. Braun", "Crous"], :year=>"2003"}, :basionymAuthorTeam=>{:authorTeam=>"H.C. Burnett", :author=>["H.C. Burnett"]}}}]
186
+ sn = "Pseudocercospora dendrobii(H.C. Burnett,1873)U. Braun & Crous 2003"
187
+ parse(sn).should_not be_nil
188
+ value(sn).should == "Pseudocercospora dendrobii (H.C. Burnett 1873) U. Braun et Crous 2003"
189
+ details(sn).should == [{:genus=>{:epitheton=>"Pseudocercospora"}, :species=>{:epitheton=>"dendrobii", :authorship=>"(H.C. Burnett,1873)U. Braun & Crous 2003", :combinationAuthorTeam=>{:authorTeam=>"U. Braun & Crous", :author=>["U. Braun", "Crous"], :year=>"2003"}, :basionymAuthorTeam=>{:authorTeam=>"H.C. Burnett", :author=>["H.C. Burnett"], :year=>"1873"}}}]
190
+ end
191
+
192
+ it 'should parse several authors with several years' do
193
+ sn = "Pseudocercospora dendrobii (H.C. Burnett 1883) U. Braun & Crous 2003"
194
+ parse(sn).should_not be_nil
195
+ value(sn).should == "Pseudocercospora dendrobii (H.C. Burnett 1883) U. Braun et Crous 2003"
196
+ canonical(sn).should == "Pseudocercospora dendrobii"
197
+ details(sn).should == [{:genus=>{:epitheton=>"Pseudocercospora"}, :species=>{:epitheton=>"dendrobii", :authorship=>"(H.C. Burnett 1883) U. Braun & Crous 2003", :combinationAuthorTeam=>{:authorTeam=>"U. Braun & Crous", :author=>["U. Braun", "Crous"], :year=>"2003"}, :basionymAuthorTeam=>{:authorTeam=>"H.C. Burnett", :author=>["H.C. Burnett"], :year=>"1883"}}}]
198
+ pos(sn).should == {0=>["genus", 16], 17=>["species", 26], 28=>["author_word", 32], 33=>["author_word", 40], 41=>["year", 45], 47=>["author_word", 49], 50=>["author_word", 55], 58=>["author_word", 63], 64=>["year", 68]}
199
+ end
200
+
201
+ it "should parse name with subspecies without rank Zoological Code" do
202
+ sn = "Hydnellum scrobiculatum zonatum (Banker) D. Hall & D.E. Stuntz 1972"
203
+ parse(sn).should_not be_nil
204
+ value(sn).should == "Hydnellum scrobiculatum zonatum (Banker) D. Hall et D.E. Stuntz 1972"
205
+ canonical(sn).should == "Hydnellum scrobiculatum zonatum"
206
+ details(sn).should == [{:genus=>{:epitheton=>"Hydnellum"}, :species=>{:epitheton=>"scrobiculatum"}, :infraspecies=>[{:epitheton=>"zonatum", :rank=>"n/a", :authorship=>"(Banker) D. Hall & D.E. Stuntz 1972", :combinationAuthorTeam=>{:authorTeam=>"D. Hall & D.E. Stuntz", :author=>["D. Hall", "D.E. Stuntz"], :year=>"1972"}, :basionymAuthorTeam=>{:authorTeam=>"Banker", :author=>["Banker"]}}]}]
207
+ pos(sn).should == {0=>["genus", 9], 10=>["species", 23], 24=>["infraspecies", 31], 33=>["author_word", 39], 41=>["author_word", 43], 44=>["author_word", 48], 51=>["author_word", 55], 56=>["author_word", 62], 63=>["year", 67]}
208
+ sn = "Begonia pingbienensis angustior"
209
+ parse(sn).should_not be_nil
210
+ details(sn).should == [{:genus=>{:epitheton=>"Begonia"}, :species=>{:epitheton=>"pingbienensis"}, :infraspecies=>[{:epitheton=>"angustior", :rank=>"n/a"}]}]
211
+ pos(sn).should == {0=>["genus", 7], 8=>["species", 21], 22=>["infraspecies", 31]}
212
+ end
213
+
214
+ it 'should parse infraspecies with rank' do
215
+ sn = "Aus bus Linn. var. bus"
216
+ parse(sn).should_not be_nil
217
+ details(sn).should == [{:genus=>{:epitheton=>"Aus"}, :species=>{:epitheton=>"bus", :authorship=>"Linn.", :basionymAuthorTeam=>{:authorTeam=>"Linn.", :author=>["Linn."]}}, :infraspecies=>[{:epitheton=>"bus", :rank=>"var."}]}]
218
+ sn = "Agalinis purpurea (L.) Briton var. borealis (Berg.) Peterson 1987"
219
+ parse(sn).should_not be_nil
220
+ details(sn).should == [{:genus=>{:epitheton=>"Agalinis"}, :species=>{:epitheton=>"purpurea", :authorship=>"(L.) Briton", :combinationAuthorTeam=>{:authorTeam=>"Briton", :author=>["Briton"]}, :basionymAuthorTeam=>{:authorTeam=>"L.", :author=>["L."]}}, :infraspecies=>[{:epitheton=>"borealis", :rank=>"var.", :authorship=>"(Berg.) Peterson 1987", :combinationAuthorTeam=>{:authorTeam=>"Peterson", :author=>["Peterson"], :year=>"1987"}, :basionymAuthorTeam=>{:authorTeam=>"Berg.", :author=>["Berg."]}}]}]
221
+ pos(sn).should == {0=>["genus", 8], 9=>["species", 17], 19=>["author_word", 21], 23=>["author_word", 29], 35=>["infraspecies", 43], 45=>["author_word", 50], 52=>["author_word", 60], 61=>["year", 65]}
222
+ sn = "Phaeographis inusta var. macularis(Leight.) A.L. Sm. 1861"
223
+ parse(sn).should_not be_nil
224
+ value(sn).should == "Phaeographis inusta var. macularis (Leight.) A.L. Sm. 1861"
225
+ canonical(sn).should == "Phaeographis inusta macularis"
226
+ pos(sn).should == {0=>["genus", 12], 13=>["species", 19], 25=>["infraspecies", 34], 35=>["author_word", 42], 44=>["author_word", 48], 49=>["author_word", 52], 53=>["year", 57]}
227
+ end
228
+
229
+ it 'should parse unknown original authors (auct.)/(hort.)/(?)' do
230
+ sn = "Tragacantha leporina (?) Kuntze"
231
+ parse(sn).should_not be_nil
232
+ value(sn).should == "Tragacantha leporina (?) Kuntze"
233
+ details(sn).should == [{:genus=>{:epitheton=>"Tragacantha"}, :species=>{:epitheton=>"leporina", :authorship=>"(?) Kuntze", :combinationAuthorTeam=>{:authorTeam=>"Kuntze", :author=>["Kuntze"]}, :basionymAuthorTeam=>{:authorTeam=>"(?)", :author=>["?"]}}}]
234
+ sn = "Lachenalia tricolor var. nelsonii (auct.) Baker"
235
+ parse(sn).should_not be_nil
236
+ value(sn).should == "Lachenalia tricolor var. nelsonii (auct.) Baker"
237
+ details(sn).should == [{:genus=>{:epitheton=>"Lachenalia"}, :species=>{:epitheton=>"tricolor"}, :infraspecies=>[{:epitheton=>"nelsonii", :rank=>"var.", :authorship=>"(auct.) Baker", :combinationAuthorTeam=>{:authorTeam=>"Baker", :author=>["Baker"]}, :basionymAuthorTeam=>{:authorTeam=>"auct.", :author=>["unknown"]}}]}]
238
+ pos(sn).should == {0=>["genus", 10], 11=>["species", 19], 25=>["infraspecies", 33], 35=>["unknown_author", 40], 42=>["author_word", 47]}
239
+ end
240
+
241
+ it 'should parse unknown authors auct./anon./hort./ht.' do
242
+ sn = "Puya acris ht."
243
+ parse(sn).should_not be_nil
244
+ pos(sn).should == {0=>["genus", 4], 5=>["species", 10], 11=>["unknown_author", 14]}
245
+ end
246
+
247
+ it 'shuould parse real world examples' do
248
+ sn = "Stagonospora polyspora M.T. Lucas & Sousa da Câmara 1934"
249
+ parse(sn).should_not be_nil
250
+ value(sn).should == "Stagonospora polyspora M.T. Lucas et Sousa da Câmara 1934"
251
+ details(sn).should == [{:genus=>{:epitheton=>"Stagonospora"}, :species=>{:epitheton=>"polyspora", :authorship=>"M.T. Lucas & Sousa da Câmara 1934", :basionymAuthorTeam=>{:authorTeam=>"M.T. Lucas & Sousa da Câmara", :author=>["M.T. Lucas", "Sousa da Câmara"], :year=>"1934"}}}]
252
+ pos(sn).should == {0=>["genus", 12], 13=>["species", 22], 23=>["author_word", 27], 28=>["author_word", 33], 36=>["author_word", 41], 42=>["author_word", 44], 45=>["author_word", 51], 52=>["year", 56]}
253
+ parse("Cladoniicola staurospora Diederich, van den Boom & Aptroot 2001").should_not be_nil
254
+ sn = "Yarrowia lipolytica var. lipolytica (Wick., Kurtzman & E.A. Herrm.) Van der Walt & Arx 1981"
255
+ parse(sn).should_not be_nil
256
+ value(sn).should == "Yarrowia lipolytica var. lipolytica (Wick., Kurtzman et E.A. Herrm.) Van der Walt et Arx 1981"
257
+ pos(sn).should == {0=>["genus", 8], 9=>["species", 19], 25=>["infraspecies", 35], 37=>["author_word", 42], 44=>["author_word", 52], 55=>["author_word", 59], 60=>["author_word", 66], 68=>["author_word", 71], 72=>["author_word", 75], 76=>["author_word", 80], 83=>["author_word", 86], 87=>["year", 91]}
258
+ parse("Physalospora rubiginosa (Fr.) anon.").should_not be_nil
259
+ parse("Pleurotus ëous (Berk.) Sacc. 1887").should_not be_nil
260
+ parse("Lecanora wetmorei Śliwa 2004").should_not be_nil
261
+ # valid
262
+ # infraspecific
263
+ parse("Calicium furfuraceum * furfuraceum (L.) Pers. 1797").should_not be_nil
264
+ parse("Exobasidium vaccinii ** andromedae (P. Karst.) P. Karst. 1882").should_not be_nil
265
+ parse("Urceolaria scruposa **** clausa Flot. 1849").should_not be_nil
266
+ parse("Cortinarius angulatus B gracilescens Fr. 1838").should_not be_nil
267
+ parse("Cyathicula scelobelonium").should_not be_nil
268
+ # single quote that did not show
269
+ # parse("Phytophthora hedraiandra De Cock & Man in ?t Veld 2004"
270
+ # Phthora vastatrix d?Hérelle 1909
271
+ # author is exception
272
+ sn = "Tuber liui A S. Xu 1999"
273
+ parse(sn).should_not be_nil
274
+ details(sn).should == [{:genus=>{:epitheton=>"Tuber"}, :species=>{:epitheton=>"liui", :authorship=>"A S. Xu 1999", :basionymAuthorTeam=>{:authorTeam=>"A S. Xu", :author=>["A S. Xu"], :year=>"1999"}}}]
275
+ parse('Xylaria potentillae A S. Xu').should_not be_nil
276
+ parse("Agaricus squamula Berk. & M.A. Curtis 1860").should_not be_nil
277
+ parse("Peltula coriacea Büdel, Henssen & Wessels 1986").should_not be_nil
278
+ #had to add no dot rule for trinomials without a rank to make it to work
279
+ sn = "Saccharomyces drosophilae anon."
280
+ parse(sn).should_not be_nil
281
+ details(sn).should == [{:genus=>{:epitheton=>"Saccharomyces"}, :species=>{:epitheton=>"drosophilae", :authorship=>"anon.", :basionymAuthorTeam=>{:authorTeam=>"anon.", :author=>["unknown"]}}}]
282
+ pos(sn).should == {0=>["genus", 13], 14=>["species", 25], 26=>["unknown_author", 31]}
283
+ sn = "Abacetus laevicollis de Chaudoir, 1869"
284
+ parse(sn).should_not be_nil
285
+ canonical(sn).should == 'Abacetus laevicollis'
286
+ sn = "Gastrosericus eremorum van Beaumont 1955"
287
+ canonical(sn).should == 'Gastrosericus eremorum'
288
+ sn = "Gastrosericus eremorum von Beaumont 1955"
289
+ canonical(sn).should == 'Gastrosericus eremorum'
290
+ sn = "Cypraeovula (Luponia) amphithales perdentata"
291
+ canonical(sn).should == 'Cypraeovula Luponia amphithales perdentata'
292
+ details(sn).should == [{:genus=>{:epitheton=>"Cypraeovula"}, :subgenus=>{:epitheton=>"Luponia"}, :species=>{:epitheton=>"amphithales"}, :infraspecies=>[{:epitheton=>"perdentata", :rank=>"n/a"}]}]
293
+ sn = "Polyrhachis orsyllus nat musculus Forel 1901"
294
+ canonical(sn).should == "Polyrhachis orsyllus musculus"
295
+ sn = 'Latrodectus 13-guttatus Thorell, 1875'
296
+ canonical(sn).should == 'Latrodectus 13-guttatus'
297
+ value(sn).should == 'Latrodectus 13-guttatus Thorell 1875'
298
+ sn = 'Latrodectus 3guttatus Thorell, 1875'
299
+ canonical(sn).should == 'Latrodectus 3-guttatus'
300
+ value(sn).should == 'Latrodectus 3-guttatus Thorell 1875'
301
+ end
302
+
303
+ it "should parse name with morph." do
304
+ sn = "Callideriphus flavicollis morph. reductus Fuchs 1961"
305
+ parse(sn).should_not be_nil
306
+ value(sn).should == "Callideriphus flavicollis morph. reductus Fuchs 1961"
307
+ canonical(sn).should == "Callideriphus flavicollis reductus"
308
+ details(sn).should == [{:genus=>{:epitheton=>"Callideriphus"}, :species=>{:epitheton=>"flavicollis"}, :infraspecies=>[{:epitheton=>"reductus", :rank=>"morph.", :authorship=>"Fuchs 1961", :basionymAuthorTeam=>{:authorTeam=>"Fuchs", :author=>["Fuchs"], :year=>"1961"}}]}]
309
+ pos(sn).should == {0=>["genus", 13], 14=>["species", 25], 33=>["infraspecies", 41], 42=>["author_word", 47], 48=>["year", 52]}
310
+ end
311
+
312
+
313
+ it "should parse name with forma/fo./form./f." do
314
+ sn = "Caulerpa cupressoides forma nuda"
315
+ parse(sn).should_not be_nil
316
+ value(sn).should == "Caulerpa cupressoides f. nuda"
317
+ canonical(sn).should == "Caulerpa cupressoides nuda"
318
+ details(sn).should == [{:genus=>{:epitheton=>"Caulerpa"}, :species=>{:epitheton=>"cupressoides"}, :infraspecies=>[{:epitheton=>"nuda", :rank=>"f."}]}]
319
+ pos(sn).should == {0=>["genus", 8], 9=>["species", 21], 28=>["infraspecies", 32]}
320
+ sn = "Chlorocyperus glaber form. fasciculariforme (Lojac.) Soó"
321
+ parse(sn).should_not be_nil
322
+ value("Chlorocyperus glaber form. fasciculariforme (Lojac.) Soó").should == "Chlorocyperus glaber f. fasciculariforme (Lojac.) Soó"
323
+ canonical(sn).should == "Chlorocyperus glaber fasciculariforme"
324
+ details(sn).should == [{:genus=>{:epitheton=>"Chlorocyperus"}, :species=>{:epitheton=>"glaber"}, :infraspecies=>[{:epitheton=>"fasciculariforme", :rank=>"f.", :authorship=>"(Lojac.) Soó", :combinationAuthorTeam=>{:authorTeam=>"Soó", :author=>["Soó"]}, :basionymAuthorTeam=>{:authorTeam=>"Lojac.", :author=>["Lojac."]}}]}]
325
+ pos(sn).should == {0=>["genus", 13], 14=>["species", 20], 27=>["infraspecies", 43], 45=>["author_word", 51], 53=>["author_word", 56]}
326
+ sn = "Bambusa nana Roxb. fo. alphonse-karri (Mitford ex Satow) Makino ex Shiros."
327
+ parse(sn).should_not be_nil
328
+ value(sn).should == "Bambusa nana Roxb. f. alphonse-karri (Mitford ex Satow) Makino ex Shiros."
329
+ canonical(sn).should == "Bambusa nana alphonse-karri"
330
+ details(sn).should == [{:genus=>{:epitheton=>"Bambusa"}, :species=>{:epitheton=>"nana", :authorship=>"Roxb.", :basionymAuthorTeam=>{:authorTeam=>"Roxb.", :author=>["Roxb."]}}, :infraspecies=>[{:epitheton=>"alphonse-karri", :rank=>"f.", :authorship=>"(Mitford ex Satow) Makino ex Shiros.", :combinationAuthorTeam=>{:authorTeam=>"Makino", :author=>["Makino"], :exAuthorTeam=>{:authorTeam=>"Shiros.", :author=>["Shiros."]}}, :basionymAuthorTeam=>{:authorTeam=>"Mitford", :author=>["Mitford"], :exAuthorTeam=>{:authorTeam=>"Satow", :author=>["Satow"]}}}]}]
331
+ pos(sn).should == {0=>["genus", 7], 8=>["species", 12], 13=>["author_word", 18], 23=>["infraspecies", 37], 39=>["author_word", 46], 50=>["author_word", 55], 57=>["author_word", 63], 67=>["author_word", 74]}
332
+ sn = " Sphaerotheca fuliginea f. dahliae Movss. 1967 "
333
+ sn = "Sphaerotheca fuliginea f. dahliae Movss. 1967"
334
+ parse(sn).should_not be_nil
335
+ value(sn).should == "Sphaerotheca fuliginea f. dahliae Movss. 1967"
336
+ canonical(sn).should == "Sphaerotheca fuliginea dahliae"
337
+ details(sn).should == [{:genus=>{:epitheton=>"Sphaerotheca"}, :species=>{:epitheton=>"fuliginea"}, :infraspecies=>[{:epitheton=>"dahliae", :rank=>"f.", :authorship=>"Movss. 1967", :basionymAuthorTeam=>{:authorTeam=>"Movss.", :author=>["Movss."], :year=>"1967"}}]}]
338
+ pos(sn).should == {0=>["genus", 12], 16=>["species", 25], 36=>["infraspecies", 43], 47=>["author_word", 53], 58=>["year", 62]}
339
+ parse('Polypodium vulgare nothosubsp. mantoniae (Rothm.) Schidlay').should_not be_nil
340
+ end
341
+
342
+ it "should parse name with several subspecies names NOT BOTANICAL CODE BUT NOT INFREQUENT" do
343
+ sn = "Hydnellum scrobiculatum var. zonatum f. parvum (Banker) D. Hall & D.E. Stuntz 1972"
344
+ parse(sn).should_not be_nil
345
+ value(sn).should == "Hydnellum scrobiculatum var. zonatum f. parvum (Banker) D. Hall et D.E. Stuntz 1972"
346
+ details(sn).should == [{:genus=>{:epitheton=>"Hydnellum"}, :species=>{:epitheton=>"scrobiculatum"}, :infraspecies=>[{:epitheton=>"zonatum", :rank=>"var."}, {:epitheton=>"parvum", :rank=>"f.", :authorship=>"(Banker) D. Hall & D.E. Stuntz 1972", :combinationAuthorTeam=>{:authorTeam=>"D. Hall & D.E. Stuntz", :author=>["D. Hall", "D.E. Stuntz"], :year=>"1972"}, :basionymAuthorTeam=>{:authorTeam=>"Banker", :author=>["Banker"]}}]}]
347
+ pos(sn).should == {0=>["genus", 9], 10=>["species", 23], 29=>["infraspecies", 36], 40=>["infraspecies", 46], 48=>["author_word", 54], 56=>["author_word", 58], 59=>["author_word", 63], 66=>["author_word", 70], 71=>["author_word", 77], 78=>["year", 82]}
348
+ parse('Senecio fuchsii C.C.Gmel. subsp. fuchsii var. expansus (Boiss. & Heldr.) Hayek').should_not be_nil
349
+ parse('Senecio fuchsii C.C.Gmel. subsp. fuchsii var. fuchsii').should_not be_nil
350
+ end
351
+
352
+
353
+ it "should parse status BOTANICAL RARE" do
354
+ #it is always latin abbrev often 2 words
355
+ sn = "Arthopyrenia hyalospora (Nyl.) R.C. Harris comb. nov."
356
+ parse(sn).should_not be_nil
357
+ value(sn).should == "Arthopyrenia hyalospora (Nyl.) R.C. Harris comb. nov."
358
+ canonical(sn).should == "Arthopyrenia hyalospora"
359
+ details(sn).should == [{:genus=>{:epitheton=>"Arthopyrenia"}, :species=>{:epitheton=>"hyalospora", :authorship=>"(Nyl.) R.C. Harris", :combinationAuthorTeam=>{:authorTeam=>"R.C. Harris ", :author=>["R.C. Harris"]}, :basionymAuthorTeam=>{:authorTeam=>"Nyl.", :author=>["Nyl."]}}, :status=>"comb. nov."}]
360
+ pos(sn).should == {0=>["genus", 12], 13=>["species", 23], 25=>["author_word", 29], 31=>["author_word", 35], 36=>["author_word", 42]}
361
+ end
362
+
363
+ it "should parse revised (ex) names" do
364
+ #invalidly published
365
+ sn = "Arthopyrenia hyalospora (Nyl. ex Banker) R.C. Harris"
366
+ parse(sn).should_not be_nil
367
+ value(sn).should == "Arthopyrenia hyalospora (Nyl. ex Banker) R.C. Harris"
368
+ canonical(sn).should == "Arthopyrenia hyalospora"
369
+ details(sn).should == [{:genus=>{:epitheton=>"Arthopyrenia"}, :species=>{:epitheton=>"hyalospora", :authorship=>"(Nyl. ex Banker) R.C. Harris", :combinationAuthorTeam=>{:authorTeam=>"R.C. Harris", :author=>["R.C. Harris"]}, :basionymAuthorTeam=>{:authorTeam=>"Nyl.", :author=>["Nyl."], :exAuthorTeam=>{:authorTeam=>"Banker", :author=>["Banker"]}}}}]
370
+ pos(sn).should == {0=>["genus", 12], 13=>["species", 23], 25=>["author_word", 29], 33=>["author_word", 39], 41=>["author_word", 45], 46=>["author_word", 52]}
371
+ sn = "Arthopyrenia hyalospora Nyl. ex Banker"
372
+ parse(sn).should_not be_nil
373
+ details(sn).should == [{:genus=>{:epitheton=>"Arthopyrenia"}, :species=>{:epitheton=>"hyalospora", :authorship=>"Nyl. ex Banker", :basionymAuthorTeam=>{:authorTeam=>"Nyl.", :author=>["Nyl."], :exAuthorTeam=>{:authorTeam=>"Banker", :author=>["Banker"]}}}}]
374
+ sn = "Glomopsis lonicerae Peck ex C.J. Gould 1945"
375
+ parse(sn).should_not be_nil
376
+ details(sn).should == [{:genus=>{:epitheton=>"Glomopsis"}, :species=>{:epitheton=>"lonicerae", :authorship=>"Peck ex C.J. Gould 1945", :basionymAuthorTeam=>{:authorTeam=>"Peck", :author=>["Peck"], :exAuthorTeam=>{:authorTeam=>"C.J. Gould", :author=>["C.J. Gould"], :year=>"1945"}}}}]
377
+ pos(sn).should == {0=>["genus", 9], 10=>["species", 19], 20=>["author_word", 24], 28=>["author_word", 32], 33=>["author_word", 38], 39=>["year", 43]}
378
+ parse("Acanthobasidium delicatum (Wakef.) Oberw. ex Jülich 1979").should_not be_nil
379
+ sn = "Mycosphaerella eryngii (Fr. ex Duby) Johanson ex Oudem. 1897"
380
+ parse(sn).should_not be_nil
381
+ details(sn).should == [{:genus=>{:epitheton=>"Mycosphaerella"}, :species=>{:epitheton=>"eryngii", :authorship=>"(Fr. ex Duby) Johanson ex Oudem. 1897", :combinationAuthorTeam=>{:authorTeam=>"Johanson", :author=>["Johanson"], :exAuthorTeam=>{:authorTeam=>"Oudem.", :author=>["Oudem."], :year=>"1897"}}, :basionymAuthorTeam=>{:authorTeam=>"Fr.", :author=>["Fr."], :exAuthorTeam=>{:authorTeam=>"Duby", :author=>["Duby"]}}}}]
382
+ pos(sn).should == {0=>["genus", 14], 15=>["species", 22], 24=>["author_word", 27], 31=>["author_word", 35], 37=>["author_word", 45], 49=>["author_word", 55], 56=>["year", 60]}
383
+ #invalid but happens
384
+ parse("Mycosphaerella eryngii (Fr. Duby) ex Oudem. 1897").should_not be_nil
385
+ parse("Mycosphaerella eryngii (Fr.ex Duby) ex Oudem. 1897").should_not be_nil
386
+ sn = "Salmonella werahensis (Castellani) Hauduroy and Ehringer in Hauduroy 1937"
387
+ parse(sn).should_not be_nil
388
+ details(sn).should == [{:genus=>{:epitheton=>"Salmonella"}, :species=>{:epitheton=>"werahensis", :authorship=>"(Castellani) Hauduroy and Ehringer in Hauduroy 1937", :combinationAuthorTeam=>{:authorTeam=>"Hauduroy and Ehringer", :author=>["Hauduroy", "Ehringer"], :exAuthorTeam=>{:authorTeam=>"Hauduroy", :author=>["Hauduroy"], :year=>"1937"}}, :basionymAuthorTeam=>{:authorTeam=>"Castellani", :author=>["Castellani"]}}}]
389
+ pos(sn).should == {0=>["genus", 10], 11=>["species", 21], 23=>["author_word", 33], 35=>["author_word", 43], 48=>["author_word", 56], 60=>["author_word", 68], 69=>["year", 73]}
390
+ end
391
+
392
+ it 'should parse named hybrids' do
393
+ [
394
+ ["×Agropogon P. Fourn. 1934", [{:uninomial=>{:epitheton=>"Agropogon", :authorship=>"P. Fourn. 1934", :basionymAuthorTeam=>{:authorTeam=>"P. Fourn.", :author=>["P. Fourn."], :year=>"1934"}}}]],
395
+ ["xAgropogon P. Fourn.", [{:uninomial=>{:epitheton=>"Agropogon", :authorship=>"P. Fourn.", :basionymAuthorTeam=>{:authorTeam=>"P. Fourn.", :author=>["P. Fourn."]}}}]],
396
+ ["XAgropogon P.Fourn.", [{:uninomial=>{:epitheton=>"Agropogon", :authorship=>"P.Fourn.", :basionymAuthorTeam=>{:authorTeam=>"P.Fourn.", :author=>["P.Fourn."]}}}]],
397
+ ["× Agropogon", [{:uninomial=>{:epitheton=>"Agropogon"}}]],
398
+ ["x Agropogon", [{:uninomial=>{:epitheton=>"Agropogon"}}]],
399
+ ["X Agropogon", [{:uninomial=>{:epitheton=>"Agropogon"}}]],
400
+ ["X Cupressocyparis leylandii", [{:genus=>{:epitheton=>"Cupressocyparis"}, :species=>{:epitheton=>"leylandii"}}]],
401
+ ["×Heucherella tiarelloides", [{:genus=>{:epitheton=>"Heucherella"}, :species=>{:epitheton=>"tiarelloides"}}]],
402
+ ["xHeucherella tiarelloides", [{:genus=>{:epitheton=>"Heucherella"}, :species=>{:epitheton=>"tiarelloides"}}]],
403
+ ["x Heucherella tiarelloides", [{:genus=>{:epitheton=>"Heucherella"}, :species=>{:epitheton=>"tiarelloides"}}]],
404
+ ["×Agropogon littoralis (Sm.) C. E. Hubb. 1946", [{:genus=>{:epitheton=>"Agropogon"}, :species=>{:epitheton=>"littoralis", :authorship=>"(Sm.) C. E. Hubb. 1946", :combinationAuthorTeam=>{:authorTeam=>"C. E. Hubb.", :author=>["C. E. Hubb."], :year=>"1946"}, :basionymAuthorTeam=>{:authorTeam=>"Sm.", :author=>["Sm."]}}}]]
405
+ ].each do |res|
406
+ parse(res[0]).should_not be_nil
407
+ parse(res[0]).hybrid.should be_true
408
+ details(res[0]).should == res[1]
409
+ end
410
+ [
411
+ ['Asplenium X inexpectatum (E.L. Braun 1940) Morton (1956)',[{:genus=>{:epitheton=>"Asplenium"}, :species=>{:epitheton=>"inexpectatum", :authorship=>"(E.L. Braun 1940) Morton (1956)", :combinationAuthorTeam=>{:authorTeam=>"Morton", :author=>["Morton"], :year=>"1956"}, :basionymAuthorTeam=>{:authorTeam=>"E.L. Braun", :author=>["E.L. Braun"], :year=>"1940"}}}]],
412
+ ['Mentha ×smithiana R. A. Graham 1949',[{:genus=>{:epitheton=>"Mentha"}, :species=>{:epitheton=>"smithiana", :authorship=>"R. A. Graham 1949", :basionymAuthorTeam=>{:authorTeam=>"R. A. Graham", :author=>["R. A. Graham"], :year=>"1949"}}}]],
413
+ ['Salix ×capreola Andersson (1867)',[{:genus=>{:epitheton=>"Salix"}, :species=>{:epitheton=>"capreola", :authorship=>"Andersson (1867)", :basionymAuthorTeam=>{:authorTeam=>"Andersson", :author=>["Andersson"], :year=>"1867"}}}]],
414
+ ['Salix x capreola Andersson',[{:genus=>{:epitheton=>"Salix"}, :species=>{:epitheton=>"capreola", :authorship=>"Andersson", :basionymAuthorTeam=>{:authorTeam=>"Andersson", :author=>["Andersson"]}}}]]
415
+ ].each do |res|
416
+ parse(res[0]).should_not be_nil
417
+ parse(res[0]).hybrid.should be_true
418
+ details(res[0]).should == res[1]
419
+ end
420
+ end
421
+
422
+ it "should parse hybrid combination" do
423
+ sn = "Arthopyrenia hyalospora X Hydnellum scrobiculatum"
424
+ parse(sn).should_not be_nil
425
+ parse(sn).hybrid.should be_true
426
+ value(sn).should == "Arthopyrenia hyalospora \303\227 Hydnellum scrobiculatum"
427
+ canonical(sn).should == "Arthopyrenia hyalospora Hydnellum scrobiculatum"
428
+ details(sn).should == [{:genus=>{:epitheton=>"Arthopyrenia"}, :species=>{:epitheton=>"hyalospora"}}, {:genus=>{:epitheton=>"Hydnellum"}, :species=>{:epitheton=>"scrobiculatum"}}]
429
+ pos(sn).should == {0=>["genus", 12], 13=>["species", 23], 26=>["genus", 35], 36=>["species", 49]}
430
+ sn = "Arthopyrenia hyalospora (Banker) D. Hall X Hydnellum scrobiculatum D.E. Stuntz"
431
+ parse(sn).should_not be_nil
432
+ parse(sn).hybrid.should be_true
433
+ value(sn).should == "Arthopyrenia hyalospora (Banker) D. Hall \303\227 Hydnellum scrobiculatum D.E. Stuntz"
434
+ canonical(sn).should == "Arthopyrenia hyalospora Hydnellum scrobiculatum"
435
+ pos(sn).should == {0=>["genus", 12], 13=>["species", 23], 25=>["author_word", 31], 33=>["author_word", 35], 36=>["author_word", 40], 43=>["genus", 52], 53=>["species", 66], 67=>["author_word", 71], 72=>["author_word", 78]}
436
+ value("Arthopyrenia hyalospora X").should == "Arthopyrenia hyalospora \303\227 ?"
437
+ sn = "Arthopyrenia hyalospora x"
438
+ parse(sn).should_not be_nil
439
+ parse(sn).hybrid.should be_true
440
+ canonical(sn).should == "Arthopyrenia hyalospora"
441
+ details(sn).should == [{:genus=>{:epitheton=>"Arthopyrenia"}, :species=>{:epitheton=>"hyalospora"}}, "?"]
442
+ pos(sn).should == {0=>["genus", 12], 13=>["species", 23]}
443
+ sn = "Arthopyrenia hyalospora × ?"
444
+ parse(sn).should_not be_nil
445
+ parse(sn).hybrid.should be_true
446
+ details(sn).should == [{:genus=>{:epitheton=>"Arthopyrenia"}, :species=>{:epitheton=>"hyalospora"}}, "?"]
447
+ pos(sn).should == {0=>["genus", 12], 13=>["species", 23]}
448
+ end
449
+
450
+ it 'should parse names with taxon concept' do
451
+ sn = "Sténométope laevissimus sec. Eschmeyer 2004"
452
+ val = @parser.failure_reason.to_s.match(/column [0-9]*/).to_s().gsub(/column /,'')
453
+ details(sn).should == [{:genus=>{:epitheton=>"Sténométope"}, :species=>{:epitheton=>"laevissimus"}, :taxon_concept=>{:authorship=>"Eschmeyer 2004", :basionymAuthorTeam=>{:authorTeam=>"Eschmeyer", :author=>["Eschmeyer"], :year=>"2004"}}}]
454
+ pos(sn).should == {0=>["genus", 11], 12=>["species", 23], 29=>["author_word", 38], 39=>["year", 43]}
455
+ sn = "Sténométope laevissimus Bibron 1855 sec. Eschmeyer 2004"
456
+ parse(sn).should_not be_nil
457
+ details(sn).should == [{:genus=>{:epitheton=>"Sténométope"}, :species=>{:epitheton=>"laevissimus", :authorship=>"Bibron 1855", :basionymAuthorTeam=>{:authorTeam=>"Bibron", :author=>["Bibron"], :year=>"1855"}}, :taxon_concept=>{:authorship=>"Eschmeyer 2004", :basionymAuthorTeam=>{:authorTeam=>"Eschmeyer", :author=>["Eschmeyer"], :year=>"2004"}}}]
458
+ pos(sn).should == {0=>["genus", 11], 12=>["species", 23], 24=>["author_word", 30], 31=>["year", 35], 41=>["author_word", 50], 51=>["year", 55]}
459
+ end
460
+
461
+ it 'should parse names with spaces inconsistencies at the start and the end and in the middle' do
462
+ parse(" Asplenium X inexpectatum (E.L. Braun 1940) Morton (1956) ").should_not be_nil
463
+ end
464
+
465
+ it 'should not parse serveral authors groups with several years NOT CORRECT' do
466
+ parse("Pseudocercospora dendrobii (H.C. Burnett 1883) (Leight.) (Movss. 1967) U. Braun & Crous 2003").should be_nil
467
+ end
468
+
469
+ it "should not parse unallowed utf-8 chars in name part" do
470
+ parse("Érematosphaeria phaespora").should be_nil
471
+ parse("Trematosphaeria phaeáapora").should be_nil
472
+ parse("Trematоsphaeria phaeáapora").should be_nil #cyrillic o
473
+ end
474
+
475
+ it "should parse new stuff" do
476
+ sn = 'Nesticus quelpartensis Paik & Namkung, in Paik, Yaginuma & Namkung, 1969'
477
+ details(sn).should == [{:genus=>{:epitheton=>"Nesticus"}, :species=>{:epitheton=>"quelpartensis", :authorship=>"Paik & Namkung, in Paik, Yaginuma & Namkung, 1969", :basionymAuthorTeam=>{:authorTeam=>"Paik & Namkung", :author=>["Paik", "Namkung"], :exAuthorTeam=>{:authorTeam=>"Paik, Yaginuma & Namkung", :author=>["Paik", "Yaginuma", "Namkung"], :year=>"1969"}}}}]
478
+ parse('Dipoena yoshidai Ono, in Ono et al., 1991').should_not be_nil
479
+ sn = 'Choriozopella trägårdhi Lawrence, 1947'
480
+ details(sn).should == [{:genus=>{:epitheton=>"Choriozopella"}, :species=>{:epitheton=>"trägårdhi", :authorship=>"Lawrence, 1947", :basionymAuthorTeam=>{:authorTeam=>"Lawrence", :author=>["Lawrence"], :year=>"1947"}}}]
481
+ sn = 'Latrodectus mactans bishopi Kaston, 1938'
482
+ details(sn).should == [{:genus=>{:epitheton=>"Latrodectus"}, :species=>{:epitheton=>"mactans"}, :infraspecies=>[{:epitheton=>"bishopi", :rank=>"n/a", :authorship=>"Kaston, 1938", :basionymAuthorTeam=>{:authorTeam=>"Kaston", :author=>["Kaston"], :year=>"1938"}}]}]
483
+ sn = 'Diplocephalus aff. procerus Thaler, 1972'
484
+ details(sn).should == [{:genus=>{:epitheton=>"Diplocephalus"}, :species=>{:epitheton=>"procerus", :authorship=>"Thaler, 1972", :basionymAuthorTeam=>{:authorTeam=>"Thaler", :author=>["Thaler"], :year=>"1972"}}}]
485
+ sn = 'Dyarcyops birói Kulczynski, 1908'
486
+ details(sn).should == [{:genus=>{:epitheton=>"Dyarcyops"}, :species=>{:epitheton=>"birói", :authorship=>"Kulczynski, 1908", :basionymAuthorTeam=>{:authorTeam=>"Kulczynski", :author=>["Kulczynski"], :year=>"1908"}}}]
487
+ sn = 'Sparassus françoisi Simon, 1898'
488
+ details(sn).should == [{:genus=>{:epitheton=>"Sparassus"}, :species=>{:epitheton=>"françoisi", :authorship=>"Simon, 1898", :basionymAuthorTeam=>{:authorTeam=>"Simon", :author=>["Simon"], :year=>"1898"}}}]
489
+ sn = 'Thiobacillus x Parker and Prisk 1953' #have to figure out black lists for this one
490
+ sn = 'Bacille de Plaut, Kritchevsky and Séguin 1921'
491
+ details(sn).should == [{:uninomial=>{:epitheton=>"Bacille", :authorship=>"de Plaut, Kritchevsky and Séguin 1921", :basionymAuthorTeam=>{:authorTeam=>"de Plaut, Kritchevsky and Séguin", :author=>["de Plaut", "Kritchevsky", "Séguin"], :year=>"1921"}}}]
492
+ sn = 'Araneus van bruysseli Petrunkevitch, 1911'
493
+ details(sn).should == [{:genus=>{:epitheton=>"Araneus"}, :species=>{:epitheton=>"van"}, :infraspecies=>[{:epitheton=>"bruysseli", :rank=>"n/a", :authorship=>"Petrunkevitch, 1911", :basionymAuthorTeam=>{:authorTeam=>"Petrunkevitch", :author=>["Petrunkevitch"], :year=>"1911"}}]}]
494
+ sn = 'Sapromyces laidlawi ab Sabin 1941'
495
+ details(sn).should == [{:genus=>{:epitheton=>"Sapromyces"}, :species=>{:epitheton=>"laidlawi", :authorship=>"ab Sabin 1941", :basionymAuthorTeam=>{:authorTeam=>"ab Sabin", :author=>["ab Sabin"], :year=>"1941"}}}]
496
+ sn = 'Nocardia rugosa di Marco and Spalla 1957'
497
+ details(sn).should == [{:genus=>{:epitheton=>"Nocardia"}, :species=>{:epitheton=>"rugosa", :authorship=>"di Marco and Spalla 1957", :basionymAuthorTeam=>{:authorTeam=>"di Marco and Spalla", :author=>["di Marco", "Spalla"], :year=>"1957"}}}]
498
+ sn = 'Flexibacter elegans Lewin 1969 non Soriano 1945'
499
+ details(sn).should == [{:genus=>{:epitheton=>"Flexibacter"}, :species=>{:epitheton=>"elegans", :authorship=>"Lewin 1969 non Soriano 1945", :basionymAuthorTeam=>{:authorTeam=>"Lewin", :author=>["Lewin"], :year=>"1969"}}}]
500
+ sn = 'Flexibacter elegans Soriano 1945, non Lewin 1969'
501
+ details(sn).should == [{:genus=>{:epitheton=>"Flexibacter"}, :species=>{:epitheton=>"elegans", :authorship=>"Soriano 1945, non Lewin 1969", :basionymAuthorTeam=>{:authorTeam=>"Soriano", :author=>["Soriano"], :year=>"1945"}}}]
502
+ end
503
+
504
+ end