mlb 0.10.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 (164) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/README.md +329 -108
  4. data/lib/mlb/affiliates.rb +28 -0
  5. data/lib/mlb/alumni.rb +30 -0
  6. data/lib/mlb/attendance.rb +28 -0
  7. data/lib/mlb/attendance_record.rb +157 -0
  8. data/lib/mlb/award.rb +89 -11
  9. data/lib/mlb/awards.rb +17 -7
  10. data/lib/mlb/baseball_stat.rb +65 -0
  11. data/lib/mlb/baseball_stats.rb +12 -0
  12. data/lib/mlb/boxscore.rb +81 -0
  13. data/lib/mlb/boxscore_team_stats.rb +210 -0
  14. data/lib/mlb/client.rb +71 -0
  15. data/lib/mlb/coaches.rb +28 -0
  16. data/lib/mlb/code_description_type.rb +45 -0
  17. data/lib/mlb/collection.rb +48 -0
  18. data/lib/mlb/comparable_by_attribute.rb +56 -0
  19. data/lib/mlb/conference.rb +8 -1
  20. data/lib/mlb/conferences.rb +18 -10
  21. data/lib/mlb/connection.rb +130 -7
  22. data/lib/mlb/context_metrics.rb +90 -0
  23. data/lib/mlb/division.rb +24 -6
  24. data/lib/mlb/divisions.rb +24 -18
  25. data/lib/mlb/draft.rb +83 -0
  26. data/lib/mlb/draft_pick.rb +155 -0
  27. data/lib/mlb/error_handler.rb +21 -1
  28. data/lib/mlb/errors/bad_gateway.rb +6 -1
  29. data/lib/mlb/errors/bad_request.rb +1 -0
  30. data/lib/mlb/errors/client_error.rb +1 -0
  31. data/lib/mlb/errors/connection_exception.rb +1 -0
  32. data/lib/mlb/errors/error.rb +1 -0
  33. data/lib/mlb/errors/forbidden.rb +1 -0
  34. data/lib/mlb/errors/gateway_timeout.rb +6 -1
  35. data/lib/mlb/errors/gone.rb +1 -0
  36. data/lib/mlb/errors/http_error.rb +22 -2
  37. data/lib/mlb/errors/internal_server_error.rb +1 -0
  38. data/lib/mlb/errors/network_error.rb +6 -1
  39. data/lib/mlb/errors/not_acceptable.rb +1 -0
  40. data/lib/mlb/errors/not_found.rb +1 -0
  41. data/lib/mlb/errors/payload_too_large.rb +1 -0
  42. data/lib/mlb/errors/retryable.rb +15 -0
  43. data/lib/mlb/errors/server_error.rb +1 -0
  44. data/lib/mlb/errors/service_unavailable.rb +6 -1
  45. data/lib/mlb/errors/too_many_redirects.rb +1 -0
  46. data/lib/mlb/errors/too_many_requests.rb +6 -1
  47. data/lib/mlb/errors/unauthorized.rb +1 -0
  48. data/lib/mlb/errors/unprocessable_entity.rb +1 -0
  49. data/lib/mlb/event_type.rb +61 -0
  50. data/lib/mlb/event_types.rb +12 -0
  51. data/lib/mlb/free_agent.rb +78 -0
  52. data/lib/mlb/free_agents.rb +34 -0
  53. data/lib/mlb/game_changes.rb +87 -0
  54. data/lib/mlb/game_content.rb +251 -0
  55. data/lib/mlb/game_data.rb +155 -0
  56. data/lib/mlb/game_pace.rb +173 -0
  57. data/lib/mlb/game_status.rb +94 -0
  58. data/lib/mlb/game_statuses.rb +12 -0
  59. data/lib/mlb/game_type.rb +98 -0
  60. data/lib/mlb/game_types.rb +12 -0
  61. data/lib/mlb/handedness.rb +28 -10
  62. data/lib/mlb/high_low.rb +121 -0
  63. data/lib/mlb/hit_trajectories.rb +12 -0
  64. data/lib/mlb/hit_trajectory.rb +6 -0
  65. data/lib/mlb/home_run_derby.rb +285 -0
  66. data/lib/mlb/id_description_type.rb +44 -0
  67. data/lib/mlb/inning_score.rb +87 -0
  68. data/lib/mlb/job.rb +58 -0
  69. data/lib/mlb/job_type.rb +39 -0
  70. data/lib/mlb/job_types.rb +12 -0
  71. data/lib/mlb/jobs.rb +87 -0
  72. data/lib/mlb/language.rb +48 -0
  73. data/lib/mlb/languages.rb +12 -0
  74. data/lib/mlb/leader.rb +79 -0
  75. data/lib/mlb/leaders.rb +68 -0
  76. data/lib/mlb/league.rb +95 -14
  77. data/lib/mlb/league_leader_type.rb +21 -0
  78. data/lib/mlb/league_leader_types.rb +12 -0
  79. data/lib/mlb/leagues.rb +24 -18
  80. data/lib/mlb/linescore.rb +342 -0
  81. data/lib/mlb/linescore_teams.rb +89 -0
  82. data/lib/mlb/live_feed.rb +130 -0
  83. data/lib/mlb/logical_event.rb +17 -0
  84. data/lib/mlb/logical_events.rb +12 -0
  85. data/lib/mlb/metric.rb +48 -0
  86. data/lib/mlb/metrics.rb +12 -0
  87. data/lib/mlb/people_changes.rb +34 -0
  88. data/lib/mlb/personnel.rb +28 -0
  89. data/lib/mlb/pitch_code.rb +90 -0
  90. data/lib/mlb/pitch_codes.rb +12 -0
  91. data/lib/mlb/pitch_type.rb +6 -0
  92. data/lib/mlb/pitch_types.rb +12 -0
  93. data/lib/mlb/platform.rb +30 -0
  94. data/lib/mlb/platforms.rb +12 -0
  95. data/lib/mlb/play.rb +300 -0
  96. data/lib/mlb/play_by_play.rb +52 -0
  97. data/lib/mlb/player.rb +182 -0
  98. data/lib/mlb/player_game_stats.rb +350 -0
  99. data/lib/mlb/player_stat.rb +70 -0
  100. data/lib/mlb/players.rb +34 -23
  101. data/lib/mlb/position.rb +65 -0
  102. data/lib/mlb/positions.rb +12 -0
  103. data/lib/mlb/postseason_schedule.rb +41 -0
  104. data/lib/mlb/postseason_series.rb +54 -0
  105. data/lib/mlb/redirect_handler.rb +80 -21
  106. data/lib/mlb/request_builder.rb +33 -2
  107. data/lib/mlb/review_reason.rb +6 -0
  108. data/lib/mlb/review_reasons.rb +12 -0
  109. data/lib/mlb/roster.rb +16 -12
  110. data/lib/mlb/roster_entry.rb +35 -0
  111. data/lib/mlb/roster_type.rb +39 -0
  112. data/lib/mlb/roster_types.rb +12 -0
  113. data/lib/mlb/schedule.rb +33 -0
  114. data/lib/mlb/schedule_date.rb +37 -0
  115. data/lib/mlb/schedule_event_type.rb +25 -0
  116. data/lib/mlb/schedule_event_types.rb +12 -0
  117. data/lib/mlb/scheduled_game.rb +321 -0
  118. data/lib/mlb/season.rb +15 -4
  119. data/lib/mlb/season_date_info.rb +1 -0
  120. data/lib/mlb/seasons.rb +24 -18
  121. data/lib/mlb/single_team_stats.rb +33 -0
  122. data/lib/mlb/situation_code.rb +116 -0
  123. data/lib/mlb/situation_codes.rb +12 -0
  124. data/lib/mlb/skies.rb +12 -0
  125. data/lib/mlb/sky.rb +3 -8
  126. data/lib/mlb/sport.rb +16 -5
  127. data/lib/mlb/sport_players.rb +30 -0
  128. data/lib/mlb/sports.rb +17 -7
  129. data/lib/mlb/standings.rb +47 -0
  130. data/lib/mlb/standings_record.rb +108 -0
  131. data/lib/mlb/standings_type.rb +25 -0
  132. data/lib/mlb/standings_types.rb +12 -0
  133. data/lib/mlb/stat_group.rb +21 -0
  134. data/lib/mlb/stat_groups.rb +12 -0
  135. data/lib/mlb/stat_type.rb +21 -0
  136. data/lib/mlb/stat_types.rb +12 -0
  137. data/lib/mlb/stat_values.rb +158 -0
  138. data/lib/mlb/stats.rb +41 -0
  139. data/lib/mlb/status.rb +14 -0
  140. data/lib/mlb/streaks.rb +184 -0
  141. data/lib/mlb/team.rb +62 -7
  142. data/lib/mlb/team_history.rb +28 -0
  143. data/lib/mlb/team_leader.rb +59 -0
  144. data/lib/mlb/team_leaders.rb +45 -0
  145. data/lib/mlb/team_record.rb +285 -0
  146. data/lib/mlb/team_stat.rb +50 -0
  147. data/lib/mlb/team_stats.rb +52 -0
  148. data/lib/mlb/teams.rb +28 -18
  149. data/lib/mlb/tied_games.rb +32 -0
  150. data/lib/mlb/transaction.rb +125 -0
  151. data/lib/mlb/transactions.rb +11 -6
  152. data/lib/mlb/uniforms.rb +97 -0
  153. data/lib/mlb/utils.rb +45 -0
  154. data/lib/mlb/venue.rb +7 -0
  155. data/lib/mlb/venues.rb +28 -18
  156. data/lib/mlb/version.rb +2 -1
  157. data/lib/mlb/win_probability.rb +64 -0
  158. data/lib/mlb/wind_direction.rb +3 -8
  159. data/lib/mlb/wind_directions.rb +12 -0
  160. data/lib/mlb.rb +61 -0
  161. data/sig/equalizer.rbs +3 -0
  162. data/sig/mlb.rbs +2055 -0
  163. data/sig/shale.rbs +29 -0
  164. metadata +107 -2
