icu_tournament 0.8.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,156 @@
1
+ module ICU
2
+ class Util
3
+
4
+ =begin rdoc
5
+
6
+ == Loading the best CSV parser
7
+
8
+ For Ruby 1.8.7 the CSV parser used here was FasterCSV (in preference to CSV from the standard library).
9
+ From Ruby 1.9.1, the standard CSV is replaced by an updated version of FasterCSV while the old
10
+ FasterCSV remains incompatible with the new Ruby. ICU::Util::CSV is therefore set to whichever
11
+ class is the right one for the version of Ruby being used.
12
+
13
+ == Parsing dates
14
+
15
+ Parse dates into yyyy-mm-dd format, preferring European over US convention. Returns nil on error.
16
+
17
+ Util.parsedate('1955-11-09') # => '1955-11-09'
18
+ Util.parsedate('02/03/2009') # => '2009-03-02'
19
+ Util.parsedate('02/23/2009') # => '2009-02-23'
20
+ Util.parsedate('16th June 1986') # => '1986-06-16'
21
+
22
+ Note that the parse method of the Date class behaves differently in Ruby 1.8.7 and 1.9.1.
23
+ In 1.8.7 it assumes American dates and will raise ArgumentError on "30/03/2003".
24
+ In 1.9.1 it assumes European dates and will raise ArgumentError on "03/30/2003".
25
+
26
+ =end
27
+
28
+ if RUBY_VERSION > '1.9'
29
+ require 'csv'
30
+ CSV = ::CSV
31
+ else
32
+ require 'fastercsv'
33
+ CSV = ::FasterCSV
34
+ end
35
+
36
+ def self.parsedate(date)
37
+ date = date.to_s.strip
38
+ return nil unless date.match(/[1-9]/)
39
+ date = [$3].concat($2.to_i > 12 ? [$1, $2] : [$2, $1]).join('-') if date.match(/^(\d{1,2}).(\d{1,2}).(\d{4})$/)
40
+ begin
41
+ Date.parse(date, true).to_s
42
+ rescue
43
+ nil
44
+ end
45
+ end
46
+ end
47
+
48
+ module Accessor
49
+ def attr_accessor(name, &block)
50
+ attr_reader name
51
+ if block
52
+ define_method("#{name}=") do |val|
53
+ val = block.call(val)
54
+ instance_variable_set("@#{name}", val)
55
+ end
56
+ end
57
+ end
58
+
59
+ def attr_integer(*names)
60
+ names.each do |name|
61
+ attr_accessor(name) do |val|
62
+ tmp = val.to_i
63
+ raise "invalid integer (#{val}) for #{name}" unless val.is_a?(Fixnum) || (val.is_a?(String) && val.include?(tmp.to_s))
64
+ tmp
65
+ end
66
+ end
67
+ end
68
+
69
+ def attr_integer_or_nil(*names)
70
+ names.each do |name|
71
+ attr_accessor(name) do |val|
72
+ tmp = case val
73
+ when nil then nil
74
+ when Fixnum then val
75
+ when /^\s*$/ then nil
76
+ else val.to_i
77
+ end
78
+ raise "invalid integer (#{val}) for #{name}" if tmp == 0 && val.is_a?(String) && !val.include?('0')
79
+ tmp
80
+ end
81
+ end
82
+ end
83
+
84
+ def attr_positive(*names)
85
+ names.each do |name|
86
+ attr_accessor(name) do |val|
87
+ tmp = val.to_i
88
+ raise "invalid positive integer (#{val}) for #{name}" unless tmp > 0
89
+ tmp
90
+ end
91
+ end
92
+ end
93
+
94
+ def attr_positive_or_nil(*names)
95
+ names.each do |name|
96
+ attr_accessor(name) do |val|
97
+ tmp = case val
98
+ when nil then nil
99
+ when Fixnum then val
100
+ when /^\s*$/ then nil
101
+ else val.to_i
102
+ end
103
+ raise "invalid positive integer or nil (#{val}) for #{name}" unless tmp.nil? || tmp > 0
104
+ tmp
105
+ end
106
+ end
107
+ end
108
+
109
+ def attr_date(*names)
110
+ names.each do |name|
111
+ attr_accessor(name) do |val|
112
+ tmp = val.to_s.strip
113
+ tmp = ICU::Util::parsedate(tmp)
114
+ raise "invalid date (#{val}) for #{name}" unless tmp
115
+ tmp
116
+ end
117
+ end
118
+ end
119
+
120
+ def attr_date_or_nil(*names)
121
+ names.each do |name|
122
+ attr_accessor(name) do |val|
123
+ tmp = val.to_s.strip
124
+ if tmp == ''
125
+ tmp = nil
126
+ else
127
+ tmp = ICU::Util::parsedate(tmp)
128
+ raise "invalid date or nil (#{val}) for #{name}" unless tmp
129
+ end
130
+ tmp
131
+ end
132
+ end
133
+ end
134
+
135
+ def attr_string(regex, *names)
136
+ names.each do |name|
137
+ attr_accessor(name) do |val|
138
+ tmp = val.to_s.strip
139
+ raise "invalid #{name} (#{val})" unless tmp.match(regex)
140
+ tmp
141
+ end
142
+ end
143
+ end
144
+
145
+ def attr_string_or_nil(regex, *names)
146
+ names.each do |name|
147
+ attr_accessor(name) do |val|
148
+ tmp = val.to_s.strip
149
+ tmp = nil if tmp == ''
150
+ raise "invalid #{name} (#{val})" unless tmp.nil? || tmp.match(regex)
151
+ tmp
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,176 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ module ICU
4
+ describe Federation do
5
+ context "#find using codes" do
6
+ it "should find a federation given a valid code" do
7
+ fed = Federation.find('IRL')
8
+ fed.code.should == 'IRL'
9
+ fed.name.should == 'Ireland'
10
+ end
11
+
12
+ it "should find a federation from code case insensitively" do
13
+ fed = Federation.find('rUs')
14
+ fed.code.should == 'RUS'
15
+ fed.name.should == 'Russia'
16
+ end
17
+
18
+ it "should find a federation despite irrelevant whitespace" do
19
+ fed = Federation.find(' mex ')
20
+ fed.code.should == 'MEX'
21
+ fed.name.should == 'Mexico'
22
+ end
23
+
24
+ it "should return nil for an invalid code" do
25
+ Federation.find('XYZ').should be_nil
26
+ end
27
+ end
28
+
29
+ context "#find using names" do
30
+ it "should find a federation given a valid name" do
31
+ fed = Federation.find('England')
32
+ fed.code.should == 'ENG'
33
+ fed.name.should == 'England'
34
+ end
35
+
36
+ it "should find a federation from name case insensitively" do
37
+ fed = Federation.find('franCE')
38
+ fed.code.should == 'FRA'
39
+ fed.name.should == 'France'
40
+ end
41
+
42
+ it "should not be fooled by irrelevant whitespace" do
43
+ fed = Federation.find(' united states of america ')
44
+ fed.code.should == 'USA'
45
+ fed.name.should == 'United States of America'
46
+ end
47
+
48
+ it "should return nil for an invalid name" do
49
+ Federation.find('Mordor').should be_nil
50
+ end
51
+ end
52
+
53
+ context "#find using parts of names" do
54
+ it "should find a federation given a substring which is unique and at least 4 characters" do
55
+ fed = Federation.find('bosni')
56
+ fed.code.should == 'BIH'
57
+ fed.name.should == 'Bosnia and Herzegovina'
58
+ end
59
+
60
+ it "should not be fooled by irrelevant whitespace" do
61
+ fed = Federation.find(' arab EMIRATES ')
62
+ fed.code.should == 'UAE'
63
+ fed.name.should == 'United Arab Emirates'
64
+ end
65
+
66
+ it "should not find a federation if the substring matches more than one" do
67
+ Federation.find('land').should be_nil
68
+ end
69
+
70
+ it "should return nil for any string smaller in length than 3" do
71
+ Federation.find('ze').should be_nil
72
+ end
73
+ end
74
+
75
+ context "#find federations with alternative names" do
76
+ it "should find Macedonia multiple ways" do
77
+ Federation.find('MKD').name.should == 'Macedonia'
78
+ Federation.find('FYROM').name.should == 'Macedonia'
79
+ Federation.find('macedoni').name.should == 'Macedonia'
80
+ Federation.find('Macedonia').name.should == 'Macedonia'
81
+ Federation.find('former YUG Rep').name.should == 'Macedonia'
82
+ Federation.find('Republic of Macedonia').name.should == 'Macedonia'
83
+ Federation.find('former yugoslav republic').name.should == 'Macedonia'
84
+ end
85
+ end
86
+
87
+ context "#find with alternative inputs" do
88
+ it "should behave robustly with completely invalid inputs" do
89
+ Federation.find().should be_nil
90
+ Federation.find(nil).should be_nil
91
+ Federation.find('').should be_nil
92
+ Federation.find(1).should be_nil
93
+ end
94
+ end
95
+
96
+ context "#new is private" do
97
+ it "#new cannot be called directly" do
98
+ lambda { Federation.new('IRL', 'Ireland') }.should raise_error(/private method/)
99
+ end
100
+ end
101
+
102
+ context "documentation examples" do
103
+ it "should all be correct for valid input" do
104
+ Federation.find('IRL').name.should == 'Ireland'
105
+ Federation.find('IRL').code.should == 'IRL'
106
+ Federation.find('rUs').code.should == 'RUS'
107
+ Federation.find('ongoli').name.should == 'Mongolia'
108
+ Federation.find(' united states ').code.should == 'USA'
109
+ end
110
+
111
+ it "should return nil for invalid input" do
112
+ Federation.find('ZYX').should be_nil
113
+ Federation.find('land').should be_nil
114
+ end
115
+ end
116
+
117
+ context "#menu" do
118
+ before(:all) do
119
+ @total = 173
120
+ end
121
+
122
+ it "should return array of name-code pairs in order of name by default" do
123
+ menu = Federation.menu
124
+ menu.should have(@total).items
125
+ names = menu.map{|m| m.first}.join(',')
126
+ codes = menu.map{|m| m.last}.join(',')
127
+ names.index('Afghanistan').should == 0
128
+ names.index('Iraq,Ireland,Israel').should_not be_nil
129
+ codes.index('AFG').should == 0
130
+ codes.index('IRQ,IRL,ISR').should_not be_nil
131
+ end
132
+
133
+ it "should be configuarble to order the list by codes" do
134
+ menu = Federation.menu(:order => "code")
135
+ menu.should have(@total).items
136
+ names = menu.map{|m| m.first}.join(',')
137
+ codes = menu.map{|m| m.last}.join(',')
138
+ names.index('Afghanistan').should == 0
139
+ names.index('Ireland,Iraq,Iceland').should_not be_nil
140
+ codes.index('AFG').should == 0
141
+ codes.index('IRL,IRQ,ISL').should_not be_nil
142
+ end
143
+
144
+ it "should be configuarble to have a selected country at the top" do
145
+ menu = Federation.menu(:top => 'IRL')
146
+ menu.should have(@total).items
147
+ names = menu.map{|m| m.first}.join(',')
148
+ codes = menu.map{|m| m.last}.join(',')
149
+ names.index('Ireland,Afghanistan').should == 0
150
+ names.index('Iraq,Israel').should_not be_nil
151
+ codes.index('IRL,AFG').should == 0
152
+ codes.index('IRQ,ISR').should_not be_nil
153
+ end
154
+
155
+ it "should be configuarble to have 'None' entry at the top" do
156
+ menu = Federation.menu(:none => 'None')
157
+ menu.should have(@total + 1).items
158
+ names = menu.map{|m| m.first}.join(',')
159
+ codes = menu.map{|m| m.last}.join(',')
160
+ names.index('None,Afghanistan').should == 0
161
+ codes.index(',AFG').should == 0
162
+ end
163
+
164
+ it "should be able to handle multiple configuarations" do
165
+ menu = Federation.menu(:top => 'IRL', :order => 'code', :none => 'None')
166
+ menu.should have(@total + 1).items
167
+ names = menu.map{|m| m.first}.join(',')
168
+ codes = menu.map{|m| m.last}.join(',')
169
+ names.index('None,Ireland,Afghanistan').should == 0
170
+ names.index('Iraq,Iceland').should_not be_nil
171
+ codes.index(',IRL,AFG').should == 0
172
+ codes.index('IRQ,ISL').should_not be_nil
173
+ end
174
+ end
175
+ end
176
+ end
data/spec/name_spec.rb ADDED
@@ -0,0 +1,208 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ module ICU
4
+ describe Name do
5
+ context "public methods" do
6
+ before(:each) do
7
+ @simple = Name.new('mark j l', 'orr')
8
+ end
9
+
10
+ it "#first returns the first name(s)" do
11
+ @simple.first.should == 'Mark J. L.'
12
+ end
13
+
14
+ it "#last returns the last name(s)" do
15
+ @simple.last.should == 'Orr'
16
+ end
17
+
18
+ it "#name returns the full name with first name(s) first" do
19
+ @simple.name.should == 'Mark J. L. Orr'
20
+ end
21
+
22
+ it "#rname returns the full name with last name(s) first" do
23
+ @simple.rname.should == 'Orr, Mark J. L.'
24
+ end
25
+
26
+ it "#to_s is the same as rname" do
27
+ @simple.to_s.should == 'Orr, Mark J. L.'
28
+ end
29
+
30
+ it "#match returns true if and only if two names match" do
31
+ @simple.match('mark j l orr').should be_true
32
+ @simple.match('malcolm g l orr').should be_false
33
+ end
34
+ end
35
+
36
+ context "rdoc expample" do
37
+ before(:each) do
38
+ @robert = Name.new(' robert j ', ' FISCHER ')
39
+ @bobby = Name.new(' bobby fischer ')
40
+ end
41
+
42
+ it "should get Robert" do
43
+ @robert.name.should == 'Robert J. Fischer'
44
+ end
45
+
46
+ it "should get Bobby" do
47
+ @bobby.last.should == 'Fischer'
48
+ @bobby.first.should == 'Bobby'
49
+ end
50
+
51
+ it "should match Robert and Bobby" do
52
+ @robert.match(@bobby).should be_true
53
+ @robert.match('R. J.', 'Fischer').should be_true
54
+ @bobby.match('R. J.', 'Fischer').should be_false
55
+ end
56
+
57
+ it "should canconicalise last names" do
58
+ Name.new('John', 'O Reilly').last.should == "O'Reilly"
59
+ Name.new('dave', 'mcmanus').last.should == "McManus"
60
+ Name.new('pete', 'MACMANUS').last.should == "MacManus"
61
+ end
62
+ end
63
+
64
+ context "names that are already canonical" do
65
+ it "should not be altered" do
66
+ Name.new('Mark J. L.', 'Orr').name.should == 'Mark J. L. Orr'
67
+ Name.new('Anna-Marie J.-K.', 'Liviu-Dieter').name.should == 'Anna-Marie J.-K. Liviu-Dieter'
68
+ end
69
+ end
70
+
71
+ context "last names beginning with a single letter followed by a quote" do
72
+ it "should be handled correctly" do
73
+ Name.new('una', "O'boyle").name.should == "Una O'Boyle"
74
+ Name.new('jonathan', 'd`arcy').name.should == "Jonathan D'Arcy"
75
+ Name.new('erwin e', "L'AMI").name.should == "Erwin E. L'Ami"
76
+ Name.new('cormac', "o brien").name.should == "Cormac O'Brien"
77
+ end
78
+ end
79
+
80
+ context "last beginning with Mc" do
81
+ it "should be handled correctly" do
82
+ Name.new('shane', "mccabe").name.should == "Shane McCabe"
83
+ Name.new('shawn', "macDonagh").name.should == "Shawn MacDonagh"
84
+ Name.new('shawn', "macdonagh").name.should == "Shawn Macdonagh"
85
+ Name.new('bartlomiej', "macieja").name.should == "Bartlomiej Macieja"
86
+ end
87
+ end
88
+
89
+ context "doubled barrelled names or initials" do
90
+ it "should be handled correctly" do
91
+ Name.new('anna-marie', 'den-otter').name.should == 'Anna-Marie Den-Otter'
92
+ Name.new('j-k', 'rowling').name.should == 'J.-K. Rowling'
93
+ Name.new("mark j. - l", 'ORR').name.should == 'Mark J.-L. Orr'
94
+ Name.new('JOHANNA', "lowry-o'REILLY").name.should == "Johanna Lowry-O'Reilly"
95
+ Name.new('hannah', "lowry - o reilly").name.should == "Hannah Lowry-O'Reilly"
96
+ end
97
+ end
98
+
99
+ context "extraneous white space" do
100
+ it "should be handled correctly" do
101
+ Name.new(' mark j l ', " \t\r\n orr \n").name.should == 'Mark J. L. Orr'
102
+ end
103
+ end
104
+
105
+ context "extraneous full stops" do
106
+ it "should be handled correctly" do
107
+ Name.new('. mark j..l', 'orr.').name.should == 'Mark J. L. Orr'
108
+ end
109
+ end
110
+
111
+ context "construction from a single string" do
112
+ before(:each) do
113
+ @mark1 = Name.new('ORR, mark j l')
114
+ @mark2 = Name.new('MARK J L ORR')
115
+ @oreil = Name.new("O'Reilly, j-k")
116
+ end
117
+
118
+ it "should be possible in simple cases" do
119
+ @mark1.first.should == 'Mark J. L.'
120
+ @mark1.last.should == 'Orr'
121
+ @mark2.first.should == 'Mark J. L.'
122
+ @mark2.last.should == 'Orr'
123
+ @oreil.name.should == "J.-K. O'Reilly"
124
+ end
125
+ end
126
+
127
+ context "construction from an instance" do
128
+ it "should be possible" do
129
+ Name.new(Name.new('ORR, mark j l')).name.should == 'Mark J. L. Orr'
130
+ end
131
+ end
132
+
133
+ context "constuction corner cases" do
134
+ it "should be handled correctly" do
135
+ Name.new('Orr').name.should == 'Orr'
136
+ Name.new('Orr').rname.should == 'Orr'
137
+ Name.new('').name.should == ''
138
+ Name.new('').rname.should == ''
139
+ Name.new.name.should == ''
140
+ Name.new.rname.should == ''
141
+ end
142
+ end
143
+
144
+ context "inputs to matching" do
145
+ before(:all) do
146
+ @mark = Name.new('Mark', 'Orr')
147
+ @kram = Name.new('Mark', 'Orr')
148
+ end
149
+
150
+ it "should be flexible" do
151
+ @mark.match('Mark', 'Orr').should be_true
152
+ @mark.match('Mark Orr').should be_true
153
+ @mark.match('Orr, Mark').should be_true
154
+ @mark.match(@kram).should be_true
155
+ end
156
+ end
157
+
158
+ context "first name matches" do
159
+ it "should match when first names are the same" do
160
+ Name.new('Mark', 'Orr').match('Mark', 'Orr').should be_true
161
+ end
162
+
163
+ it "should be flexible with regards to hyphens in double barrelled names" do
164
+ Name.new('J.-K.', 'Rowling').match('J. K.', 'Rowling').should be_true
165
+ Name.new('Joanne-K.', 'Rowling').match('Joanne K.', 'Rowling').should be_true
166
+ end
167
+
168
+ it "should match initials" do
169
+ Name.new('M. J. L.', 'Orr').match('Mark John Legard', 'Orr').should be_true
170
+ Name.new('M.', 'Orr').match('Mark', 'Orr').should be_true
171
+ Name.new('M. J. L.', 'Orr').match('Mark', 'Orr').should be_true
172
+ Name.new('M.', 'Orr').match('M. J.', 'Orr').should be_true
173
+ Name.new('M. J. L.', 'Orr').match('M. G.', 'Orr').should be_false
174
+ end
175
+
176
+ it "should not match on full names not in first position or without an exact match" do
177
+ Name.new('J. M.', 'Orr').match('John', 'Orr').should be_true
178
+ Name.new('M. J.', 'Orr').match('John', 'Orr').should be_false
179
+ Name.new('M. John', 'Orr').match('John', 'Orr').should be_true
180
+ end
181
+
182
+ it "should handle common nicknames" do
183
+ Name.new('William', 'Orr').match('Bill', 'Orr').should be_true
184
+ Name.new('David', 'Orr').match('Dave', 'Orr').should be_true
185
+ Name.new('Mick', 'Orr').match('Mike', 'Orr').should be_true
186
+ end
187
+
188
+ it "should not mix up nick names" do
189
+ Name.new('David', 'Orr').match('Bill', 'Orr').should be_false
190
+ end
191
+ end
192
+
193
+ context "last name matches" do
194
+ it "should be flexible with regards to hyphens in double barrelled names" do
195
+ Name.new('Johanna', "Lowry-O'Reilly").match('Johanna', "Lowry O'Reilly").should be_true
196
+ end
197
+
198
+ it "should be case insensitive in matches involving Macsomething and MacSomething" do
199
+ Name.new('Alan', 'MacDonagh').match('Alan', 'Macdonagh').should be_true
200
+ end
201
+
202
+ it "should cater for the common mispelling of names beginning with Mc or Mac" do
203
+ Name.new('Alan', 'McDonagh').match('Alan', 'MacDonagh').should be_true
204
+ Name.new('Darko', 'Polimac').match('Darko', 'Polimc').should be_false
205
+ end
206
+ end
207
+ end
208
+ end