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.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +6 -0
  5. data/Gemfile +4 -0
  6. data/Gemfile.lock +60 -0
  7. data/LICENSE +20 -0
  8. data/README.md +58 -0
  9. data/Rakefile +1 -0
  10. data/lib/luchadeer.rb +32 -0
  11. data/lib/luchadeer/api.rb +51 -0
  12. data/lib/luchadeer/api/characters.rb +11 -0
  13. data/lib/luchadeer/api/companies.rb +11 -0
  14. data/lib/luchadeer/api/concepts.rb +11 -0
  15. data/lib/luchadeer/api/franchises.rb +11 -0
  16. data/lib/luchadeer/api/games.rb +11 -0
  17. data/lib/luchadeer/api/locations.rb +11 -0
  18. data/lib/luchadeer/api/objects.rb +11 -0
  19. data/lib/luchadeer/api/people.rb +11 -0
  20. data/lib/luchadeer/api/videos.rb +11 -0
  21. data/lib/luchadeer/character.rb +13 -0
  22. data/lib/luchadeer/client.rb +73 -0
  23. data/lib/luchadeer/company.rb +13 -0
  24. data/lib/luchadeer/concept.rb +13 -0
  25. data/lib/luchadeer/error.rb +50 -0
  26. data/lib/luchadeer/franchise.rb +13 -0
  27. data/lib/luchadeer/game.rb +13 -0
  28. data/lib/luchadeer/location.rb +13 -0
  29. data/lib/luchadeer/middleware/follow_redirects.rb +50 -0
  30. data/lib/luchadeer/middleware/parse_api_error.rb +18 -0
  31. data/lib/luchadeer/middleware/parse_http_error.rb +18 -0
  32. data/lib/luchadeer/middleware/parse_json.rb +28 -0
  33. data/lib/luchadeer/object.rb +13 -0
  34. data/lib/luchadeer/person.rb +13 -0
  35. data/lib/luchadeer/resource.rb +37 -0
  36. data/lib/luchadeer/search.rb +45 -0
  37. data/lib/luchadeer/version.rb +3 -0
  38. data/lib/luchadeer/video.rb +13 -0
  39. data/luchadeer.gemspec +27 -0
  40. data/spec/luchadeer/api/characters_spec.rb +28 -0
  41. data/spec/luchadeer/api/companies_spec.rb +28 -0
  42. data/spec/luchadeer/api/concepts_spec.rb +28 -0
  43. data/spec/luchadeer/api/franchises_spec.rb +28 -0
  44. data/spec/luchadeer/api/games_spec.rb +28 -0
  45. data/spec/luchadeer/api/locations_spec.rb +28 -0
  46. data/spec/luchadeer/api/objects_spec.rb +28 -0
  47. data/spec/luchadeer/api/people_spec.rb +28 -0
  48. data/spec/luchadeer/api/videos_spec.rb +28 -0
  49. data/spec/luchadeer/api_spec.rb +101 -0
  50. data/spec/luchadeer/character_spec.rb +27 -0
  51. data/spec/luchadeer/client_spec.rb +54 -0
  52. data/spec/luchadeer/company_spec.rb +27 -0
  53. data/spec/luchadeer/concept_spec.rb +27 -0
  54. data/spec/luchadeer/error_spec.rb +53 -0
  55. data/spec/luchadeer/franchise_spec.rb +27 -0
  56. data/spec/luchadeer/game_spec.rb +27 -0
  57. data/spec/luchadeer/location_spec.rb +27 -0
  58. data/spec/luchadeer/object_spec.rb +27 -0
  59. data/spec/luchadeer/person_spec.rb +27 -0
  60. data/spec/luchadeer/resource_spec.rb +70 -0
  61. data/spec/luchadeer/search_spec.rb +69 -0
  62. data/spec/luchadeer/video_spec.rb +27 -0
  63. data/spec/luchadeer_spec.rb +24 -0
  64. data/spec/spec_helper.rb +5 -0
  65. metadata +216 -0
@@ -0,0 +1,13 @@
1
+ require 'luchadeer/resource'
2
+
3
+ module Luchadeer
4
+ class Company < Resource
5
+
6
+ class << self
7
+ def find(id, refresh = false)
8
+ Luchadeer.client.company(id, refresh)
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'luchadeer/resource'
2
+
3
+ module Luchadeer
4
+ class Concept < Resource
5
+
6
+ class << self
7
+ def find(id, refresh = false)
8
+ Luchadeer.client.concept(id, refresh)
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -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,13 @@
1
+ require 'luchadeer/resource'
2
+
3
+ module Luchadeer
4
+ class Franchise < Resource
5
+
6
+ class << self
7
+ def find(id, refresh = false)
8
+ Luchadeer.client.franchise(id, refresh)
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'luchadeer/resource'
2
+
3
+ module Luchadeer
4
+ class Game < Resource
5
+
6
+ class << self
7
+ def find(id, refresh = false)
8
+ Luchadeer.client.game(id, refresh)
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'luchadeer/resource'
2
+
3
+ module Luchadeer
4
+ class Location < Resource
5
+
6
+ class << self
7
+ def find(id, refresh = false)
8
+ Luchadeer.client.location(id, refresh)
9
+ end
10
+ end
11
+
12
+ end
13
+ 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,13 @@
1
+ require 'luchadeer/resource'
2
+
3
+ module Luchadeer
4
+ class Object < Resource
5
+
6
+ class << self
7
+ def find(id, refresh = false)
8
+ Luchadeer.client.object(id, refresh)
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'luchadeer/resource'
2
+
3
+ module Luchadeer
4
+ class Person < Resource
5
+
6
+ class << self
7
+ def find(id, refresh = false)
8
+ Luchadeer.client.person(id, refresh)
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module Luchadeer
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,13 @@
1
+ require 'luchadeer/resource'
2
+
3
+ module Luchadeer
4
+ class Video < Resource
5
+
6
+ class << self
7
+ def find(id, refresh = false)
8
+ Luchadeer.client.video(id, refresh)
9
+ end
10
+ end
11
+
12
+ end
13
+ 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