data/lib/mlb/client.rb CHANGED
@@ -5,18 +5,46 @@ require_relative "request_builder"
5
5
  require_relative "error_handler"
6
6
 
7
7
  module MLB
8
+ # HTTP client for making requests to the MLB Stats API
8
9
  class Client
9
10
  extend Forwardable
10
11
 
12
+ # Default base URL for the MLB Stats API
11
13
  DEFAULT_BASE_URL = "https://statsapi.mlb.com/api/v1/".freeze
12
14
 
15
+ # Returns the base URL for API requests
16
+ #
17
+ # @api public
18
+ # @example
19
+ # client.base_url #=> "https://statsapi.mlb.com/api/v1/"
20
+ # @return [String] the base URL for API requests
13
21
  attr_accessor :base_url
14
22
 
23
+ # @!method base_url=(value)
24
+ # Sets the base URL for API requests
25
+ # @api public
26
+ # @example
27
+ # client.base_url = "https://example.com/api/"
28
+ # @param value [String] the base URL for API requests
29
+ # @return [String] the base URL for API requests
30
+
15
31
  def_delegators :@connection, :open_timeout, :read_timeout, :write_timeout, :proxy_url, :debug_output
16
32
  def_delegators :@connection, :open_timeout=, :read_timeout=, :write_timeout=, :proxy_url=, :debug_output=
