mlb_gameday 0.0.11 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 05cb0963a6f9526bd2c8f340327d3c94d84ae327
4
- data.tar.gz: 41ec430d36c0b420764f404023f57370196d3d78
3
+ metadata.gz: 2ce56fe941c7aa4f12153197768ee98e687f92b6
4
+ data.tar.gz: 44b99fa141ae3ef58ba69789265546d3bc7dc7d9
5
5
  SHA512:
6
- metadata.gz: 5662712aa5ccce3ba76f4d496c247a023658e3f822c11a6ab44f98a4334949f0217e11c2dcb5c1beced7653270516373fd96ad21718814c0a9f3b9ae6241adc6
7
- data.tar.gz: 592dd76a7351cd360a3838393791a41590354bd87e35e3518d83603bec32fad27392547f5332335398393dffe87a25b6270ec39a53eb82b182a65b9daee871b9
6
+ metadata.gz: f19a6879750da08a088288a2a940b5cb30fe5b7cfaa8fe0128760c993a4d2478f1ab3ef35ff3e015afcf79915f278417a2cfaa650a4a83c69fb4c0834b00d9f9
7
+ data.tar.gz: d7e4c7d2068db64398847a2e61823bfc22c344a584b07646a113e4e13c5fdd2c836acc6e7a78c1302ad77b2d3f0dab9c7bee03b13d9f68956388ff88040496aa
data/.rubocop.yml ADDED
@@ -0,0 +1,19 @@
1
+ AllCops:
2
+ Include:
3
+ - Rakefile
4
+ - Gemfile
5
+
6
+ Metrics/AbcSize:
7
+ Enabled: false
8
+
9
+ Metrics/ClassLength:
10
+ Max: 100
11
+
12
+ Metrics/CyclomaticComplexity:
13
+ Enabled: false
14
+
15
+ Metrics/MethodLength:
16
+ Max: 15
17
+
18
+ Style/Documentation:
19
+ Enabled: false
data/Rakefile CHANGED
@@ -1 +1,6 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/lib/mlb_gameday.rb CHANGED
@@ -3,157 +3,151 @@ require 'nokogiri'
3
3
  require 'open-uri'
4
4
  require 'yaml'
5
5
 
6
- %w{version league division team game player pitcher batter}.each do |file|
7
- require "mlb_gameday/#{file}"
6
+ %w(version league division team game player pitcher batter).each do |file|
7
+ require "mlb_gameday/#{file}"
8
8
  end
9
9
 
10
10
  module MLBGameday
11
- API_URL = "http://gd2.mlb.com/components/game/mlb"
12
-
13
- class API
14
- def initialize
15
- # File File File File File File File File File File File File File File File
16
- @leagues = YAML.load File.open(File.join(File.dirname(File.expand_path(__FILE__)), '../resources/data.yml'))
17
- end
18
-
19
- def leagues
20
- @leagues
21
- end
22
-
23
- def league(name)
24
- return name if name.is_a? MLBGameday::League
25
-
26
- @leagues[name]
27
- end
28
-
29
- def team(name)
30
- return name if name.is_a? MLBGameday::Team
31
-
32
- teams.each do |team|
33
- return team if team.names.include? name.downcase
34
- end
35
-
36
- nil
37
- end
38
-
39
- def teams
40
- @teams ||= divisions.map(&:teams).map(&:values).flatten
41
- end
42
-
43
- def division(league, name)
44
- @leagues[league][name]
45
- end
46
-
47
- def divisions
48
- @divisions ||= @leagues[:AL].divisions.values + @leagues[:NL].divisions.values
49
- end
50
-
51
- def pitcher(id)
52
- return nil if id.empty?
11
+ API_URL = 'http://gd2.mlb.com/components/game/mlb'
53
12
 
54
- MLBGameday::Pitcher.new(self, id, fetch_pitcher_xml(id))
55
- end
13
+ BATTER = '/year_%{year}/batters/%{id}'
14
+ PITCHER = '/year_%{year}/pitchers/%{id}'
15
+ BOXSCORE = '/year_%{year}/month_%{month}/day_%{day}/gid_%{gid}/boxscore'
16
+ GAMECENTER = '/year_%{year}/month_%{month}/day_%{day}/gid_%{gid}/gamecenter'
17
+ LINESCORE = '/year_%{year}/month_%{month}/day_%{day}/gid_%{gid}/linescore'
18
+ SCOREBOARD = '/year_%Y/month_%m/day_%d/miniscoreboard'
56
19
 
