boston_marathon 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +12 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +28 -0
- data/README.md +99 -0
- data/Rakefile +11 -0
- data/boston_marathon.gemspec +29 -0
- data/lib/boston_marathon/record_not_found.rb +7 -0
- data/lib/boston_marathon/result.rb +95 -0
- data/lib/boston_marathon/result_set.rb +18 -0
- data/lib/boston_marathon/version.rb +3 -0
- data/lib/boston_marathon.rb +40 -0
- data/test/helper.rb +14 -0
- data/test/test_boston_marathon.rb +97 -0
- data/test/test_result.rb +47 -0
- data/test/vcr_cassettes/bad_bib.yml +175 -0
- data/test/vcr_cassettes/bib_200.yml +152 -0
- data/test/vcr_cassettes/bib_43.yml +154 -0
- data/test/vcr_cassettes/ryan_hall.yml +156 -0
- data/test/vcr_cassettes/top_20_women.yml +1226 -0
- metadata +186 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
boston_marathon (0.0.1)
|
5
|
+
nokogiri (= 1.5.0)
|
6
|
+
rest-client (= 1.6.7)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
specs:
|
11
|
+
fakeweb (1.3.0)
|
12
|
+
mime-types (1.17.2)
|
13
|
+
nokogiri (1.5.0)
|
14
|
+
redgreen (1.2.2)
|
15
|
+
rest-client (1.6.7)
|
16
|
+
mime-types (>= 1.16)
|
17
|
+
shoulda-context (1.0.0)
|
18
|
+
vcr (1.11.3)
|
19
|
+
|
20
|
+
PLATFORMS
|
21
|
+
ruby
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
boston_marathon!
|
25
|
+
fakeweb (= 1.3.0)
|
26
|
+
redgreen (>= 1.2.2)
|
27
|
+
shoulda-context (~> 1.0.0)
|
28
|
+
vcr (= 1.11.3)
|
data/README.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
Boston Marathon
|
2
|
+
===============
|
3
|
+
|
4
|
+
A Ruby wrapper for race results from the [2011 Boston Marathon](http://www.baa.org/races/boston-marathon/results-commentary/results-search.aspx).
|
5
|
+
Result data is retrieved by parsing HTML documents with [Nokogiri](http://nokogiri.org/),
|
6
|
+
so this version of the gem will work as long as the format of the site doesn't change.
|
7
|
+
|
8
|
+
Install / Getting Started
|
9
|
+
-------------------------
|
10
|
+
|
11
|
+
gem install boston_marathon
|
12
|
+
|
13
|
+
Usage
|
14
|
+
-----
|
15
|
+
|
16
|
+
### Look up an athlete's result by their bib number
|
17
|
+
|
18
|
+
result = BostonMarathon.result(43)
|
19
|
+
result.name # => 'Krah, Nathan M.'
|
20
|
+
result.time # => '2:22:24'
|
21
|
+
result.pace # => '0:05:26'
|
22
|
+
result.place # => '29'
|
23
|
+
|
24
|
+
Like ActiveRecord, a RecordNotFound exception is raised when looking up a bib that has no result.
|
25
|
+
|
26
|
+
### Search the results
|
27
|
+
|
28
|
+
This method will return a maximum of 25 entries, a limit imposed by the Boston Marathon website.
|
29
|
+
To specify a smaller limit, use the VarTargetCount param.
|
30
|
+
|
31
|
+
top_20_women = BostonMarathon.results({'GenderID' => 2, 'VarTargetCount' => 20})
|
32
|
+
top_20_women.each do |result|
|
33
|
+
puts "#{result.gender_place.rjust(2)}. #{result.name}"
|
34
|
+
end
|
35
|
+
|
36
|
+
Query Params for #results
|
37
|
+
-------------------------
|
38
|
+
|
39
|
+
- BibNumber
|
40
|
+
- LastName
|
41
|
+
- FirstName
|
42
|
+
- GenderID
|
43
|
+
- City
|
44
|
+
- StateID
|
45
|
+
- CountryOfResID
|
46
|
+
- ReportingSegmentID
|
47
|
+
- QualClassID
|
48
|
+
- AwardsDivisionID
|
49
|
+
- VarTargetCount
|
50
|
+
|
51
|
+
### IDs for genders
|
52
|
+
|
53
|
+
{"1" => "M", "2" => "F"}
|
54
|
+
|
55
|
+
### IDs for reporting segments
|
56
|
+
|
57
|
+
{"1" => "Runners", "2" => "Wheelchairs", "3" => "Handcycles"}
|
58
|
+
|
59
|
+
### IDs for qualification classes
|
60
|
+
|
61
|
+
{"8" => "Handcycle", "2" => "Mobility Impaired", "4" => "Push Rim Wheelchair Class 1 (Quad)", "5" => "Push Rim Wheelchair Class 2 (Quad)", "6" => "Push Rim Wheelchair Class 3 (Open)", "7" => "Push Rim Wheelchair Class 4 (Open)", "1" => "Visually Impaired (B1, B2, B3)"}
|
62
|
+
|
63
|
+
### IDs for awards divisions
|
64
|
+
|
65
|
+
{"1" => "18-39 Age Group", "2" => "40-44 Age Group", "9" => "45-49 Age Group", "3" => "50-54 Age Group", "10" => "55-59 Age Group", "4" => "60-64 Age Group", "11" => "65-69 Age Group", "8" => "70-74 Age Group", "12" => "75-79 Age Group", "13" => "80+ Age Group"}
|
66
|
+
|
67
|
+
### IDs for states
|
68
|
+
|
69
|
+
{"5" => "Alabama", "4" => "Alaska", "64" => "Alberta", "8" => "American Samoa", "9" => "Arizona", "7" => "Arkansas", "2" => "Armed Forces Americas", "3" => "Armed Forces Europe", "6" => "Armed Forces Pacific", "65" => "British Columbia", "10" => "California", "11" => "Colorado", "12" => "Connecticut", "14" => "Delaware", "13" => "District Of Columbia", "16" => "Federated States of Micronesia", "15" => "Florida", "17" => "Georgia", "18" => "Guam", "19" => "Hawaii", "21" => "Idaho", "22" => "Illinois", "23" => "Indiana", "20" => "Iowa", "24" => "Kansas", "25" => "Kentucky", "26" => "Louisiana", "29" => "Maine", "67" => "Manitoba", "30" => "Marshall Island", "28" => "Maryland", "27" => "Massachusetts", "31" => "Michigan", "32" => "Minnesota", "35" => "Mississippi", "33" => "Missouri", "36" => "Montana", "39" => "Nebraska", "43" => "Nevada", "68" => "New Brunswick", "40" => "New Hampshire", "41" => "New Jersey", "42" => "New Mexico", "44" => "New York", "66" => "Newfoundland and Labrador", "37" => "North Carolina", "38" => "North Dakota", "34" => "Northern Mariana Islands", "71" => "Northwest Territories", "70" => "Nova Scotia", "77" => "Nunavut", "45" => "Ohio", "46" => "Oklahoma", "72" => "Ontario", "47" => "Oregon", "50" => "Palau", "48" => "Pennsylvania", "73" => "Prince Edward Island", "49" => "Puerto Rico", "74" => "Quebec", "51" => "Rhode Island", "75" => "Saskatchewan", "52" => "South Carolina", "53" => "South Dakota", "54" => "Tennessee", "55" => "Texas", "56" => "Utah", "59" => "Vermont", "58" => "Virgin Islands", "57" => "Virginia", "60" => "Washington", "62" => "West Virginia", "61" => "Wisconsin", "63" => "Wyoming", "76" => "Yukon"}
|
70
|
+
|
71
|
+
### IDs for countries
|
72
|
+
|
73
|
+
{"196"=>"Venezuela", "168"=>"Saint Lucia", "54"=>"Djibouti", "25"=>"Belize", "82"=>"Hong Kong", "197"=>"Vietnam", "169"=>"Sao Tome and Principe", "26"=>"Burkina Faso", "110"=>"Luxembourg", "83"=>"Honduras", "55"=>"Dominica", "27"=>"Belarus", "84"=>"Hungary", "140"=>"Oman", "56"=>"Dominican Republic", "28"=>"Bolivia", "112"=>"Madagascar", "85"=>"Indonesia", "141"=>"Pakistan", "113"=>"Morocco", "29"=>"Botswana", "86"=>"India", "57"=>"Ecuador", "170"=>"Saint Vincent and the Grenadines", "142"=>"Palestine", "114"=>"Malaysia", "87"=>"Ireland", "58"=>"Egypt", "171"=>"Sudan", "143"=>"Panama", "115"=>"Malawi", "59"=>"El Salvador", "172"=>"Switzerland", "144"=>"Paraguay", "116"=>"Mexico", "30"=>"Brazil", "88"=>"Islamic Republic of Iran", "200"=>"Yugoslavia", "173"=>"Suriname", "145"=>"Peru", "117"=>"Mongolia", "31"=>"Brunei Darussalam", "89"=>"Iraq", "60"=>"Spain", "174"=>"Slovakia", "146"=>"Philippines", "118"=>"Maldives", "32"=>"Bulgaria", "201"=>"Congo, The Democratic Republic of the", "202"=>"Zambia", "175"=>"Sweden", "147"=>"Poland", "119"=>"Mali", "61"=>"Estonia", "203"=>"Zimbabwe", "176"=>"Swaziland", "148"=>"Portugal", "90"=>"Iceland", "62"=>"Ethiopia", "177"=>"Syrian Arab Republic", "34"=>"British Virgin Islands", "91"=>"Israel", "149"=>"Korea, Democratic People's Republic of", "63"=>"Fiji", "204"=>"Burundi", "178"=>"Tanzania, United Republic of", "35"=>"Central African Republic", "64"=>"Finland", "205"=>"Bosnia and Herzegovina", "179"=>"Thailand", "120"=>"Malta", "2"=>"Afghanistan", "93"=>"Italy", "65"=>"France", "206"=>"Eritrea", "36"=>"Canada", "3"=>"Netherlands Antilles", "94"=>"Jamaica", "37"=>"Cayman Islands", "150"=>"Yemen", "122"=>"Moldova, Republic of", "4"=>"Albania", "95"=>"Jordan", "66"=>"Gabon", "208"=>"Lao People's Democratic Republic", "38"=>"Congo", "209"=>"Slovenia", "151"=>"Qatar", "123"=>"Monaco", "5"=>"Algeria", "96"=>"Japan", "67"=>"Gambia", "39"=>"Chad", "68"=>"United Kingdom", "180"=>"Tajikistan", "124"=>"Mozambique", "97"=>"Kenya", "10"=>"Antigua and Barbuda", "181"=>"Turkmenistan", "153"=>"Romania", "125"=>"Mauritius", "7"=>"Andorra", "98"=>"Kyrgyzstan", "69"=>"Guinea-Bissau", "154"=>"South Africa", "11"=>"Argentina", "40"=>"Chile", "8"=>"Angola", "183"=>"Togo", "155"=>"Russian Federation", "12"=>"Armenia", "210"=>"Macedonia, The Former Yugoslav Republic of", "127"=>"Mauritania", "41"=>"China", "99"=>"Cambodia", "184"=>"Tonga", "211"=>"Taiwan", "156"=>"Rwanda", "128"=>"Myanmar", "13"=>"Aruba", "70"=>"Georgia", "42"=>"Cote d'Ivoire (Ivory Coast)", "212"=>"Serbia and Montenegro", "157"=>"Samoa", "129"=>"Namibia", "14"=>"Australia", "71"=>"Equatorial Guinea", "43"=>"Cook Islands", "186"=>"Trinidad and Tobago", "158"=>"Saudi Arabia", "72"=>"Germany", "15"=>"Austria", "187"=>"Tunisia", "159"=>"Senegal", "100"=>"Korea, Republic of", "73"=>"Ghana", "44"=>"Cameroon", "16"=>"Azerbaijan", "188"=>"Turkey", "101"=>"Kuwait", "45"=>"Colombia", "17"=>"Bahamas", "189"=>"United Arab Emirates", "130"=>"Nauru", "75"=>"Greece", "46"=>"Comoros", "18"=>"Bangladesh", "102"=>"Kazakhstan", "131"=>"Nicaragua", "76"=>"Grenada", "47"=>"Costa Rica", "19"=>"Barbados", "103"=>"Latvia", "160"=>"Seychelles", "132"=>"Netherlands", "48"=>"Croatia", "104"=>"Libyan Arab Jamahiriya", "161"=>"Singapore", "133"=>"Nepal", "77"=>"Guatemala", "49"=>"Cuba", "105"=>"Liberia", "190"=>"Uganda", "162"=>"Sierra Leone", "134"=>"Nigeria", "106"=>"Lesotho", "20"=>"Belgium", "78"=>"Guinea", "191"=>"Ukraine", "163"=>"San Marino", "135"=>"Papua New Guinea", "107"=>"Lebanon", "21"=>"Benin", "192"=>"Uruguay", "164"=>"Solomon Islands", "136"=>"Niger", "50"=>"Cape Verde", "108"=>"Liechtenstein", "193"=>"United States of America", "165"=>"Somalia", "51"=>"Cyprus", "109"=>"Lithuania", "22"=>"Bermuda", "194"=>"Uzbekistan", "166"=>"Sri Lanka", "138"=>"Norway", "52"=>"Czech Republic", "23"=>"Bahrain", "80"=>"Guyana", "195"=>"Vanatu", "167"=>"Saint Kitts and Nevis", "139"=>"New Zealand", "53"=>"Denmark", "24"=>"Bhutan", "81"=>"Haiti"}
|
74
|
+
|
75
|
+
License
|
76
|
+
-------
|
77
|
+
|
78
|
+
(The MIT License)
|
79
|
+
|
80
|
+
Copyright (c) 2011 John B. Hall
|
81
|
+
|
82
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
83
|
+
a copy of this software and associated documentation files (the
|
84
|
+
'Software'), to deal in the Software without restriction, including
|
85
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
86
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
87
|
+
permit persons to whom the Software is furnished to do so, subject to
|
88
|
+
the following conditions:
|
89
|
+
|
90
|
+
The above copyright notice and this permission notice shall be
|
91
|
+
included in all copies or substantial portions of the Software.
|
92
|
+
|
93
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
94
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
95
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
96
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
97
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
98
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
99
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "boston_marathon/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'boston_marathon'
|
7
|
+
s.version = BostonMarathon::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.date = '2011-11-25'
|
10
|
+
s.summary = "Ruby wrapper for race results from the 2011 Boston Marathon"
|
11
|
+
s.authors = ["John B. Hall"]
|
12
|
+
s.email = 'johnbrianhall@gmail.com'
|
13
|
+
|
14
|
+
s.add_runtime_dependency 'rest-client', '= 1.6.7'
|
15
|
+
s.add_runtime_dependency 'nokogiri', '= 1.5.0'
|
16
|
+
|
17
|
+
s.add_development_dependency 'redgreen', ">= 1.2.2"
|
18
|
+
s.add_development_dependency 'vcr', '1.11.3'
|
19
|
+
s.add_development_dependency 'fakeweb', '1.3.0'
|
20
|
+
s.add_development_dependency 'shoulda-context', '~> 1.0.0'
|
21
|
+
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
|
24
|
+
s.files = `git ls-files`.split("\n")
|
25
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
26
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
27
|
+
s.require_paths = ["lib"]
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module BostonMarathon
|
2
|
+
|
3
|
+
class Result
|
4
|
+
|
5
|
+
ATTR_TRANSLATE = {
|
6
|
+
'm/f' => 'gender',
|
7
|
+
'st' => 'state',
|
8
|
+
'ctry' => 'country',
|
9
|
+
'ctz' => 'citizenship',
|
10
|
+
'proj. time' => 'projected_time',
|
11
|
+
'offl. time' => 'time',
|
12
|
+
'overall' => 'place',
|
13
|
+
'gender' => 'gender_place',
|
14
|
+
'division' => 'division_place'
|
15
|
+
}
|
16
|
+
|
17
|
+
attr_reader :attrs
|
18
|
+
|
19
|
+
def initialize(header, infogrid)
|
20
|
+
@header = header # athlete bib, name, age, gender, city, state, country
|
21
|
+
@infogrid = infogrid # splits and finish info (pace, time, place, gender_place, division_place)
|
22
|
+
set_attrs
|
23
|
+
end
|
24
|
+
|
25
|
+
def method_missing(method, *args)
|
26
|
+
m = method.to_s.downcase
|
27
|
+
if @attrs.has_key? m
|
28
|
+
@attrs[m]
|
29
|
+
else
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# ATHLETE
|
37
|
+
|
38
|
+
def athlete_keys
|
39
|
+
['Bib', 'Name', 'Age', 'M/F', 'City', 'St', 'Ctry', 'Ctz', '']
|
40
|
+
end
|
41
|
+
|
42
|
+
def athlete_values
|
43
|
+
cleanse(@header.css('td'))
|
44
|
+
end
|
45
|
+
|
46
|
+
# RESULT (which contains splits and finish info)
|
47
|
+
|
48
|
+
def results_rows
|
49
|
+
@results_rows ||= @infogrid.css('tr')
|
50
|
+
end
|
51
|
+
|
52
|
+
# SPLITS
|
53
|
+
|
54
|
+
def split_keys
|
55
|
+
cleanse(results_rows[0].css('th')[1..-1])
|
56
|
+
end
|
57
|
+
|
58
|
+
def split_values
|
59
|
+
cleanse(results_rows[1].css('td'))
|
60
|
+
end
|
61
|
+
|
62
|
+
# FINISH INFO
|
63
|
+
|
64
|
+
def finish_info_keys
|
65
|
+
cleanse(results_rows[2].css('th')[1..-1])
|
66
|
+
end
|
67
|
+
|
68
|
+
def finish_info_values
|
69
|
+
cleanse(results_rows[3].css('td'))
|
70
|
+
end
|
71
|
+
|
72
|
+
def set_attrs
|
73
|
+
athlete = hashify(athlete_keys, athlete_values)
|
74
|
+
splits = {'splits' => hashify(split_keys, split_values)}
|
75
|
+
finish_info = hashify(finish_info_keys, finish_info_values)
|
76
|
+
@attrs = athlete.merge(splits).merge(finish_info)
|
77
|
+
end
|
78
|
+
|
79
|
+
# hashify does not belong on the result model. Move it later.
|
80
|
+
# usage: hashify(["dog", "cat"], ["woof", "meow"]) # => {"cat"=>"meow", "dog"=>"woof"}
|
81
|
+
def hashify(keys, values)
|
82
|
+
keys = keys.map(&:downcase).map{ |k| ATTR_TRANSLATE.has_key?(k) ? ATTR_TRANSLATE[k] : k }
|
83
|
+
arr_of_arrs = keys.zip(values).delete_if { |arr| arr[0] == "" }
|
84
|
+
Hash[arr_of_arrs]
|
85
|
+
end
|
86
|
+
|
87
|
+
# cleanse does not belong on the result model. Move it later.
|
88
|
+
# remove whitespaces and instances of
|
89
|
+
def cleanse(arr)
|
90
|
+
arr.map{|cell| cell.inner_text.gsub(/\302\240/, ' ').strip}
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module BostonMarathon
|
2
|
+
|
3
|
+
class ResultSet < Array
|
4
|
+
|
5
|
+
def initialize(response)
|
6
|
+
noko = Nokogiri::HTML(response.body)
|
7
|
+
results = []
|
8
|
+
headers = noko.css('.tr_header')
|
9
|
+
infogrids = noko.css('.table_infogrid')
|
10
|
+
unless headers.empty? || infogrids.empty?
|
11
|
+
headers.zip(infogrids).map { |arr| results << Result.new(arr[0], arr[1]) }
|
12
|
+
end
|
13
|
+
replace(results)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require 'rest_client'
|
4
|
+
require 'nokogiri'
|
5
|
+
|
6
|
+
require 'boston_marathon/record_not_found'
|
7
|
+
require 'boston_marathon/result_set'
|
8
|
+
require 'boston_marathon/result'
|
9
|
+
|
10
|
+
module BostonMarathon
|
11
|
+
|
12
|
+
BASE_URL = "http://registration.baa.org/2011/cf/Public/iframe_ResultsSearch.cfm?mode=results"
|
13
|
+
RESULTS_PER_PAGE = 25 # up to 25 results can be retrieved at a time
|
14
|
+
RACE_CAP = 30000 # an arbitrary number that's larger than the number of race participants
|
15
|
+
|
16
|
+
class << self
|
17
|
+
|
18
|
+
# returns the result for a specific bib number
|
19
|
+
def result(bib)
|
20
|
+
result_set = fetch({'BibNumber' => bib})
|
21
|
+
raise BostonMarathon::RecordNotFound if result_set.empty?
|
22
|
+
result_set[0]
|
23
|
+
end
|
24
|
+
|
25
|
+
# returns a max of RESULTS_PER_PAGE results.
|
26
|
+
# see README for list of query params
|
27
|
+
def results(params)
|
28
|
+
fetch({'VarTargetCount' => RACE_CAP}.merge params)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def fetch(params={})
|
34
|
+
response = RestClient.post BASE_URL, params
|
35
|
+
BostonMarathon::ResultSet.new(response)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# I know it's bad to require rubygems, but I haven't yet figured out how to get around it
|
2
|
+
require 'rubygems'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'redgreen'
|
5
|
+
require 'vcr'
|
6
|
+
require 'fakeweb'
|
7
|
+
require 'shoulda-context'
|
8
|
+
require 'boston_marathon'
|
9
|
+
|
10
|
+
VCR.config do |c|
|
11
|
+
c.cassette_library_dir = 'test/vcr_cassettes'
|
12
|
+
c.stub_with :fakeweb
|
13
|
+
c.default_cassette_options = { :record => :once }
|
14
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'test/helper'
|
2
|
+
|
3
|
+
class TestBostonMarathon < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "when looking up a result by an INVALID bib number" do
|
6
|
+
|
7
|
+
should "raise a RecordNotFound exception" do
|
8
|
+
assert_raise BostonMarathon::RecordNotFound do
|
9
|
+
VCR.use_cassette('bad_bib') do
|
10
|
+
@result = BostonMarathon.result('a')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when looking up a result by a VALID bib number" do
|
18
|
+
|
19
|
+
setup do
|
20
|
+
VCR.use_cassette('bib_43') do
|
21
|
+
@result = BostonMarathon.result(43)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
should "have the correct athlete attributes" do
|
26
|
+
expected = ['43', 'Krah, Nathan M.', '25', 'M', 'Boston', 'MA', 'USA']
|
27
|
+
tested = [@result.bib, @result.name, @result.age, @result.gender, @result.city, @result.state, @result.country]
|
28
|
+
assert_equal expected, tested
|
29
|
+
end
|
30
|
+
|
31
|
+
should "have the correct splits" do
|
32
|
+
expected = {
|
33
|
+
'5k' => '0:16:40',
|
34
|
+
'10k' => '0:33:24',
|
35
|
+
'15k' => '0:50:03',
|
36
|
+
'20k' => '1:06:46',
|
37
|
+
'half' => '1:10:25',
|
38
|
+
'25k' => '1:23:42',
|
39
|
+
'30k' => '1:40:39',
|
40
|
+
'35k' => '1:57:52',
|
41
|
+
'40k' => '2:14:53'
|
42
|
+
}
|
43
|
+
assert_equal expected, @result.splits
|
44
|
+
end
|
45
|
+
|
46
|
+
should "have the correct finish info" do
|
47
|
+
expected = ['0:05:26', '', '2:22:24', '29', '29', '25']
|
48
|
+
tested = [@result.pace, @result.projected_time, @result.time, @result.place, @result.gender_place, @result.division_place]
|
49
|
+
assert_equal expected, tested
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when searching for" do
|
55
|
+
|
56
|
+
context "the top 20 women" do
|
57
|
+
|
58
|
+
setup do
|
59
|
+
VCR.use_cassette('top_20_women') do
|
60
|
+
@results = BostonMarathon.results({'GenderID' => 2, 'VarTargetCount' => 20})
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
should "return the results of the 20 fastest female runners" do
|
65
|
+
assert_equal 20, @results.length
|
66
|
+
assert_equal ["F"], @results.map(&:gender).uniq
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
context "someone matching very specific attributes" do
|
72
|
+
|
73
|
+
setup do
|
74
|
+
VCR.use_cassette('ryan_hall') do
|
75
|
+
@results = BostonMarathon.results({
|
76
|
+
'BibNumber' => 3,
|
77
|
+
'LastName' => 'Hall',
|
78
|
+
'FirstName' => 'Ryan',
|
79
|
+
'ReportingSegmentId' => 1,
|
80
|
+
'GenderID' => 1,
|
81
|
+
'City' => 'Mammoth Lakes',
|
82
|
+
'StateID' => '10',
|
83
|
+
'CountryOfResID' => 193
|
84
|
+
})
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
should "return the athlete we're looking for" do
|
89
|
+
assert_equal 1, @results.length
|
90
|
+
assert_equal "Hall, Ryan", @results[0].name
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|