17
33
  def_delegators :@redirect_handler, :max_redirects
18
34
  def_delegators :@redirect_handler, :max_redirects=
19
35
 
36
+ # Initializes a new Client instance
37
+ #
38
+ # @api public
39
+ # @example
40
+ # client = MLB::Client.new
41
+ # @param base_url [String] the base URL for API requests
42
+ # @param open_timeout [Integer] the connection open timeout in seconds
43
+ # @param read_timeout [Integer] the read timeout in seconds
44
+ # @param write_timeout [Integer] the write timeout in seconds
45
+ # @param debug_output [IO] the IO object for debug output
46
+ # @param proxy_url [String, nil] the proxy URL
47
+ # @param max_redirects [Integer] the maximum number of redirects to follow
20
48
  def initialize(base_url: DEFAULT_BASE_URL,
21
49
  open_timeout: Connection::DEFAULT_OPEN_TIMEOUT,
22
50
  read_timeout: Connection::DEFAULT_READ_TIMEOUT,
@@ -31,24 +59,66 @@ module MLB
31
59
  @error_handler = ErrorHandler.new
32
60
  end
33
61
 
62
+ # Performs a GET request
63
+ #
64
+ # @api public
65
+ # @example
66
+ # client.get("teams")
67
+ # @param endpoint [String] the API endpoint
68
+ # @param headers [Hash] optional HTTP headers
69
+ # @return [String, nil] the response body
34
70
  def get(endpoint, headers: {})
35
71
  execute_request(:get, endpoint, headers:)
36
72
  end
37
73
 
74
+ # Performs a POST request
75
+ #
76
+ # @api public
77
+ # @example
78
+ # client.post("endpoint", '{"key": "value"}')
79
+ # @param endpoint [String] the API endpoint
80
+ # @param body [String, nil] the request body
81
+ # @param headers [Hash] optional HTTP headers
82
+ # @return [String, nil] the response body
38
83
  def post(endpoint, body = nil, headers: {})
39
84
  execute_request(:post, endpoint, body:, headers:)
40
85
  end
41
86
 
87
+ # Performs a PUT request
88
+ #
89
+ # @api public
90
+ # @example
91
+ # client.put("endpoint", '{"key": "value"}')
92
+ # @param endpoint [String] the API endpoint
93
+ # @param body [String, nil] the request body
94
+ # @param headers [Hash] optional HTTP headers
95
+ # @return [String, nil] the response body
42
96
  def put(endpoint, body = nil, headers: {})
43
97
  execute_request(:put, endpoint, body:, headers:)
44
98
  end
45
99
 
100
+ # Performs a DELETE request
101
+ #
102
+ # @api public
103
+ # @example
104
+ # client.delete("endpoint")
105
+ # @param endpoint [String] the API endpoint
106
+ # @param headers [Hash] optional HTTP headers
107
+ # @return [String, nil] the response body
46
108
  def delete(endpoint, headers: {})
47
109
  execute_request(:delete, endpoint, headers:)
48
110
  end
49
111
 
50
112
  private
51
113
 
114
+ # Executes an HTTP request
115
+ #
116
+ # @api private
117
+ # @param http_method [Symbol] the HTTP method
118
+ # @param endpoint [String] the API endpoint
119
+ # @param headers [Hash] HTTP headers
120
+ # @param body [String, nil] the request body
121
+ # @return [String, nil] the response body
52
122
  def execute_request(http_method, endpoint, headers:, body: nil)
53
123
  uri = URI.join(base_url, endpoint)
54
124
  request = @request_builder.build(http_method:, uri:, body:, headers:)
@@ -58,5 +128,6 @@ module MLB
58
128
  end
59
129
  end
60
130
 
131
+ # Default client instance
61
132
  CLIENT = Client.new
62
133
  end
@@ -0,0 +1,28 @@
1
+ require "shale"
2
+ require_relative "job"
3
+
4
+ module MLB
5
+ # Provides methods for fetching team coaching staff from the API
6
+ class Coaches < Shale::Mapper
7
+ attribute :roster, Job, collection: true
8
+
9
+ json do
10
+ map "roster", to: :roster
11
+ end
12
+
13
+ # Retrieves coaches for a team
14
+ #
15
+ # @api public
16
+ # @example Get coaches for a team
17
+ # MLB::Coaches.find(team: 147, season: 2024)
18
+ # @param team [Team, Integer] the team or team ID
19
+ # @param season [Integer, nil] the season year (defaults to current year)
20
+ # @return [Array<Job>] the coaches
21
+ def self.find(team:, season: nil)
22
+ season ||= Utils.current_season
23
+ team_id = Utils.extract_id(team)
24
+ response = CLIENT.get("teams/#{team_id}/coaches?#{Utils.build_query(season:)}")
25
+ from_json(response).roster
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,45 @@
1
+ require "equalizer"
2
+ require "shale"
3
+
4
+ module MLB
5
+ # Base class for simple code/description value objects from the MLB Stats API.
6
+ # These represent enumerated types that consist of a code identifier and
7
+ # human-readable description (e.g., pitch types, wind directions, sky conditions).
8
+ #
9
+ # @api private
10
+ # @abstract Subclass and define specific domain semantics
11
+ class CodeDescriptionType < Shale::Mapper
12
+ include Equalizer.new(:code)
13
+
14
+ # @!attribute [rw] code
15
+ # Returns the type code identifier
16
+ # @api public
17
+ # @example
18
+ # instance.code #=> "SL"
19
+ # @return [String] the type code identifier
20
+ attribute :code, Shale::Type::String
21
+
22
+ # @!attribute [rw] description
23
+ # Returns the human-readable description
24
+ # @api public
25
+ # @example
26
+ # instance.description #=> "Slider"
27
+ # @return [String] the human-readable description
28
+ attribute :description, Shale::Type::String
29
+
30
+ json do
31
+ map "code", to: :code
32
+ map "description", to: :description
33
+ end
34
+
35
+ # Returns a string representation of the object
36
+ #
37
+ # @api public
38
+ # @example
39
+ # pitch_type.to_s #=> "SL (Slider)"
40
+ # @return [String] string representation
41
+ def to_s
42
+ "#{code} (#{description})"
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,48 @@
1
+ require "shale"
2
+
3
+ module MLB
4
+ # Base class for simple collection endpoints that only need an .all method.
5
+ # Provides a class macro to define the endpoint, item type, and attribute name,
6
+ # eliminating repetitive boilerplate across 20+ similar collection classes.
7
+ #
8
+ # @api private
9
+ class Collection < Shale::Mapper
10
+ class << self
11
+ # Returns the API endpoint for this collection
12
+ #
13
+ # @api private
14
+ # @return [String] the API endpoint
15
+ attr_reader :endpoint
16
+
17
+ # Returns the collection attribute name
18
+ #
19
+ # @api private
20
+ # @return [Symbol] the collection attribute name
21
+ attr_reader :collection_name
22
+
23
+ # Configures the collection with endpoint and item type
24
+ #
25
+ # @api private
26
+ # @param endpoint [String] the API endpoint to fetch from
27
+ # @param item_type [Class] the Shale::Mapper class for individual items
28
+ # @param collection_name [Symbol] the attribute name for the collection
29
+ # @return [void]
30
+ def collection(endpoint:, item_type:, collection_name:)
31
+ @endpoint = endpoint
32
+ @collection_name = collection_name
33
+ attribute collection_name, item_type, collection: true
34
+ end
35
+
36
+ # Retrieves all items from this collection
37
+ #
38
+ # @api public
39
+ # @example
40
+ # MLB::Sports.all #=> [#<MLB::Sport id=1 name="Major League Baseball">, ...]
41
+ # @return [Array] the list of items
42
+ def all
43
+ response = CLIENT.get(endpoint)
44
+ from_json("{\"#{collection_name}\":#{response}}").public_send(collection_name)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,56 @@
1
+ module MLB
2
+ # Provides comparison behavior for model classes by a specified attribute.
3
+ # Handles nil values gracefully by placing them at the end when sorting.
4
+ #
5
+ # @api private
6
+ # @example
7
+ # class Award < Shale::Mapper
8
+ # include Comparable
9
+ # include MLB::ComparableByAttribute
10
+ #
11
+ # def comparable_attribute = :sort_order
12
+ # end
13
+ module ComparableByAttribute
14
+ # Compares this object with another by the comparable attribute
15
+ #
16
+ # @api public
17
+ # @example
18
+ # award1 <=> award2 #=> -1
19
+ # @param other [Object] the object to compare with
20
+ # @return [Integer, nil] -1, 0, 1, or nil if not comparable
21
+ def <=>(other)
22
+ return nil unless other.is_a?(self.class) || is_a?(other.class)
23
+
24
+ compare_values(
25
+ public_send(comparable_attribute),
26
+ other.public_send(comparable_attribute)
27
+ )
28
+ end
29
+
30
+ private
31
+
32
+ # Returns the attribute name used for comparison
33
+ #
34
+ # @api private
35
+ # @abstract Override in including class
36
+ # @return [Symbol] the attribute name
37
+ def comparable_attribute
38
+ raise NotImplementedError
39
+ end
40
+
41
+ # Compares two values, treating nil as greater than any value
42
+ #
43
+ # @api private
44
+ # @param self_val [Object, nil] the value from self
45
+ # @param other_val [Object, nil] the value from other
46
+ # @return [Integer] -1, 0, or 1
47
+ def compare_values(self_val, other_val)
48
+ case [self_val, other_val]
49
+ in [nil, nil] then 0
50
+ in [nil, _] then 1
51
+ in [_, nil] then -1
52
+ else self_val <=> other_val
53
+ end
54
+ end
55
+ end
56
+ end
@@ -4,6 +4,7 @@ require_relative "league"
4
4
  require_relative "sport"
5
5
 
6
6
  module MLB
7
+ # Represents a conference
7
8
  class Conference < Shale::Mapper
8
9
  include Equalizer.new(:id)
9
10
 
@@ -16,7 +17,13 @@ module MLB
16
17
  attribute :sport, Sport
17
18
  attribute :name_short, Shale::Type::String
18
19
 
19
- alias_method :wildcard?, :has_wildcard
20
+ # Checks if the conference has a wildcard
21
+ #
22
+ # @api public
23
+ # @example
24
+ # conference.wildcard? #=> true
25
+ # @return [Boolean] whether the conference has a wildcard
26
+ def wildcard? = has_wildcard
20
27
 
21
28
  json do
22
29
  map "id", to: :id
@@ -1,25 +1,33 @@
1
1
  require "shale"
2
- require "uri"
3
2
  require_relative "conference"
4
3
 
5
4
  module MLB
5
+ # Collection of conferences from the MLB Stats API
6
6
  class Conferences < Shale::Mapper
7
- attribute :copyright, Shale::Type::String
8
7
  attribute :conferences, Conference, collection: true
9
8
 
9
+ # Retrieves all conferences
10
+ #
11
+ # @api public
12
+ # @example
13
+ # MLB::Conferences.all
14
+ # @return [Array<Conference>] list of all conferences
10
15
  def self.all
11
16
  response = CLIENT.get("conferences")
12
- conferences = from_json(response)
13
- conferences.conferences
17
+ from_json(response).conferences
14
18
  end
15
19
 
20
+ # Finds a conference by ID
21
+ #
22
+ # @api public
23
+ # @example
24
+ # MLB::Conferences.find(301)
25
+ # @param conference [Integer, Conference] the conference ID or Conference object
26
+ # @return [Conference, nil] the conference if found
16
27
  def self.find(conference)
17
- id = conference.respond_to?(:id) ? conference.id : conference
18
- params = {conferenceId: id}
19
- query_string = URI.encode_www_form(params)
20
- response = CLIENT.get("conferences?#{query_string}")
21
- conferences = from_json(response)
22
- conferences.conferences.first
28
+ params = {conferenceId: Utils.extract_id(conference)}
29
+ response = CLIENT.get("conferences?#{Utils.build_query(params)}")
30
+ from_json(response).conferences.first
23
31
  end
24
32
  end
25
33
  end
@@ -5,15 +5,25 @@ require "uri"
5
5
  require_relative "errors/network_error"
6
6
 
7
7
  module MLB
8
+ # Manages HTTP connections to the MLB Stats API
8
9
  class Connection
9
10
  extend Forwardable
10
11
 
12
+ # Default host for API requests
11
13
  DEFAULT_HOST = "statsapi.mlb.com".freeze
14
+ # Default port for HTTPS connections
12
15
  DEFAULT_PORT = 443
16
+ # Default connection open timeout in seconds
13
17
  DEFAULT_OPEN_TIMEOUT = 60 # seconds
18
+ # Default read timeout in seconds
14
19
  DEFAULT_READ_TIMEOUT = 60 # seconds
20
+ # Default write timeout in seconds
15
21
  DEFAULT_WRITE_TIMEOUT = 60 # seconds
22
+ # Default debug output destination
16
23
  DEFAULT_DEBUG_OUTPUT = File.open(File::NULL, "w")
24
+ # HTTPS scheme identifier
25
+ HTTPS_SCHEME = "https".freeze
26
+ # Network errors that trigger a NetworkError exception
17
27
  NETWORK_ERRORS = [
18
28
  Errno::ECONNREFUSED,
19
29
  Errno::ECONNRESET,
@@ -22,33 +32,135 @@ module MLB
22
32
  OpenSSL::SSL::SSLError
23
33
  ].freeze
24
34
 
25
- attr_accessor :open_timeout, :read_timeout, :write_timeout, :debug_output
26
- attr_reader :proxy_url, :proxy_uri
35
+ # Returns the connection open timeout in seconds
36
+ #
37
+ # @api public
38
+ # @example
39
+ # connection.open_timeout #=> 60
40
+ # @return [Integer] the connection open timeout in seconds
41
+ attr_accessor :open_timeout
42
+
43
+ # @!method open_timeout=(value)
44
+ # Sets the connection open timeout in seconds
45
+ # @api public
46
+ # @example
47
+ # connection.open_timeout = 30
48
+ # @param value [Integer] the timeout in seconds
49
+ # @return [Integer] the timeout in seconds
50
+
51
+ # Returns the read timeout in seconds
52
+ #
53
+ # @api public
54
+ # @example
55
+ # connection.read_timeout #=> 60
56
+ # @return [Integer] the read timeout in seconds
57
+ attr_accessor :read_timeout
58
+
59
+ # @!method read_timeout=(value)
60
+ # Sets the read timeout in seconds
61
+ # @api public
62
+ # @example
63
+ # connection.read_timeout = 30
64
+ # @param value [Integer] the timeout in seconds
65
+ # @return [Integer] the timeout in seconds
66
+
67
+ # Returns the write timeout in seconds
68
+ #
69
+ # @api public
70
+ # @example
71
+ # connection.write_timeout #=> 60
72
+ # @return [Integer] the write timeout in seconds
73
+ attr_accessor :write_timeout
74
+
75
+ # @!method write_timeout=(value)
76
+ # Sets the write timeout in seconds
77
+ # @api public
78
+ # @example
79
+ # connection.write_timeout = 30
80
+ # @param value [Integer] the timeout in seconds
81
+ # @return [Integer] the timeout in seconds
82
+
83
+ # Returns the IO object for debug output
84
+ #
85
+ # @api public
86
+ # @example
87
+ # connection.debug_output #=> #<File:/dev/null>
88
+ # @return [IO] the IO object for debug output
89
+ attr_accessor :debug_output
90
+
91
+ # @!method debug_output=(value)
92
+ # Sets the IO object for debug output
93
+ # @api public
94
+ # @example
95
+ # connection.debug_output = $stderr
96
+ # @param value [IO] the IO object for debug output
97
+ # @return [IO] the IO object for debug output
98
+
99
+ # Returns the proxy URL
100
+ #
101
+ # @api public
102
+ # @example
103
+ # connection.proxy_url #=> "http://proxy.example.com:8080"
104
+ # @return [String, nil] the proxy URL
105
+ attr_reader :proxy_url
106
+
107
+ # Returns the parsed proxy URI
108
+ #
109
+ # @api public
110
+ # @example
111
+ # connection.proxy_uri #=> #<URI::HTTP http://proxy.example.com:8080>
112
+ # @return [URI::HTTP, nil] the parsed proxy URI
113
+ attr_reader :proxy_uri
27
114
 
28
115
  def_delegator :proxy_uri, :host, :proxy_host
29
116
  def_delegator :proxy_uri, :port, :proxy_port
30
117
  def_delegator :proxy_uri, :user, :proxy_user
31
118
  def_delegator :proxy_uri, :password, :proxy_pass
32
119
 
120
+ # Initializes a new Connection instance
121
+ #
122
+ # @api public
123
+ # @example
124
+ # connection = MLB::Connection.new
125
+ # @param open_timeout [Integer] the connection open timeout in seconds
126
+ # @param read_timeout [Integer] the read timeout in seconds
127
+ # @param write_timeout [Integer] the write timeout in seconds
128
+ # @param debug_output [IO] the IO object for debug output
129
+ # @param proxy_url [String, nil] the proxy URL
33
130
  def initialize(open_timeout: DEFAULT_OPEN_TIMEOUT, read_timeout: DEFAULT_READ_TIMEOUT,
34
131
  write_timeout: DEFAULT_WRITE_TIMEOUT, debug_output: DEFAULT_DEBUG_OUTPUT, proxy_url: nil)
35
132
  @open_timeout = open_timeout
36
133
  @read_timeout = read_timeout
37
134
  @write_timeout = write_timeout
38
135
  @debug_output = debug_output
39
- self.proxy_url = proxy_url unless proxy_url.nil?
136
+ self.proxy_url = proxy_url if proxy_url
40
137
  end
41
138
 
139
+ # Performs an HTTP request
140
+ #
141
+ # @api public
142
+ # @example
143
+ # connection.perform(request: request)
144
+ # @param request [Net::HTTPRequest] the HTTP request to perform
145
+ # @return [Net::HTTPResponse] the HTTP response
146
+ # @raise [NetworkError] if a network error occurs
42
147
  def perform(request:)
43
- host = request.uri.host || DEFAULT_HOST
44
- port = request.uri.port || DEFAULT_PORT
45
- http_client = build_http_client(host, port)
46
- http_client.use_ssl = request.uri.scheme.eql?("https")
148
+ uri = request.uri
149
+ http_client = build_http_client(uri.host || DEFAULT_HOST, uri.port || DEFAULT_PORT)
150
+ http_client.use_ssl = uri.scheme.eql?(HTTPS_SCHEME)
47
151
  http_client.request(request)
48
152
  rescue *NETWORK_ERRORS => e
49
153
  raise NetworkError, "Network error: #{e}"
50
154
  end
51
155
 
156
+ # Sets the proxy URL
157
+ #
158
+ # @api public
159
+ # @example
160
+ # connection.proxy_url = "http://proxy.example.com:8080"
161
+ # @param proxy_url [String] the proxy URL
162
+ # @return [void]
163
+ # @raise [ArgumentError] if the proxy URL is invalid
52
164
  def proxy_url=(proxy_url)
53
165
  @proxy_url = proxy_url
54
166
  proxy_uri = URI(proxy_url)
@@ -59,6 +171,12 @@ module MLB
59
171
 
60
172
  private
61
173
 
174
+ # Builds an HTTP client instance
175
+ #
176
+ # @api private
177
+ # @param host [String] the host to connect to
178
+ # @param port [Integer] the port to connect to
179
+ # @return [Net::HTTP] the configured HTTP client
62
180
  def build_http_client(host = DEFAULT_HOST, port = DEFAULT_PORT)
63
181
  http_client = if proxy_uri
64
182
  Net::HTTP.new(host, port, proxy_host, proxy_port, proxy_user, proxy_pass)
@@ -68,6 +186,11 @@ module MLB
68
186
  configure_http_client(http_client)
69
187
  end
70
188
 
189
+ # Configures an HTTP client with timeout settings
190
+ #
191
+ # @api private
192
+ # @param http_client [Net::HTTP] the HTTP client to configure
193
+ # @return [Net::HTTP] the configured HTTP client
71
194
  def configure_http_client(http_client)
72
195
  http_client.tap do |c|
73
196
  c.open_timeout = open_timeout
@@ -0,0 +1,90 @@
1
+ require "shale"
2
+
3
+ module MLB
4
+ # Represents sacrifice fly probability for a specific field zone
5
+ class SacFlyProbability < Shale::Mapper
6
+ include Equalizer.new(:probability)
7
+
8
+ # @!attribute [rw] probability
9
+ # Returns the probability of a sacrifice fly to this field zone
10
+ # @api public
11
+ # @example
12
+ # sac_fly.probability #=> 0.25
13
+ # @return [Float] probability value between 0 and 1
14
+ attribute :probability, Shale::Type::Float
15
+
16
+ json do
17
+ map "probability", to: :probability
18
+ end
19
+ end
20
+
21
+ # Provides real-time context metrics for a game including win probability
22
+ # and sacrifice fly probabilities by field zone
23
+ #
24
+ # Context metrics are useful for understanding game state and
25
+ # making predictions about likely outcomes.
26
+ class ContextMetrics < Shale::Mapper
27
+ # @!attribute [rw] home_win_probability
28
+ # Returns the home team's probability of winning
29
+ # @api public
30
+ # @example
31
+ # metrics.home_win_probability #=> 0.52
32
+ # @return [Float] probability value between 0 and 1
33
+ attribute :home_win_probability, Shale::Type::Float
34
+
35
+ # @!attribute [rw] away_win_probability
36
+ # Returns the away team's probability of winning
37
+ # @api public
38
+ # @example
39
+ # metrics.away_win_probability #=> 0.48
40
+ # @return [Float] probability value between 0 and 1
41
+ attribute :away_win_probability, Shale::Type::Float
42
+
43
+ # @!attribute [rw] left_field_sac_fly_probability
44
+ # Returns sacrifice fly probability for left field
45
+ # @api public
46
+ # @example
47
+ # metrics.left_field_sac_fly_probability.probability #=> 0.25
48
+ # @return [SacFlyProbability] left field sac fly metrics
49
+ attribute :left_field_sac_fly_probability, SacFlyProbability
50
+
51
+ # @!attribute [rw] center_field_sac_fly_probability
52
+ # Returns sacrifice fly probability for center field
53
+ # @api public
54
+ # @example
55
+ # metrics.center_field_sac_fly_probability.probability #=> 0.30
56
+ # @return [SacFlyProbability] center field sac fly metrics
57
+ attribute :center_field_sac_fly_probability, SacFlyProbability
58
+
59
+ # @!attribute [rw] right_field_sac_fly_probability
60
+ # Returns sacrifice fly probability for right field
61
+ # @api public
62
+ # @example
63
+ # metrics.right_field_sac_fly_probability.probability #=> 0.35
64
+ # @return [SacFlyProbability] right field sac fly metrics
65
+ attribute :right_field_sac_fly_probability, SacFlyProbability
66
+
67
+ json do
68
+ map "homeWinProbability", to: :home_win_probability
69
+ map "awayWinProbability", to: :away_win_probability
70
+ map "leftFieldSacFlyProbability", to: :left_field_sac_fly_probability
71
+ map "centerFieldSacFlyProbability", to: :center_field_sac_fly_probability
72
+ map "rightFieldSacFlyProbability", to: :right_field_sac_fly_probability
73
+ end
74
+
75
+ # Retrieves context metrics for a specific game
76
+ #
77
+ # @api public
78
+ # @example Using a game primary key
79
+ # MLB::ContextMetrics.find(game: 745571)
80
+ # @example Using a ScheduledGame object
81
+ # MLB::ContextMetrics.find(game: scheduled_game)
82
+ # @param game [Integer, #game_pk] game ID or object responding to #game_pk
83
+ # @return [ContextMetrics] the context metrics for the game
84
+ def self.find(game:)
85
+ game_pk = game.respond_to?(:game_pk) ? game.game_pk : game
86
+ response = CLIENT.get("game/#{game_pk}/contextMetrics")
87
+ from_json(response)
88
+ end
89
+ end
90
+ end