57
- def batter(id)
58
- return nil if id.empty?
59
-
60
- MLBGameday::Batter.new(self, id, fetch_batter_xml(id))
61
- end
62
-
63
- def game(gid)
64
- MLBGameday::Game.new(
65
- self,
66
- gid,
67
- gamecenter: fetch_gamecenter_xml(gid),
68
- linescore: fetch_linescore_xml(gid),
69
- boxscore: fetch_boxscore_xml(gid)
70
- )
71
- end
72
-
73
- def find_games(team: nil, date: nil)
74
- date = Date.today if date.nil?
75
-
76
- doc = fetch_scoreboard_xml(date)
77
-
78
- if team.nil?
79
- doc.xpath("//games/game").map do |game|
80
- gid = game.xpath("@gameday_link").first.value
81
-
82
- MLBGameday::Game.new(
83
- self,
84
- gid,
85
- gamecenter: fetch_gamecenter_xml(gid),
86
- linescore: fetch_linescore_xml(gid),
87
- boxscore: fetch_boxscore_xml(gid)
88
- )
89
- end
90
- else
91
- team = team(team)
92
-
93
- doc.xpath("//games/game").map do |game|
94
- if [game.xpath("@home_name_abbrev").first.value, game.xpath("@away_name_abbrev").first.value].include? team.code
95
- gid = game.xpath("@gameday_link").first.value
96
-
97
- MLBGameday::Game.new(
98
- self,
99
- gid,
100
- gamecenter: fetch_gamecenter_xml(gid),
101
- linescore: fetch_linescore_xml(gid),
102
- boxscore: fetch_boxscore_xml(gid),
103
- )
104
- end
105
- end.compact!
106
- end
107
- end
108
-
109
- def fetch_scoreboard_xml(date)
110
- Nokogiri::XML(open(API_URL + date.strftime("/year_%Y/month_%m/day_%d/miniscoreboard.xml")))
111
- end
20
+ class API
21
+ attr_reader :leagues
22
+
23
+ def initialize
24
+ @leagues = YAML.load File.open File.join(
25
+ File.dirname(File.expand_path __FILE__), '../resources/data.yml'
26
+ )
27
+ end
112
28
 
113
- def fetch_linescore_xml(gid)
114
- year, month, day, _ = gid.split("_")
115
-
116
- Nokogiri::XML(open(API_URL + "/year_#{ year }/month_#{ month }/day_#{ day }/gid_#{ gid }/linescore.xml"))
117
- end
29
+ def league(name)
30
+ return name if name.is_a? MLBGameday::League
118
31
 
119
- def fetch_boxscore_xml(gid)
120
- year, month, day, _ = gid.split("_")
32
+ @leagues[name]
33
+ end
34
+
35
+ def team(name)
36
+ return name if name.is_a? MLBGameday::Team
121
37
 
