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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/README.md +329 -108
- 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 +89 -11
- data/lib/mlb/awards.rb +17 -7
- 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 -0
- 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 +8 -1
- data/lib/mlb/conferences.rb +18 -10
- data/lib/mlb/connection.rb +130 -7
- data/lib/mlb/context_metrics.rb +90 -0
- data/lib/mlb/division.rb +24 -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 +28 -10
- 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 +95 -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 +182 -0
- data/lib/mlb/player_game_stats.rb +350 -0
- data/lib/mlb/player_stat.rb +70 -0
- data/lib/mlb/players.rb +34 -23
- data/lib/mlb/position.rb +65 -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 +35 -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 +15 -4
- data/lib/mlb/season_date_info.rb +1 -0
- data/lib/mlb/seasons.rb +24 -18
- 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 +3 -8
- data/lib/mlb/sport.rb +16 -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 +14 -0
- data/lib/mlb/streaks.rb +184 -0
- data/lib/mlb/team.rb +62 -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 +125 -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 +7 -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 +3 -8
- data/lib/mlb/wind_directions.rb +12 -0
- data/lib/mlb.rb +61 -0
- data/sig/equalizer.rbs +3 -0
- data/sig/mlb.rbs +2055 -0
- data/sig/shale.rbs +29 -0
- 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
|
data/lib/mlb/coaches.rb
ADDED
|
@@ -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
|
data/lib/mlb/conference.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
data/lib/mlb/conferences.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
data/lib/mlb/connection.rb
CHANGED
|
@@ -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
|
-
|
|
26
|
-
|
|
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
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
http_client =
|
|
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
|