Skip to content

Commit

Permalink
Add core library and specs
Browse files Browse the repository at this point in the history
  • Loading branch information
Brandon Tilley committed Apr 15, 2011
1 parent 69ffe83 commit 824dbdd
Show file tree
Hide file tree
Showing 11 changed files with 352 additions and 4 deletions.
5 changes: 2 additions & 3 deletions lib/battlenet.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
module Battlenet
# Your code goes here...
end
require 'battlenet/api'
require 'battlenet/api/realm'
11 changes: 11 additions & 0 deletions lib/battlenet/adapter/abstract_adapter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Battlenet
module Adapter
class NotImplementedException < Exception; end

class AbstractAdapter
def get(url)
raise NotImplementedException.new("Please implement #get in your adapter")
end
end
end
end
14 changes: 14 additions & 0 deletions lib/battlenet/adapter/net_http.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require 'battlenet/adapter/abstract_adapter'
require 'net/http'

module Battlenet
module Adapter
class NetHTTP < AbstractAdapter
def get(url)
uri = URI.parse url
response = Net::HTTP.get_response uri
[response.code.to_i, response.body]
end
end
end
end
13 changes: 13 additions & 0 deletions lib/battlenet/adapter/typhoeus.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require 'battlenet/adapter/abstract_adapter'
require 'typhoeus'

module Battlenet
module Adapter
class Typhoeus < AbstractAdapter
def get(url)
response = ::Typhoeus::Request.get url
[response.code.to_i, response.body]
end
end
end
end
47 changes: 47 additions & 0 deletions lib/battlenet/adapter_manager.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module Battlenet
module Adapter; end

module AdapterManager
class InvalidAdapter < Exception; end

extend self

@adapters = {
:net_http => "NetHTTP",
:typhoeus => "Typhoeus"
}

def adapters
@adapters
end

def fetch(adapter_name)
unless adapters.include? adapter_name
raise InvalidAdapter.new("#{adapter_name.to_s} is not a valid adapter")
end

adapter_class = adapters[adapter_name]
adapter = load_adapter adapter_name, adapter_class
end

private

def load_adapter(adapter_name, klass_name)
begin
klass = Battlenet::Adapter.const_get("#{klass_name}", false)
rescue NameError
begin
adapter_file = "battlenet/adapter/#{adapter_name.to_s}"
require adapter_file
klass = Battlenet::Adapter.const_get("#{klass_name}", false)
rescue LoadError
raise InvalidAdapter.new("adapter #{klass_name} does not exist, and file #{adapter_file} does not exist")
rescue NameError
raise InvalidAdapter.new("expected #{adapter_file} to define Battlenet::Adapter::#{klass_name}")
end
end

return klass.new
end
end
end
73 changes: 73 additions & 0 deletions lib/battlenet/api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
require 'battlenet/adapter_manager'
require 'cgi'
require 'json'

module Battlenet
module API
class APIError < Exception
attr_reader :code, :body
def initialize(code, body)
@code = code
@body = body
end

def to_s
"Status: #{code} Body: #{body}"
end
end

extend self

@config = {
:indifferent_hashes => true,
:http_adapter => :net_http,
:region => :us
}

def set_option(setting, value)
@config[setting] = value
end

def get_option(setting, default = nil)
@config[setting] || default
end

def adapter
@adapter ||= Battlenet::AdapterManager.fetch get_option(:http_adapter)
end

def base_url
region = get_option(:region, :us).to_s
"http://#{region}.battle.net/api/wow"
end

def make_api_call(path, query = {})
query_string = query.empty? ? '' : make_query_string(query)
url = base_url
url << (path.start_with?('/') ? '' : '/')
url << path
url << query_string
code, body = get url
raise APIError.new(code, body) unless code == 200
JSON::parse body
end

def get(url)
adapter.get(url)
end

def make_query_string(query)
query_string = "?"
query.each do |key, value|
case value
when String
query_string << "#{key}=#{CGI.escape value}&"
when Array
value.each { |v| query_string << "#{key}=#{CGI.escape v}&" }
end
end

query_string.chomp("&").chomp("?")
end
end
end
14 changes: 14 additions & 0 deletions lib/battlenet/api/realm.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module Battlenet
module API
module Realm
extend self

@api = Battlenet::API

def status(options = {})
data = @api.make_api_call 'realm/status', options
data["realms"]
end
end
end
end
2 changes: 1 addition & 1 deletion lib/battlenet/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Battlenet
VERSION = "0.0.0"
VERSION = "0.1.0"
end
15 changes: 15 additions & 0 deletions spec/abstract_adapter_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require 'battlenet/adapter/abstract_adapter'

describe Battlenet::Adapter::AbstractAdapter do
it "has a method 'get'" do
subject.should respond_to :get
end

context "#get" do
it "is abstract" do
lambda {
subject.get('fake_url')
}.should raise_error Battlenet::Adapter::NotImplementedException
end
end
end
47 changes: 47 additions & 0 deletions spec/adapter_manager_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
require 'battlenet/adapter_manager'

describe Battlenet::AdapterManager do
it "has a list of adapters" do
subject.adapters.should be_a Hash
end

