luchadeer 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rspec +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +60 -0
- data/LICENSE +20 -0
- data/README.md +58 -0
- data/Rakefile +1 -0
- data/lib/luchadeer.rb +32 -0
- data/lib/luchadeer/api.rb +51 -0
- data/lib/luchadeer/api/characters.rb +11 -0
- data/lib/luchadeer/api/companies.rb +11 -0
- data/lib/luchadeer/api/concepts.rb +11 -0
- data/lib/luchadeer/api/franchises.rb +11 -0
- data/lib/luchadeer/api/games.rb +11 -0
- data/lib/luchadeer/api/locations.rb +11 -0
- data/lib/luchadeer/api/objects.rb +11 -0
- data/lib/luchadeer/api/people.rb +11 -0
- data/lib/luchadeer/api/videos.rb +11 -0
- data/lib/luchadeer/character.rb +13 -0
- data/lib/luchadeer/client.rb +73 -0
- data/lib/luchadeer/company.rb +13 -0
- data/lib/luchadeer/concept.rb +13 -0
- data/lib/luchadeer/error.rb +50 -0
- data/lib/luchadeer/franchise.rb +13 -0
- data/lib/luchadeer/game.rb +13 -0
- data/lib/luchadeer/location.rb +13 -0
- data/lib/luchadeer/middleware/follow_redirects.rb +50 -0
- data/lib/luchadeer/middleware/parse_api_error.rb +18 -0
- data/lib/luchadeer/middleware/parse_http_error.rb +18 -0
- data/lib/luchadeer/middleware/parse_json.rb +28 -0
- data/lib/luchadeer/object.rb +13 -0
- data/lib/luchadeer/person.rb +13 -0
- data/lib/luchadeer/resource.rb +37 -0
- data/lib/luchadeer/search.rb +45 -0
- data/lib/luchadeer/version.rb +3 -0
- data/lib/luchadeer/video.rb +13 -0
- data/luchadeer.gemspec +27 -0
- data/spec/luchadeer/api/characters_spec.rb +28 -0
- data/spec/luchadeer/api/companies_spec.rb +28 -0
- data/spec/luchadeer/api/concepts_spec.rb +28 -0
- data/spec/luchadeer/api/franchises_spec.rb +28 -0
- data/spec/luchadeer/api/games_spec.rb +28 -0
- data/spec/luchadeer/api/locations_spec.rb +28 -0
- data/spec/luchadeer/api/objects_spec.rb +28 -0
- data/spec/luchadeer/api/people_spec.rb +28 -0
- data/spec/luchadeer/api/videos_spec.rb +28 -0
- data/spec/luchadeer/api_spec.rb +101 -0
- data/spec/luchadeer/character_spec.rb +27 -0
- data/spec/luchadeer/client_spec.rb +54 -0
- data/spec/luchadeer/company_spec.rb +27 -0
- data/spec/luchadeer/concept_spec.rb +27 -0
- data/spec/luchadeer/error_spec.rb +53 -0
- data/spec/luchadeer/franchise_spec.rb +27 -0
- data/spec/luchadeer/game_spec.rb +27 -0
- data/spec/luchadeer/location_spec.rb +27 -0
- data/spec/luchadeer/object_spec.rb +27 -0
- data/spec/luchadeer/person_spec.rb +27 -0
- data/spec/luchadeer/resource_spec.rb +70 -0
- data/spec/luchadeer/search_spec.rb +69 -0
- data/spec/luchadeer/video_spec.rb +27 -0
- data/spec/luchadeer_spec.rb +24 -0
- data/spec/spec_helper.rb +5 -0
- metadata +216 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
module Luchadeer
|
2
|
+
class Error < StandardError
|
3
|
+
|
4
|
+
# http://www.giantbomb.com/api/documentation#toc-0-0
|
5
|
+
module Codes
|
6
|
+
OK = 1
|
7
|
+
INVALID_API_KEY = 100
|
8
|
+
OBJECT_NOT_FOUND = 101
|
9
|
+
URL_FORMAT_ERROR = 102
|
10
|
+
FILTER_ERROR = 104
|
11
|
+
SUBSCRIBER_ONLY = 105
|
12
|
+
end
|
13
|
+
|
14
|
+
# Used for bespoke status codes returned in the response body
|
15
|
+
def self.api_errors
|
16
|
+
{
|
17
|
+
Codes::INVALID_API_KEY => Luchadeer::Error::InvalidAPIKey,
|
18
|
+
Codes::OBJECT_NOT_FOUND => Luchadeer::Error::NotFound,
|
19
|
+
Codes::URL_FORMAT_ERROR => Luchadeer::Error::URLFormatError,
|
20
|
+
Codes::FILTER_ERROR => Luchadeer::Error::FilterError,
|
21
|
+
Codes::SUBSCRIBER_ONLY => Luchadeer::Error::SubscriberOnly
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
# Used for HTTP status codes
|
26
|
+
def self.http_errors
|
27
|
+
{
|
28
|
+
400 => Luchadeer::Error::BadRequest,
|
29
|
+
404 => Luchadeer::Error::NotFound,
|
30
|
+
500 => Luchadeer::Error::InternalServerError,
|
31
|
+
502 => Luchadeer::Error::InternalServerError, # unconfirmed
|
32
|
+
503 => Luchadeer::Error::InternalServerError, # unconfirmed
|
33
|
+
504 => Luchadeer::Error::InternalServerError, # unconfirmed
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
class RedirectLimitReached < Luchadeer::Error; end
|
40
|
+
class RequestTimeout < Luchadeer::Error; end
|
41
|
+
|
42
|
+
class InvalidAPIKey < Luchadeer::Error; end
|
43
|
+
class URLFormatError < Luchadeer::Error; end
|
44
|
+
class FilterError < Luchadeer::Error; end
|
45
|
+
class SubscriberOnly < Luchadeer::Error; end
|
46
|
+
class BadRequest < Luchadeer::Error; end
|
47
|
+
class NotFound < Luchadeer::Error; end
|
48
|
+
class InternalServerError < Luchadeer::Error; end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'set'
|
3
|
+
require 'luchadeer/error'
|
4
|
+
|
5
|
+
module Luchadeer
|
6
|
+
module Middleware
|
7
|
+
class FollowRedirects < Faraday::Response::Middleware
|
8
|
+
FOLLOW_LIMIT = 3
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
follow_redirect(env, FOLLOW_LIMIT)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def follow_redirect(env, follows)
|
17
|
+
request_body = env[:body]
|
18
|
+
response = @app.call(env)
|
19
|
+
|
20
|
+
response.on_complete do |env|
|
21
|
+
if follow_redirect?(env, response)
|
22
|
+
raise Luchadeer::Error::RedirectLimitReached, response if follows.zero?
|
23
|
+
|
24
|
+
env = update_env(env, request_body, response)
|
25
|
+
response = follow_redirect(env, follows - 1)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
response
|
30
|
+
end
|
31
|
+
|
32
|
+
def follow_redirect?(env, response)
|
33
|
+
env[:method] == :get and [301, 302, 303, 307].include? response.status
|
34
|
+
end
|
35
|
+
|
36
|
+
def update_env(env, request_body, response)
|
37
|
+
env[:url] += response['location']
|
38
|
+
env[:method] = :get
|
39
|
+
|
40
|
+
[:status, :response, :response_headers].each do |key|
|
41
|
+
env.delete key
|
42
|
+
end
|
43
|
+
|
44
|
+
env
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Faraday::Response.register_middleware follow_redirects: Luchadeer::Middleware::FollowRedirects
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'luchadeer/error'
|
3
|
+
|
4
|
+
module Luchadeer
|
5
|
+
module Middleware
|
6
|
+
class ParseAPIError < Faraday::Response::Middleware
|
7
|
+
|
8
|
+
def on_complete(response)
|
9
|
+
klass = Luchadeer::Error.api_errors[response.body[:status_code]]
|
10
|
+
|
11
|
+
fail(klass.new(response.body[:error])) if klass
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Faraday::Response.register_middleware parse_api_error: Luchadeer::Middleware::ParseAPIError
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'luchadeer/error'
|
3
|
+
|
4
|
+
module Luchadeer
|
5
|
+
module Middleware
|
6
|
+
class ParseHTTPError < Faraday::Response::Middleware
|
7
|
+
|
8
|
+
def on_complete(response)
|
9
|
+
klass = Luchadeer::Error.http_errors[response.status.to_i]
|
10
|
+
|
11
|
+
fail(klass.new) if klass
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Faraday::Response.register_middleware parse_http_error: Luchadeer::Middleware::ParseHTTPError
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
# adapted from the 'twitter' gem
|
5
|
+
# https://github.com/sferik/twitter/blob/88d7b19e295f27e1dcf970b979585fa9d3984cd6/lib/twitter/rest/response/parse_json.rb
|
6
|
+
module Luchadeer
|
7
|
+
module Middleware
|
8
|
+
class ParseJSON < Faraday::Response::Middleware
|
9
|
+
WHITESPACE_REGEX = /\A^\s*$\z/
|
10
|
+
|
11
|
+
def parse(body)
|
12
|
+
case body
|
13
|
+
when WHITESPACE_REGEX, nil
|
14
|
+
nil
|
15
|
+
else
|
16
|
+
JSON.parse(body, symbolize_names: true)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_complete(response)
|
21
|
+
response.body = parse(response.body)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Faraday::Response.register_middleware parse_json: Luchadeer::Middleware::ParseJSON
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Luchadeer
|
2
|
+
class Resource < OpenStruct
|
3
|
+
|
4
|
+
# http://andreapavoni.com/blog/2013/4/create-recursive-openstruct-from-a-ruby-hash
|
5
|
+
def initialize(hash = nil)
|
6
|
+
@table = {}
|
7
|
+
@hash_table = {}
|
8
|
+
|
9
|
+
return unless hash
|
10
|
+
|
11
|
+
hash.each do |k, v|
|
12
|
+
@hash_table[k.to_sym] = v
|
13
|
+
@table[k.to_sym] = deep_structify(k, v)
|
14
|
+
new_ostruct_member(k)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_h
|
19
|
+
@hash_table
|
20
|
+
end
|
21
|
+
|
22
|
+
def detail
|
23
|
+
api_detail_url ? Luchadeer.client.fetch(api_detail_url, false, self.class) : self
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def deep_structify(k, v)
|
29
|
+
case v
|
30
|
+
when Hash then Luchadeer::Resource.new(v)
|
31
|
+
when Array then v.map { |n| Luchadeer::Resource.new(n) }
|
32
|
+
else v
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'luchadeer/character'
|
2
|
+
require 'luchadeer/company'
|
3
|
+
require 'luchadeer/concept'
|
4
|
+
require 'luchadeer/franchise'
|
5
|
+
require 'luchadeer/game'
|
6
|
+
require 'luchadeer/location'
|
7
|
+
require 'luchadeer/object'
|
8
|
+
require 'luchadeer/person'
|
9
|
+
require 'luchadeer/video'
|
10
|
+
|
11
|
+
module Luchadeer
|
12
|
+
class Search
|
13
|
+
attr_accessor :query
|
14
|
+
|
15
|
+
RESOURCE_TYPES = {
|
16
|
+
'character' => Luchadeer::Character,
|
17
|
+
'company' => Luchadeer::Company,
|
18
|
+
'concept' => Luchadeer::Concept,
|
19
|
+
'franchise' => Luchadeer::Franchise,
|
20
|
+
'game' => Luchadeer::Game,
|
21
|
+
'location' => Luchadeer::Location,
|
22
|
+
'object' => Luchadeer::Object,
|
23
|
+
'person' => Luchadeer::Person,
|
24
|
+
'video' => Luchadeer::Video
|
25
|
+
}
|
26
|
+
|
27
|
+
def initialize(query)
|
28
|
+
@query = query
|
29
|
+
end
|
30
|
+
|
31
|
+
def fetch
|
32
|
+
Luchadeer.client.get('search', search_params).body[:results].map do |r|
|
33
|
+
type = r.delete(:resource_type)
|
34
|
+
RESOURCE_TYPES[type] && RESOURCE_TYPES[type].new(r)
|
35
|
+
end.compact
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def search_params
|
41
|
+
{ query: @query }
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
data/luchadeer.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'luchadeer/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'luchadeer'
|
8
|
+
spec.version = Luchadeer::VERSION
|
9
|
+
spec.authors = ['Paul Friedman']
|
10
|
+
spec.email = ['paulrfri@gmail.com']
|
11
|
+
spec.summary = %q{The bombingest Giant Bomb API client library for Ruby.}
|
12
|
+
spec.homepage = 'https://github.com/paulfri/luchadeer'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
|
15
|
+
spec.files = `git ls-files`.split($/)
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
|
20
|
+
spec.add_dependency 'faraday', '~> 0.9.0'
|
21
|
+
|
22
|
+
spec.add_development_dependency 'bundler', '~> 1.5'
|
23
|
+
spec.add_development_dependency 'rake'
|
24
|
+
spec.add_development_dependency 'rspec'
|
25
|
+
spec.add_development_dependency 'webmock'
|
26
|
+
spec.add_development_dependency 'coveralls'
|
27
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Luchadeer::API::Characters do
|
4
|
+
let(:client) { Luchadeer::Client.new }
|
5
|
+
|
6
|
+
describe '#characters' do
|
7
|
+
let(:character_id) { 1 }
|
8
|
+
let(:character_path) { %r(#{Luchadeer::Client::GIANT_BOMB}/character/3005-#{character_id}) }
|
9
|
+
|
10
|
+
it 'requests the right url' do
|
11
|
+
stub = stub_request(:get, character_path).to_return(body: '{ }')
|
12
|
+
client.character("#{character_id}")
|
13
|
+
expect(stub).to have_been_requested
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'returns a Luchadeer::Character' do
|
17
|
+
stub_request(:get, character_path).to_return(body: '{ "results": { "key": "value"}}')
|
18
|
+
expect(client.character("#{character_id}")).to be_instance_of Luchadeer::Character
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'caches responses' do
|
22
|
+
stub_request(:get, character_path).to_return(body: '{ }')
|
23
|
+
expect(client).to receive :cache
|
24
|
+
client.character("#{character_id}")
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Luchadeer::API::Characters do
|
4
|
+
let(:client) { Luchadeer::Client.new }
|
5
|
+
|
6
|
+
describe '#companies' do
|
7
|
+
let(:company_id) { 1 }
|
8
|
+
let(:company_path) { %r(#{Luchadeer::Client::GIANT_BOMB}/company/3010-#{company_id}) }
|
9
|
+
|
10
|
+
it 'requests the right url' do
|
11
|
+
stub = stub_request(:get, company_path).to_return(body: '{ }')
|
12
|
+
client.company("#{company_id}")
|
13
|
+
expect(stub).to have_been_requested
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'returns a Luchadeer::Company' do
|
17
|
+
stub_request(:get, company_path).to_return(body: '{ "results": { "key": "value"}}')
|
18
|
+
expect(client.company("#{company_id}")).to be_instance_of Luchadeer::Company
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'caches responses' do
|
22
|
+
stub_request(:get, company_path).to_return(body: '{ }')
|
23
|
+
expect(client).to receive :cache
|
24
|
+
client.company("#{company_id}")
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|