122
- Nokogiri::XML(open(API_URL + "/year_#{ year }/month_#{ month }/day_#{ day }/gid_#{ gid }/boxscore.xml"))
123
- rescue
124
- nil
125
- end
126
-
127
- def fetch_gamecenter_xml(gid)
128
- year, month, day, _ = gid.split("_")
129
-
130
- Nokogiri::XML(open(API_URL + "/year_#{ year }/month_#{ month }/day_#{ day }/gid_#{ gid }/gamecenter.xml"))
131
- rescue
132
- nil
133
- end
134
-
135
- def fetch_batter_xml(id, year: nil)
136
- year = Date.today.year if year.nil?
137
-
138
- # We only really want one piece of data from this file...
139
- year_data = Nokogiri::XML(open(API_URL + "/year_#{ year }/batters/#{ id }.xml"))
140
-
141
- gid = year_data.xpath("//pitching/@game_id").first.value
142
- year, month, day, _ = gid.split("/")
143
-
144
- Nokogiri::XML(open(MLBGameday::API_URL + "/year_#{ year }/month_#{ month }/day_#{ day }/gid_#{ gid.gsub(/[^a-z0-9]/, "_") }/batters/#{ id }.xml"))
145
- end
146
-
147
- def fetch_pitcher_xml(id, year: nil)
148
- year = Date.today.year if year.nil?
149
-
150
- # We only really want one piece of data from this file...
151
- year_data = Nokogiri::XML(open(API_URL + "/year_#{ year }/pitchers/#{ id }.xml"))
152
-
153
- gid = year_data.xpath("//pitching/@game_id").first.value
154
- year, month, day, _ = gid.split("/")
155
-
156
- Nokogiri::XML(open(MLBGameday::API_URL + "/year_#{ year }/month_#{ month }/day_#{ day }/gid_#{ gid.gsub(/[^a-z0-9]/, "_") }/pitchers/#{ id }.xml"))
157
- end
158
- end
38
+ teams.each do |team|
39
+ return team if team.names.include? name.downcase
40
+ end
41
+
42
+ nil
43
+ end
44
+
45
+ def teams
46
+ @teams ||= divisions.map(&:teams).map(&:values).flatten
47
+ end
48
+
49
+ def division(league, name)
50
+ @leagues[league][name]
51
+ end
52
+
53
+ def divisions
54
+ @divisions ||= @leagues[:AL].divisions.values +
55
+ @leagues[:NL].divisions.values
56
+ end
57
+
58
+ def pitcher(id)
59
+ return nil if id.empty?
60
+
61
+ MLBGameday::Pitcher.new id: id, xml: pitcher_xml(id)
62
+ end
63
+
64
+ def batter(id)
65
+ return nil if id.empty?
66
+
67
+ MLBGameday::Batter.new id: id, xml: batter_xml(id)
68
+ end
69
+
70
+ def game(gid)
71
+ MLBGameday::Game.new(
72
+ self,
73
+ gid,
74
+ gamecenter: gamecenter_xml(gid),
75
+ linescore: linescore_xml(gid),
76
+ boxscore: boxscore_xml(gid)
77
+ )
78
+ end
79
+
80
+ def find_games(team: nil, date: nil)
81
+ doc = scoreboard_xml(date || Date.today)
82
+
83
+ if team
84
+ code = team(team).code
85
+
86
+ doc.xpath('//games/game').map do |game|
87
+ next unless [game.xpath('@home_name_abbrev').text,
88
+ game.xpath('@away_name_abbrev').text].include? code
89
+
90
+ game game.xpath('@gameday_link').text
91
+ end.compact!
92
+ else
93
+ doc.xpath('//games/game').map do |game|
94
+ game game.xpath('@gameday_link').to_s
95
+ end
96
+ end
97
+ end
98
+
99
+ def scoreboard_xml(date)
100
+ fetch_xml date.strftime SCOREBOARD
101
+ end
102
+
103
+ def linescore_xml(gid)
104
+ year, month, day, _ = gid.split '_'
105
+
106
+ fetch_xml LINESCORE, year: year, month: month, day: day, gid: gid
107
+ end
108
+
109
+ def boxscore_xml(gid)
110
+ year, month, day, _ = gid.split '_'
111
+
112
+ fetch_xml BOXSCORE, year: year, month: month, day: day, gid: gid
113
+ rescue
114
+ nil
115
+ end
116
+
117
+ def gamecenter_xml(gid)
118
+ year, month, day, _ = gid.split '_'
119
+
120
+ fetch_xml GAMECENTER, year: year, month: month, day: day, gid: gid
121
+ rescue
122
+ nil
123
+ end
124
+
125
+ def batter_xml(id, year: nil)
126
+ # We only really want one piece of data from this file...
127
+ year_data = fetch_xml BATTER, id: id, year: (year || Date.today.year)
128
+
129
+ gid = year_data.xpath('//batting/@game_id').text
130
+ year, month, day, _ = gid.split '/'
131
+
132
+ fetch_xml "/year_#{year}/month_#{month}/day_#{day}/" \
133
+ "gid_#{gid.gsub(/[^a-z0-9]/, '_')}/batters/#{id}"
134
+ end
135
+
136
+ def pitcher_xml(id, year: nil)
137
+ # We only really want one piece of data from this file...
138
+ year_data = fetch_xml PITCHER, id: id, year: (year || Date.today.year)
139
+
140
+ gid = year_data.xpath('//pitching/@game_id').text
141
+ year, month, day, _ = gid.split '/'
142
+
143
+ fetch_xml "/year_#{year}/month_#{month}/day_#{day}/" \
144
+ "gid_#{gid.gsub(/[^a-z0-9]/, '_')}/pitchers/#{id}"
145
+ end
146
+
147
+ protected
148
+
149
+ def fetch_xml(path, interpolations = {})
150
+ Nokogiri::XML open format(API_URL + path + '.xml', interpolations)
151
+ end
152
+ end
159
153
  end
