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,99 @@
1
+ require "shale"
2
+ require "equalizer"
3
+
4
+ module NBA
5
+ # Represents a home page leader entry
6
+ #
7
+ # @api public
8
+ # @example
9
+ # leader.rank #=> 1
10
+ # leader.player #=> "Stephen Curry"
11
+ # leader.pts #=> 26.4
12
+ class HomePageLeader < Shale::Mapper
13
+ include Equalizer.new(:player_id, :team_id, :rank)
14
+
15
+ # Returns the rank
16
+ # @api public
17
+ # @example
18
+ # leader.rank #=> 1
19
+ # @return [Integer, nil] the rank
20
+ attribute :rank, Shale::Type::Integer
21
+
22
+ # Returns the player ID
23
+ # @api public
24
+ # @example
25
+ # leader.player_id #=> 201939
26
+ # @return [Integer, nil] the player ID
27
+ attribute :player_id, Shale::Type::Integer
28
+
29
+ # Returns the player name
30
+ # @api public
31
+ # @example
32
+ # leader.player #=> "Stephen Curry"
33
+ # @return [String, nil] the player name
34
+ attribute :player, Shale::Type::String
35
+
36
+ # Returns the team ID
37
+ # @api public
38
+ # @example
39
+ # leader.team_id #=> 1610612744
40
+ # @return [Integer, nil] the team ID
41
+ attribute :team_id, Shale::Type::Integer
42
+
43
+ # Returns the team abbreviation
44
+ # @api public
45
+ # @example
46
+ # leader.team_abbreviation #=> "GSW"
47
+ # @return [String, nil] the team abbreviation
48
+ attribute :team_abbreviation, Shale::Type::String
49
+
50
+ # Returns points
51
+ # @api public
52
+ # @example
53
+ # leader.pts #=> 26.4
54
+ # @return [Float, nil] points
55
+ attribute :pts, Shale::Type::Float
56
+
57
+ # Returns field goal percentage
58
+ # @api public
59
+ # @example
60
+ # leader.fg_pct #=> 0.493
61
+ # @return [Float, nil] field goal percentage
62
+ attribute :fg_pct, Shale::Type::Float
63
+
64
+ # Returns three-point percentage
65
+ # @api public
66
+ # @example
67
+ # leader.fg3_pct #=> 0.423
68
+ # @return [Float, nil] three-point percentage
69
+ attribute :fg3_pct, Shale::Type::Float
70
+
71
+ # Returns free throw percentage
72
+ # @api public
73
+ # @example
74
+ # leader.ft_pct #=> 0.913
75
+ # @return [Float, nil] free throw percentage
76
+ attribute :ft_pct, Shale::Type::Float
77
+
78
+ # Returns efficiency rating
79
+ # @api public
80
+ # @example
81
+ # leader.eff #=> 31.5
82
+ # @return [Float, nil] efficiency rating
83
+ attribute :eff, Shale::Type::Float
84
+
85
+ # Returns assists
86
+ # @api public
87
+ # @example
88
+ # leader.ast #=> 6.1
89
+ # @return [Float, nil] assists
90
+ attribute :ast, Shale::Type::Float
91
+
92
+ # Returns rebounds
93
+ # @api public
94
+ # @example
95
+ # leader.reb #=> 5.2
96
+ # @return [Float, nil] rebounds
97
+ attribute :reb, Shale::Type::Float
98
+ end
99
+ end
@@ -0,0 +1,75 @@
1
+ require "json"
2
+ require_relative "client"
3
+ require_relative "collection"
4
+ require_relative "home_page_leader"
5
+ require_relative "league"
6
+ require_relative "response_parser"
7
+ require_relative "utils"
8
+
9
+ module NBA
10
+ # Provides methods to retrieve NBA home page leaders
11
+ module HomePageLeaders
12
+ # Result set name for home page leaders
13
+ # @return [String] the result set name
14
+ RESULT_SET = "HomePageLeaders".freeze
15
+
16
+ # Retrieves home page leaders for a season
17
+ #
18
+ # @api public
19
+ # @example
20
+ # leaders = NBA::HomePageLeaders.all(season: 2023, stat_category: "PTS")
21
+ # leaders.each { |l| puts "#{l.rank}. #{l.player}: #{l.pts}" }
22
+ # @param season [Integer] the season year
23
+ # @param season_type [String] the season type
24
+ # @param stat_category [String] the stat category (PTS, AST, REB, etc.)
25
+ # @param game_scope [String] the game scope (Season, Yesterday, etc.)
26
+ # @param player_or_team [String] player or team (Player, Team)
27
+ # @param player_scope [String] the player scope (All Players, Rookies)
28
+ # @param league [String, League] the league ID or League object
29
+ # @param client [Client] the API client to use
30
+ # @return [Collection] a collection of leaders
31
+ def self.all(season: Utils.current_season, season_type: "Regular Season", stat_category: "PTS",
32
+ game_scope: "Season", player_or_team: "Player", player_scope: "All Players",
33
+ league: League::NBA, client: CLIENT)
34
+ league_id = Utils.extract_league_id(league)
35
+ opts = {season: season, season_type: season_type, stat_category: stat_category,
36
+ game_scope: game_scope, player_or_team: player_or_team, player_scope: player_scope,
37
+ league_id: league_id}
38
+ ResponseParser.parse(client.get(build_path(opts)), result_set: RESULT_SET) { |data| build_leader(data) }
39
+ end
40
+
41
+ # Builds the API request path
42
+ # @api private
43
+ # @return [String] the request path
44
+ def self.build_path(opts)
45
+ "homepageleaders?GameScope=#{opts[:game_scope]}&LeagueID=#{opts[:league_id]}" \
46
+ "&PlayerOrTeam=#{opts[:player_or_team]}&PlayerScope=#{opts[:player_scope]}" \
47
+ "&Season=#{Utils.format_season(opts[:season])}&SeasonType=#{opts[:season_type]}" \
48
+ "&StatCategory=#{opts[:stat_category]}"
49
+ end
50
+ private_class_method :build_path
51
+
52
+ # Builds a HomePageLeader object from raw data
53
+ # @api private
54
+ # @param data [Hash] the row data
55
+ # @return [HomePageLeader] the leader object
56
+ def self.build_leader(data)
57
+ HomePageLeader.new(**leader_attributes(data))
58
+ end
59
+ private_class_method :build_leader
60
+
61
+ # Extracts leader attributes from data
62
+ # @api private
63
+ # @param data [Hash] the row data
64
+ # @return [Hash] leader attributes
65
+ def self.leader_attributes(data)
66
+ {rank: data["RANK"], player_id: data["PLAYER_ID"],
67
+ player: data["PLAYER"], team_id: data["TEAM_ID"],
68
+ team_abbreviation: data["TEAM_ABBREVIATION"],
69
+ pts: data["PTS"], fg_pct: data["FG_PCT"],
70
+ fg3_pct: data["FG3_PCT"], ft_pct: data["FT_PCT"],
71
+ eff: data["EFF"], ast: data["AST"], reb: data["REB"]}
72
+ end
73
+ private_class_method :leader_attributes
74
+ end
75
+ end
@@ -0,0 +1,57 @@
1
+ require "equalizer"
2
+ require "shale"
3
+
4
+ module NBA
5
+ # Represents a home page statistic entry
6
+ #
7
+ # @api public
8
+ # @example
9
+ # stat.rank #=> 1
10
+ # stat.team_abbreviation #=> "BOS"
11
+ # stat.value #=> 120.5
12
+ class HomePageStat < Shale::Mapper
13
+ include Equalizer.new(:rank, :team_id)
14
+
15
+ # Returns the rank
16
+ # @api public
17
+ # @example
18
+ # stat.rank #=> 1
19
+ # @return [Integer, nil] the rank
20
+ attribute :rank, Shale::Type::Integer
21
+
22
+ # Returns the team ID
23
+ # @api public
24
+ # @example
25
+ # stat.team_id #=> 1610612738
26
+ # @return [Integer, nil] the team ID
27
+ attribute :team_id, Shale::Type::Integer
28
+
29
+ # Returns the team abbreviation
30
+ # @api public
31
+ # @example
32
+ # stat.team_abbreviation #=> "BOS"
33
+ # @return [String, nil] the team abbreviation
34
+ attribute :team_abbreviation, Shale::Type::String
35
+
36
+ # Returns the team name
37
+ # @api public
38
+ # @example
39
+ # stat.team_name #=> "Boston Celtics"
40
+ # @return [String, nil] the team name
41
+ attribute :team_name, Shale::Type::String
42
+
43
+ # Returns the statistic value
44
+ # @api public
45
+ # @example
46
+ # stat.value #=> 120.5
47
+ # @return [Float, nil] the statistic value
48
+ attribute :value, Shale::Type::Float
49
+
50
+ # Returns the statistic name
51
+ # @api public
52
+ # @example
53
+ # stat.stat_name #=> "pts"
54
+ # @return [String, nil] the statistic name
55
+ attribute :stat_name, Shale::Type::String
56
+ end
57
+ end
@@ -0,0 +1,110 @@
1
+ require "json"
2
+ require_relative "client"
3
+ require_relative "collection"
4
+ require_relative "home_page_stat"
5
+ require_relative "league"
6
+ require_relative "response_parser"
7
+ require_relative "utils"
8
+
9
+ module NBA
10
+ # Provides methods to retrieve NBA home page V2 statistics
11
+ module HomePageV2
12
+ # Stat categories available in home page V2
13
+ STAT_CATEGORIES = {
14
+ pts: "HomePageStat1",
15
+ reb: "HomePageStat2",
16
+ ast: "HomePageStat3",
17
+ stl: "HomePageStat4",
18
+ fg_pct: "HomePageStat5",
19
+ ft_pct: "HomePageStat6",
20
+ fg3_pct: "HomePageStat7",
21
+ blk: "HomePageStat8"
22
+ }.freeze
23
+
24
+ # Retrieves home page V2 statistics for a stat category
25
+ #
26
+ # @api public
27
+ # @example
28
+ # stats = NBA::HomePageV2.all(stat_category: :pts, season: 2023)
29
+ # stats.each { |s| puts "#{s.rank}. #{s.team_abbreviation}: #{s.value}" }
30
+ # @param stat_category [Symbol] the stat category (:pts, :reb, :ast, etc.)
31
+ # @param season [Integer] the season year (defaults to current season)
32
+ # @param season_type [String] the season type (Regular Season, Playoffs, etc.)
33
+ # @param game_scope [String] the game scope (Season, Yesterday, etc.)
34
+ # @param player_or_team [String] player or team stats (Player, Team)
35
+ # @param player_scope [String] the player scope (All Players, Rookies)
36
+ # @param stat_type [String] the stat type (Traditional, Advanced, etc.)
37
+ # @param league [String, League] the league ID or League object
38
+ # @param client [Client] the API client to use
39
+ # @return [Collection] a collection of home page stats
40
+ def self.all(stat_category:, season: Utils.current_season, season_type: "Regular Season",
41
+ game_scope: "Season", player_or_team: "Team", player_scope: "All Players",
42
+ stat_type: "Traditional", league: League::NBA, client: CLIENT)
43
+ result_set = STAT_CATEGORIES.fetch(stat_category)
44
+ league_id = Utils.extract_league_id(league)
45
+ opts = {season: season, season_type: season_type, game_scope: game_scope,
46
+ player_or_team: player_or_team, player_scope: player_scope,
47
+ stat_type: stat_type, league_id: league_id}
48
+ response = client.get(build_path(opts))
49
+ parse_response(response, result_set, stat_category)
50
+ end
51
+
52
+ # Builds the API request path
53
+ # @api private
54
+ # @return [String] the request path
55
+ def self.build_path(opts)
56
+ "homepagev2?GameScope=#{opts[:game_scope]}&LeagueID=#{opts[:league_id]}" \
57
+ "&PlayerOrTeam=#{opts[:player_or_team]}&PlayerScope=#{opts[:player_scope]}" \
58
+ "&Season=#{Utils.format_season(opts[:season])}&SeasonType=#{opts[:season_type]}" \
59
+ "&StatType=#{opts[:stat_type]}"
60
+ end
61
+ private_class_method :build_path
62
+
63
+ # Parses the API response
64
+ # @api private
65
+ # @param response [String, nil] the raw API response
66
+ # @param result_set [String] the result set name
67
+ # @param stat_category [Symbol] the stat category
68
+ # @return [Collection] parsed stats
69
+ def self.parse_response(response, result_set, stat_category)
70
+ ResponseParser.parse(response, result_set: result_set) do |data|
71
+ build_stat(data, stat_category)
72
+ end
73
+ end
74
+ private_class_method :parse_response
75
+
76
+ # Builds a HomePageStat object from raw data
77
+ # @api private
78
+ # @param data [Hash] the row data
79
+ # @param stat_category [Symbol] the stat category
80
+ # @return [HomePageStat] the stat object
81
+ def self.build_stat(data, stat_category)
82
+ HomePageStat.new(**stat_attributes(data, stat_category))
83
+ end
84
+ private_class_method :build_stat
85
+
86
+ # Extracts stat attributes from data
87
+ # @api private
88
+ # @param data [Hash] the row data
89
+ # @param stat_category [Symbol] the stat category name
90
+ # @return [Hash] stat attributes
91
+ def self.stat_attributes(data, stat_category)
92
+ {rank: data["RANK"], team_id: data["TEAM_ID"],
93
+ team_abbreviation: data["TEAM_ABBREVIATION"],
94
+ team_name: data["TEAM_NAME"],
95
+ value: extract_value(data), stat_name: stat_category}
96
+ end
97
+ private_class_method :stat_attributes
98
+
99
+ # Extracts the stat value from data (different keys per result set)
100
+ # @api private
101
+ # @param data [Hash] the row data
102
+ # @return [Float, nil] the stat value
103
+ def self.extract_value(data)
104
+ value_keys = %w[PTS REB AST STL FG_PCT FT_PCT FG3_PCT BLK]
105
+ value_keys.each { |key| return data.fetch(key) if data.key?(key) }
106
+ nil
107
+ end
108
+ private_class_method :extract_value
109
+ end
110
+ end
@@ -0,0 +1,182 @@
1
+ require "json"
2
+ require_relative "client"
3
+ require_relative "collection"
4
+ require_relative "box_score_hustle_player_stat"
5
+ require_relative "box_score_hustle_team_stat"
6
+ require_relative "utils"
7
+
8
+ module NBA
9
+ # Provides methods to retrieve hustle statistics for a game via hustlestatsboxscore endpoint
10
+ module HustleStatsBoxScore
11
+ # Result set name for availability status
12
+ # @return [String] the result set name
13
+ HUSTLE_STATS_AVAILABLE = "HustleStatsAvailable".freeze
14
+
15
+ # Result set name for player stats
16
+ # @return [String] the result set name
17
+ PLAYER_STATS = "PlayerStats".freeze
18
+
19
+ # Result set name for team stats
20
+ # @return [String] the result set name
21
+ TEAM_STATS = "TeamStats".freeze
22
+
23
+ # API endpoint path
24
+ # @return [String] the endpoint path
25
+ ENDPOINT = "hustlestatsboxscore".freeze
26
+
27
+ # Retrieves hustle statistics for players in a game
28
+ #
29
+ # @api public
30
+ # @example
31
+ # stats = NBA::HustleStatsBoxScore.player_stats(game: "0022400001")
32
+ # @param game [String, Game] the game ID or Game object
33
+ # @param client [Client] the API client to use
34
+ # @return [Collection] a collection of player hustle stats
35
+ def self.player_stats(game:, client: CLIENT)
36
+ game_id = Utils.extract_id(game)
37
+ parse_stats(client.get("#{ENDPOINT}?GameID=#{game_id}"), game_id, PLAYER_STATS, :build_player_stat)
38
+ end
39
+
40
+ # Retrieves hustle statistics for teams in a game
41
+ #
42
+ # @api public
43
+ # @example
44
+ # stats = NBA::HustleStatsBoxScore.team_stats(game: "0022400001")
45
+ # @param game [String, Game] the game ID or Game object
46
+ # @param client [Client] the API client to use
47
+ # @return [Collection] a collection of team hustle stats
48
+ def self.team_stats(game:, client: CLIENT)
49
+ game_id = Utils.extract_id(game)
50
+ parse_stats(client.get("#{ENDPOINT}?GameID=#{game_id}"), game_id, TEAM_STATS, :build_team_stat)
51
+ end
52
+
53
+ # Checks if hustle stats are available for a game
54
+ #
55
+ # @api public
56
+ # @example
57
+ # NBA::HustleStatsBoxScore.available?(game: "0022400001") #=> true
58
+ # @param game [String, Game] the game ID or Game object
59
+ # @param client [Client] the API client to use
60
+ # @return [Boolean] true if hustle stats are available
61
+ def self.available?(game:, client: CLIENT)
62
+ game_id = Utils.extract_id(game)
63
+ stats_available?(client.get("#{ENDPOINT}?GameID=#{game_id}"))
64
+ end
65
+
66
+ # Parses hustle stats from API response
67
+ # @api private
68
+ # @param response [String] the API response
69
+ # @param game_id [String] the game ID
70
+ # @param result_set_name [String] the result set name to find
71
+ # @param builder_method [Symbol] the method to build stat objects
72
+ # @return [Collection] collection of stat objects
73
+ def self.parse_stats(response, game_id, result_set_name, builder_method)
74
+ return Collection.new unless response
75
+
76
+ result_set = find_result_set(JSON.parse(response), result_set_name)
77
+ return Collection.new unless result_set
78
+
79
+ headers = result_set["headers"]
80
+ rows = result_set["rowSet"]
81
+ return Collection.new unless headers && rows
82
+
83
+ Collection.new(rows.map { |row| __send__(builder_method, headers, row, game_id) })
84
+ end
85
+ private_class_method :parse_stats
86
+
87
+ # Checks if hustle stats are available in the response
88
+ # @api private
89
+ # @param response [String] the API response
90
+ # @return [Boolean] true if stats are available
91
+ def self.stats_available?(response)
92
+ return false unless response
93
+
94
+ result_set = find_result_set(JSON.parse(response), HUSTLE_STATS_AVAILABLE)
95
+ return false unless result_set
96
+
97
+ rows = result_set["rowSet"]
98
+ headers = result_set["headers"]
99
+ return false unless rows && !rows.empty? && headers
100
+
101
+ headers.zip(rows.first).to_h["HUSTLE_STATUS"].eql?(1)
102
+ end
103
+ private_class_method :stats_available?
104
+
105
+ # Finds a result set by name in the response data
106
+ # @api private
107
+ # @param data [Hash] the parsed JSON data
108
+ # @param name [String] the result set name to find
109
+ # @return [Hash, nil] the result set hash or nil
110
+ def self.find_result_set(data, name)
111
+ result_sets = data["resultSets"]
112
+ return unless result_sets
113
+
114
+ result_sets.find { |rs| rs.fetch("name").eql?(name) }
115
+ end
116
+ private_class_method :find_result_set
117
+
118
+ # Builds a player stat object from row data
119
+ # @api private
120
+ # @param headers [Array<String>] the column headers
121
+ # @param row [Array] the row data
122
+ # @param game_id [String] the game ID
123
+ # @return [BoxScoreHustlePlayerStat] the player stat object
124
+ def self.build_player_stat(headers, row, game_id)
125
+ data = headers.zip(row).to_h
126
+ BoxScoreHustlePlayerStat.new(**player_identity(data, game_id), **hustle_stats(data))
127
+ end
128
+ private_class_method :build_player_stat
129
+
130
+ # Builds a team stat object from row data
131
+ # @api private
132
+ # @param headers [Array<String>] the column headers
133
+ # @param row [Array] the row data
134
+ # @param game_id [String] the game ID
135
+ # @return [BoxScoreHustleTeamStat] the team stat object
136
+ def self.build_team_stat(headers, row, game_id)
137
+ data = headers.zip(row).to_h
138
+ BoxScoreHustleTeamStat.new(**team_identity(data, game_id), **hustle_stats(data))
139
+ end
140
+ private_class_method :build_team_stat
141
+
142
+ # Extracts player identity attributes from data
143
+ # @api private
144
+ # @param data [Hash] the row data hash
145
+ # @param game_id [String] the game ID
146
+ # @return [Hash] player identity attributes
147
+ def self.player_identity(data, game_id)
148
+ {game_id: game_id, team_id: data.fetch("TEAM_ID"), team_abbreviation: data.fetch("TEAM_ABBREVIATION"),
149
+ team_city: data.fetch("TEAM_CITY"), player_id: data.fetch("PLAYER_ID"), player_name: data.fetch("PLAYER_NAME"),
150
+ start_position: data.fetch("START_POSITION"), comment: data.fetch("COMMENT"),
151
+ min: data.fetch("MINUTES"), pts: data.fetch("PTS")}
152
+ end
153
+ private_class_method :player_identity
154
+
155
+ # Extracts team identity attributes from data
156
+ # @api private
157
+ # @param data [Hash] the row data hash
158
+ # @param game_id [String] the game ID
159
+ # @return [Hash] team identity attributes
160
+ def self.team_identity(data, game_id)
161
+ {game_id: game_id, team_id: data.fetch("TEAM_ID"), team_name: data.fetch("TEAM_NAME"),
162
+ team_abbreviation: data.fetch("TEAM_ABBREVIATION"), team_city: data.fetch("TEAM_CITY"),
163
+ min: data.fetch("MINUTES"), pts: data.fetch("PTS")}
164
+ end
165
+ private_class_method :team_identity
166
+
167
+ # Extracts hustle stats attributes from data
168
+ # @api private
169
+ # @param data [Hash] the row data hash
170
+ # @return [Hash] hustle stats attributes
171
+ def self.hustle_stats(data)
172
+ {contested_shots: data.fetch("CONTESTED_SHOTS"), contested_shots_2pt: data.fetch("CONTESTED_SHOTS_2PT"),
173
+ contested_shots_3pt: data.fetch("CONTESTED_SHOTS_3PT"), deflections: data.fetch("DEFLECTIONS"),
174
+ charges_drawn: data.fetch("CHARGES_DRAWN"), screen_assists: data.fetch("SCREEN_ASSISTS"),
175
+ screen_ast_pts: data.fetch("SCREEN_AST_PTS"), loose_balls_recovered: data.fetch("LOOSE_BALLS_RECOVERED"),
176
+ off_loose_balls_recovered: data.fetch("OFF_LOOSE_BALLS_RECOVERED"),
177
+ def_loose_balls_recovered: data.fetch("DEF_LOOSE_BALLS_RECOVERED"), box_outs: data.fetch("BOX_OUTS"),
178
+ off_box_outs: data.fetch("OFF_BOXOUTS"), def_box_outs: data.fetch("DEF_BOXOUTS")}
179
+ end
180
+ private_class_method :hustle_stats
181
+ end
182
+ end
@@ -0,0 +1,139 @@
1
+ require "json"
2
+ require_relative "client"
3
+ require_relative "collection"
4
+ require_relative "infographic_fan_duel_player_stat"
5
+ require_relative "utils"
6
+
7
+ module NBA
8
+ # Provides methods to retrieve FanDuel infographic player statistics
9
+ module InfographicFanDuelPlayer
10
+ # Result set name for FanDuel player stats
11
+ # @return [String] the result set name
12
+ FAN_DUEL_PLAYER = "FanDuelPlayer".freeze
13
+
14
+ # Retrieves FanDuel infographic statistics for all players in a game
15
+ #
16
+ # @api public
17
+ # @example
18
+ # stats = NBA::InfographicFanDuelPlayer.find(game: "0022400001")
19
+ # stats.each { |s| puts "#{s.player_name}: #{s.fan_duel_pts} FD pts" }
20
+ # @param game [String, Game] the game ID or Game object
21
+ # @param client [Client] the API client to use
22
+ # @return [Collection] a collection of FanDuel player stats
23
+ def self.find(game:, client: CLIENT)
24
+ game_id = Utils.extract_id(game)
25
+ path = "infographicfanduelplayer?GameID=#{game_id}"
26
+ response = client.get(path)
27
+ parse_response(response, game_id)
28
+ end
29
+
30
+ # Parses the API response
31
+ #
32
+ # @api private
33
+ # @param response [String, nil] the JSON response
34
+ # @param game_id [String] the game ID
35
+ # @return [Collection] collection of player stats
36
+ def self.parse_response(response, game_id)
37
+ return Collection.new unless response
38
+
39
+ data = JSON.parse(response)
40
+ result_set = find_result_set(data)
41
+ return Collection.new unless result_set
42
+
43
+ headers = result_set["headers"]
44
+ rows = result_set["rowSet"]
45
+ return Collection.new unless headers && rows
46
+
47
+ stats = rows.map { |row| build_player_stat(headers, row, game_id) }
48
+ Collection.new(stats)
49
+ end
50
+ private_class_method :parse_response
51
+
52
+ # Finds the result set
53
+ #
54
+ # @api private
55
+ # @param data [Hash] the parsed JSON data
56
+ # @return [Hash, nil] the result set
57
+ def self.find_result_set(data)
58
+ result_sets = data["resultSets"]
59
+ return unless result_sets
60
+
61
+ result_sets.find { |rs| rs["name"].eql?(FAN_DUEL_PLAYER) }
62
+ end
63
+ private_class_method :find_result_set
64
+
65
+ # Builds a player stat from a row
66
+ #
67
+ # @api private
68
+ # @param headers [Array<String>] the column headers
69
+ # @param row [Array] the row data
70
+ # @param game_id [String] the game ID
71
+ # @return [InfographicFanDuelPlayerStat] the player stat object
72
+ def self.build_player_stat(headers, row, game_id)
73
+ data = headers.zip(row).to_h
74
+ InfographicFanDuelPlayerStat.new(**player_stat_attributes(data, game_id))
75
+ end
76
+ private_class_method :build_player_stat
77
+
78
+ # Extracts player stat attributes from row data
79
+ #
80
+ # @api private
81
+ # @param data [Hash] the player stat row data
82
+ # @param game_id [String] the game ID
83
+ # @return [Hash] the player stat attributes
84
+ def self.player_stat_attributes(data, game_id)
85
+ identity_attributes(data, game_id).merge(fantasy_attributes(data), shooting_attributes(data), counting_attributes(data))
86
+ end
87
+ private_class_method :player_stat_attributes
88
+
89
+ # Extracts identity attributes
90
+ #
91
+ # @api private
92
+ # @param data [Hash] the player stat data
93
+ # @param game_id [String] the game ID
94
+ # @return [Hash] the identity attributes
95
+ def self.identity_attributes(data, game_id)
96
+ {game_id: game_id, player_id: data["PLAYER_ID"], player_name: data["PLAYER_NAME"],
97
+ team_id: data["TEAM_ID"], team_name: data["TEAM_NAME"],
98
+ team_abbreviation: data["TEAM_ABBREVIATION"], jersey_num: data["JERSEY_NUM"],
99
+ player_position: data["PLAYER_POSITION"], location: data["LOCATION"]}
100
+ end
101
+ private_class_method :identity_attributes
102
+
103
+ # Extracts fantasy attributes
104
+ #
105
+ # @api private
106
+ # @param data [Hash] the player stat data
107
+ # @return [Hash] the fantasy attributes
108
+ def self.fantasy_attributes(data)
109
+ {fan_duel_pts: data["FAN_DUEL_PTS"], nba_fantasy_pts: data["NBA_FANTASY_PTS"],
110
+ usg_pct: data["USG_PCT"], min: data["MIN"]}
111
+ end
112
+ private_class_method :fantasy_attributes
113
+
114
+ # Extracts shooting attributes
115
+ #
116
+ # @api private
117
+ # @param data [Hash] the player stat data
118
+ # @return [Hash] the shooting attributes
119
+ def self.shooting_attributes(data)
120
+ {fgm: data["FGM"], fga: data["FGA"], fg_pct: data["FG_PCT"],
121
+ fg3m: data["FG3M"], fg3a: data["FG3A"], fg3_pct: data["FG3_PCT"],
122
+ ftm: data["FTM"], fta: data["FTA"], ft_pct: data["FT_PCT"]}
123
+ end
124
+ private_class_method :shooting_attributes
125
+
126
+ # Extracts counting stats attributes
127
+ #
128
+ # @api private
129
+ # @param data [Hash] the player stat data
130
+ # @return [Hash] the counting stats attributes
131
+ def self.counting_attributes(data)
132
+ {oreb: data["OREB"], dreb: data["DREB"], reb: data["REB"],
133
+ ast: data["AST"], tov: data["TOV"], stl: data["STL"],
134
+ blk: data["BLK"], blka: data["BLKA"], pf: data["PF"],
135
+ pfd: data["PFD"], pts: data["PTS"], plus_minus: data["PLUS_MINUS"]}
136
+ end
137
+ private_class_method :counting_attributes
138
+ end
139
+ end