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.
Files changed (165) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -1
  3. data/LICENSE.txt +1 -1
  4. data/README.md +329 -32
  5. data/lib/mlb/affiliates.rb +28 -0
  6. data/lib/mlb/alumni.rb +30 -0
  7. data/lib/mlb/attendance.rb +28 -0
  8. data/lib/mlb/attendance_record.rb +157 -0
  9. data/lib/mlb/award.rb +126 -0
  10. data/lib/mlb/awards.rb +32 -0
  11. data/lib/mlb/baseball_stat.rb +65 -0
  12. data/lib/mlb/baseball_stats.rb +12 -0
  13. data/lib/mlb/boxscore.rb +81 -0
  14. data/lib/mlb/boxscore_team_stats.rb +210 -0
  15. data/lib/mlb/client.rb +71 -1
  16. data/lib/mlb/coaches.rb +28 -0
  17. data/lib/mlb/code_description_type.rb +45 -0
  18. data/lib/mlb/collection.rb +48 -0
  19. data/lib/mlb/comparable_by_attribute.rb +56 -0
  20. data/lib/mlb/conference.rb +39 -0
  21. data/lib/mlb/conferences.rb +33 -0
  22. data/lib/mlb/connection.rb +130 -7
  23. data/lib/mlb/context_metrics.rb +90 -0
  24. data/lib/mlb/division.rb +26 -6
  25. data/lib/mlb/divisions.rb +24 -18
  26. data/lib/mlb/draft.rb +83 -0
  27. data/lib/mlb/draft_pick.rb +155 -0
  28. data/lib/mlb/error_handler.rb +21 -1
  29. data/lib/mlb/errors/bad_gateway.rb +6 -1
  30. data/lib/mlb/errors/bad_request.rb +1 -0
  31. data/lib/mlb/errors/client_error.rb +1 -0
  32. data/lib/mlb/errors/connection_exception.rb +1 -0
  33. data/lib/mlb/errors/error.rb +1 -0
  34. data/lib/mlb/errors/forbidden.rb +1 -0
  35. data/lib/mlb/errors/gateway_timeout.rb +6 -1
  36. data/lib/mlb/errors/gone.rb +1 -0
  37. data/lib/mlb/errors/http_error.rb +22 -2
  38. data/lib/mlb/errors/internal_server_error.rb +1 -0
  39. data/lib/mlb/errors/network_error.rb +6 -1
  40. data/lib/mlb/errors/not_acceptable.rb +1 -0
  41. data/lib/mlb/errors/not_found.rb +1 -0
  42. data/lib/mlb/errors/payload_too_large.rb +1 -0
  43. data/lib/mlb/errors/retryable.rb +15 -0
  44. data/lib/mlb/errors/server_error.rb +1 -0
  45. data/lib/mlb/errors/service_unavailable.rb +6 -1
  46. data/lib/mlb/errors/too_many_redirects.rb +1 -0
  47. data/lib/mlb/errors/too_many_requests.rb +6 -1
  48. data/lib/mlb/errors/unauthorized.rb +1 -0
  49. data/lib/mlb/errors/unprocessable_entity.rb +1 -0
  50. data/lib/mlb/event_type.rb +61 -0
  51. data/lib/mlb/event_types.rb +12 -0
  52. data/lib/mlb/free_agent.rb +78 -0
  53. data/lib/mlb/free_agents.rb +34 -0
  54. data/lib/mlb/game_changes.rb +87 -0
  55. data/lib/mlb/game_content.rb +251 -0
  56. data/lib/mlb/game_data.rb +155 -0
  57. data/lib/mlb/game_pace.rb +173 -0
  58. data/lib/mlb/game_status.rb +94 -0
  59. data/lib/mlb/game_statuses.rb +12 -0
  60. data/lib/mlb/game_type.rb +98 -0
  61. data/lib/mlb/game_types.rb +12 -0
  62. data/lib/mlb/handedness.rb +30 -9
  63. data/lib/mlb/high_low.rb +121 -0
  64. data/lib/mlb/hit_trajectories.rb +12 -0
  65. data/lib/mlb/hit_trajectory.rb +6 -0
  66. data/lib/mlb/home_run_derby.rb +285 -0
  67. data/lib/mlb/id_description_type.rb +44 -0
  68. data/lib/mlb/inning_score.rb +87 -0
  69. data/lib/mlb/job.rb +58 -0
  70. data/lib/mlb/job_type.rb +39 -0
  71. data/lib/mlb/job_types.rb +12 -0
  72. data/lib/mlb/jobs.rb +87 -0
  73. data/lib/mlb/language.rb +48 -0
  74. data/lib/mlb/languages.rb +12 -0
  75. data/lib/mlb/leader.rb +79 -0
  76. data/lib/mlb/leaders.rb +68 -0
  77. data/lib/mlb/league.rb +97 -14
  78. data/lib/mlb/league_leader_type.rb +21 -0
  79. data/lib/mlb/league_leader_types.rb +12 -0
  80. data/lib/mlb/leagues.rb +24 -18
  81. data/lib/mlb/linescore.rb +342 -0
  82. data/lib/mlb/linescore_teams.rb +89 -0
  83. data/lib/mlb/live_feed.rb +130 -0
  84. data/lib/mlb/logical_event.rb +17 -0
  85. data/lib/mlb/logical_events.rb +12 -0
  86. data/lib/mlb/metric.rb +48 -0
  87. data/lib/mlb/metrics.rb +12 -0
  88. data/lib/mlb/people_changes.rb +34 -0
  89. data/lib/mlb/personnel.rb +28 -0
  90. data/lib/mlb/pitch_code.rb +90 -0
  91. data/lib/mlb/pitch_codes.rb +12 -0
  92. data/lib/mlb/pitch_type.rb +6 -0
  93. data/lib/mlb/pitch_types.rb +12 -0
  94. data/lib/mlb/platform.rb +30 -0
  95. data/lib/mlb/platforms.rb +12 -0
  96. data/lib/mlb/play.rb +300 -0
  97. data/lib/mlb/play_by_play.rb +52 -0
  98. data/lib/mlb/player.rb +186 -0
  99. data/lib/mlb/player_game_stats.rb +350 -0
  100. data/lib/mlb/player_stat.rb +70 -0
  101. data/lib/mlb/players.rb +42 -11
  102. data/lib/mlb/position.rb +68 -0
  103. data/lib/mlb/positions.rb +12 -0
  104. data/lib/mlb/postseason_schedule.rb +41 -0
  105. data/lib/mlb/postseason_series.rb +54 -0
  106. data/lib/mlb/redirect_handler.rb +80 -21
  107. data/lib/mlb/request_builder.rb +33 -2
  108. data/lib/mlb/review_reason.rb +6 -0
  109. data/lib/mlb/review_reasons.rb +12 -0
  110. data/lib/mlb/roster.rb +16 -12
  111. data/lib/mlb/roster_entry.rb +38 -0
  112. data/lib/mlb/roster_type.rb +39 -0
  113. data/lib/mlb/roster_types.rb +12 -0
  114. data/lib/mlb/schedule.rb +33 -0
  115. data/lib/mlb/schedule_date.rb +37 -0
  116. data/lib/mlb/schedule_event_type.rb +25 -0
  117. data/lib/mlb/schedule_event_types.rb +12 -0
  118. data/lib/mlb/scheduled_game.rb +321 -0
  119. data/lib/mlb/season.rb +74 -0
  120. data/lib/mlb/season_date_info.rb +4 -0
  121. data/lib/mlb/seasons.rb +36 -0
  122. data/lib/mlb/single_team_stats.rb +33 -0
  123. data/lib/mlb/situation_code.rb +116 -0
  124. data/lib/mlb/situation_codes.rb +12 -0
  125. data/lib/mlb/skies.rb +12 -0
  126. data/lib/mlb/sky.rb +6 -0
  127. data/lib/mlb/sport.rb +18 -5
  128. data/lib/mlb/sport_players.rb +30 -0
  129. data/lib/mlb/sports.rb +17 -7
  130. data/lib/mlb/standings.rb +47 -0
  131. data/lib/mlb/standings_record.rb +108 -0
  132. data/lib/mlb/standings_type.rb +25 -0
  133. data/lib/mlb/standings_types.rb +12 -0
  134. data/lib/mlb/stat_group.rb +21 -0
  135. data/lib/mlb/stat_groups.rb +12 -0
  136. data/lib/mlb/stat_type.rb +21 -0
  137. data/lib/mlb/stat_types.rb +12 -0
  138. data/lib/mlb/stat_values.rb +158 -0
  139. data/lib/mlb/stats.rb +41 -0
  140. data/lib/mlb/status.rb +17 -0
  141. data/lib/mlb/streaks.rb +184 -0
  142. data/lib/mlb/team.rb +66 -7
  143. data/lib/mlb/team_history.rb +28 -0
  144. data/lib/mlb/team_leader.rb +59 -0
  145. data/lib/mlb/team_leaders.rb +45 -0
  146. data/lib/mlb/team_record.rb +285 -0
  147. data/lib/mlb/team_stat.rb +50 -0
  148. data/lib/mlb/team_stats.rb +52 -0
  149. data/lib/mlb/teams.rb +28 -18
  150. data/lib/mlb/tied_games.rb +32 -0
  151. data/lib/mlb/transaction.rb +128 -0
  152. data/lib/mlb/transactions.rb +11 -6
  153. data/lib/mlb/uniforms.rb +97 -0
  154. data/lib/mlb/utils.rb +45 -0
  155. data/lib/mlb/venue.rb +10 -0
  156. data/lib/mlb/venues.rb +28 -18
  157. data/lib/mlb/version.rb +2 -1
  158. data/lib/mlb/win_probability.rb +64 -0
  159. data/lib/mlb/wind_direction.rb +6 -0
  160. data/lib/mlb/wind_directions.rb +12 -0
  161. data/lib/mlb.rb +64 -0
  162. data/sig/equalizer.rbs +3 -0
  163. data/sig/mlb.rbs +1972 -103
  164. data/sig/shale.rbs +29 -0
  165. 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