context "#fetch" do
it "raises an exception if an invalid adapter is passed" do
lambda {
subject.fetch(:some_fake_adapter)
}.should raise_error Battlenet::AdapterManager::InvalidAdapter
end

it "requires a library file based on the adapter chosen if the class doesn't exist" do
subject.should_receive(:require).once.with('battlenet/adapter/net_http')
begin
subject.fetch(:net_http)
rescue Battlenet::AdapterManager::InvalidAdapter
# expected
end
end

it "raises an error if the adapter class doesn't exist and the library can't be loaded" do
old_adapters = subject.adapters
subject.instance_variable_set(:@adapters, {:adapter => "SuperAdapter"})

lambda {
subject.fetch(:adapter)
}.should raise_error Battlenet::AdapterManager::InvalidAdapter, /does not exist/

subject.instance_variable_set(:@adapters, old_adapters)
end

it "raises an error if the adapter library is loaded but doesn't define the adapter class" do
subject.should_receive(:require).once.with('battlenet/adapter/net_http').and_return(nil)
lambda {
subject.fetch(:net_http)
}.should raise_error Battlenet::AdapterManager::InvalidAdapter, /expected (.*) to define/
end

it "returns an instance of the adapter" do
adapter = subject.fetch(:net_http)
adapter.should be_a Battlenet::Adapter::NetHTTP
end
end
end
115 changes: 115 additions & 0 deletions spec/api_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
require 'battlenet/api'

describe Battlenet::API do
context "configuration management" do
it "defauls to nil when getting an unset option" do
subject.get_option(:a_fake_setting_i_made_up).should be_nil
end

it "returns a passed value if an option is not set" do
subject.get_option(:a_fake_setting_i_made_up, "value").should == "value"
end

it "allows setting and retrieving a value" do
subject.set_option(:a_fake_setting_i_made_up, "my_value")
subject.get_option(:a_fake_setting_i_made_up).should == "my_value"
end
end

context "default configuration" do
it "defaults to the US region" do
subject.get_option(:region).should == :us
end

it "defaults to indifferent hashes" do
subject.get_option(:indifferent_hashes).should == true
end

it "defaults to the Net::HTTP library" do
subject.get_option(:http_adapter).should == :net_http
end
end

context "HTTP adapter" do
it "retrieval is via the Adapter module" do
adapter = subject.get_option(:http_adapter)
Battlenet::AdapterManager.should_receive(:fetch).with(adapter)
subject.send(:adapter)
end

it "caches its adapter" do
mock_adapter = mock("adapter")
Battlenet::AdapterManager.should_receive(:fetch).with(any_args()).and_return(mock_adapter)
adapter = subject.send(:adapter)
subject.set_option(:http_adapter, :fake_adapter)
subject.send(:adapter).should == mock_adapter
end
end

it "returns a base URL dependant on the region" do
old_region = subject.get_option(:region)
subject.set_option(:region, :eu)
subject.send(:base_url).should == "http://eu.battle.net/api/wow"
subject.set_option(:region, old_region)
end

context "#make_query_string" do
it "produces an empty string with an empty hash" do
str = subject.send(:make_query_string, {})
str.should be_empty
end

it "starts with a question mark" do
str = subject.send(:make_query_string, {:first => "value1", :second => "value2"})
str.start_with?("?").should == true
end

it "parses a basic hash" do
str = subject.send(:make_query_string, {:first => "value1", :second => "value2"})
str.should == "?first=value1&second=value2"
end

it "parses a hash with array values" do
str = subject.send(:make_query_string, {:first => ["value1", "value2"], :second => "value3"})
str.should == "?first=value1&first=value2&second=value3"
end

it "escapes values" do
str = subject.send(:make_query_string, {:first => ["value1", "va&lue2"], :second => "val&ue3"})
str.should == "?first=value1&first=va%26lue2&second=val%26ue3"
end
end

context "#make_api_call" do
it "makes a get call with the correct URL" do
subject.should_receive(:get).once.with("http://us.battle.net/api/wow/my/path").and_return([200, '{"testing": "value"}'])
subject.send(:make_api_call, '/my/path')
end

it "adds a slash to the path if necessary" do
subject.should_receive(:get).once.with("http://us.battle.net/api/wow/my/path").and_return([200, '{"testing": "value"}'])
subject.send(:make_api_call, 'my/path')
end

it "converts the second parameter into a query string" do
options = {:first => ["value1", "va&lue2"], :second => "val&ue3"}
query_string = subject.send(:make_query_string, options)
subject.should_receive(:get).once.with("http://us.battle.net/api/wow/my/path#{query_string}").and_return([200, '{"testing": "value"}'])
subject.send(:make_api_call, 'my/path', options)
end

it "raises an exception on an error" do
subject.adapter.should_receive(:get).and_return([404, "not found"])
lambda {
subject.make_api_call('my/path')
}.should raise_error Battlenet::API::APIError, /404 (.*) not found/
end

it "returns the data as JSON" do
subject.adapter.should_receive(:get).and_return([200, '{"testing": "value"}'])
data = subject.make_api_call('my/path')
data.should be_a Hash
data["testing"].should == "value"
end
end
end

0 comments on commit 824dbdd

Please sign in to comment.