README.md
<img style='width:100%' src='./readme/logo-hoopscrape.png' />
hoopscrape is not associated with ESPN or the NBA
[![Gem Version](https://badge.fury.io/rb/hoopscrape.svg)](https://badge.fury.io/rb/hoopscrape)
[![Code Climate](https://codeclimate.com/github/meissadia/hoopscrape/badges/gpa.svg)](https://codeclimate.com/github/meissadia/hoopscrape)
[![Build Status](https://travis-ci.org/meissadia/hoopscrape.svg?branch=master)](https://travis-ci.org/meissadia/hoopscrape)
[![Test Coverage](https://codeclimate.com/github/meissadia/hoopscrape/badges/coverage.svg)](https://codeclimate.com/github/meissadia/hoopscrape/coverage)
## Table of Contents
+ [Introduction](#introduction)
+ [Installation](#installation)
+ [Rails](#rails)
+ [Manual](#manual)
+ [Arrays, Hashes or Structs](#arrays-hashes-or-structs)
+ [Working With Multiple Formats](#working-with-multiple-formats)
+ [Default format](#default-format)
+ [Same data using Hashes](#same-data-using-hashes)
+ [Same data using Structs](#same-data-using-structs)
+ [Customize Field Names for Hash and Struct Conversion](#customize-field-names-for-hash-and-struct-conversion)
+ [Default As Template](#default-as-template)
+ [Overwrite Default](#overwrite-default)
+ [Working with Navigators](#working-with-navigators)
+ [Navigator Methods](#navigator-methods)
+ [Data Access](#data-access)
+ [NBA Team List](#nba-team-list)
+ [Boxscore](#boxscore)
+ [Player Data](#player-data)
+ [Team Data](#team-data)
+ [Roster](#roster)
+ [Player](#player)
+ [Schedule](#schedule)
+ [Past Schedule Games as Structs](#past-schedule-games-as-structs)
+ [Future Schedule Games as Structs](#future-schedule-games-as-structs)
+ [Select a specific Season Type](#select-a-specific-season-type)
+ [Select Historic Schedule data](#select-historic-schedule-data)
+ [Chaining it all together](#chaining-it-all-together)
+ [Documentation](#documentation)
+ [Requirements](#requirements)
+ [Ruby version](#ruby-version)
+ [Dependencies](#dependencies)
+ [Testing](#testing)
+ [Contributing](#contributing)
## Introduction
The hoopscrape Ruby gem is a scraper for NBA data.
It provides a number of ways to simplify data interaction, including :
+ Structs - Intuitively access data via dot notation.
+ Hashes - Pass data directly to ActiveRecord CRUD methods for easy database interaction.
+ String arrays - Raw data for you to manipulate as you see fit.
Version 1.1
+ Fixed security vulnerabilities with Nokogiri and Rubocop. Unfortunately, this means HoopScrape now requires Ruby >= 2.1.0
+ Updated test suite.
+ Please report any [issues] you encounter!
## Installation
#### Rails
In your application's Gemfile, include :
```
gem 'hoopscrape'
```
In your project dir, execute :
```
$ bundle install
```
#### Manual
```
$ gem install hoopscrape
```
## Arrays, Hashes or Structs
If you intend to work with a single format, you can specify it at initialization. When working with multiple formats you should start with the default and convert as necessary using [Array#to_structs] or [Array#to_hashes].
```ruby
require 'hoopscrape'
hs = HoopScrape.new # String Arrays
hs_h = HoopScrape.new(format: :to_hashes) # Hash Arrays
hs_s = HoopScrape.new(format: :to_structs) # Struct Arrays
```
#### Working With Multiple Formats
Arrays can easily be converted to Hashes or Structs
##### Default format
```ruby
hs = HoopScrape.new
bs = es.boxscore(400828991) # Return an NbaBoxscore object
stats = bs.homePlayers # Returns a multidimensional array of Home Player stats
stats[4][2] # Player Name # => 'R. Hood'
stats[4][20] # Player Points # => '30'
```
##### Same data using Hashes
```ruby
s_hashes = stats.to_hashes # Returns array of Hashes
s_hashes[4][:name] # Player Name # => 'R. Hood'
s_hashes[4][:points] # Player Points # => '30'
```
##### Same data using Structs
```ruby
s_structs = stats.to_structs # Returns array of Structs
s_structs[4].name # Player Name # => 'R. Hood'
s_structs[4].points # Player Points # => '30'
```
#### Customize Field Names for Hash and Struct Conversion
The [Array#to_hashes] and [Array#to_structs] methods can be passed an array of Symbols
to use in place of the default field names.
```ruby
team_list = HoopScrape.teamList
team_list_s = t.to_structs([:abbrev, :long_team_name, :div, :conf]) # New Field Names
team_list_s.last.long_team_name # => 'Utah Jazz'
```
Defaults are defined in the [SymbolDefaults] module.
You can overwrite them or use them as templates, replacing individual symbols using
the [Array#change_sym!] method.
##### Default As Template
`Safe method`
```ruby
my_names = S_ROSTER.dup.change_sym!(:p_name, :full_name).change_sym!(:salary, :crazy_money)
players = HoopScrape.roster('CLE').players.to_structs(my_names)
players[3].full_name # => 'LeBron James'
players[3].crazy_money # => '22970500'
```
##### Overwrite Default
`Note: Changes affect all instances of hoopscrape`
```ruby
S_TEAM # => [:team, :name, :division, :conference]
S_TEAM.replace [:short, :long, :div, :conf]
t = HoopScrape.teamList.to_structs
t.first.short # => 'BOS'
t.first.long # => 'Boston Celtics'
```
## Working with Navigators
Table data is wrapped in a [Navigator] class which provides helper methods for moving through the table. The type of object the Navigator returns matches the format provided at hoopscrape instantiation.
Note: Data converted using [Array#to_structs] or [Array#to_hashes] is not wrapped in a Navigator.
### Navigator Methods
```ruby
# <Navigator> A Navigator for Home Player Stats Table
navigator = HoopScrape.boxscore(400878158).homePlayers
navigator[] # Array<Object> Returns the underlying Array of the Navigator
navigator[5] # <Object> 6th row of data
navigator.size # <Fixnum> Number of table rows
navigator.first # <Object> Access the first data row
navigator.last # <Object> Access the last data row
navigator.next # <Object> Access the next data row (nil if there is no more data)
navigator.curr # <Object> Access the current data row (nil at initialization)
navigator.prev # <Object> Access the previous data row (nil if there is no more data)
```
## Data Access
### NBA Team List
```ruby
hs = HoopScrape.new
team_list = es.teamList # multidimensional array of Team info
team_list.last # => ['UTA', 'Utah Jazz', 'Northwest', 'Western']
team_list.last[0] # => 'UTA'
team_list.last[1] # => 'Utah Jazz'
team_list.last[2] # => 'Northwest'
team_list.last[3] # => 'Western'
```
### Boxscore
Boxscore #homePlayers, #awayPlayers return a [Navigator]
```ruby
hs = HoopScrape.new(format: :to_structs)
bs = es.boxscore(400875892) # Return an NbaBoxscore object
bs.id # <String> Boxscore ID # => '400875892'
bs.gameDate # <String> Game DateTime # => '2016-05-07 00:00:00'
bs.homeName # <String> Full Team Name
bs.homeScore # <String> Team Score
bs.homeTotals # <Object> Access the cumulative team totals
bs.homePlayers # <Navigator> A Navigator for Home Player Stats Table
bs.awayName # <String> Full Team Name
bs.awayScore # <String> Team Score
bs.awayTotals # <Object> Access the cumulative team totals
bs.awayPlayers # <Navigator> A Navigator for Home Player Stats Table
```
##### Player Data
```ruby
wade = bs.homePlayers[4] # <Object> of data for Row 5
wade.team # <String> Team ID # => 'MIA'
wade.id # <String> Player ID # => '1987'
wade.name # <String> Short Name # => 'D. Wade'
wade.position # <String> Position # => 'SG'
wade.minutes # <String> Minutes # => '36'
wade.fgm # <String> Shots Made # => '13'
wade.fga # <String> Shots Attempted # => '25'
wade.tpm # <String> 3P Made # => '4'
wade.tpa # <String> 3P Attempted # => '6'
wade.ftm # <String> Freethrows Made # => '8'
wade.fta # <String> Freethrows Att. # => '8'
wade.oreb # <String> Offensive Reb. # => '1'
wade.dreb # <String> Defensive Reb. # => '7'
wade.rebounds # <String> Total Rebounds # => '8'
wade.assists # <String> Assists # => '4'
wade.steals # <String> Steals # => '0'
wade.blocks # <String> Blocks # => '0'
wade.tos # <String> Turnovers # => '4'
wade.fouls # <String> Personal Fouls # => '1'
wade.plusminus # <String> Plus/Minus # => '-8'
wade.points # <String> Points # => '38'
wade.starter # <String> Starter? # => 'true'
```
##### Team Data
```ruby
miami = bs.homeTotals # <Object> Access the team totals
miami.team
miami.fgm
miami.fga
miami.tpm
miami.tpa
miami.ftm
miami.fta
miami.oreb
miami.dreb
miami.rebounds
miami.assists
miami.steals
miami.blocks
miami.turnovers
miami.fouls
miami.points
```
### Roster
Roster #players is a [Navigator].
```ruby
roster = es.roster('UTA')
r_hash = es.roster('UTA', format: :to_hashes) # Pre-format players data
players = roster.players # Returns multidimensional array of Roster info
coach = roster.coach # Coach Name # => 'Quinn Snyder'
# Roster as an array of objects
players = players.to_structs # Returns array of Structs
players[2].team # Team ID # => 'UTA'
players[2].jersey # Jersey Number # => '11'
players[2].name # Name # => 'Alec Burks'
players[2].id # ID # => '6429'
players[2].position # Position # => 'SG'
players[2].age # Age # => '24'
players[2].height_ft # Height (ft) # => '6'
players[2].height_in # Height (in) # => '6'
players[2].salary # Salary # => '9463484'
players[2].weight # Weight # => '214'
players[2].college # College # => 'Colorado'
players[2].salary # Salary # => '9463484'
```
### Player
```ruby
player = es.player(2991473) # Returns an NbaPlayer object
player.name #=> "Anthony Bennett"
player.age #=> "23"
player.weight #=> "245"
player.college #=> "UNLV"
player.height_ft #=> "6"
player.height_in #=> "8"
```
### Schedule
Schedule #allGames, #pastGames, #futureGames return a [Navigator]
```ruby
schedule = es.schedule('UTA') # Gets latest available year and season type data
schedule = es.schedule('LAC', format: :to_structs) # Pre-format data (:to_hashes / :to_structs)
schedule = es.schedule('SAS', year: 2005) # Gets historical data for season ending in 2005
schedule = es.schedule('POR', season: 1) # Get specific season type (1 Pre/ 2 Regular/3 Post)
schedule.nextGame # <Object> Next unplayed game info
schedule.lastGame # <Object> Previously completed game info
schedule.nextTeamId # <String> Team ID of next opponent # => 'OKC'
schedule.nextGameId # <Fixnum> Index of next unplayed game
schedule.pastGames[] # Completed Games : [Object]
schedule.futureGames[] # Upcoming Games : [Object]
past = schedule.pastGames # Completed Games : <Navigator>
future = schedule.futureGames # Upcoming Games : <Navigator>
```
##### Past Schedule Games as Structs
```ruby
past = schedule.pastGames # Completed Games : <Navigator>
game = past.next # <Object> Game info
game.team # Team ID
game.game_num # Game # in Season
game.date # Game Date
game.home # Home?
game.opponent # Opponent ID
game.win # Win?
game.team_score # Team Score
game.opp_score # Opponent Score
game.boxscore_id # Boxscore ID
game.wins # Team Win Count
game.losses # Team Loss Count
game.datetime # Game DateTime
game.season_type # Season Type
```
##### Future Schedule Games as Structs
```ruby
future = schedule.futureGames # Upcoming Games : <Navigator>
game = future.next # <Object> Game info
game.team # Team ID
game.game_num # Game # in Season
game.date # Game Date
game.home # Home?
game.opponent # Opponent ID
game.time # Game Time
game.win # Win?
game.tv # Game on TV?
game.opp_score # Opponent Score
game.datetime # Game DateTime
game.season_type # Season Type
```
##### Select a specific Season Type
```ruby
preseason = es.schedule('BOS', season: 1) # Get Preseason schedule
regular = es.schedule('NYK', season: 2) # Get Regular schedule
playoffs = es.schedule('OKC', season: 3) # Get Playoff schedule
```
##### Select Historic Schedule data
The year parameter should correspond to the year in which the season ended.
```ruby
schedule = es.schedule('SAS', year: 2005) # Data for 2004-05 Season
```
## Chaining it all together
```ruby
# Get a Boxscore from a past game
HoopScrape.schedule('OKC', season: 2).allGames[42].boxscore(:to_structs).awayPlayers.first.name
# Get a Roster from a Team ID
HoopScrape.boxscore(400827977).homeTotals[0].roster(:to_hashes).players.first[:name]
'cle'.roster(:to_structs).players.next.position
# Get a Schedule from a Team ID
HoopScrape.teamList.last[0].schedule(:to_hashes).lastGame.boxscore
'gsw'.schedule(:to_structs).lastGame.boxscore
```
## Documentation
Available on [RubyDoc.info] or locally:
```
$ yard doc
$ yard server
```
## Requirements
### Ruby version
- Ruby >= 1.9.3
### Dependencies
- Nokogiri ~> 1.6
- Rake
- minitest
## Testing
```
$ rake
```
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/meissadia/hoopscrape
<br>
<br>
<br>
© 2016 Meissa Dia
[CHANGELOG]: ./CHANGELOG.md
[RubyDoc.info]: http://www.rubydoc.info/gems/hoopscrape/1.0.0
[Navigator]: http://www.rubydoc.info/gems/hoopscrape/1.0.0/Navigator
[Array#to_structs]: http://www.rubydoc.info/gems/hoopscrape/1.0.0/Array#to_structs-instance_method
[Array#to_hashes]: http://www.rubydoc.info/gems/hoopscrape/1.0.0/Array#to_hashes-instance_method
[Array#change_sym!]: http://www.rubydoc.info/gems/hoopscrape/1.0.0/Array#change_sym%21-instance_method
[SymbolDefaults]: ./lib/hoopscrape/SymbolDefaults.rb
[issues]: https://github.com/meissadia/hoopscrape/issues