diff --git a/lib/nfl.rb b/lib/nfl.rb new file mode 100644 index 0000000..13b7e45 --- /dev/null +++ b/lib/nfl.rb @@ -0,0 +1,90 @@ +module SportsDataApi + module Nfl + + class Exception < ::Exception + end + + class Season + attr_reader :year, :type, :weeks + + def initialize(xml) + @weeks = [] + if xml.is_a? Nokogiri::XML::NodeSet + @year = xml.first["season"].to_i + @type = xml.first["type"].to_sym + @weeks = xml.first.xpath("week").map do |week_xml| + Week.new(week_xml) + end + end + end + end + + class Week + attr_reader :number, :games + + def initialize(xml) + @games = [] + if xml.is_a? Nokogiri::XML::Element + @number = xml["week"].to_i + @games = xml.xpath("game").map do |game_xml| + Game.new(game_xml) + end + end + end + end + + class Game + attr_reader :id, :scheduled, :home, :away, :status + + def initialize(xml) + if xml.is_a? Nokogiri::XML::Element + @id = xml["id"] + @scheduled = Time.parse xml["scheduled"] + @home = xml["home"] + @away = xml["away"] + @status = xml["status"] + end + end + end + + BASE_URL = "http://api.sportsdatallc.org/nfl-%{access_level}%{version}" + SEASONS = [:PRE, :REG, :PST] + + def self.schedule(year, season, version = 1) + base_url = BASE_URL % { access_level: SportsDataApi.access_level, version: version } + season = season.to_s.upcase.to_sym + raise SportsDataApi::Nfl::Exception.new("#{season} is not a valid season") unless season?(season) + url = "#{base_url}/#{year}/#{season}/schedule.xml" + + begin + # Perform the request + response = RestClient.get(url, params: { api_key: SportsDataApi.key }) + + # Load the XML and ignore namespaces in Nokogiri + schedule = Nokogiri::XML(response.to_s) + schedule.remove_namespaces! + + return Season.new(schedule.xpath("/season")) + rescue RestClient::Exception => e + message = if e.response.headers.key? :x_server_error + JSON.parse(error_json, { symbolize_names: true })[:message] + elsif e.response.headers.key? :x_mashery_error_code + e.response.headers[:x_mashery_error_code] + else + "The server did not specify a message" + end + raise SportsDataApi::Exception, message + end + end + + ## + # Check if the requested season is a valid + # NFL season type. + # + # The only valid types are: :PRE, :REG, :PST + def self.season?(season) + SEASONS.include?(season) + end + + end +end diff --git a/lib/sports_data_api.rb b/lib/sports_data_api.rb index 7c55f7e..7b57fe9 100644 --- a/lib/sports_data_api.rb +++ b/lib/sports_data_api.rb @@ -1,5 +1,17 @@ require "sports_data_api/version" +require "nokogiri" +require "rest_client" module SportsDataApi - # Your code goes here... + def self.key + "garbage" + end + + def self.access_level + "t" + end + + autoload :Nfl, File.join(File.dirname(__FILE__), 'nfl') + + class Exception < ::Exception; end end diff --git a/spec/lib/nfl_spec.rb b/spec/lib/nfl_spec.rb new file mode 100644 index 0000000..376ba94 --- /dev/null +++ b/spec/lib/nfl_spec.rb @@ -0,0 +1,67 @@ +require 'spec_helper' + +describe SportsDataApi::Nfl do + let(:key) { "1234567890abcdef" } + describe ".schedule" do + it "creates a valid Sports Data LLC url" do + SportsDataApi.stub(:key).and_return(key) + RestClient.stub(:get).with("http://api.sportsdatallc.org/nfl-t1/2012/REG/schedule.xml", params: { api_key: key }).and_return(schedule_xml) + subject.schedule(2012, :REG) + end + it "creates a SportsDataApi::Exception when there is no response from the api" do + error = RestClient::ResourceNotFound.new + error.stub_chain(:response, :headers).and_return(Hash.new) + SportsDataApi.stub(:key).and_return(key) + RestClient.stub(:get).and_raise(error) + expect { subject.schedule(2999, :REG) }.to raise_error(SportsDataApi::Exception) + end + + describe "returned data structures" do + before(:each) { RestClient.stub(:get).and_return(schedule_xml) } + let(:season) { SportsDataApi::Nfl.schedule(2012, :REG) } + describe "SportsDataApi::Nfl::Season" do + subject { season } + it { should be_an_instance_of(SportsDataApi::Nfl::Season) } + its(:year) { should eq 2012 } + its(:type) { should eq :REG } + its(:weeks) { should have(17).weeks } + end + describe "SportsDataApi::Nfl::Week" do + subject { season.weeks.first } + it { should be_an_instance_of(SportsDataApi::Nfl::Week) } + its(:number) { should eq 1 } + its(:games) { should have(16).games } + end + describe "SportsDataApi::Nfl::Game" do + subject { season.weeks.first.games.first } + it { should be_an_instance_of(SportsDataApi::Nfl::Game) } + its(:id) { should eq "8c0bce5a-7ca2-41e5-9838-d1b8c356ddc3" } + its(:scheduled) { should eq Time.new(2012, 9, 5, 19, 30, 00, "-05:00") } + its(:home) { should eq "NYG" } + its(:away) { should eq "DAL" } + its(:status) { should eq "closed" } + end + end + end + + describe ".season?" do + context :PRE do + it { subject.season?(:PRE).should be_true } + end + context :REG do + it { subject.season?(:REG).should be_true } + end + context :PST do + it { subject.season?(:PST).should be_true } + end + context :pre do + it { subject.season?(:pre).should be_false } + end + context :reg do + it { subject.season?(:reg).should be_false } + end + context :pst do + it { subject.season?(:pst).should be_false } + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 12ee4af..9ae1d05 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -6,6 +6,15 @@ # loaded once. # # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration + +def load_xml(filename) + File.read("#{File.dirname(__FILE__)}/xml/#{filename}.xml") +end + +def schedule_xml + load_xml("schedule") +end + RSpec.configure do |config| config.treat_symbols_as_metadata_keys_with_true_values = true config.run_all_when_everything_filtered = true