luchadeer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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