nba 0.1.1 → 0.2.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 (321) hide show
  1. checksums.yaml +5 -5
  2. data/AGENTS.md +362 -0
  3. data/CHANGELOG.md +169 -0
  4. data/CLAUDE.md +1 -0
  5. data/LICENSE +21 -0
  6. data/README.md +501 -101
  7. data/bin/console +10 -0
  8. data/bin/setup +6 -0
  9. data/exe/nba +8 -0
  10. data/lib/nba/all_time_leader.rb +77 -0
  11. data/lib/nba/all_time_leaders.rb +185 -0
  12. data/lib/nba/assist_leader.rb +92 -0
  13. data/lib/nba/assist_leaders.rb +64 -0
  14. data/lib/nba/assist_tracker.rb +108 -0
  15. data/lib/nba/assist_tracker_entry.rb +206 -0
  16. data/lib/nba/award.rb +128 -0
  17. data/lib/nba/box_score.rb +2 -0
  18. data/lib/nba/box_score_advanced.rb +114 -0
  19. data/lib/nba/box_score_advanced_player_stat.rb +297 -0
  20. data/lib/nba/box_score_advanced_team_stat.rb +237 -0
  21. data/lib/nba/box_score_advanced_v3.rb +124 -0
  22. data/lib/nba/box_score_defensive_player_stat.rb +281 -0
  23. data/lib/nba/box_score_defensive_team_stat.rb +85 -0
  24. data/lib/nba/box_score_defensive_v2.rb +190 -0
  25. data/lib/nba/box_score_four_factors.rb +91 -0
  26. data/lib/nba/box_score_four_factors_player_stat.rb +185 -0
  27. data/lib/nba/box_score_four_factors_team_stat.rb +141 -0
  28. data/lib/nba/box_score_four_factors_v3.rb +133 -0
  29. data/lib/nba/box_score_hustle.rb +226 -0
  30. data/lib/nba/box_score_hustle_player_stat.rb +233 -0
  31. data/lib/nba/box_score_hustle_team_stat.rb +189 -0
  32. data/lib/nba/box_score_matchup_stat.rb +417 -0
  33. data/lib/nba/box_score_matchups_v3.rb +184 -0
  34. data/lib/nba/box_score_misc.rb +100 -0
  35. data/lib/nba/box_score_misc_player_stat.rb +217 -0
  36. data/lib/nba/box_score_misc_team_stat.rb +173 -0
  37. data/lib/nba/box_score_misc_v3.rb +163 -0
  38. data/lib/nba/box_score_player_stat.rb +273 -0
  39. data/lib/nba/box_score_player_track.rb +223 -0
  40. data/lib/nba/box_score_player_track_stat.rb +273 -0
  41. data/lib/nba/box_score_player_track_team_stat.rb +229 -0
  42. data/lib/nba/box_score_scoring.rb +103 -0
  43. data/lib/nba/box_score_scoring_player_stat.rb +241 -0
  44. data/lib/nba/box_score_scoring_team_stat.rb +197 -0
  45. data/lib/nba/box_score_scoring_v3.rb +170 -0
  46. data/lib/nba/box_score_similarity_score.rb +119 -0
  47. data/lib/nba/box_score_similarity_stat.rb +76 -0
  48. data/lib/nba/box_score_starter_bench_stat.rb +257 -0
  49. data/lib/nba/box_score_summary.rb +285 -0
  50. data/lib/nba/box_score_summary_v2.rb +202 -0
  51. data/lib/nba/box_score_summary_v3.rb +120 -0
  52. data/lib/nba/box_score_summary_v3_data.rb +419 -0
  53. data/lib/nba/box_score_team_stat.rb +229 -0
  54. data/lib/nba/box_score_traditional.rb +101 -0
  55. data/lib/nba/box_score_traditional_v3.rb +195 -0
  56. data/lib/nba/box_score_usage.rb +102 -0
  57. data/lib/nba/box_score_usage_player_stat.rb +265 -0
  58. data/lib/nba/box_score_usage_team_stat.rb +221 -0
  59. data/lib/nba/box_score_usage_v3.rb +169 -0
  60. data/lib/nba/box_score_v3_helpers.rb +144 -0
  61. data/lib/nba/career_stats.rb +217 -0
  62. data/lib/nba/cli/display/player_display.rb +98 -0
  63. data/lib/nba/cli/display.rb +178 -0
  64. data/lib/nba/cli/formatters/game_formatters.rb +86 -0
  65. data/lib/nba/cli/formatters/leaders_formatters.rb +26 -0
  66. data/lib/nba/cli/formatters/player_formatters.rb +52 -0
  67. data/lib/nba/cli/formatters/standings_formatters.rb +26 -0
  68. data/lib/nba/cli/formatters/team_formatters.rb +67 -0
  69. data/lib/nba/cli/formatters/time_formatters.rb +82 -0
  70. data/lib/nba/cli/formatters.rb +56 -0
  71. data/lib/nba/cli/helpers.rb +135 -0
  72. data/lib/nba/cli.rb +171 -20
  73. data/lib/nba/client.rb +35 -0
  74. data/lib/nba/collection.rb +89 -0
  75. data/lib/nba/college_player_stat.rb +200 -0
  76. data/lib/nba/common_player_info.rb +142 -0
  77. data/lib/nba/common_playoff_series.rb +90 -0
  78. data/lib/nba/common_team_years.rb +113 -0
  79. data/lib/nba/conference.rb +39 -0
  80. data/lib/nba/connection.rb +84 -0
  81. data/lib/nba/cume_stats_player.rb +358 -0
  82. data/lib/nba/cume_stats_player_game.rb +217 -0
  83. data/lib/nba/cume_stats_player_games.rb +99 -0
  84. data/lib/nba/cume_stats_player_games_entry.rb +25 -0
  85. data/lib/nba/cume_stats_player_total.rb +481 -0
  86. data/lib/nba/cume_stats_team.rb +349 -0
  87. data/lib/nba/cume_stats_team_games.rb +145 -0
  88. data/lib/nba/cume_stats_team_games_entry.rb +25 -0
  89. data/lib/nba/cume_stats_team_player.rb +485 -0
  90. data/lib/nba/cume_stats_team_total.rb +267 -0
  91. data/lib/nba/data.rb +73 -0
  92. data/lib/nba/defense_hub.rb +109 -0
  93. data/lib/nba/defense_hub_stat.rb +57 -0
  94. data/lib/nba/defensive_shot_stat.rb +102 -0
  95. data/lib/nba/division.rb +49 -0
  96. data/lib/nba/draft_board.rb +126 -0
  97. data/lib/nba/draft_board_pick.rb +173 -0
  98. data/lib/nba/draft_combine_anthro_measurement.rb +163 -0
  99. data/lib/nba/draft_combine_drill_result.rb +115 -0
  100. data/lib/nba/draft_combine_drill_results.rb +112 -0
  101. data/lib/nba/draft_combine_non_stationary_shooting.rb +268 -0
  102. data/lib/nba/draft_combine_non_stationary_shooting_result.rb +355 -0
  103. data/lib/nba/draft_combine_player_anthro.rb +133 -0
  104. data/lib/nba/draft_combine_spot_shooting.rb +243 -0
  105. data/lib/nba/draft_combine_spot_shooting_result.rb +419 -0
  106. data/lib/nba/draft_combine_stat.rb +211 -0
  107. data/lib/nba/draft_combine_stats.rb +160 -0
  108. data/lib/nba/draft_history.rb +142 -0
  109. data/lib/nba/draft_pick.rb +154 -0
  110. data/lib/nba/dunk_score_leader.rb +93 -0
  111. data/lib/nba/dunk_score_leaders.rb +77 -0
  112. data/lib/nba/estimated_metrics_stat.rb +152 -0
  113. data/lib/nba/fantasy_profile_stat.rb +142 -0
  114. data/lib/nba/fantasy_widget.rb +72 -0
  115. data/lib/nba/fantasy_widget_player.rb +98 -0
  116. data/lib/nba/found_game.rb +260 -0
  117. data/lib/nba/franchise.rb +136 -0
  118. data/lib/nba/franchise_history.rb +142 -0
  119. data/lib/nba/franchise_leader.rb +147 -0
  120. data/lib/nba/franchise_leaders.rb +162 -0
  121. data/lib/nba/franchise_player.rb +224 -0
  122. data/lib/nba/franchise_players.rb +147 -0
  123. data/lib/nba/game.rb +80 -64
  124. data/lib/nba/game_log.rb +349 -0
  125. data/lib/nba/game_rotation.rb +152 -0
  126. data/lib/nba/game_streak.rb +102 -0
  127. data/lib/nba/games.rb +46 -0
  128. data/lib/nba/home_page_leader.rb +99 -0
  129. data/lib/nba/home_page_leaders.rb +75 -0
  130. data/lib/nba/home_page_stat.rb +57 -0
  131. data/lib/nba/home_page_v2.rb +110 -0
  132. data/lib/nba/hustle_stats_box_score.rb +182 -0
  133. data/lib/nba/infographic_fan_duel_player.rb +139 -0
  134. data/lib/nba/infographic_fan_duel_player_stat.rb +311 -0
  135. data/lib/nba/ist_standing.rb +167 -0
  136. data/lib/nba/ist_standings.rb +81 -0
  137. data/lib/nba/leader.rb +103 -0
  138. data/lib/nba/leaders.rb +110 -0
  139. data/lib/nba/leaders_tile.rb +57 -0
  140. data/lib/nba/leaders_tiles.rb +90 -0
  141. data/lib/nba/league.rb +37 -0
  142. data/lib/nba/league_dash_lineup_stat.rb +270 -0
  143. data/lib/nba/league_dash_lineups.rb +177 -0
  144. data/lib/nba/league_dash_opp_pt_shot.rb +150 -0
  145. data/lib/nba/league_dash_player_bio_stat.rb +217 -0
  146. data/lib/nba/league_dash_player_bio_stats.rb +164 -0
  147. data/lib/nba/league_dash_player_clutch.rb +212 -0
  148. data/lib/nba/league_dash_player_clutch_stat.rb +271 -0
  149. data/lib/nba/league_dash_player_pt_shot.rb +152 -0
  150. data/lib/nba/league_dash_player_pt_shot_stat.rb +193 -0
  151. data/lib/nba/league_dash_player_shot_location_stat.rb +265 -0
  152. data/lib/nba/league_dash_player_shot_locations.rb +210 -0
  153. data/lib/nba/league_dash_player_stat.rb +306 -0
  154. data/lib/nba/league_dash_player_stats.rb +176 -0
  155. data/lib/nba/league_dash_pt_defend.rb +160 -0
  156. data/lib/nba/league_dash_pt_defend_stat.rb +145 -0
  157. data/lib/nba/league_dash_pt_stats.rb +152 -0
  158. data/lib/nba/league_dash_pt_stats_stat.rb +169 -0
  159. data/lib/nba/league_dash_pt_team_defend.rb +158 -0
  160. data/lib/nba/league_dash_pt_team_defend_stat.rb +110 -0
  161. data/lib/nba/league_dash_team_clutch.rb +211 -0
  162. data/lib/nba/league_dash_team_clutch_stat.rb +237 -0
  163. data/lib/nba/league_dash_team_pt_shot.rb +150 -0
  164. data/lib/nba/league_dash_team_pt_shot_stat.rb +166 -0
  165. data/lib/nba/league_dash_team_shot_location_stat.rb +230 -0
  166. data/lib/nba/league_dash_team_shot_locations.rb +208 -0
  167. data/lib/nba/league_dash_team_stat.rb +275 -0
  168. data/lib/nba/league_dash_team_stats.rb +172 -0
  169. data/lib/nba/league_game_finder.rb +170 -0
  170. data/lib/nba/league_game_log.rb +224 -0
  171. data/lib/nba/league_hustle_stats_player.rb +161 -0
  172. data/lib/nba/league_hustle_stats_player_stat.rb +253 -0
  173. data/lib/nba/league_hustle_stats_team.rb +157 -0
  174. data/lib/nba/league_hustle_stats_team_stat.rb +179 -0
  175. data/lib/nba/league_lineup_viz.rb +184 -0
  176. data/lib/nba/league_lineup_viz_stat.rb +214 -0
  177. data/lib/nba/league_player_on_details.rb +175 -0
  178. data/lib/nba/league_player_on_details_stat.rb +313 -0
  179. data/lib/nba/league_season_matchup_stat.rb +241 -0
  180. data/lib/nba/league_season_matchups.rb +181 -0
  181. data/lib/nba/league_standing.rb +284 -0
  182. data/lib/nba/league_standings.rb +159 -0
  183. data/lib/nba/league_wide_shot_stat.rb +62 -0
  184. data/lib/nba/live_action.rb +240 -0
  185. data/lib/nba/live_box_score.rb +143 -0
  186. data/lib/nba/live_connection.rb +84 -0
  187. data/lib/nba/live_game.rb +230 -0
  188. data/lib/nba/live_play_by_play.rb +120 -0
  189. data/lib/nba/live_player_stat.rb +276 -0
  190. data/lib/nba/live_scoreboard.rb +102 -0
  191. data/lib/nba/matchup_rollup.rb +98 -0
  192. data/lib/nba/matchups_rollup.rb +81 -0
  193. data/lib/nba/pass_stat.rb +209 -0
  194. data/lib/nba/play.rb +258 -0
  195. data/lib/nba/play_by_play.rb +85 -0
  196. data/lib/nba/play_by_play_v3.rb +91 -0
  197. data/lib/nba/play_type_stat.rb +206 -0
  198. data/lib/nba/player.rb +242 -24
  199. data/lib/nba/player_awards.rb +110 -0
  200. data/lib/nba/player_career_by_college.rb +86 -0
  201. data/lib/nba/player_career_by_college_rollup.rb +143 -0
  202. data/lib/nba/player_career_stats.rb +77 -0
  203. data/lib/nba/player_compare.rb +156 -0
  204. data/lib/nba/player_comparison_stat.rb +242 -0
  205. data/lib/nba/player_dash_pt_pass.rb +164 -0
  206. data/lib/nba/player_dash_pt_reb.rb +235 -0
  207. data/lib/nba/player_dash_pt_shot_defend.rb +119 -0
  208. data/lib/nba/player_dash_pt_shots.rb +279 -0
  209. data/lib/nba/player_dashboard.rb +259 -0
  210. data/lib/nba/player_dashboard_stat.rb +248 -0
  211. data/lib/nba/player_estimated_metrics.rb +84 -0
  212. data/lib/nba/player_fantasy_profile_bar_graph.rb +147 -0
  213. data/lib/nba/player_game_log.rb +72 -0
  214. data/lib/nba/player_game_logs.rb +117 -0
  215. data/lib/nba/player_game_streak_finder.rb +108 -0
  216. data/lib/nba/player_index.rb +135 -0
  217. data/lib/nba/player_index_entry.rb +266 -0
  218. data/lib/nba/player_info.rb +225 -0
  219. data/lib/nba/player_next_n_games.rb +64 -0
  220. data/lib/nba/player_profile_v2.rb +169 -0
  221. data/lib/nba/player_vs_player.rb +153 -0
  222. data/lib/nba/players.rb +107 -0
  223. data/lib/nba/playoff_matchup.rb +84 -0
  224. data/lib/nba/playoff_picture.rb +98 -0
  225. data/lib/nba/playoff_series.rb +76 -0
  226. data/lib/nba/position.rb +48 -0
  227. data/lib/nba/rebound_stat.rb +189 -0
  228. data/lib/nba/response_parser.rb +116 -0
  229. data/lib/nba/roster.rb +74 -0
  230. data/lib/nba/rotation_entry.rb +154 -0
  231. data/lib/nba/schedule.rb +183 -0
  232. data/lib/nba/schedule_international.rb +182 -0
  233. data/lib/nba/scheduled_game.rb +240 -0
  234. data/lib/nba/scoreboard.rb +183 -0
  235. data/lib/nba/scoreboard_v3.rb +104 -0
  236. data/lib/nba/shot.rb +208 -0
  237. data/lib/nba/shot_chart.rb +75 -0
  238. data/lib/nba/shot_chart_league_wide.rb +102 -0
  239. data/lib/nba/shot_chart_lineup_detail.rb +109 -0
  240. data/lib/nba/shot_stat.rb +174 -0
  241. data/lib/nba/standing.rb +129 -0
  242. data/lib/nba/standings.rb +75 -0
  243. data/lib/nba/static.rb +107 -0
  244. data/lib/nba/synergy_play_types.rb +211 -0
  245. data/lib/nba/team.rb +203 -127
  246. data/lib/nba/team_and_players_vs_players.rb +227 -0
  247. data/lib/nba/team_and_players_vs_players_stat.rb +155 -0
  248. data/lib/nba/team_dash_pt_pass.rb +157 -0
  249. data/lib/nba/team_dash_pt_reb.rb +216 -0
  250. data/lib/nba/team_dash_pt_shots.rb +244 -0
  251. data/lib/nba/team_dashboard.rb +275 -0
  252. data/lib/nba/team_dashboard_stat.rb +248 -0
  253. data/lib/nba/team_detail.rb +117 -0
  254. data/lib/nba/team_details.rb +173 -0
  255. data/lib/nba/team_estimated_metrics.rb +91 -0
  256. data/lib/nba/team_estimated_metrics_stat.rb +146 -0
  257. data/lib/nba/team_game_log.rb +143 -0
  258. data/lib/nba/team_game_log_entry.rb +246 -0
  259. data/lib/nba/team_game_log_stat.rb +275 -0
  260. data/lib/nba/team_game_logs.rb +163 -0
  261. data/lib/nba/team_game_streak.rb +111 -0
  262. data/lib/nba/team_game_streak_finder.rb +109 -0
  263. data/lib/nba/team_historical_leader.rb +207 -0
  264. data/lib/nba/team_historical_leaders.rb +98 -0
  265. data/lib/nba/team_historical_record.rb +139 -0
  266. data/lib/nba/team_info.rb +150 -0
  267. data/lib/nba/team_info_common.rb +177 -0
  268. data/lib/nba/team_on_off_overall_stat.rb +477 -0
  269. data/lib/nba/team_on_off_player_stat.rb +523 -0
  270. data/lib/nba/team_on_off_player_summary.rb +135 -0
  271. data/lib/nba/team_pass_stat.rb +183 -0
  272. data/lib/nba/team_player_dashboard.rb +212 -0
  273. data/lib/nba/team_player_on_off_details.rb +218 -0
  274. data/lib/nba/team_player_on_off_summary.rb +214 -0
  275. data/lib/nba/team_player_stat.rb +275 -0
  276. data/lib/nba/team_rebound_stat.rb +189 -0
  277. data/lib/nba/team_season_rank.rb +110 -0
  278. data/lib/nba/team_shot_stat.rb +173 -0
  279. data/lib/nba/team_vs_player.rb +151 -0
  280. data/lib/nba/team_vs_player_stat.rb +157 -0
  281. data/lib/nba/team_year.rb +55 -0
  282. data/lib/nba/team_year_by_year_stats.rb +152 -0
  283. data/lib/nba/team_year_stat.rb +282 -0
  284. data/lib/nba/teams.rb +33 -0
  285. data/lib/nba/upcoming_game.rb +115 -0
  286. data/lib/nba/utils.rb +94 -0
  287. data/lib/nba/version.rb +5 -2
  288. data/lib/nba/video_detail.rb +103 -0
  289. data/lib/nba/video_details.rb +118 -0
  290. data/lib/nba/video_details_asset.rb +115 -0
  291. data/lib/nba/video_details_asset_entry.rb +91 -0
  292. data/lib/nba/video_event.rb +83 -0
  293. data/lib/nba/video_event_asset.rb +91 -0
  294. data/lib/nba/video_events.rb +106 -0
  295. data/lib/nba/video_events_asset.rb +107 -0
  296. data/lib/nba/video_status.rb +129 -0
  297. data/lib/nba/video_status_entry.rb +161 -0
  298. data/lib/nba/vs_player_stat.rb +156 -0
  299. data/lib/nba/win_probability.rb +117 -0
  300. data/lib/nba/win_probability_point.rb +140 -0
  301. data/lib/nba.rb +249 -5
  302. data/sig/equalizer.rbs +3 -0
  303. data/sig/nba.rbs +7297 -0
  304. data/sig/shale.rbs +24 -0
  305. data/sig/thor.rbs +19 -0
  306. metadata +324 -95
  307. data/.gitignore +0 -18
  308. data/.travis.yml +0 -22
  309. data/Gemfile +0 -23
  310. data/LICENSE.md +0 -22
  311. data/Rakefile +0 -18
  312. data/bin/nba +0 -7
  313. data/cache/teams.json +0 -16529
  314. data/lib/faraday_middleware/scrape_game.rb +0 -41
  315. data/lib/nba/request.rb +0 -37
  316. data/nba.gemspec +0 -28
  317. data/spec/fixtures/games.html +0 -785
  318. data/spec/fixtures/teams.json +0 -16529
  319. data/spec/game_spec.rb +0 -40
  320. data/spec/spec_helper.rb +0 -25
  321. data/spec/team_spec.rb +0 -93