@@ -1,4 +1,4 @@
1
1
  module MLBGameday
2
- class Batter < Player
3
- end
2
+ class Batter < Player
3
+ end
4
4
  end
@@ -1,21 +1,12 @@
1
1
  module MLBGameday
2
- class Division
3
- def initialize(league, name, teams)
4
- @league = league
5
- @name = name
6
- @teams = teams
7
- end
2
+ class Division
3
+ attr_reader :league, :name, :teams
8
4
 
9
- def league
10
- @league
11
- end
12
-
13
- def name
14
- @name
15
- end
16
-
17
- def teams
18
- @teams
19
- end
20
- end
5
+ def initialize(id:, league:, name:, teams:)
6
+ @id = id
7
+ @league = league
8
+ @name = name
9
+ @teams = teams
10
+ end
11
+ end
21
12
  end
@@ -1,227 +1,218 @@
1
1
  module MLBGameday
2
- class Game
3
- def initialize(api, gid, linescore: nil, gamecenter: nil, boxscore: nil)
4
- @api = api
5
- @gid = gid
6
-
7
- @linescore = linescore
8
- @gamecenter = gamecenter
9
- @boxscore = boxscore
10
-
11
- @home = @api.team(linescore.xpath("//game/@home_name_abbrev").first.value)
12
- @away = @api.team(linescore.xpath("//game/@away_name_abbrev").first.value)
13
- end
14
-
15
- def gid
16
- @gid
17
- end
18
-
19
- def teams
20
- [@home, @away]
21
- end
22
-
23
- def home_team
24
- @home
25
- end
26
-
27
- def away_team
28
- @away
29
- end
30
-
31
- def venue
32
- @linescore.xpath("//game/@venue").first.value
33
- end
34
-
35
- def home_start_time(ampm: true)
36
- if ampm
37
- "#{ @linescore.xpath("//game/@home_time").first.value } #{ @linescore.xpath("//game/@home_ampm").first.value } #{ @linescore.xpath("//game/@home_time_zone").first.value }"
38
- else
39
- "#{ @linescore.xpath("//game/@home_time").first.value } #{ @linescore.xpath("//game/@home_time_zone").first.value }"
40
- end
41
- end
42
-
43
- def away_start_time(ampm: true)
44
- if ampm
45
- "#{ @linescore.xpath("//game/@away_time").first.value } #{ @linescore.xpath("//game/@away_ampm").first.value } #{ @linescore.xpath("//game/@away_time_zone").first.value }"
46
- else
47
- "#{ @linescore.xpath("//game/@away_time").first.value } #{ @linescore.xpath("//game/@away_time_zone").first.value }"
48
- end
49
- end
50
-
51
- # Preview, Pre-Game, In Progress, Final
52
- def status
53
- @status ||= @linescore.xpath("//game/@status").first.value
54
- end
55
-
56
- # [3, Top/Middle/Bottom/End]
57
- def inning
58
- return [0, '?'] if @linescore.xpath("//game/@inning").nil?
59
-
60
- [@linescore.xpath("//game/@inning").first.value.to_i, @linescore.xpath("//game/@inning_state").first.value]
61
- end
62
-
63
- def runners
64
- first, second, third = [nil, nil, nil]
65
-
66
- [first, second, third]
67
- end
68
-
69
- def over?
70
- status == "Final" || status == "Game Over"
71
- end
72
- alias_method :fat_lady_has_sung?, :over?
73
-
74
- def in_progress?
75
- status == "In Progress"
76
- end
77
-
78
- def home_record
79
- [@linescore.xpath("//game/@home_win").first.value, @linescore.xpath("//game/@home_loss").first.value].map(&:to_i)
80
- end
81
-
82
- def away_record
83
- [@linescore.xpath("//game/@away_win").first.value, @linescore.xpath("//game/@away_loss").first.value].map(&:to_i)
84
- end
85
-
86
- def current_pitcher
87
- return nil if !in_progress?
88
-
89
- @api.pitcher @linescore.xpath("//game/current_pitcher/@id").first.value
90
- end
91
-
92
- def opposing_pitcher
93
- return nil if !in_progress?
94
-
95
- @api.pitcher @linescore.xpath("//game/opposing_pitcher/@id").first.value
96
- end
97
-
98
- def winning_pitcher
99
- return nil if !over?
100
-
101
- @api.pitcher @linescore.xpath("//game/winning_pitcher/@id").first.value
102
- end
103
-
104
- def losing_pitcher
105
- return nil if !over?
106
-
107
- @api.pitcher @linescore.xpath("//game/losing_pitcher/@id").first.value
108
- end
109
-
110
- def save_pitcher
111
- return nil if !over?
112
-
113
- @api.pitcher @linescore.xpath("//game/save_pitcher/@id").first.value
114
- end
115
-
116
- def score
117
- return [0, 0] if !in_progress? && !over?
118
-
119
- [@linescore.xpath("//game/@home_team_runs").first.value, @linescore.xpath("//game/@away_team_runs").first.value].map(&:to_i)
120
- end
121
-
122
- def home_pitcher
123
- case status
124
- when "In Progress"
125
- # The xpath changes based on which half of the inning it is
126
- if @linescore.xpath("//game/@top_inning").first.value == "Y"
127
- opposing_pitcher
128
- else
129
- current_pitcher
130
- end
131
- when "Preview", "Warmup", "Pre-Game"
132
- @api.pitcher @linescore.xpath("//game/home_probable_pitcher/@id").first.value
133
- when "Final"
134
- home, away = score
135
-
136
- if home > away
137
- winning_pitcher
138
- elsif away > home
139
- losing_pitcher
140
- else
141
- # Spring training games can end in ties, in which case there's really no pitching data
142
- # See: http://gd2.mlb.com/components/game/mlb/year_2013/month_03/day_07/gid_2013_03_07_texmlb_lanmlb_1/linescore.xml
143
- # This should really give a null object pitcher back
144
- nil
145
- end
146
- else
147
- end
148
- end
149
-
150
- def away_pitcher
151
- case status
152
- when "In Progress"
153
- # The xpath changes based on which half of the inning it is
154
- if @linescore.xpath("//game/@top_inning").first.value == "Y"
155
- current_pitcher
156
- else
157
- opposing_pitcher
158
- end
159
- when "Preview", "Warmup", "Pre-Game"
160
- @api.pitcher @linescore.xpath("//game/away_probable_pitcher/@id").first.value
161
- when "Final", "Game Over"
162
- home, away = score
163
-
164
- if home > away
165
- losing_pitcher
166
- elsif away > home
167
- winning_pitcher
168
- else
169
- # Spring training games can end in ties, in which case there's really no pitching data
170
- # See: http://gd2.mlb.com/components/game/mlb/year_2013/month_03/day_07/gid_2013_03_07_texmlb_lanmlb_1/linescore.xml
171
- # This should really give a null object pitcher back
172
- nil
173
- end
174
- else
175
- end
176
- end
177
-
178
- def home_tv
179
- return nil if !@gamecenter
180
-
181
- @gamecenter.xpath("//game/broadcast/home/tv").first.content
182
- end
183
-
184
- def away_tv
185
- return nil if !@gamecenter
186
-
187
- @gamecenter.xpath("//game/broadcast/away/tv").first.content
188
- end
189
-
190
- def home_radio
191
- return nil if !@gamecenter
192
-
193
- @gamecenter.xpath("//game/broadcast/home/radio").first.content
194
- end
195
-
196
- def away_radio
197
- return nil if !@gamecenter
198
-
199
- @gamecenter.xpath("//game/broadcast/away/radio").first.content
200
- end
201
-
202
- def is_free?
203
- @linescore.xpath("//game/game_media/media/@free").first.value == "ALL"
204
- end
205
-
206
- def date
207
- @date ||= DateTime.strptime(@linescore.xpath("//game/@original_date").first.value, '%Y/%m/%d').to_date
208
- end
209
-
210
- def linescore
211
- @linescore
212
- end
213
-
214
- def gamecenter
215
- @gamecenter
216
- end
217
-
218
- def boxscore
219
- @boxscore
220
- end
221
-
222
- # So we don't get huge printouts
223
- def inspect
224
- %Q{#<MLBGameday::Game @gid="#{@gid}">}
225
- end
226
- end
2
+ # This class is just too long. It might be able to be split up, but it's not
3
+ # likely to happen any time soon. For now, we'll disable the cop.
4
+ # rubocop:disable Metrics/ClassLength
5
+ class Game
6
+ attr_reader :gid, :home_team, :away_team, :linescore, :gamecenter, :boxscore
7
+
8
+ def initialize(api, gid, linescore: nil, gamecenter: nil, boxscore: nil)
9
+ @api = api
10
+ @gid = gid
11
+
12
+ @linescore = linescore
13
+ @gamecenter = gamecenter
14
+ @boxscore = boxscore
15
+
16
+ @home_team = @api.team linescore.xpath('//game/@home_name_abbrev').text
17
+ @away_team = @api.team linescore.xpath('//game/@away_name_abbrev').text
18
+ end
19
+
20
+ def teams
21
+ [@home_team, @away_team]
22
+ end
23
+
24
+ def venue
25
+ @linescore.xpath('//game/@venue').text
26
+ end
27
+
28
+ def home_start_time(ampm: true)
29
+ if ampm
30
+ [
31
+ @linescore.xpath('//game/@home_time').text,
32
+ @linescore.xpath('//game/@home_ampm').text,
33
+ @linescore.xpath('//game/@home_time_zone').text
34
+ ].join ' '
35
+ else
36
+ [
37
+ @linescore.xpath('//game/@home_time').text,
38
+ @linescore.xpath('//game/@home_time_zone').text
39
+ ].join ' '
40
+ end
41
+ end
42
+
43
+ def away_start_time(ampm: true)
44
+ if ampm
45
+ [
46
+ @linescore.xpath('//game/@away_time').text,
47
+ @linescore.xpath('//game/@away_ampm').text,
48
+ @linescore.xpath('//game/@away_time_zone').text
49
+ ].join ' '
50
+ else
51
+ [
52
+ @linescore.xpath('//game/@away_time').text,
53
+ @linescore.xpath('//game/@away_time_zone').text
54
+ ].join ' '
55
+ end
56
+ end
57
+
58
+ # Preview, Pre-Game, In Progress, Final
59
+ def status
60
+ @status ||= @linescore.xpath('//game/@status').text
61
+ end
62
+
63
+ # [3, Top/Middle/Bottom/End]
64
+ def inning
65
+ return [0, '?'] unless @linescore.xpath('//game/@inning')
66
+
67
+ [@linescore.xpath('//game/@inning').text.to_i,
68
+ @linescore.xpath('//game/@inning_state').text]
69
+ end
70
+
71
+ def runners
72
+ first, second, third = [nil, nil, nil]
73
+
74
+ [first, second, third]
75
+ end
76
+
77
+ def over?
78
+ ['Final', 'Game Over', 'Completed Early'].include? status
79
+ end
80
+ alias_method :fat_lady_has_sung?, :over?
81
+
82
+ def in_progress?
83
+ status == 'In Progress'
84
+ end
85
+
86
+ def home_record
87
+ [@linescore.xpath('//game/@home_win'),
88
+ @linescore.xpath('//game/@home_loss')].map(&:text).map(&:to_i)
89
+ end
90
+
91
+ def away_record
92
+ [@linescore.xpath('//game/@away_win'),
93
+ @linescore.xpath('//game/@away_loss')].map(&:text).map(&:to_i)
94
+ end
95
+
96
+ def current_pitcher
97
+ return nil unless in_progress?
98
+
99
+ @api.pitcher @linescore.xpath('//game/current_pitcher/@id').text
100
+ end
101
+
102
+ def opposing_pitcher
103
+ return nil unless in_progress?
104
+
105
+ @api.pitcher @linescore.xpath('//game/opposing_pitcher/@id').text
106
+ end
107
+
108
+ def winning_pitcher
109
+ return nil unless over?
110
+
111
+ @api.pitcher @linescore.xpath('//game/winning_pitcher/@id').text
112
+ end
113
+
114
+ def losing_pitcher
115
+ return nil unless over?
116
+
117
+ @api.pitcher @linescore.xpath('//game/losing_pitcher/@id').text
118
+ end
119
+
120
+ def save_pitcher
121
+ return nil unless over?
122
+
123
+ @api.pitcher @linescore.xpath('//game/save_pitcher/@id').text
124
+ end
125
+
126
+ def away_starting_pitcher
127
+ @linescore.xpath('//game/away_probable_pitcher/@id').text
128
+ end
129
+
130
+ def home_starting_pitcher
131
+ @linescore.xpath('//game/home_probable_pitcher/@id').text
132
+ end
133
+
134
+ def score
135
+ return [0, 0] unless in_progress? || over?
136
+
137
+ [@linescore.xpath('//game/@home_team_runs').text,
138
+ @linescore.xpath('//game/@away_team_runs').text].map(&:to_i)
139
+ end
140
+
141
+ def home_pitcher
142
+ # Spring training games can end in ties, in which case there's
143
+ # really no pitching data. This should really return a null object.
144
+ case status
145
+ when 'In Progress'
146
+ # The xpath changes based on which half of the inning it is
147
+ if @linescore.xpath('//game/@top_inning').text == 'Y'
148
+ opposing_pitcher
149
+ else
150
+ current_pitcher
151
+ end
152
+ when 'Preview', 'Warmup', 'Pre-Game'
153
+ @api.pitcher home_starting_pitcher
154
+ when 'Final'
155
+ home, away = score
156
+
157
+ home > away ? winning_pitcher : losing_pitcher
158
+ end
159
+ end
160
+
161
+ def away_pitcher
162
+ # Spring training games can end in ties, in which case there's
163
+ # really no pitching data. This should really return a null object.
164
+ case status
165
+ when 'In Progress'
166
+ # The xpath changes based on which half of the inning it is
167
+ if @linescore.xpath('//game/@top_inning').text == 'Y'
168
+ current_pitcher
169
+ else
170
+ opposing_pitcher
171
+ end
172
+ when 'Preview', 'Warmup', 'Pre-Game'
173
+ @api.pitcher away_starting_pitcher
174
+ when 'Final', 'Game Over'
175
+ home, away = score
176
+
177
+ home > away ? losing_pitcher : winning_pitcher
178
+ end
179
+ end
180
+
181
+ def home_tv
182
+ return nil unless @gamecenter
183
+
184
+ @gamecenter.xpath('//game/broadcast/home/tv').text
185
+ end
186
+
187
+ def away_tv
188
+ return nil unless @gamecenter
189
+
190
+ @gamecenter.xpath('//game/broadcast/away/tv').text
191
+ end
192
+
193
+ def home_radio
194
+ return nil unless @gamecenter
195
+
196
+ @gamecenter.xpath('//game/broadcast/home/radio').text
197
+ end
198
+
199
+ def away_radio
200
+ return nil unless @gamecenter
201
+
202
+ @gamecenter.xpath('//game/broadcast/away/radio').text
203
+ end
204
+
205
+ def free?
206
+ @linescore.xpath('//game/game_media/media/@free').text == 'ALL'
207
+ end
208
+
209
+ def date
210
+ @date ||= Chronic.parse @linescore.xpath('//game/@original_date').text
211
+ end
212
+
213
+ # So we don't get huge printouts
214
+ def inspect
215
+ %(#<MLBGameday::Game @gid="#{@gid}">)
216
+ end
217
+ end
227
218
  end