sleeper_api 1.0.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.
data/README.md ADDED
@@ -0,0 +1,447 @@
1
+ # SleeperApi
2
+
3
+ A comprehensive Ruby gem for interacting with [Sleeper's fantasy football API](https://docs.sleeper.com/). Built with performance, reliability, and developer experience in mind.
4
+
5
+ ## Features
6
+
7
+ - Complete API Coverage - Users, leagues, drafts, players, matchups, transactions
8
+
9
+ - Performance Optimized - Smart caching, connection pooling, rate limiting
10
+
11
+ - Robust Error Handling - Automatic retries, timeout management, detailed error messages
12
+
13
+ - Well Tested - 90%+ test coverage with RSpec
14
+
15
+ - Highly Configurable - Custom timeouts, retries, logging
16
+
17
+ - Production Ready - Type signatures, CI/CD, code quality tools
18
+
19
+ ## Installation
20
+
21
+ Add this line to your application's Gemfile:
22
+
23
+ ```ruby
24
+ gem 'sleeper_api'
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ```ruby
30
+ require 'sleeper_api'
31
+
32
+ # Basic usage with default configuration
33
+ client = SleeperApi.client
34
+ league = client.league("123456789012345678")
35
+
36
+ # Access league information
37
+ puts league.name          # "My Fantasy League"
38
+ puts league.total_rosters # 12
39
+ puts league.status        # "in_season"
40
+
41
+ # Get rosters
42
+ rosters = league.rosters
43
+ rosters.each do |roster|
44
+   puts "#{roster[:owner_display_name]}: #{roster[:wins]}-#{roster[:losses]}"
45
+ end
46
+ ```
47
+
48
+ ## Configuration
49
+
50
+ Customize the gem's behavior:
51
+
52
+ ```ruby
53
+ SleeperApi.configure do |config|
54
+   config.timeout = 45    # Request timeout in seconds (10-60)
55
+   config.retries = 5     # Number of retries on failure (0-5)
56
+   config.logger = Logger.new(STDOUT)  # Custom logger
57
+ end
58
+
59
+ # Configuration is applied to all subsequent client instances
60
+ client = SleeperApi.client
61
+ ```
62
+
63
+ ## API Coverage
64
+
65
+ ### Users
66
+
67
+ Get user information and their leagues/drafts:
68
+
69
+ ```ruby
70
+ # Find a user by username
71
+ user = SleeperApi.client.user('johndoe')
72
+
73
+ # Basic profile
74
+ user.username # => "johndoe"
75
+ user.display_name # => "John Doe"
76
+ user.avatar_url # => "https://sleepercdn.com/avatars/abc123"
77
+ user.is_bot # => false
78
+
79
+ # All leagues for current season
80
+ leagues = user.leagues
81
+ leagues.each do |league|
82
+ status = league[:status] == "finished" ? "🏆" : league[:status]
83
+ puts "#{status} #{league[:name]} (#{league[:total_rosters]} teams)"
84
+ end
85
+
86
+ # Specific season
87
+ old_leagues = user.leagues(2023)
88
+ championships = old_leagues.select { |l| l[:status] == "finished" }
89
+
90
+ # Get all my rosters across all leagues
91
+ rosters = user.rosters
92
+ rosters.each do |roster|
93
+ puts "#{roster[:league_name]}: #{roster[:team_name]} (#{roster[:wins]}-#{roster[:losses]})"
94
+ end
95
+
96
+ # Best performing team this season
97
+ best_roster = user.rosters.max_by { |r| r[:wins].to_i - r[:losses].to_i }
98
+ puts "Top team: #{best_roster[:team_name]} (#{best_roster[:wins]}-#{best_roster[:losses]})"
99
+
100
+ # All drafts with enhanced info
101
+ drafts = user.drafts(2024)
102
+ drafts.each do |draft|
103
+ status = draft[:status] == "complete" ? "✅" : draft[:status]
104
+ puts "#{status} #{draft[:league_name]} - #{draft[:type]} (#{draft[:total_teams]} teams)"
105
+ end
106
+
107
+ # Live drafts only
108
+ live_drafts = user.drafts.select { |d| d[:status] == "live" }
109
+
110
+ # Season summary
111
+ summary = user.summary
112
+ puts "#{summary[:total_leagues]} leagues this season"
113
+ puts "Winning record in #{summary[:winning_record].length} leagues"
114
+ puts "Overall: #{summary[:total_wins]}-#{summary[:total_losses]}"
115
+ ```
116
+
117
+ ### Leagues
118
+
119
+ Access league data, rosters, matchups, and transactions:
120
+
121
+ ```ruby
122
+ league = SleeperApi.client.league("123456")
123
+
124
+ # Basic league info
125
+ league.name # => "My Fantasy League"
126
+ league.season # => "2024"
127
+ league.total_rosters # => 12
128
+ league.status # => "regular"
129
+ league.avatar_url # => "https://sleepercdn.com/avatars/abc123"
130
+
131
+ # Formatted rosters (with team names and records)
132
+ rosters = league.rosters
133
+ roster = rosters.first
134
+ roster[:owner_display_name] # => "John Doe"
135
+ roster[:team_name] # => "Gridiron Gang"
136
+ roster[:wins] # => 7
137
+ roster[:losses] # => 2
138
+ roster[:remaining_faab] # => 45
139
+ roster[:starters] # => ["player_id_1", "player_id_2", ...]
140
+ roster[:bench] # => ["player_id_4", "player_id_5", ...]
141
+
142
+ # My roster only
143
+ my_roster = league.rosters(user_id: my_user_id)
144
+
145
+ # Week-specific matchups
146
+ matchups = league.matchups_by_week(week: 5)
147
+ matchup = matchups.first
148
+ team = matchup[:rosters].first
149
+ team[:points] # => 142.5
150
+ team[:starter_points] # => [{ "player_id" => 18.2 }, ...]
151
+
152
+ # Transactions
153
+ transactions = league.transactions(week: 5)
154
+ tx = transactions.first
155
+ tx[:type] # => "tr"
156
+ tx[:adds] # => [{ player_id: "123", roster_id: 1 }]
157
+ tx[:drops] # => [{ player_id: "456", roster_id: 1 }]
158
+ tx[:waiver_bid] # => 12
159
+
160
+ # Users
161
+ users = league.users
162
+ user = users.find { |u| u[:commissioner] }
163
+ user[:display_name] # => "Commish McGee"
164
+ user[:team_name] # => "Champions"
165
+
166
+ # Playoffs
167
+ playoff_matchups = league.playoff_bracket
168
+ matchup = playoff_matchups.first
169
+ matchup[:team1_owner] # => "Team Alpha"
170
+ matchup[:winner_owner] # => "Team Alpha"
171
+
172
+ # Tourlet Bowl / Losers Bracket
173
+ toilet_bowl_matchups = league.toilet_bowl
174
+ matchup = toilet_bowl_matchups.first
175
+ matchup[:team1_owner] # => "Team Beta"
176
+ matchup[:winner_owner] # => "Team Beta"
177
+ ```
178
+
179
+ ### Drafts
180
+
181
+ Access draft information, picks, and traded picks:
182
+
183
+ ```ruby
184
+ # Get a specific draft
185
+ draft = SleeperApi.client.draft("draft_123456")
186
+
187
+ # Basic draft info
188
+ draft.type # => "snake" or "auction"
189
+ draft.status # => "pre_draft", "live", "complete"
190
+ draft.season # => "2024"
191
+ draft.start_time # => "2024-08-27T20:00:00.000Z"
192
+ draft.league.name # => "My Fantasy League"
193
+
194
+ # All picks (180 picks for 15-round, 12-team draft)
195
+ picks = draft.picks
196
+ puts "#{picks.length} total picks made"
197
+
198
+ # Filter picks
199
+ round_1 = draft.picks(round: 1)
200
+ team_3_picks = draft.picks(roster_id: 3)
201
+
202
+ round_1.each do |pick|
203
+ player = SleeperApi.client.get_player_by_id(pick[:player_id])
204
+ puts "#{pick[:pick_no]}. #{player['full_name']} (#{player['position']}) - Team #{pick[:roster_id]}"
205
+ end
206
+
207
+ # Who drafted a specific player?
208
+ picker = draft.picked_by("4036") # User 1
209
+
210
+ # Traded picks
211
+ trades = draft.traded_picks
212
+ team_3_trades = draft.traded_picks(original_owner_id: 3)
213
+
214
+ team_3_trades.each do |trade|
215
+ puts "Team 3 traded away #{trade[:round]}.#{trade[:draft_slot]} to Team #{trade[:owner_id]}"
216
+ end
217
+
218
+ # Live draft - next pick info
219
+ if draft.status == "live"
220
+ next_pick = draft.next_pick
221
+ puts "Next up: Round #{next_pick[:round]}, Pick #{next_pick[:pick_no]} (slot #{next_pick[:draft_slot]})"
222
+ end
223
+
224
+ # Quick summary with top picks
225
+ summary = draft.summary
226
+ puts "#{summary[:total_picks]} picks in #{summary[:total_rounds]} rounds"
227
+ summary[:top_picks].each do |pick|
228
+ puts "#{pick[:pick_no]}. #{pick[:player_name]} (#{pick[:position]}, #{pick[:team]}) - #{pick[:roster_id]}"
229
+ end
230
+
231
+ # Grouped views
232
+ rounds = draft.rounds # { 1 => [picks], 2 => [picks], ... }
233
+ team_3_rounds = draft.team_picks[3] # Team 3's picks by round
234
+ ```
235
+
236
+ ### Players
237
+
238
+ Access player data with automatic caching:
239
+
240
+ ```ruby
241
+ # Get all players (cached for 24 hours)
242
+ players = client.get_players
243
+
244
+ # Find specific player
245
+ player = client.get_player_by_id("1234")
246
+ puts "#{player['first_name']} #{player['last_name']} - #{player['position']}"
247
+
248
+ # Get trending players
249
+ trending_adds = client.trending_players(type: "add", limit: 10)
250
+ trending_drops = client.trending_players(type: "drop", limit: 10)
251
+ ```
252
+
253
+ ### Player Helper
254
+
255
+ All resource classes (`League`, `User`, `Draft`) include a `player_details` helper method that returns formatted Sleeper player data. This eliminates repetitive player lookups and ensures consistent symbolization.
256
+
257
+ ```ruby
258
+ # In any resource (League, User, Draft)
259
+ league = SleeperApi.client.league("123456")
260
+
261
+ # Get player details with one method call
262
+ player = league.player_details("4036") # Christian McCaffrey
263
+ if player
264
+ puts "#{player[:full_name]} (#{player[:position]}, #{player[:team]})"
265
+ puts "Bye week: #{player[:bye]} | Injury status: #{player[:injury_status]}"
266
+ end
267
+
268
+ # Works in matchups
269
+ matchups = league.matchups_by_week(week: 5)
270
+ matchup = matchups.first
271
+ team = matchup[:rosters].first
272
+
273
+ # Get starter names and positions
274
+ team[:starters].each do |player_id|
275
+ player = league.player_details(player_id)
276
+ if player
277
+ puts "#{player[:full_name]} (#{player[:position]}) - #{team[:starter_points].find { |p| p.keys.first == player_id }&.values&.first || 0} pts"
278
+ end
279
+ end
280
+
281
+ # Works in rosters
282
+ roster = league.rosters.first
283
+ roster[:starters].each_with_index do |player_id, index|
284
+ player = league.player_details(player_id)
285
+ position = player ? player[:position] : "?"
286
+ points = roster[:starter_points]&.dig(index, player_id) || 0
287
+ puts "#{position}: #{player ? player[:full_name] : 'Unknown'} (#{points} pts)"
288
+ end
289
+
290
+ # Works in drafts
291
+ draft = SleeperApi.client.draft("draft_123")
292
+ round_1 = draft.picks(round: 1)
293
+
294
+ round_1.each do |pick|
295
+ player = draft.player_details(pick[:player_id])
296
+ if player
297
+ puts "#{pick[:pick_no]}. #{player[:full_name]} (#{player[:position]}, #{player[:team]})"
298
+ end
299
+ end
300
+
301
+ # Works across user's leagues
302
+ user = SleeperApi.client.user('johndoe')
303
+ rosters = user.rosters
304
+
305
+ rosters.each do |roster|
306
+ puts "\n#{roster[:team_name]} starters:"
307
+ roster[:starters].each do |player_id|
308
+ player = roster.player_details(player_id) # Note: uses the League instance under the hood
309
+ puts " • #{player ? player[:full_name] : 'Unknown'} (#{player[:position] if player})"
310
+ end
311
+ end
312
+ ```
313
+
314
+ ### Additional Endpoints
315
+
316
+ ```ruby
317
+ # Get NFL state
318
+ state = client.get_nfl_state
319
+ puts "Current week: #{state['week']}"
320
+
321
+ # Get playoff brackets
322
+ winners_bracket = client.get_league_playoff_bracket("league_id")
323
+ losers_bracket = client.get_league_toilet_bowl("league_id")
324
+
325
+ # Get league drafts
326
+ league_drafts = client.get_league_drafts("league_id")
327
+
328
+ # Get traded picks
329
+ traded_picks = client.get_league_traded_picks("league_id")
330
+ ```
331
+
332
+ ## Error Handling
333
+
334
+ The gem provides comprehensive error handling:
335
+
336
+ ```ruby
337
+ begin
338
+   league = client.league("invalid_id")
339
+   # Process league data
340
+ rescue SleeperApi::Error => e
341
+   puts "API Error: #{e.message}"
342
+ rescue ArgumentError => e
343
+   puts "Invalid parameter: #{e.message}"
344
+ end
345
+ ```
346
+
347
+ ### Error Types
348
+
349
+ - SleeperApi::Error - API-related errors (404, 500, timeouts, etc.)
350
+
351
+ - ArgumentError - Invalid parameters passed to methods
352
+
353
+ ## Performance Considerations
354
+
355
+ ### Caching
356
+
357
+ - Player data is cached for 24 hours to reduce API calls
358
+
359
+ - League/User/Draft data is cached per instance
360
+
361
+ - Cache files are stored in the current working directory
362
+
363
+ ### Rate Limiting
364
+
365
+ - Be mindful of Sleeper's rate limits: stay under 1000 API calls per minute
366
+
367
+ - The gem automatically handles timeouts and retries
368
+
369
+ - Consider caching frequently accessed data in your application
370
+
371
+ ### Memory Usage
372
+
373
+ - Large datasets (like all players) are cached to disk
374
+
375
+ - League rosters and matchups are fetched lazily
376
+
377
+ - Use no_data: true when initializing leagues if you don't need immediate data
378
+
379
+ ## Testing
380
+
381
+ The gem includes comprehensive tests:
382
+
383
+ ```shellscript
384
+ # Run all tests
385
+ bundle exec rspec
386
+ # Run with coverage
387
+ bundle exec rspec --coverage
388
+ # Run specific test file
389
+ bundle exec rspec spec/sleeper_api/client_spec.rb
390
+ ```
391
+
392
+ ### Setup
393
+
394
+ ```shellscript
395
+ git clone https://github.com/eruity1/sleeper_api.git
396
+ cd sleeper_api
397
+ bundle install
398
+ ```
399
+
400
+ ### Code Quality
401
+
402
+ ```shellscript
403
+ # Run all checks (tests + linting)
404
+ bundle exec rake ci
405
+
406
+ # Run RuboCop
407
+ bundle exec rubocop
408
+
409
+ # Auto-fix RuboCop issues
410
+ bundle exec rubocop -a
411
+ ```
412
+
413
+ ### Contributing
414
+
415
+ 1. Fork the repository
416
+
417
+ 1. Create a feature branch (git checkout -b feature/amazing-feature)
418
+
419
+ 1. Commit your changes (git commit -m 'Add amazing feature')
420
+
421
+ 1. Push to the branch (git push origin feature/amazing-feature)
422
+
423
+ 1. Open a Pull Request
424
+
425
+ ### Development Dependencies
426
+
427
+ - rspec - Testing framework
428
+
429
+ - rubocop - Code style and quality
430
+
431
+ - simplecov - Test coverage
432
+
433
+ - webmock - HTTP request mocking
434
+
435
+ ## Requirements
436
+
437
+ - Ruby 2.6.0 or higher
438
+
439
+ - No external dependencies (HTTParty is bundled)
440
+
441
+ ## License
442
+
443
+ The gem is available as open source under the terms of the MIT License.
444
+
445
+ ## Changelog
446
+
447
+ See CHANGELOG.md for version history and updates.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require "rubocop/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ RuboCop::RakeTask.new
8
+
9
+ task ci: %i[rubocop spec]
10
+
11
+ task default: :ci
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "sleeper_api"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ require "irb"
11
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here