@@ -0,0 +1,48 @@
1
+ require "equalizer"
2
+ require "shale"
3
+
4
+ module NBA
5
+ # Represents a player position
6
+ class Position < Shale::Mapper
7
+ include Equalizer.new(:code)
8
+
9
+ # @!attribute [rw] code
10
+ # Returns the position code
11
+ # @api public
12
+ # @example
13
+ # position.code #=> "PG"
14
+ # @return [String] the position code
15
+ attribute :code, Shale::Type::String
16
+
17
+ # @!attribute [rw] name
18
+ # Returns the position name
19
+ # @api public
20
+ # @example
21
+ # position.name #=> "Point Guard"
22
+ # @return [String] the position name
23
+ attribute :name, Shale::Type::String
24
+
25
+ # @!attribute [rw] type
26
+ # Returns the position type
27
+ # @api public
28
+ # @example
29
+ # position.type #=> "Guard"
30
+ # @return [String] the position type
31
+ attribute :type, Shale::Type::String
32
+
33
+ # @!attribute [rw] abbreviation
34
+ # Returns the position abbreviation
35
+ # @api public
36
+ # @example
37
+ # position.abbreviation #=> "G"
38
+ # @return [String] the position abbreviation
39
+ attribute :abbreviation, Shale::Type::String
40
+
41
+ json do
42
+ map "code", to: :code
43
+ map "name", to: :name
44
+ map "type", to: :type
45
+ map "abbreviation", to: :abbreviation
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,189 @@
1
+ require "equalizer"
2
+ require "shale"
3
+
4
+ module NBA
5
+ # Represents player tracking rebound statistics
6
+ #
7
+ # @api public
8
+ class ReboundStat < Shale::Mapper
9
+ include Equalizer.new(:player_id, :g, :reb)
10
+
11
+ # @!attribute [rw] player_id
12
+ # Returns the player ID
13
+ # @api public
14
+ # @example
15
+ # stat.player_id #=> 201939
16
+ # @return [Integer, nil] the player's ID
17
+ attribute :player_id, Shale::Type::Integer
18
+
19
+ # @!attribute [rw] player_name_last_first
20
+ # Returns the player name in "Last, First" format
21
+ # @api public
22
+ # @example
23
+ # stat.player_name_last_first #=> "Curry, Stephen"
24
+ # @return [String, nil] the player's name in "Last, First" format
25
+ attribute :player_name_last_first, Shale::Type::String
26
+
27
+ # @!attribute [rw] sort_order
28
+ # Returns the sort order for the row
29
+ # @api public
30
+ # @example
31
+ # stat.sort_order #=> 1
32
+ # @return [Integer, nil] the sort order for the row
33
+ attribute :sort_order, Shale::Type::Integer
34
+
35
+ # @!attribute [rw] g
36
+ # Returns the number of games
37
+ # @api public
38
+ # @example
39
+ # stat.g #=> 74
40
+ # @return [Integer, nil] the number of games
41
+ attribute :g, Shale::Type::Integer
42
+
43
+ # @!attribute [rw] reb_num_contesting_range
44
+ # Returns the number of contesting players range
45
+ # @api public
46
+ # @example
47
+ # stat.reb_num_contesting_range #=> "0 Contests"
48
+ # @return [String, nil] the number of contesting players range
49
+ attribute :reb_num_contesting_range, Shale::Type::String
50
+
51
+ # @!attribute [rw] overall
52
+ # Returns the overall category
53
+ # @api public
54
+ # @example
55
+ # stat.overall #=> "Overall"
56
+ # @return [String, nil] the overall category
57
+ attribute :overall, Shale::Type::String
58
+
59
+ # @!attribute [rw] reb_dist_range
60
+ # Returns the rebound distance range
61
+ # @api public
62
+ # @example
63
+ # stat.reb_dist_range #=> "0-6 Feet"
64
+ # @return [String, nil] the rebound distance range
65
+ attribute :reb_dist_range, Shale::Type::String
66
+
67
+ # @!attribute [rw] shot_dist_range
68
+ # Returns the shot distance range
69
+ # @api public
70
+ # @example
71
+ # stat.shot_dist_range #=> "0-6 Feet"
72
+ # @return [String, nil] the shot distance range
73
+ attribute :shot_dist_range, Shale::Type::String
74
+
75
+ # @!attribute [rw] shot_type_range
76
+ # Returns the shot type range
77
+ # @api public
78
+ # @example
79
+ # stat.shot_type_range #=> "2PT FGs"
80
+ # @return [String, nil] the shot type range
81
+ attribute :shot_type_range, Shale::Type::String
82
+
83
+ # @!attribute [rw] reb_frequency
84
+ # Returns the rebound frequency
85
+ # @api public
86
+ # @example
87
+ # stat.reb_frequency #=> 0.25
88
+ # @return [Float, nil] the rebound frequency
89
+ attribute :reb_frequency, Shale::Type::Float
90
+
91
+ # @!attribute [rw] oreb
92
+ # Returns offensive rebounds per game
93
+ # @api public
94
+ # @example
95
+ # stat.oreb #=> 1.2
96
+ # @return [Float, nil] offensive rebounds per game
97
+ attribute :oreb, Shale::Type::Float
98
+
99
+ # @!attribute [rw] dreb
100
+ # Returns defensive rebounds per game
101
+ # @api public
102
+ # @example
103
+ # stat.dreb #=> 4.5
104
+ # @return [Float, nil] defensive rebounds per game
105
+ attribute :dreb, Shale::Type::Float
106
+
107
+ # @!attribute [rw] reb
108
+ # Returns total rebounds per game
109
+ # @api public
110
+ # @example
111
+ # stat.reb #=> 5.7
112
+ # @return [Float, nil] total rebounds per game
113
+ attribute :reb, Shale::Type::Float
114
+
115
+ # @!attribute [rw] c_oreb
116
+ # Returns contested offensive rebounds per game
117
+ # @api public
118
+ # @example
119
+ # stat.c_oreb #=> 0.8
120
+ # @return [Float, nil] contested offensive rebounds per game
121
+ attribute :c_oreb, Shale::Type::Float
122
+
123
+ # @!attribute [rw] c_dreb
124
+ # Returns contested defensive rebounds per game
125
+ # @api public
126
+ # @example
127
+ # stat.c_dreb #=> 2.1
128
+ # @return [Float, nil] contested defensive rebounds per game
129
+ attribute :c_dreb, Shale::Type::Float
130
+
131
+ # @!attribute [rw] c_reb
132
+ # Returns contested total rebounds per game
133
+ # @api public
134
+ # @example
135
+ # stat.c_reb #=> 2.9
136
+ # @return [Float, nil] contested total rebounds per game
137
+ attribute :c_reb, Shale::Type::Float
138
+
139
+ # @!attribute [rw] c_reb_pct
140
+ # Returns contested rebound percentage
141
+ # @api public
142
+ # @example
143
+ # stat.c_reb_pct #=> 0.509
144
+ # @return [Float, nil] contested rebound percentage
145
+ attribute :c_reb_pct, Shale::Type::Float
146
+
147
+ # @!attribute [rw] uc_oreb
148
+ # Returns uncontested offensive rebounds per game
149
+ # @api public
150
+ # @example
151
+ # stat.uc_oreb #=> 0.4
152
+ # @return [Float, nil] uncontested offensive rebounds per game
153
+ attribute :uc_oreb, Shale::Type::Float
154
+
155
+ # @!attribute [rw] uc_dreb
156
+ # Returns uncontested defensive rebounds per game
157
+ # @api public
158
+ # @example
159
+ # stat.uc_dreb #=> 2.4
160
+ # @return [Float, nil] uncontested defensive rebounds per game
161
+ attribute :uc_dreb, Shale::Type::Float
162
+
163
+ # @!attribute [rw] uc_reb
164
+ # Returns uncontested total rebounds per game
165
+ # @api public
166
+ # @example
167
+ # stat.uc_reb #=> 2.8
168
+ # @return [Float, nil] uncontested total rebounds per game
169
+ attribute :uc_reb, Shale::Type::Float
170
+
171
+ # @!attribute [rw] uc_reb_pct
172
+ # Returns uncontested rebound percentage
173
+ # @api public
174
+ # @example
175
+ # stat.uc_reb_pct #=> 0.491
176
+ # @return [Float, nil] uncontested rebound percentage
177
+ attribute :uc_reb_pct, Shale::Type::Float
178
+
179
+ # Returns the player associated with this stat
180
+ #
181
+ # @api public
182
+ # @example
183
+ # stat.player #=> #<NBA::Player ...>
184
+ # @return [Player, nil] the Player object
185
+ def player
186
+ Players.find(player_id)
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,116 @@
1
+ require "json"
2
+ require_relative "collection"
3
+
4
+ module NBA
5
+ # Parses NBA API responses and builds collections of objects
6
+ #
7
+ # This module consolidates the common pattern of parsing JSON responses
8
+ # from the NBA Stats API, extracting result sets, and building objects
9
+ # from header/row pairs.
10
+ module ResponseParser
11
+ # Parses an API response and returns a collection of objects
12
+ #
13
+ # @api public
14
+ # @example
15
+ # ResponseParser.parse(response, result_set: "PlayerStats") { |data| Player.new(**data) }
16
+ # @param response [String, nil] the JSON response body
17
+ # @param result_set [String, nil] the name of the result set to extract (nil for first)
18
+ # @yield [Hash] yields each row as a hash with header keys
19
+ # @return [Collection] a collection of parsed objects
20
+ def self.parse(response, result_set: nil, &builder)
21
+ return Collection.new unless response
22
+
23
+ data = parse_json(response)
24
+ return Collection.new unless data
25
+
26
+ result = find_result_set(data, result_set)
27
+ return Collection.new unless result
28
+
29
+ build_collection(result, &builder)
30
+ end
31
+
32
+ # Parses an API response and returns a single object
33
+ #
34
+ # @api public
35
+ # @example
36
+ # ResponseParser.parse_single(response) { |data| Player.new(**data) }
37
+ # @param response [String, nil] the JSON response body
38
+ # @param result_set [String, nil] the name of the result set to extract
39
+ # @yield [Hash] yields the first row as a hash with header keys
40
+ # @return [Object, nil] the parsed object or nil
41
+ def self.parse_single(response, result_set: nil)
42
+ return unless response
43
+
44
+ data = parse_json(response)
45
+ return unless data
46
+
47
+ result = find_result_set(data, result_set)
48
+ return unless result
49
+
50
+ headers = result["headers"]
51
+ row = result.dig("rowSet", 0)
52
+ return unless headers && row
53
+
54
+ yield zip_to_hash(headers, row)
55
+ end
56
+
57
+ # Zips headers and row into a hash
58
+ #
59
+ # @api public
60
+ # @example
61
+ # ResponseParser.zip_to_hash(["NAME", "AGE"], ["LeBron", 39])
62
+ # #=> {"NAME" => "LeBron", "AGE" => 39}
63
+ # @param headers [Array<String>] the column headers
64
+ # @param row [Array] the row data
65
+ # @return [Hash] a hash with header keys and row values
66
+ def self.zip_to_hash(headers, row)
67
+ headers.zip(row).to_h
68
+ end
69
+
70
+ # Parses JSON safely, returning nil on error
71
+ #
72
+ # @api private
73
+ # @param response [String] the JSON string to parse
74
+ # @return [Hash, nil] the parsed JSON or nil on error
75
+ def self.parse_json(response)
76
+ JSON.parse(response)
77
+ rescue JSON::ParserError
78
+ nil
79
+ end
80
+ private_class_method :parse_json
81
+
82
+ # Finds a result set by name or returns the first one
83
+ #
84
+ # @api private
85
+ # @param data [Hash] the parsed JSON data
86
+ # @param name [String, nil] the result set name (nil for first)
87
+ # @return [Hash, nil] the result set or nil
88
+ def self.find_result_set(data, name)
89
+ result_sets = data["resultSets"]
90
+ return unless result_sets
91
+
92
+ if name
93
+ result_sets.find { |rs| rs["name"].eql?(name) }
94
+ else
95
+ result_sets.first
96
+ end
97
+ end
98
+ private_class_method :find_result_set
99
+
100
+ # Builds a collection from a result set
101
+ #
102
+ # @api private
103
+ # @param result [Hash] the result set
104
+ # @yield [Hash] yields each row as a hash
105
+ # @return [Collection] a collection of objects
106
+ def self.build_collection(result)
107
+ headers = result["headers"]
108
+ rows = result["rowSet"]
109
+ return Collection.new unless headers && rows
110
+
111
+ objects = rows.map { |row| yield zip_to_hash(headers, row) }
112
+ Collection.new(objects)
113
+ end
114
+ private_class_method :build_collection
115
+ end
116
+ end
data/lib/nba/roster.rb ADDED
@@ -0,0 +1,74 @@
1
+ require_relative "client"
2
+ require_relative "response_parser"
3
+ require_relative "player"
4
+ require_relative "utils"
5
+
6
+ module NBA
7
+ # Provides methods to retrieve team rosters
8
+ module Roster
9
+ # Finds the roster for a team
10
+ #
11
+ # @api public
12
+ # @example
13
+ # roster = NBA::Roster.find(team: NBA::Team::GSW)
14
+ # roster.each { |player| puts player.full_name }
15
+ # @param team [Integer, Team] the team ID or Team object
16
+ # @param season [Integer] the season year (defaults to current season)
17
+ # @param client [Client] the API client to use
18
+ # @return [Collection] a collection of players on the roster
19
+ def self.find(team:, season: Utils.current_season, client: CLIENT)
20
+ path = "commonteamroster?TeamID=#{Utils.extract_id(team)}&Season=#{Utils.format_season(season)}"
21
+ ResponseParser.parse(client.get(path)) { |data| build_player(data) }
22
+ end
23
+
24
+ # Builds a player from roster data
25
+ #
26
+ # @api private
27
+ # @param data [Hash] the roster row data
28
+ # @return [Player] the player object
29
+ def self.build_player(data)
30
+ Player.new(**player_attributes(data))
31
+ end
32
+ private_class_method :build_player
33
+
34
+ # Extracts player attributes from data
35
+ # @api private
36
+ # @return [Hash]
37
+ def self.player_attributes(data)
38
+ {id: data["PLAYER_ID"], height: data["HEIGHT"], college: data["SCHOOL"], is_active: true,
39
+ jersey_number: Utils.parse_integer(data["NUM"]), weight: Utils.parse_integer(data["WEIGHT"]),
40
+ country: extract_country(data["BIRTH_DATE"]), position: build_position(data["POSITION"]),
41
+ **name_attributes(data["PLAYER"])}
42
+ end
43
+ private_class_method :player_attributes
44
+
45
+ # Builds a position from abbreviation
46
+ # @api private
47
+ # @return [Position, nil]
48
+ def self.build_position(abbreviation)
49
+ return unless abbreviation
50
+
51
+ Position.new(abbreviation: abbreviation)
52
+ end
53
+ private_class_method :build_position
54
+
55
+ # Extracts name attributes from full name
56
+ # @api private
57
+ # @return [Hash]
58
+ def self.name_attributes(full_name)
59
+ parts = full_name&.split || []
60
+ {full_name: full_name, first_name: parts.first, last_name: parts.drop(1).join(" ")}
61
+ end
62
+ private_class_method :name_attributes
63
+
64
+ # Extracts country from birth date string
65
+ # @api private
66
+ # @return [String, nil]
67
+ def self.extract_country(birth_date)
68
+ return unless birth_date
69
+
70
+ birth_date.split(",").last&.strip
71
+ end
72
+ private_class_method :extract_country
73
+ end
74
+ end
@@ -0,0 +1,154 @@
1
+ module NBA
2
+ # Represents a player's rotation entry in a game
3
+ class RotationEntry < Shale::Mapper
4
+ include Equalizer.new(:game_id, :player_id, :in_time_real)
5
+
6
+ # @!attribute [rw] game_id
7
+ # Returns the game ID
8
+ # @api public
9
+ # @example
10
+ # entry.game_id #=> "0022400001"
11
+ # @return [String] the game ID
12
+ attribute :game_id, Shale::Type::String
13
+
14
+ # @!attribute [rw] team_id
15
+ # Returns the team ID
16
+ # @api public
17
+ # @example
18
+ # entry.team_id #=> 1610612744
19
+ # @return [Integer] the team ID
20
+ attribute :team_id, Shale::Type::Integer
21
+
22
+ # @!attribute [rw] team_city
23
+ # Returns the team city
24
+ # @api public
25
+ # @example
26
+ # entry.team_city #=> "Golden State"
27
+ # @return [String] the team city
28
+ attribute :team_city, Shale::Type::String
29
+
30
+ # @!attribute [rw] team_name
31
+ # Returns the team name
32
+ # @api public
33
+ # @example
34
+ # entry.team_name #=> "Warriors"
35
+ # @return [String] the team name
36
+ attribute :team_name, Shale::Type::String
37
+
38
+ # @!attribute [rw] player_id
39
+ # Returns the player ID
40
+ # @api public
41
+ # @example
42
+ # entry.player_id #=> 201939
43
+ # @return [Integer] the player ID
44
+ attribute :player_id, Shale::Type::Integer
45
+
46
+ # @!attribute [rw] player_first
47
+ # Returns the player's first name
48
+ # @api public
49
+ # @example
50
+ # entry.player_first #=> "Stephen"
51
+ # @return [String] the first name
52
+ attribute :player_first, Shale::Type::String
53
+
54
+ # @!attribute [rw] player_last
55
+ # Returns the player's last name
56
+ # @api public
57
+ # @example
58
+ # entry.player_last #=> "Curry"
59
+ # @return [String] the last name
60
+ attribute :player_last, Shale::Type::String
61
+
62
+ # @!attribute [rw] in_time_real
63
+ # Returns the time the player checked in (in tenths of seconds from game start)
64
+ # @api public
65
+ # @example
66
+ # entry.in_time_real #=> 0
67
+ # @return [Integer] the in time
68
+ attribute :in_time_real, Shale::Type::Integer
69
+
70
+ # @!attribute [rw] out_time_real
71
+ # Returns the time the player checked out (in tenths of seconds from game start)
72
+ # @api public
73
+ # @example
74
+ # entry.out_time_real #=> 4320
75
+ # @return [Integer] the out time
76
+ attribute :out_time_real, Shale::Type::Integer
77
+
78
+ # @!attribute [rw] player_pts
79
+ # Returns the points scored during this stint
80
+ # @api public
81
+ # @example
82
+ # entry.player_pts #=> 12
83
+ # @return [Integer] the points
84
+ attribute :player_pts, Shale::Type::Integer
85
+
86
+ # @!attribute [rw] pt_diff
87
+ # Returns the point differential during this stint
88
+ # @api public
89
+ # @example
90
+ # entry.pt_diff #=> 8
91
+ # @return [Integer] the point differential
92
+ attribute :pt_diff, Shale::Type::Integer
93
+
94
+ # @!attribute [rw] usg_pct
95
+ # Returns the usage percentage during this stint
96
+ # @api public
97
+ # @example
98
+ # entry.usg_pct #=> 32.5
99
+ # @return [Float] the usage percentage
100
+ attribute :usg_pct, Shale::Type::Float
101
+
102
+ # Returns the player object
103
+ #
104
+ # @api public
105
+ # @example
106
+ # entry.player #=> #<NBA::Player>
107
+ # @return [Player, nil] the player object
108
+ def player
109
+ Players.find(player_id)
110
+ end
111
+
112
+ # Returns the team object
113
+ #
114
+ # @api public
115
+ # @example
116
+ # entry.team #=> #<NBA::Team>
117
+ # @return [Team, nil] the team object
118
+ def team
119
+ Teams.find(team_id)
120
+ end
121
+
122
+ # Returns the game object
123
+ #
124
+ # @api public
125
+ # @example
126
+ # entry.game #=> #<NBA::Game>
127
+ # @return [Game, nil] the game object
128
+ def game
129
+ Games.find(game_id)
130
+ end
131
+
132
+ # Returns the player's full name
133
+ #
134
+ # @api public
135
+ # @example
136
+ # entry.player_name #=> "Stephen Curry"
137
+ # @return [String] the full name
138
+ def player_name
139
+ "#{player_first} #{player_last}".strip
140
+ end
141
+
142
+ # Returns the stint duration in tenths of seconds
143
+ #
144
+ # @api public
145
+ # @example
146
+ # entry.duration #=> 4320
147
+ # @return [Integer, nil] the duration
148
+ def duration
149
+ return unless in_time_real && out_time_real
150
+
151
+ out_time_real - in_time_real
152
+ end
153
+ end
154
+ end