mlb 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -1
- data/LICENSE.txt +1 -1
- data/README.md +329 -32
- data/lib/mlb/affiliates.rb +28 -0
- data/lib/mlb/alumni.rb +30 -0
- data/lib/mlb/attendance.rb +28 -0
- data/lib/mlb/attendance_record.rb +157 -0
- data/lib/mlb/award.rb +126 -0
- data/lib/mlb/awards.rb +32 -0
- data/lib/mlb/baseball_stat.rb +65 -0
- data/lib/mlb/baseball_stats.rb +12 -0
- data/lib/mlb/boxscore.rb +81 -0
- data/lib/mlb/boxscore_team_stats.rb +210 -0
- data/lib/mlb/client.rb +71 -1
- data/lib/mlb/coaches.rb +28 -0
- data/lib/mlb/code_description_type.rb +45 -0
- data/lib/mlb/collection.rb +48 -0
- data/lib/mlb/comparable_by_attribute.rb +56 -0
- data/lib/mlb/conference.rb +39 -0
- data/lib/mlb/conferences.rb +33 -0
- data/lib/mlb/connection.rb +130 -7
- data/lib/mlb/context_metrics.rb +90 -0
- data/lib/mlb/division.rb +26 -6
- data/lib/mlb/divisions.rb +24 -18
- data/lib/mlb/draft.rb +83 -0
- data/lib/mlb/draft_pick.rb +155 -0
- data/lib/mlb/error_handler.rb +21 -1
- data/lib/mlb/errors/bad_gateway.rb +6 -1
- data/lib/mlb/errors/bad_request.rb +1 -0
- data/lib/mlb/errors/client_error.rb +1 -0
- data/lib/mlb/errors/connection_exception.rb +1 -0
- data/lib/mlb/errors/error.rb +1 -0
- data/lib/mlb/errors/forbidden.rb +1 -0
- data/lib/mlb/errors/gateway_timeout.rb +6 -1
- data/lib/mlb/errors/gone.rb +1 -0
- data/lib/mlb/errors/http_error.rb +22 -2
- data/lib/mlb/errors/internal_server_error.rb +1 -0
- data/lib/mlb/errors/network_error.rb +6 -1
- data/lib/mlb/errors/not_acceptable.rb +1 -0
- data/lib/mlb/errors/not_found.rb +1 -0
- data/lib/mlb/errors/payload_too_large.rb +1 -0
- data/lib/mlb/errors/retryable.rb +15 -0
- data/lib/mlb/errors/server_error.rb +1 -0
- data/lib/mlb/errors/service_unavailable.rb +6 -1
- data/lib/mlb/errors/too_many_redirects.rb +1 -0
- data/lib/mlb/errors/too_many_requests.rb +6 -1
- data/lib/mlb/errors/unauthorized.rb +1 -0
- data/lib/mlb/errors/unprocessable_entity.rb +1 -0
- data/lib/mlb/event_type.rb +61 -0
- data/lib/mlb/event_types.rb +12 -0
- data/lib/mlb/free_agent.rb +78 -0
- data/lib/mlb/free_agents.rb +34 -0
- data/lib/mlb/game_changes.rb +87 -0
- data/lib/mlb/game_content.rb +251 -0
- data/lib/mlb/game_data.rb +155 -0
- data/lib/mlb/game_pace.rb +173 -0
- data/lib/mlb/game_status.rb +94 -0
- data/lib/mlb/game_statuses.rb +12 -0
- data/lib/mlb/game_type.rb +98 -0
- data/lib/mlb/game_types.rb +12 -0
- data/lib/mlb/handedness.rb +30 -9
- data/lib/mlb/high_low.rb +121 -0
- data/lib/mlb/hit_trajectories.rb +12 -0
- data/lib/mlb/hit_trajectory.rb +6 -0
- data/lib/mlb/home_run_derby.rb +285 -0
- data/lib/mlb/id_description_type.rb +44 -0
- data/lib/mlb/inning_score.rb +87 -0
- data/lib/mlb/job.rb +58 -0
- data/lib/mlb/job_type.rb +39 -0
- data/lib/mlb/job_types.rb +12 -0
- data/lib/mlb/jobs.rb +87 -0
- data/lib/mlb/language.rb +48 -0
- data/lib/mlb/languages.rb +12 -0
- data/lib/mlb/leader.rb +79 -0
- data/lib/mlb/leaders.rb +68 -0
- data/lib/mlb/league.rb +97 -14
- data/lib/mlb/league_leader_type.rb +21 -0
- data/lib/mlb/league_leader_types.rb +12 -0
- data/lib/mlb/leagues.rb +24 -18
- data/lib/mlb/linescore.rb +342 -0
- data/lib/mlb/linescore_teams.rb +89 -0
- data/lib/mlb/live_feed.rb +130 -0
- data/lib/mlb/logical_event.rb +17 -0
- data/lib/mlb/logical_events.rb +12 -0
- data/lib/mlb/metric.rb +48 -0
- data/lib/mlb/metrics.rb +12 -0
- data/lib/mlb/people_changes.rb +34 -0
- data/lib/mlb/personnel.rb +28 -0
- data/lib/mlb/pitch_code.rb +90 -0
- data/lib/mlb/pitch_codes.rb +12 -0
- data/lib/mlb/pitch_type.rb +6 -0
- data/lib/mlb/pitch_types.rb +12 -0
- data/lib/mlb/platform.rb +30 -0
- data/lib/mlb/platforms.rb +12 -0
- data/lib/mlb/play.rb +300 -0
- data/lib/mlb/play_by_play.rb +52 -0
- data/lib/mlb/player.rb +186 -0
- data/lib/mlb/player_game_stats.rb +350 -0
- data/lib/mlb/player_stat.rb +70 -0
- data/lib/mlb/players.rb +42 -11
- data/lib/mlb/position.rb +68 -0
- data/lib/mlb/positions.rb +12 -0
- data/lib/mlb/postseason_schedule.rb +41 -0
- data/lib/mlb/postseason_series.rb +54 -0
- data/lib/mlb/redirect_handler.rb +80 -21
- data/lib/mlb/request_builder.rb +33 -2
- data/lib/mlb/review_reason.rb +6 -0
- data/lib/mlb/review_reasons.rb +12 -0
- data/lib/mlb/roster.rb +16 -12
- data/lib/mlb/roster_entry.rb +38 -0
- data/lib/mlb/roster_type.rb +39 -0
- data/lib/mlb/roster_types.rb +12 -0
- data/lib/mlb/schedule.rb +33 -0
- data/lib/mlb/schedule_date.rb +37 -0
- data/lib/mlb/schedule_event_type.rb +25 -0
- data/lib/mlb/schedule_event_types.rb +12 -0
- data/lib/mlb/scheduled_game.rb +321 -0
- data/lib/mlb/season.rb +74 -0
- data/lib/mlb/season_date_info.rb +4 -0
- data/lib/mlb/seasons.rb +36 -0
- data/lib/mlb/single_team_stats.rb +33 -0
- data/lib/mlb/situation_code.rb +116 -0
- data/lib/mlb/situation_codes.rb +12 -0
- data/lib/mlb/skies.rb +12 -0
- data/lib/mlb/sky.rb +6 -0
- data/lib/mlb/sport.rb +18 -5
- data/lib/mlb/sport_players.rb +30 -0
- data/lib/mlb/sports.rb +17 -7
- data/lib/mlb/standings.rb +47 -0
- data/lib/mlb/standings_record.rb +108 -0
- data/lib/mlb/standings_type.rb +25 -0
- data/lib/mlb/standings_types.rb +12 -0
- data/lib/mlb/stat_group.rb +21 -0
- data/lib/mlb/stat_groups.rb +12 -0
- data/lib/mlb/stat_type.rb +21 -0
- data/lib/mlb/stat_types.rb +12 -0
- data/lib/mlb/stat_values.rb +158 -0
- data/lib/mlb/stats.rb +41 -0
- data/lib/mlb/status.rb +17 -0
- data/lib/mlb/streaks.rb +184 -0
- data/lib/mlb/team.rb +66 -7
- data/lib/mlb/team_history.rb +28 -0
- data/lib/mlb/team_leader.rb +59 -0
- data/lib/mlb/team_leaders.rb +45 -0
- data/lib/mlb/team_record.rb +285 -0
- data/lib/mlb/team_stat.rb +50 -0
- data/lib/mlb/team_stats.rb +52 -0
- data/lib/mlb/teams.rb +28 -18
- data/lib/mlb/tied_games.rb +32 -0
- data/lib/mlb/transaction.rb +128 -0
- data/lib/mlb/transactions.rb +11 -6
- data/lib/mlb/uniforms.rb +97 -0
- data/lib/mlb/utils.rb +45 -0
- data/lib/mlb/venue.rb +10 -0
- data/lib/mlb/venues.rb +28 -18
- data/lib/mlb/version.rb +2 -1
- data/lib/mlb/win_probability.rb +64 -0
- data/lib/mlb/wind_direction.rb +6 -0
- data/lib/mlb/wind_directions.rb +12 -0
- data/lib/mlb.rb +64 -0
- data/sig/equalizer.rbs +3 -0
- data/sig/mlb.rbs +1972 -103
- data/sig/shale.rbs +29 -0
- metadata +137 -14
data/lib/mlb/players.rb
CHANGED
|
@@ -1,25 +1,56 @@
|
|
|
1
1
|
require "shale"
|
|
2
|
-
require "uri"
|
|
3
|
-
require_relative "sport"
|
|
4
2
|
require_relative "player"
|
|
5
3
|
|
|
6
4
|
module MLB
|
|
5
|
+
# Provides methods for fetching MLB players from the API
|
|
7
6
|
class Players < Shale::Mapper
|
|
8
|
-
attribute :copyright, Shale::Type::String
|
|
9
7
|
attribute :players, Player, collection: true
|
|
10
8
|
|
|
11
9
|
json do
|
|
12
|
-
map "copyright", to: :copyright
|
|
13
10
|
map "people", to: :players
|
|
14
11
|
end
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
13
|
+
# Retrieves all players for a given season and sport
|
|
14
|
+
#
|
|
15
|
+
# @api public
|
|
16
|
+
# @example
|
|
17
|
+
# MLB::Players.all #=> [#<MLB::Player>, ...]
|
|
18
|
+
# @param season [Integer, nil] the season year (defaults to current year)
|
|
19
|
+
# @param sport [Sport, Integer] the sport or sport ID
|
|
20
|
+
# @return [Array<Player>] the list of players
|
|
21
|
+
def self.all(season: nil, sport: Utils::DEFAULT_SPORT_ID)
|
|
22
|
+
season ||= Utils.current_season
|
|
23
|
+
sport_id = Utils.extract_id(sport)
|
|
24
|
+
response = CLIENT.get("sports/#{sport_id}/players?#{Utils.build_query(season:)}")
|
|
25
|
+
from_json(response).players
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Finds a single player by ID or Player object
|
|
29
|
+
#
|
|
30
|
+
# @api public
|
|
31
|
+
# @example
|
|
32
|
+
# MLB::Players.find(660271) #=> #<MLB::Player>
|
|
33
|
+
# @param player [Player, Integer] the player or player ID
|
|
34
|
+
# @return [Player, nil] the found player or nil
|
|
35
|
+
def self.find(player)
|
|
36
|
+
params = {personIds: Utils.extract_id(player)}
|
|
37
|
+
response = CLIENT.get("people?#{Utils.build_query(params)}")
|
|
38
|
+
from_json(response).players.first
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Finds multiple players by IDs or Player objects
|
|
42
|
+
#
|
|
43
|
+
# @api public
|
|
44
|
+
# @example
|
|
45
|
+
# MLB::Players.find_all(660271, 545361) #=> [#<MLB::Player>, ...]
|
|
46
|
+
# @param players [Array<Player, Integer>] the players or player IDs
|
|
47
|
+
# @return [Array<Player>] the list of found players
|
|
48
|
+
def self.find_all(*players)
|
|
49
|
+
return all if players.empty?
|
|
50
|
+
|
|
51
|
+
player_ids = players.map { |p| Utils.extract_id(p) }.join(",")
|
|
52
|
+
response = CLIENT.get("people?#{Utils.build_query(personIds: player_ids)}")
|
|
53
|
+
from_json(response).players
|
|
23
54
|
end
|
|
24
55
|
end
|
|
25
56
|
end
|
data/lib/mlb/position.rb
CHANGED
|
@@ -1,12 +1,80 @@
|
|
|
1
|
+
require "equalizer"
|
|
1
2
|
require "shale"
|
|
2
3
|
|
|
3
4
|
module MLB
|
|
5
|
+
# Represents a baseball position
|
|
4
6
|
class Position < Shale::Mapper
|
|
7
|
+
include Equalizer.new(:code)
|
|
8
|
+
|
|
9
|
+
TYPE_PITCHER = "Pitcher".freeze
|
|
10
|
+
TYPE_CATCHER = "Catcher".freeze
|
|
11
|
+
TYPE_INFIELDER = "Infielder".freeze
|
|
12
|
+
TYPE_OUTFIELDER = "Outfielder".freeze
|
|
13
|
+
|
|
14
|
+
# @!attribute [rw] code
|
|
15
|
+
# Returns the position code
|
|
16
|
+
# @api public
|
|
17
|
+
# @example
|
|
18
|
+
# position.code #=> "1"
|
|
19
|
+
# @return [String] the position code
|
|
5
20
|
attribute :code, Shale::Type::String
|
|
21
|
+
|
|
22
|
+
# @!attribute [rw] name
|
|
23
|
+
# Returns the position name
|
|
24
|
+
# @api public
|
|
25
|
+
# @example
|
|
26
|
+
# position.name #=> "Pitcher"
|
|
27
|
+
# @return [String] the position name
|
|
6
28
|
attribute :name, Shale::Type::String
|
|
29
|
+
|
|
30
|
+
# @!attribute [rw] type
|
|
31
|
+
# Returns the position type
|
|
32
|
+
# @api public
|
|
33
|
+
# @example
|
|
34
|
+
# position.type #=> "Pitcher"
|
|
35
|
+
# @return [String] the position type
|
|
7
36
|
attribute :type, Shale::Type::String
|
|
37
|
+
|
|
38
|
+
# @!attribute [rw] abbreviation
|
|
39
|
+
# Returns the position abbreviation
|
|
40
|
+
# @api public
|
|
41
|
+
# @example
|
|
42
|
+
# position.abbreviation #=> "P"
|
|
43
|
+
# @return [String] the position abbreviation
|
|
8
44
|
attribute :abbreviation, Shale::Type::String
|
|
9
45
|
|
|
46
|
+
# Returns whether this is a pitcher position
|
|
47
|
+
#
|
|
48
|
+
# @api public
|
|
49
|
+
# @example
|
|
50
|
+
# position.pitcher? #=> true
|
|
51
|
+
# @return [Boolean] whether this is a pitcher
|
|
52
|
+
def pitcher? = type.eql?(TYPE_PITCHER)
|
|
53
|
+
|
|
54
|
+
# Returns whether this is a catcher position
|
|
55
|
+
#
|
|
56
|
+
# @api public
|
|
57
|
+
# @example
|
|
58
|
+
# position.catcher? #=> false
|
|
59
|
+
# @return [Boolean] whether this is a catcher
|
|
60
|
+
def catcher? = type.eql?(TYPE_CATCHER)
|
|
61
|
+
|
|
62
|
+
# Returns whether this is an infielder position
|
|
63
|
+
#
|
|
64
|
+
# @api public
|
|
65
|
+
# @example
|
|
66
|
+
# position.infielder? #=> false
|
|
67
|
+
# @return [Boolean] whether this is an infielder
|
|
68
|
+
def infielder? = type.eql?(TYPE_INFIELDER)
|
|
69
|
+
|
|
70
|
+
# Returns whether this is an outfielder position
|
|
71
|
+
#
|
|
72
|
+
# @api public
|
|
73
|
+
# @example
|
|
74
|
+
# position.outfielder? #=> false
|
|
75
|
+
# @return [Boolean] whether this is an outfielder
|
|
76
|
+
def outfielder? = type.eql?(TYPE_OUTFIELDER)
|
|
77
|
+
|
|
10
78
|
json do
|
|
11
79
|
map "code", to: :code
|
|
12
80
|
map "name", to: :name
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require_relative "collection"
|
|
2
|
+
require_relative "position"
|
|
3
|
+
|
|
4
|
+
module MLB
|
|
5
|
+
# Provides methods for fetching positions from the API
|
|
6
|
+
#
|
|
7
|
+
# @example Fetch all positions
|
|
8
|
+
# MLB::Positions.all #=> [#<MLB::Position>, ...]
|
|
9
|
+
class Positions < Collection
|
|
10
|
+
collection endpoint: "positions", item_type: Position, collection_name: :positions
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require "shale"
|
|
2
|
+
require_relative "schedule_date"
|
|
3
|
+
|
|
4
|
+
module MLB
|
|
5
|
+
# Provides methods for fetching postseason schedule data from the API
|
|
6
|
+
class PostseasonSchedule < Shale::Mapper
|
|
7
|
+
# @!attribute [rw] total_games
|
|
8
|
+
# Returns the total number of games
|
|
9
|
+
# @api public
|
|
10
|
+
# @example
|
|
11
|
+
# schedule.total_games #=> 43
|
|
12
|
+
# @return [Integer] the total games
|
|
13
|
+
attribute :total_games, Shale::Type::Integer
|
|
14
|
+
|
|
15
|
+
# @!attribute [rw] dates
|
|
16
|
+
# Returns the schedule dates
|
|
17
|
+
# @api public
|
|
18
|
+
# @example
|
|
19
|
+
# schedule.dates #=> [#<MLB::ScheduleDate>, ...]
|
|
20
|
+
# @return [Array<ScheduleDate>] the dates
|
|
21
|
+
attribute :dates, ScheduleDate, collection: true
|
|
22
|
+
|
|
23
|
+
json do
|
|
24
|
+
map "totalGames", to: :total_games
|
|
25
|
+
map "dates", to: :dates
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Retrieves postseason games for a season
|
|
29
|
+
#
|
|
30
|
+
# @api public
|
|
31
|
+
# @example Get postseason games for 2024
|
|
32
|
+
# MLB::PostseasonSchedule.games(season: 2024)
|
|
33
|
+
# @param season [Integer, nil] the season year (defaults to current year)
|
|
34
|
+
# @return [Array<ScheduledGame>] the postseason games
|
|
35
|
+
def self.games(season: nil)
|
|
36
|
+
season ||= Utils.current_season
|
|
37
|
+
response = CLIENT.get("schedule/postseason?#{Utils.build_query(season:)}")
|
|
38
|
+
from_json(response).dates.flat_map(&:games)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require "shale"
|
|
2
|
+
require_relative "scheduled_game"
|
|
3
|
+
|
|
4
|
+
module MLB
|
|
5
|
+
# Represents postseason series information
|
|
6
|
+
class SeriesInfo < Shale::Mapper
|
|
7
|
+
attribute :id, Shale::Type::String
|
|
8
|
+
attribute :sort_number, Shale::Type::Integer
|
|
9
|
+
attribute :game_type, Shale::Type::String
|
|
10
|
+
|
|
11
|
+
json do
|
|
12
|
+
map "id", to: :id
|
|
13
|
+
map "sortNumber", to: :sort_number
|
|
14
|
+
map "gameType", to: :game_type
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Represents a postseason series with games
|
|
19
|
+
class PostseasonSeriesEntry < Shale::Mapper
|
|
20
|
+
attribute :series, SeriesInfo
|
|
21
|
+
attribute :total_games, Shale::Type::Integer
|
|
22
|
+
attribute :games, ScheduledGame, collection: true
|
|
23
|
+
|
|
24
|
+
json do
|
|
25
|
+
map "series", to: :series
|
|
26
|
+
map "totalGames", to: :total_games
|
|
27
|
+
map "games", to: :games
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Provides methods for fetching postseason series from the API
|
|
32
|
+
class PostseasonSeries < Shale::Mapper
|
|
33
|
+
attribute :series, PostseasonSeriesEntry, collection: true
|
|
34
|
+
|
|
35
|
+
json do
|
|
36
|
+
map "series", to: :series
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Retrieves postseason series for a season
|
|
40
|
+
#
|
|
41
|
+
# @api public
|
|
42
|
+
# @example Get postseason series for a season
|
|
43
|
+
# MLB::PostseasonSeries.all(season: 2024)
|
|
44
|
+
# @param season [Integer, nil] the season year (defaults to current year)
|
|
45
|
+
# @param sport [Integer, Sport] the sport ID or Sport object
|
|
46
|
+
# @return [Array<PostseasonSeriesEntry>] the postseason series
|
|
47
|
+
def self.all(season: nil, sport: Utils::DEFAULT_SPORT_ID)
|
|
48
|
+
season ||= Utils.current_season
|
|
49
|
+
params = {season:, sportId: Utils.extract_id(sport)}
|
|
50
|
+
response = CLIENT.get("schedule/postseason/series?#{Utils.build_query(params)}")
|
|
51
|
+
from_json(response).series
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
data/lib/mlb/redirect_handler.rb
CHANGED
|
@@ -5,12 +5,55 @@ require_relative "errors/too_many_redirects"
|
|
|
5
5
|
require_relative "request_builder"
|
|
6
6
|
|
|
7
7
|
module MLB
|
|
8
|
+
# Handles HTTP redirects for API requests
|
|
8
9
|
class RedirectHandler
|
|
10
|
+
# Default maximum number of redirects to follow
|
|
9
11
|
DEFAULT_MAX_REDIRECTS = 10
|
|
12
|
+
# HTTP redirect codes that preserve the original method and body
|
|
13
|
+
PRESERVE_METHOD_CODES = [307, 308].freeze
|
|
14
|
+
# HTTP header for redirect location
|
|
15
|
+
LOCATION_HEADER = "location".freeze
|
|
10
16
|
|
|
17
|
+
# Returns the maximum number of redirects to follow
|
|
18
|
+
#
|
|
19
|
+
# @api public
|
|
20
|
+
# @example
|
|
21
|
+
# handler.max_redirects #=> 10
|
|
22
|
+
# @return [Integer] the maximum redirects
|
|
11
23
|
attr_accessor :max_redirects
|
|
12
|
-
attr_reader :connection, :request_builder
|
|
13
24
|
|
|
25
|
+
# @!method max_redirects=(value)
|
|
26
|
+
# Sets the maximum number of redirects to follow
|
|
27
|
+
# @api public
|
|
28
|
+
# @example
|
|
29
|
+
# handler.max_redirects = 5
|
|
30
|
+
# @param value [Integer] the maximum number of redirects
|
|
31
|
+
# @return [Integer] the maximum number of redirects
|
|
32
|
+
|
|
33
|
+
# Returns the connection instance
|
|
34
|
+
#
|
|
35
|
+
# @api public
|
|
36
|
+
# @example
|
|
37
|
+
# handler.connection #=> #<MLB::Connection>
|
|
38
|
+
# @return [Connection] the connection instance
|
|
39
|
+
attr_reader :connection
|
|
40
|
+
|
|
41
|
+
# Returns the request builder instance
|
|
42
|
+
#
|
|
43
|
+
# @api public
|
|
44
|
+
# @example
|
|
45
|
+
# handler.request_builder #=> #<MLB::RequestBuilder>
|
|
46
|
+
# @return [RequestBuilder] the request builder instance
|
|
47
|
+
attr_reader :request_builder
|
|
48
|
+
|
|
49
|
+
# Initializes a new RedirectHandler instance
|
|
50
|
+
#
|
|
51
|
+
# @api public
|
|
52
|
+
# @example
|
|
53
|
+
# handler = MLB::RedirectHandler.new
|
|
54
|
+
# @param connection [Connection] the connection instance
|
|
55
|
+
# @param request_builder [RequestBuilder] the request builder instance
|
|
56
|
+
# @param max_redirects [Integer] the maximum number of redirects to follow
|
|
14
57
|
def initialize(connection: Connection.new, request_builder: RequestBuilder.new,
|
|
15
58
|
max_redirects: DEFAULT_MAX_REDIRECTS)
|
|
16
59
|
@connection = connection
|
|
@@ -18,38 +61,54 @@ module MLB
|
|
|
18
61
|
@max_redirects = max_redirects
|
|
19
62
|
end
|
|
20
63
|
|
|
64
|
+
# Handles HTTP redirects
|
|
65
|
+
#
|
|
66
|
+
# @api public
|
|
67
|
+
# @example
|
|
68
|
+
# handler.handle(response: response, request: request, base_url: "https://example.com")
|
|
69
|
+
# @param response [Net::HTTPResponse] the HTTP response
|
|
70
|
+
# @param request [Net::HTTPRequest] the original HTTP request
|
|
71
|
+
# @param base_url [String] the base URL for relative redirects
|
|
72
|
+
# @param redirect_count [Integer] the current redirect count
|
|
73
|
+
# @return [Net::HTTPResponse] the final HTTP response
|
|
74
|
+
# @raise [TooManyRedirects] if the maximum number of redirects is exceeded
|
|
21
75
|
def handle(response:, request:, base_url:, redirect_count: 0)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
new_uri = build_new_uri(response, base_url)
|
|
76
|
+
return response unless response.is_a?(Net::HTTPRedirection)
|
|
77
|
+
raise TooManyRedirects, "Too many redirects" if redirect_count > max_redirects
|
|
26
78
|
|
|
27
|
-
|
|
28
|
-
|
|
79
|
+
new_uri = build_redirect_uri(response, base_url)
|
|
80
|
+
new_request = build_redirect_request(request, new_uri, Integer(response.code))
|
|
81
|
+
new_response = connection.perform(request: new_request)
|
|
29
82
|
|
|
30
|
-
|
|
31
|
-
else
|
|
32
|
-
response
|
|
33
|
-
end
|
|
83
|
+
handle(response: new_response, request: new_request, base_url:, redirect_count: redirect_count + 1)
|
|
34
84
|
end
|
|
35
85
|
|
|
36
86
|
private
|
|
37
87
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
88
|
+
# Builds a new URI from a redirect response
|
|
89
|
+
#
|
|
90
|
+
# @api private
|
|
91
|
+
# @param response [Net::HTTPResponse] the redirect response
|
|
92
|
+
# @param base_url [String] the base URL for relative redirects
|
|
93
|
+
# @return [URI::Generic] the new URI
|
|
94
|
+
def build_redirect_uri(response, base_url)
|
|
95
|
+
location = response.fetch(LOCATION_HEADER)
|
|
41
96
|
URI.join(base_url, location)
|
|
42
97
|
end
|
|
43
98
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
99
|
+
# Builds a new request for a redirect
|
|
100
|
+
#
|
|
101
|
+
# @api private
|
|
102
|
+
# @param request [Net::HTTPRequest] the original request
|
|
103
|
+
# @param uri [URI::Generic] the new URI
|
|
104
|
+
# @param response_code [Integer] the redirect response code
|
|
105
|
+
# @return [Net::HTTPRequest] the new request
|
|
106
|
+
def build_redirect_request(request, uri, response_code)
|
|
107
|
+
if PRESERVE_METHOD_CODES.include?(response_code)
|
|
108
|
+
request_builder.build(http_method: request.method.downcase.to_sym, uri:, body: request.body)
|
|
48
109
|
else
|
|
49
|
-
|
|
110
|
+
request_builder.build(http_method: :get, uri:)
|
|
50
111
|
end
|
|
51
|
-
|
|
52
|
-
request_builder.build(http_method:, uri:, body:)
|
|
53
112
|
end
|
|
54
113
|
end
|
|
55
114
|
end
|
data/lib/mlb/request_builder.rb
CHANGED
|
@@ -3,11 +3,14 @@ require "uri"
|
|
|
3
3
|
require_relative "version"
|
|
4
4
|
|
|
5
5
|
module MLB
|
|
6
|
+
# Builds HTTP requests for the MLB Stats API
|
|
6
7
|
class RequestBuilder
|
|
8
|
+
# Default HTTP headers for API requests
|
|
7
9
|
DEFAULT_HEADERS = {
|
|
8
10
|
"Content-Type" => "application/json; charset=utf-8",
|
|
9
11
|
"User-Agent" => "MLB-Client/#{VERSION} #{RUBY_ENGINE}/#{RUBY_VERSION} (#{RUBY_PLATFORM})"
|
|
10
12
|
}.freeze
|
|
13
|
+
# Mapping of HTTP method symbols to Net::HTTP request classes
|
|
11
14
|
HTTP_METHODS = {
|
|
12
15
|
get: Net::HTTP::Get,
|
|
13
16
|
post: Net::HTTP::Post,
|
|
@@ -15,6 +18,16 @@ module MLB
|
|
|
15
18
|
delete: Net::HTTP::Delete
|
|
16
19
|
}.freeze
|
|
17
20
|
|
|
21
|
+
# Builds an HTTP request
|
|
22
|
+
#
|
|
23
|
+
# @api public
|
|
24
|
+
# @example
|
|
25
|
+
# builder.build(http_method: :get, uri: URI("https://example.com"))
|
|
26
|
+
# @param http_method [Symbol] the HTTP method (:get, :post, :put, :delete)
|
|
27
|
+
# @param uri [URI::Generic] the request URI
|
|
28
|
+
# @param body [String, nil] the request body
|
|
29
|
+
# @param headers [Hash] additional HTTP headers
|
|
30
|
+
# @return [Net::HTTPRequest] the built HTTP request
|
|
18
31
|
def build(http_method:, uri:, body: nil, headers: {})
|
|
19
32
|
request = create_request(http_method:, uri:, body:)
|
|
20
33
|
add_headers(request:, headers:)
|
|
@@ -23,6 +36,14 @@ module MLB
|
|
|
23
36
|
|
|
24
37
|
private
|
|
25
38
|
|
|
39
|
+
# Creates an HTTP request object
|
|
40
|
+
#
|
|
41
|
+
# @api private
|
|
42
|
+
# @param http_method [Symbol] the HTTP method
|
|
43
|
+
# @param uri [URI::Generic] the request URI
|
|
44
|
+
# @param body [String, nil] the request body
|
|
45
|
+
# @return [Net::HTTPRequest] the created HTTP request
|
|
46
|
+
# @raise [ArgumentError] if the HTTP method is not supported
|
|
26
47
|
def create_request(http_method:, uri:, body:)
|
|
27
48
|
http_method_class = HTTP_METHODS[http_method]
|
|
28
49
|
|
|
@@ -34,13 +55,23 @@ module MLB
|
|
|
34
55
|
request
|
|
35
56
|
end
|
|
36
57
|
|
|
58
|
+
# Adds headers to an HTTP request
|
|
59
|
+
#
|
|
60
|
+
# @api private
|
|
61
|
+
# @param request [Net::HTTPRequest] the HTTP request
|
|
62
|
+
# @param headers [Hash] additional headers to add
|
|
63
|
+
# @return [void]
|
|
37
64
|
def add_headers(request:, headers:)
|
|
38
65
|
DEFAULT_HEADERS.merge(headers).each do |key, value|
|
|
39
|
-
request
|
|
40
|
-
request.add_field(key, value)
|
|
66
|
+
request[key] = value
|
|
41
67
|
end
|
|
42
68
|
end
|
|
43
69
|
|
|
70
|
+
# Escapes query parameters in a URI
|
|
71
|
+
#
|
|
72
|
+
# @api private
|
|
73
|
+
# @param uri [URI::Generic] the URI to escape
|
|
74
|
+
# @return [URI::Generic] the URI with escaped query parameters
|
|
44
75
|
def escape_query_params(uri)
|
|
45
76
|
URI(uri).tap { |u| u.query = URI.encode_www_form(URI.decode_www_form(u.query)) if u.query }
|
|
46
77
|
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require_relative "collection"
|
|
2
|
+
require_relative "review_reason"
|
|
3
|
+
|
|
4
|
+
module MLB
|
|
5
|
+
# Provides methods for fetching review reasons from the API
|
|
6
|
+
#
|
|
7
|
+
# @example Fetch all review reasons
|
|
8
|
+
# MLB::ReviewReasons.all #=> [#<MLB::ReviewReason>, ...]
|
|
9
|
+
class ReviewReasons < Collection
|
|
10
|
+
collection endpoint: "reviewReasons", item_type: ReviewReason, collection_name: :review_reasons
|
|
11
|
+
end
|
|
12
|
+
end
|
data/lib/mlb/roster.rb
CHANGED
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
require "shale"
|
|
2
|
-
require "uri"
|
|
3
2
|
require_relative "roster_entry"
|
|
4
|
-
require_relative "sport"
|
|
5
|
-
require_relative "team"
|
|
6
3
|
|
|
7
4
|
module MLB
|
|
5
|
+
# Provides methods for fetching team rosters from the API
|
|
8
6
|
class Roster < Shale::Mapper
|
|
9
|
-
attribute :copyright, Shale::Type::String
|
|
10
7
|
attribute :roster, RosterEntry, collection: true
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
# Finds a team's roster for a given season and sport
|
|
10
|
+
#
|
|
11
|
+
# @api public
|
|
12
|
+
# @example
|
|
13
|
+
# MLB::Roster.find(team: 119) #=> [#<MLB::RosterEntry>, ...]
|
|
14
|
+
# @param team [Team, Integer] the team or team ID
|
|
15
|
+
# @param season [Integer, nil] the season year (defaults to current year)
|
|
16
|
+
# @param sport [Sport, Integer] the sport or sport ID
|
|
17
|
+
# @return [Array<RosterEntry>] the list of roster entries
|
|
18
|
+
def self.find(team:, season: nil, sport: Utils::DEFAULT_SPORT_ID)
|
|
19
|
+
season ||= Utils.current_season
|
|
20
|
+
team_id = Utils.extract_id(team)
|
|
21
|
+
params = {season:, sportId: Utils.extract_id(sport)}
|
|
22
|
+
response = CLIENT.get("teams/#{team_id}/roster?#{Utils.build_query(params)}")
|
|
23
|
+
from_json(response).roster
|
|
20
24
|
end
|
|
21
25
|
end
|
|
22
26
|
end
|
data/lib/mlb/roster_entry.rb
CHANGED
|
@@ -1,14 +1,52 @@
|
|
|
1
|
+
require "equalizer"
|
|
1
2
|
require "shale"
|
|
2
3
|
require_relative "player"
|
|
3
4
|
require_relative "position"
|
|
4
5
|
require_relative "status"
|
|
5
6
|
|
|
6
7
|
module MLB
|
|
8
|
+
# Represents a player's entry on a team roster
|
|
7
9
|
class RosterEntry < Shale::Mapper
|
|
10
|
+
include Equalizer.new(:team_id, :player)
|
|
11
|
+
|
|
12
|
+
# @!attribute [rw] player
|
|
13
|
+
# Returns the player on the roster
|
|
14
|
+
# @api public
|
|
15
|
+
# @example
|
|
16
|
+
# entry.player #=> #<MLB::Player>
|
|
17
|
+
# @return [Player] the player on the roster
|
|
8
18
|
attribute :player, Player
|
|
19
|
+
|
|
20
|
+
# @!attribute [rw] jersey_number
|
|
21
|
+
# Returns the player's jersey number
|
|
22
|
+
# @api public
|
|
23
|
+
# @example
|
|
24
|
+
# entry.jersey_number #=> 17
|
|
25
|
+
# @return [Integer] the player's jersey number
|
|
9
26
|
attribute :jersey_number, Shale::Type::Integer
|
|
27
|
+
|
|
28
|
+
# @!attribute [rw] position
|
|
29
|
+
# Returns the player's position
|
|
30
|
+
# @api public
|
|
31
|
+
# @example
|
|
32
|
+
# entry.position #=> #<MLB::Position>
|
|
33
|
+
# @return [Position] the player's position
|
|
10
34
|
attribute :position, Position
|
|
35
|
+
|
|
36
|
+
# @!attribute [rw] status
|
|
37
|
+
# Returns the player's roster status
|
|
38
|
+
# @api public
|
|
39
|
+
# @example
|
|
40
|
+
# entry.status #=> #<MLB::Status>
|
|
41
|
+
# @return [Status] the player's roster status
|
|
11
42
|
attribute :status, Status
|
|
43
|
+
|
|
44
|
+
# @!attribute [rw] team_id
|
|
45
|
+
# Returns the parent team ID
|
|
46
|
+
# @api public
|
|
47
|
+
# @example
|
|
48
|
+
# entry.team_id #=> 119
|
|
49
|
+
# @return [Integer] the parent team ID
|
|
12
50
|
attribute :team_id, Shale::Type::Integer
|
|
13
51
|
|
|
14
52
|
json do
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require "equalizer"
|
|
2
|
+
require "shale"
|
|
3
|
+
|
|
4
|
+
module MLB
|
|
5
|
+
# Represents a roster type
|
|
6
|
+
class RosterType < Shale::Mapper
|
|
7
|
+
include Equalizer.new(:parameter)
|
|
8
|
+
|
|
9
|
+
# @!attribute [rw] description
|
|
10
|
+
# Returns the roster type description
|
|
11
|
+
# @api public
|
|
12
|
+
# @example
|
|
13
|
+
# roster_type.description #=> "40-Man Roster"
|
|
14
|
+
# @return [String] the roster type description
|
|
15
|
+
attribute :description, Shale::Type::String
|
|
16
|
+
|
|
17
|
+
# @!attribute [rw] lookup_name
|
|
18
|
+
# Returns the lookup name
|
|
19
|
+
# @api public
|
|
20
|
+
# @example
|
|
21
|
+
# roster_type.lookup_name #=> "40Man"
|
|
22
|
+
# @return [String] the lookup name
|
|
23
|
+
attribute :lookup_name, Shale::Type::String
|
|
24
|
+
|
|
25
|
+
# @!attribute [rw] parameter
|
|
26
|
+
# Returns the API parameter value
|
|
27
|
+
# @api public
|
|
28
|
+
# @example
|
|
29
|
+
# roster_type.parameter #=> "40Man"
|
|
30
|
+
# @return [String] the API parameter value
|
|
31
|
+
attribute :parameter, Shale::Type::String
|
|
32
|
+
|
|
33
|
+
json do
|
|
34
|
+
map "description", to: :description
|
|
35
|
+
map "lookupName", to: :lookup_name
|
|
36
|
+
map "parameter", to: :parameter
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require_relative "collection"
|
|
2
|
+
require_relative "roster_type"
|
|
3
|
+
|
|
4
|
+
module MLB
|
|
5
|
+
# Provides methods for fetching roster types from the API
|
|
6
|
+
#
|
|
7
|
+
# @example Fetch all roster types
|
|
8
|
+
# MLB::RosterTypes.all #=> [#<MLB::RosterType>, ...]
|
|
9
|
+
class RosterTypes < Collection
|
|
10
|
+
collection endpoint: "rosterTypes", item_type: RosterType, collection_name: :roster_types
|
|
11
|
+
end
|
|
12
|
+
end
|
data/lib/mlb/schedule.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require "shale"
|
|
2
|
+
require_relative "schedule_date"
|
|
3
|
+
|
|
4
|
+
module MLB
|
|
5
|
+
# Provides methods for fetching game schedules from the API
|
|
6
|
+
class Schedule < Shale::Mapper
|
|
7
|
+
attribute :total_games, Shale::Type::Integer
|
|
8
|
+
attribute :dates, ScheduleDate, collection: true
|
|
9
|
+
|
|
10
|
+
json do
|
|
11
|
+
map "totalGames", to: :total_games
|
|
12
|
+
map "dates", to: :dates
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Retrieves the schedule for a given date
|
|
16
|
+
#
|
|
17
|
+
# @api public
|
|
18
|
+
# @example Get schedule for a specific date
|
|
19
|
+
# MLB::Schedule.games(date: Date.new(2024, 7, 4))
|
|
20
|
+
# @example Get schedule for a team on a specific date
|
|
21
|
+
# MLB::Schedule.games(date: Date.new(2024, 7, 4), team: 147)
|
|
22
|
+
# @param date [Date] the date to get the schedule for
|
|
23
|
+
# @param sport [Sport, Integer] the sport or sport ID (default: MLB)
|
|
24
|
+
# @param team [Team, Integer] optional team or team ID to filter by
|
|
25
|
+
# @return [Array<ScheduledGame>] the list of scheduled games
|
|
26
|
+
def self.games(date: Date.today, sport: Utils::DEFAULT_SPORT_ID, team: nil)
|
|
27
|
+
params = {sportId: Utils.extract_id(sport), date:}
|
|
28
|
+
params[:teamId] = Utils.extract_id(team) if team
|
|
29
|
+
response = CLIENT.get("schedule?#{Utils.build_query(params)}")
|
|
30
|
+
from_json(response).dates.flat_map(&:games)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|