- def self.all(season: Time.now.year, sport: Sport.new(id: 1))
17
- sport_id = sport.respond_to?(:id) ? sport.id : sport
18
- params = {season:}
19
- query_string = URI.encode_www_form(params)
20
- response = CLIENT.get("sports/#{sport_id}/players?#{query_string}")
21
- players = from_json(response)
22
- players.players
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
@@ -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
- if response.is_a?(Net::HTTPRedirection)
23
- raise TooManyRedirects, "Too many redirects" if redirect_count > max_redirects
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
- new_request = build_request(request, new_uri, Integer(response.code))
28
- new_response = connection.perform(request: new_request)
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
- handle(response: new_response, request: new_request, base_url:, redirect_count: redirect_count + 1)
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
- def build_new_uri(response, base_url)
39
- location = response.fetch("location")
40
- # If location is relative, it will join with the original base URL, otherwise it will overwrite it
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
- def build_request(request, uri, response_code)
45
- http_method, body = case response_code
46
- in 307 | 308
47
- [request.method.downcase.to_sym, request.body]
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
- [:get, nil]
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
@@ -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.delete(key)
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,6 @@
1
+ require_relative "code_description_type"
2
+
3
+ module MLB
4
+ # Represents a review reason
5
+ class ReviewReason < CodeDescriptionType; end
6
+ 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
- def self.find(team:, season: Time.now.year, sport: Sport.new(id: 1))
13
- team_id = team.respond_to?(:id) ? team.id : team
14
- sport_id = sport.respond_to?(:id) ? sport.id : sport
15
- params = {season:, sportId: sport_id}
16
- query_string = URI.encode_www_form(params)
17
- response = CLIENT.get("teams/#{team_id}/roster?#{query_string}")
18
- roster = from_json(response)
19
- roster.roster
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
@@ -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
@@ -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