Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Refactor code and add documentation.

* Add documentation for the main Battlenet class
* Refactor some code in Battlenet into private methods and modify
  specs accordingly
* Move individual API modules into Battlenet::Modules parent module
  • Loading branch information...
commit 62a131c0263288aa362c24782d3d266773af8f36 1 parent 40753e9
@BinaryMuse authored
View
2  .gitignore
@@ -3,3 +3,5 @@
Gemfile.lock
pkg/*
.rvmrc
+.yardoc/*
+doc/*
View
8 .yardopts
@@ -0,0 +1,8 @@
+--readme README.md
+--markup markdown
+--markup-provider redcarpet
+--title "Battlenet Documentation"
+lib/**/*.rb
+-
+README.md
+LICENSE
View
6 Gemfile
@@ -2,3 +2,9 @@ source "http://rubygems.org"
# Specify your gem's dependencies in battlenet.gemspec
gemspec
+
+group :documentation do
+ gem "yard"
+ gem "redcarpet"
+ gem "github-markup"
+end
View
56 README.md
@@ -79,6 +79,62 @@ If you would like to contribute to the project, please feel free to do so. Just
Please do not change the contents of the `VERSION` file, or if you do, do so in a separate commit so that I can cherry-pick around it.
+Setting Up the Development Environment
+--------------------------------------
+
+The development environment is managed with Bundler.
+
+To install just the gems you need to hack on Battlenet and run the specs, run
+
+ bundle install --without documentation
+
+To install all development gems, including the ones used to generate documentation, run
+
+ bundle install
+
+To run the specs, run
+
+ rake
+
+Writing an Integration Test
+---------------------------
+
+High-level integration testing against the Community Platform API is handled via VCR. After the first time running a spec that hits the API, VCR saves the HTTP response in a fixture file and uses this file to run against in the future.
+
+Here's an example (the character integration specs):
+
+ it "fetches character data" do
+ VCR.use_cassette('character_mortawa') do
+ character = api.character 'nazjatar', 'mortawa'
+ character['level'].should == 85
+ end
+ end
+
+ it "fetches additional character data" do
+ VCR.use_cassette('character_mortawa_titles') do
+ character = api.character 'nazjatar', 'mortawa', :fields => 'titles'
+ character['titles'].find { |t| t['selected'] == true }['name'].should == "Twilight Vanquisher %s"
+ end
+ end
+
+ it "fetches characters with non-ASCII characters in their name" do
+ VCR.use_cassette('character_nonstandard_name') do
+ character = api.character 'nazjatar', 'Hikô'
+ character['level'].should == 85
+ end
+ end
+
+You should always wrap tests that hit the actual API in the `VCR.use_cassette` block, and the resulting fixture file should be checked in with your test.
+
+Building the Documentation
+--------------------------
+
+If you have the necessary gems installed (defined in the `documentation` group in the Gemfile), you can easily generate the documentation via
+
+ yard
+
+The generated documentation can be found in `doc/`; open `doc/index.html` to view it.
+
License
=======
View
156 lib/battlenet.rb
@@ -9,25 +9,75 @@
require 'battlenet/modules/arena'
require 'battlenet/modules/data'
+# Battlenet exposes the Blizzard Battle.net Community Platform API via an
+# easy-to-use interface.
+#
+# The main API class includes several Modules that define methods for collecting
+# specific API data. See the documentation for `Battlenet::Modules` for a list of
+# these modules.
+#
+# Specific details about the information returned from the API can be found at Blizzard's
+# [official Community Platform API documentation](https://blizzard.github.com/api-wow-docs/).
+#
+# @example Return basic information about a character named Cyaga from the US realm Nazjatar
+#
+# api = Battlenet.new :us
+# char = api.character 'Nazjatar', 'Cyaga'
+# puts char['level']
+# # => 85
+#
+# @example Return additional information about a character
+#
+# api = Battlenet.new :us
+# char = api.character 'Nazjatar', 'Cyaga', :fields => 'titles'
+# selected_title = char['titles'].find { |t| t['selected'] == true }
+# puts selected_title['name']
+# # => %s, Guardian of Cenarius
+#
+# @see Battlenet::Modules
+#
+# @author Brandon Tilley <brandon@brandontilley.com>
class Battlenet
+
+ # `Battlenet::Modules` is a namespace for modules that define methods that
+ # retrieve data from the Community Platform API. Methods for retrieving information
+ # about related resources are grouped in the same sub-Module. See documentation for
+ # the individual Modules for more information about the methods contained within.
+ module Modules; end
+
include HTTParty
- include Battlenet::Character
- include Battlenet::Guild
- include Battlenet::Realm
- include Battlenet::Auction
- include Battlenet::Item
- include Battlenet::Quest
- include Battlenet::Arena
- include Battlenet::Data
+ include Battlenet::Modules::Character
+ include Battlenet::Modules::Guild
+ include Battlenet::Modules::Realm
+ include Battlenet::Modules::Auction
+ include Battlenet::Modules::Item
+ include Battlenet::Modules::Quest
+ include Battlenet::Modules::Arena
+ include Battlenet::Modules::Data
class << self
+ # Whether or not to raise exceptions on non-200 responses from the API endpoint.
+ # A value of `false` causes exceptions to be raised. Defaults to `false`.
+ #
+ # @return [boolean]
attr_accessor :fail_silently
+
+ # The locale to use for API calls. Defaults to `nil`, which makes requests with
+ # no `locale` parameter set.
+ #
+ # @return [String|nil]
attr_accessor :locale
+
@fail_silently = false
@locale = nil
end
+ # Creates a new instance of the Battlenet API.
+ #
+ # @param region [Symbol] the region to perform API calls against.
+ # @param public [String|nil] the public key to use when signing requests
+ # @param private [String|nil] the private key to use when signing requests
def initialize(region = :us, public = nil, private = nil)
@public = public
@private = private
@@ -53,45 +103,75 @@ def initialize(region = :us, public = nil, private = nil)
self.class.base_uri @base_uri
end
- def fullpath(path)
- "#{@endpoint}#{path}"
- end
-
+ # Signs and performs an HTTP GET request. The request is only signed if a public and private
+ # key were provided during object instantiation.
+ #
+ # @param path (see #make_request)
+ # @param params (see #make_request)
+ # @return (see #make_request)
+ # @raise (see #make_request)
+ # @see (see #make_request)
def get(path, params = {})
make_request :get, path, params
end
- def make_request(verb, path, params = {})
- options = {}
- headers = {}
-
- if @public && @private
- now = Time.now
- signed = sign_request verb, path, now
- headers.merge!({
- "Authorization" => "BNET #{@public}:#{signed}",
- "Date" => now.httpdate
- })
+ private
+
+ # Returns the full URI for the given path based on the API endpoint set (varies by region).
+ #
+ # @return [String] the full URI for the path
+ def fullpath(path)
+ "#{@endpoint}#{path}"
end
- options[:headers] = headers unless headers.empty?
- options[:query] = params unless params.empty?
+ # Signs and performs an HTTP request. The request is only signed if a public and private
+ # key were provided during object instantiation.
+ #
+ # @param verb [Symbol] the HTTP verb to perform
+ # @param path [String] the path to GET
+ # @param params [Hash] options to be turned into query string parameters
+ # @return [Object] the response object from HTTParty
+ # @raise Exception if the response has a non-200 response and `Battlenet.fail_silently` is `false`
+ # @see http://rubydoc.info/github/jnunemaker/httparty/master/HTTParty/Response
+ # @private
+ def make_request(verb, path, params = {})
+ options = {}
+ headers = {}
- if Battlenet.locale
- options[:query] ||= {}
- options[:query].merge!({ :locale => Battlenet.locale })
- end
+ if @public && @private
+ now = Time.now
+ signed = sign_request verb, path, now
+ headers.merge!({
+ "Authorization" => "BNET #{@public}:#{signed}",
+ "Date" => now.httpdate
+ })
+ end
+
+ options[:headers] = headers unless headers.empty?
+ options[:query] = params unless params.empty?
+
+ if Battlenet.locale
+ options[:query] ||= {}
+ options[:query].merge!({ :locale => Battlenet.locale })
+ end
- response = self.class.send(verb, path, options)
+ response = self.class.send(verb, path, options)
- if response.code != 200 && Battlenet.fail_silently == false
- raise "Non-200 response: #{response.code}, #{response.body}"
+ if response.code != 200 && Battlenet.fail_silently == false
+ raise "Non-200 response: #{response.code}, #{response.body}"
+ end
+ response
end
- response
- end
- def sign_request(verb, path, time)
- auth = Battlenet::Authentication.new @private
- auth.sign verb, fullpath(path), time
- end
+ # Signs an HTTP request.
+ #
+ # @param verb [Symbol] the HTTP verb for the request being signed
+ # @param path [String] the path for the rquest being signed
+ # @param time [Time] the time to use when signing the request
+ # @return [String] value to be used as the final portion of the `Authorization` HTTP header
+ # @see Battlenet::Authentication
+ def sign_request(verb, path, time)
+ auth = Battlenet::Authentication.new @private
+ auth.sign verb, fullpath(path), time
+ end
end
View
14 lib/battlenet/modules/arena.rb
@@ -1,13 +1,15 @@
require 'uri'
class Battlenet
- module Arena
- def arena(realm, size, name, options = {})
- realm = URI.escape realm
- size = URI.escape size
- name = URI.escape name
+ module Modules
+ module Arena
+ def arena(realm, size, name, options = {})
+ realm = URI.escape realm
+ size = URI.escape size
+ name = URI.escape name
- get "/arena/#{realm}/#{size}/#{name}", options
+ get "/arena/#{realm}/#{size}/#{name}", options
+ end
end
end
end
View
22 lib/battlenet/modules/auction.rb
@@ -1,18 +1,20 @@
require 'uri'
class Battlenet
- module Auction
- def auction(realm, options = {})
- realm = URI.escape realm
+ module Modules
+ module Auction
+ def auction(realm, options = {})
+ realm = URI.escape realm
- get "/auction/data/#{realm}", options
- end
+ get "/auction/data/#{realm}", options
+ end
- def auction_data(realm, options = {})
- data = auction(realm, options)
- files = data["files"].first
- url = files["url"]
- get url
+ def auction_data(realm, options = {})
+ data = auction(realm, options)
+ files = data["files"].first
+ url = files["url"]
+ get url
+ end
end
end
end
View
12 lib/battlenet/modules/character.rb
@@ -1,12 +1,14 @@
require 'uri'
class Battlenet
- module Character
- def character(realm, name, options = {})
- realm = URI.escape realm
- name = URI.escape name
+ module Modules
+ module Character
+ def character(realm, name, options = {})
+ realm = URI.escape realm
+ name = URI.escape name
- get "/character/#{realm}/#{name}", options
+ get "/character/#{realm}/#{name}", options
+ end
end
end
end
View
44 lib/battlenet/modules/data.rb
@@ -1,31 +1,33 @@
class Battlenet
- module Data
- def character_races(options = {})
- get "/data/character/races", options
- end
+ module Modules
+ module Data
+ def character_races(options = {})
+ get "/data/character/races", options
+ end
- def character_classes(options = {})
- get "/data/character/classes", options
- end
+ def character_classes(options = {})
+ get "/data/character/classes", options
+ end
- def guild_rewards(options = {})
- get "/data/guild/rewards", options
- end
+ def guild_rewards(options = {})
+ get "/data/guild/rewards", options
+ end
- def guild_perks(options = {})
- get "/data/guild/perks", options
- end
+ def guild_perks(options = {})
+ get "/data/guild/perks", options
+ end
- def item_classes(options = {})
- get "/data/item/classes", options
- end
+ def item_classes(options = {})
+ get "/data/item/classes", options
+ end
- def character_achievements(options = {})
- get "/data/character/achievements", options
- end
+ def character_achievements(options = {})
+ get "/data/character/achievements", options
+ end
- def guild_achievements(options = {})
- get "/data/guild/achievements", options
+ def guild_achievements(options = {})
+ get "/data/guild/achievements", options
+ end
end
end
end
View
12 lib/battlenet/modules/guild.rb
@@ -1,12 +1,14 @@
require 'uri'
class Battlenet
- module Guild
- def guild(realm, name, options = {})
- realm = URI.escape realm
- name = URI.escape name
+ module Modules
+ module Guild
+ def guild(realm, name, options = {})
+ realm = URI.escape realm
+ name = URI.escape name
- get "/guild/#{realm}/#{name}", options
+ get "/guild/#{realm}/#{name}", options
+ end
end
end
end
View
8 lib/battlenet/modules/item.rb
@@ -1,7 +1,9 @@
class Battlenet
- module Item
- def item(id, options = {})
- get "/item/#{id}", options
+ module Modules
+ module Item
+ def item(id, options = {})
+ get "/item/#{id}", options
+ end
end
end
end
View
8 lib/battlenet/modules/quest.rb
@@ -1,7 +1,9 @@
class Battlenet
- module Quest
- def quest(id, options = {})
- get "/quest/#{id}", options
+ module Modules
+ module Quest
+ def quest(id, options = {})
+ get "/quest/#{id}", options
+ end
end
end
end
View
8 lib/battlenet/modules/realm.rb
@@ -1,7 +1,9 @@
class Battlenet
- module Realm
- def realm(options = {})
- get "/realm/status", options
+ module Modules
+ module Realm
+ def realm(options = {})
+ get "/realm/status", options
+ end
end
end
end
View
2  spec/integration/arena_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Battlenet::Arena do
+describe Battlenet::Modules::Arena do
let(:api) { Battlenet.new }
it "fetches arena data" do
View
2  spec/integration/auction_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Battlenet::Auction do
+describe Battlenet::Modules::Auction do
let(:api) { Battlenet.new }
it "fetches auction data" do
View
2  spec/integration/character_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Battlenet::Character do
+describe Battlenet::Modules::Character do
let(:api) { Battlenet.new }
it "fetches character data" do
View
2  spec/integration/data_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Battlenet::Data do
+describe Battlenet::Modules::Data do
let(:api) { Battlenet.new }
it "fetches character races data" do
View
2  spec/integration/guild_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Battlenet::Guild do
+describe Battlenet::Modules::Guild do
let(:api) { Battlenet.new }
it "fetches guild data" do
View
2  spec/integration/item_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Battlenet::Item do
+describe Battlenet::Modules::Item do
let(:api) { Battlenet.new }
it "fetches item data" do
View
2  spec/integration/quest_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Battlenet::Quest do
+describe Battlenet::Modules::Quest do
let(:api) { Battlenet.new }
it "fetches quest data" do
View
2  spec/integration/realm_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Battlenet::Realm do
+describe Battlenet::Modules::Realm do
let(:api) { Battlenet.new }
it "fetches realm data" do
View
66 spec/unit/battlenet_spec.rb
@@ -34,34 +34,17 @@
end
end
- context "#fullpath" do
- it "returns the full path for the resource" do
- api = Battlenet.new
- api.instance_variable_set(:@endpoint, "/test/testing")
- api.fullpath("/thetest").should == "/test/testing/thetest"
- end
- end
-
context "#get" do
let(:api) { Battlenet.new :us }
- it "delegates to #make_request" do
- api.should_receive(:make_request).with(:get, '/test', {})
- api.get '/test'
- end
- end
-
- context "#make_request" do
- let(:api) { Battlenet.new :us }
-
it "delegates to HTTParty" do
Battlenet.should_receive(:get).with('/test', {})
- api.make_request :get, '/test'
+ api.get '/test'
end
it "passes query string parameters to HTTParty" do
Battlenet.should_receive(:get).with('/test', {:query => {:fields => 'talents'}})
- api.make_request :get, '/test', :fields => 'talents'
+ api.get '/test', :fields => 'talents'
end
context "when the locale is set" do
@@ -75,7 +58,7 @@
it "adds the locale parameter to the query string" do
Battlenet.should_receive(:get).with('/test', {:query => {:fields => 'talents', :locale => 'es_ES'}})
- api.make_request :get, '/test', :fields => 'talents'
+ api.get '/test', :fields => 'talents'
end
end
@@ -92,13 +75,33 @@
it "signs the request if the public and private key are present" do
api.should_receive(:sign_request).with(:get, '/test', Time.now)
- api.make_request :get, '/test'
+ api.get '/test'
end
it "sets the Authorization and Date headers" do
Battlenet::Authentication.any_instance.stub(:sign).and_return("signature")
Battlenet.should_receive(:get).with('/test', :headers => { "Authorization" => "BNET public:signature", "Date" => Time.now.httpdate })
- api.make_request :get, '/test'
+ api.get '/test'
+ end
+ end
+
+ context "when the public and private key are set" do
+ let(:api) { Battlenet.new :us, 'public', 'private' }
+
+ before(:each) do
+ Timecop.freeze
+ end
+
+ after(:each) do
+ Timecop.return
+ end
+
+ it "signs the request using the full path" do
+ api.instance_variable_set(:@endpoint, "/tester/testing")
+ auth = mock(:auth)
+ Battlenet::Authentication.should_receive(:new).with('private').and_return(auth)
+ auth.should_receive(:sign).with(:get, '/tester/testing/test', Time.now)
+ api.get '/test'
end
end
@@ -134,23 +137,4 @@
end
end
end
-
- context "#sign_request" do
- let(:api) { Battlenet.new :us, 'public', 'private' }
-
- before(:each) do
- Timecop.freeze
- end
-
- after(:each) do
- Timecop.return
- end
-
- it "delegates to a Battlenet::Authentication" do
- auth = mock(:auth)
- Battlenet::Authentication.should_receive(:new).with('private').and_return(auth)
- auth.should_receive(:sign).with(:get, api.fullpath('/test'), Time.now)
- api.sign_request :get, '/test', Time.now
- end
- end
end
Please sign in to comment.
Something went wrong with that request. Please try again.