tatooine 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +52 -0
  6. data/Rakefile +8 -0
  7. data/lib/tatooine.rb +17 -0
  8. data/lib/tatooine/error.rb +25 -0
  9. data/lib/tatooine/film.rb +6 -0
  10. data/lib/tatooine/middleware/raise_http_error.rb +34 -0
  11. data/lib/tatooine/person.rb +6 -0
  12. data/lib/tatooine/planet.rb +6 -0
  13. data/lib/tatooine/resource.rb +125 -0
  14. data/lib/tatooine/species.rb +6 -0
  15. data/lib/tatooine/starship.rb +6 -0
  16. data/lib/tatooine/vehicle.rb +6 -0
  17. data/lib/tatooine/version.rb +3 -0
  18. data/spec/error_spec.rb +21 -0
  19. data/spec/film_spec.rb +107 -0
  20. data/spec/fixtures/film_schema.json +1 -0
  21. data/spec/fixtures/vcr_cassettes/film_1.yml +57 -0
  22. data/spec/fixtures/vcr_cassettes/film_1_and_schema.yml +57 -0
  23. data/spec/fixtures/vcr_cassettes/film_count.yml +101 -0
  24. data/spec/fixtures/vcr_cassettes/film_list.yml +441 -0
  25. data/spec/fixtures/vcr_cassettes/film_schema.yml +101 -0
  26. data/spec/fixtures/vcr_cassettes/person_1.yml +49 -0
  27. data/spec/fixtures/vcr_cassettes/person_1_and_schema.yml +49 -0
  28. data/spec/fixtures/vcr_cassettes/person_count.yml +58 -0
  29. data/spec/fixtures/vcr_cassettes/person_list.yml +58 -0
  30. data/spec/fixtures/vcr_cassettes/person_next.yml +56 -0
  31. data/spec/fixtures/vcr_cassettes/person_previous.yml +58 -0
  32. data/spec/fixtures/vcr_cassettes/person_schema.yml +58 -0
  33. data/spec/fixtures/vcr_cassettes/planet_1.yml +50 -0
  34. data/spec/fixtures/vcr_cassettes/planet_1_and_schema.yml +50 -0
  35. data/spec/fixtures/vcr_cassettes/planet_count.yml +60 -0
  36. data/spec/fixtures/vcr_cassettes/planet_list.yml +60 -0
  37. data/spec/fixtures/vcr_cassettes/planet_next.yml +63 -0
  38. data/spec/fixtures/vcr_cassettes/planet_previous.yml +60 -0
  39. data/spec/fixtures/vcr_cassettes/planet_schema.yml +60 -0
  40. data/spec/fixtures/vcr_cassettes/species_1.yml +52 -0
  41. data/spec/fixtures/vcr_cassettes/species_1_and_schema.yml +52 -0
  42. data/spec/fixtures/vcr_cassettes/species_count.yml +63 -0
  43. data/spec/fixtures/vcr_cassettes/species_list.yml +63 -0
  44. data/spec/fixtures/vcr_cassettes/species_next.yml +59 -0
  45. data/spec/fixtures/vcr_cassettes/species_previous.yml +63 -0
  46. data/spec/fixtures/vcr_cassettes/species_schema.yml +63 -0
  47. data/spec/fixtures/vcr_cassettes/starship_10.yml +51 -0
  48. data/spec/fixtures/vcr_cassettes/starship_2_and_schema.yml +51 -0
  49. data/spec/fixtures/vcr_cassettes/starship_count.yml +78 -0
  50. data/spec/fixtures/vcr_cassettes/starship_list.yml +78 -0
  51. data/spec/fixtures/vcr_cassettes/starship_next.yml +76 -0
  52. data/spec/fixtures/vcr_cassettes/starship_previous.yml +78 -0
  53. data/spec/fixtures/vcr_cassettes/starship_schema.yml +78 -0
  54. data/spec/fixtures/vcr_cassettes/vehicle_14.yml +50 -0
  55. data/spec/fixtures/vcr_cassettes/vehicle_14_and_schema.yml +50 -0
  56. data/spec/fixtures/vcr_cassettes/vehicle_count.yml +70 -0
  57. data/spec/fixtures/vcr_cassettes/vehicle_list.yml +70 -0
  58. data/spec/fixtures/vcr_cassettes/vehicle_next.yml +68 -0
  59. data/spec/fixtures/vcr_cassettes/vehicle_previous.yml +70 -0
  60. data/spec/fixtures/vcr_cassettes/vehicle_schema.yml +70 -0
  61. data/spec/person_spec.rb +120 -0
  62. data/spec/planet_spec.rb +106 -0
  63. data/spec/spec_helper.rb +21 -0
  64. data/spec/species_spec.rb +111 -0
  65. data/spec/starship_spec.rb +106 -0
  66. data/spec/vehicle_spec.rb +106 -0
  67. data/tatooine.gemspec +30 -0
  68. metadata +272 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 75e1fa6852675a23a818217a4bc9bfc9070e6cff
4
+ data.tar.gz: 0b675f61ea00c7c2d94a5e411ec8c7b49527ada0
5
+ SHA512:
6
+ metadata.gz: 37d6d51419db7ade8e3e35a6997606d7ea498ed027179e2664d9170a7ddd373d4b69821d752276925967bec877b4c85867eb861f98594a67d9e6e97d8bc9e97c
7
+ data.tar.gz: 587a2a3bb77d0b7aa67f83becda01f972e399d995f4c202d0ffede6563daa35ea711d5fcd3ed4af3fbb3752751f6bcc098fef0aef389af120f69a60c1b5c1ed7
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in tatooine.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Phil Nash
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,52 @@
1
+ # Tatooine
2
+
3
+ A Ruby interface to [SWAPI](http://swapi.co/) (the Star Wars API).
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'tatooine'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install tatooine
18
+
19
+ ## Usage
20
+
21
+ There are 6 resources available: Planets, Starships, Vehicles, People, Films and Species. They all have a similar interface.
22
+
23
+ ```ruby
24
+ planets = Tatooine::Planet.list
25
+ Tatooine::Planet.count
26
+ # => 60
27
+ planets.length
28
+ # => 10 # resources are paginated
29
+ planets.concat Tatooine::Planet.next
30
+ planets.length
31
+ # => 20
32
+
33
+ tatooine = Tatooine::Planet.get(1)
34
+ tatooine.name
35
+ # => "Tatooine"
36
+ tatooine.residents
37
+ # => [<Tatooine::Person>, ...]
38
+ tatooine.residents.first.name
39
+ # => "Luke Skywalker" # Will make the request to get the resource
40
+ ```
41
+
42
+ ## Schema
43
+
44
+ In SWAPI the objects are described by their [schemas](http://swapi.co/documentation#schema). Check the [SWAPI documentation](http://swapi.co/documentation) to find out more about each of the objects.
45
+
46
+ ## Contributing
47
+
48
+ 1. Fork it ( https://github.com/philnash/tatooine/fork )
49
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
50
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
51
+ 4. Push to the branch (`git push origin my-new-feature`)
52
+ 5. Create a new Pull Request
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require "rspec/core/rake_task"
4
+ RSpec::Core::RakeTask.new(:spec) do |task|
5
+ task.rspec_opts = %w[--color]
6
+ end
7
+
8
+ task :default => :spec
@@ -0,0 +1,17 @@
1
+ require "faraday"
2
+ require "faraday_middleware"
3
+
4
+ require "tatooine/version"
5
+ require "tatooine/error"
6
+ require "tatooine/middleware/raise_http_error"
7
+ require "tatooine/resource"
8
+ require "tatooine/film"
9
+ require "tatooine/person"
10
+ require "tatooine/planet"
11
+ require "tatooine/species"
12
+ require "tatooine/starship"
13
+ require "tatooine/vehicle"
14
+
15
+ module Tatooine
16
+ API_BASE = "http://swapi.co/api/"
17
+ end
@@ -0,0 +1,25 @@
1
+ module Tatooine
2
+ # Custom error class for rescuing from all client errors
3
+ class Error < StandardError; end
4
+
5
+ # Raised when Swapi returns the HTTP status code 400
6
+ class BadRequest < Error; end
7
+
8
+ # Raised when Swapi returns the HTTP status code 404
9
+ class NotFound < Error; end
10
+
11
+ # Raised when Swapi returns the HTTP status code 500
12
+ class InternalServerError < Error; end
13
+
14
+ # Raised when Swapi returns the HTTP status code 502
15
+ class BadGateway < Error; end
16
+
17
+ # Raised when Swapi returns the HTTP status code 503
18
+ class ServiceUnavailable < Error; end
19
+
20
+ # Raised when Swapi returns the HTTP status code 504
21
+ class GatewayTimeout < Error; end
22
+
23
+ # Raised when Swapi returns the HTTP status code 521
24
+ class OriginDown < Error; end
25
+ end
@@ -0,0 +1,6 @@
1
+ module Tatooine
2
+ class Film
3
+ include Resource
4
+ resource_path "films"
5
+ end
6
+ end
@@ -0,0 +1,34 @@
1
+ module Tatooine
2
+ module Middleware
3
+ class RaiseHttpError < Faraday::Response::Middleware
4
+ def on_complete(env)
5
+ case env[:status].to_i
6
+ when 400
7
+ raise Tatooine::BadRequest, response_values(env)
8
+ when 404
9
+ raise Tatooine::NotFound, response_values(env)
10
+ when 500
11
+ raise Tatooine::InternalServerError, response_values(env)
12
+ when 502
13
+ raise Tatooine::BadGateway, response_values(env)
14
+ when 503
15
+ raise Tatooine::ServiceUnavailable, response_values(env)
16
+ when 504
17
+ raise Tatooine::GatewayTimeout, response_values(env)
18
+ when 521
19
+ raise Tatooine::OriginDown, response_values(env)
20
+ end
21
+ end
22
+
23
+ def response_values(env)
24
+ {:status => env.status, :headers => env.response_headers, :body => env.body}
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ module Faraday
31
+ class Response
32
+ register_middleware :raise_http_error => Tatooine::Middleware::RaiseHttpError
33
+ end
34
+ end
@@ -0,0 +1,6 @@
1
+ module Tatooine
2
+ class Person
3
+ include Resource
4
+ resource_path "people"
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Tatooine
2
+ class Planet
3
+ include Resource
4
+ resource_path "planets"
5
+ end
6
+ end
@@ -0,0 +1,125 @@
1
+ module Tatooine
2
+ module Resource
3
+ MAPPING = {
4
+ "films" => :Film,
5
+ "people" => :Person,
6
+ "planets" => :Planet,
7
+ "species" => :Species,
8
+ "starships" => :Starship,
9
+ "vehicles" => :Vehicle,
10
+ "homeworld" => :Planet,
11
+ "characters" => :Person,
12
+ "pilots" => :Person,
13
+ "residents" => :Person
14
+ }
15
+
16
+ def self.included(base)
17
+ base.extend(ClassMethods)
18
+ end
19
+
20
+ def initialize(opts={})
21
+ @properties = opts
22
+ set_properties
23
+ end
24
+
25
+ private
26
+
27
+ def set_properties
28
+ self.class.schema["properties"].each do |property, values|
29
+ properties = @properties.dup
30
+ if properties[property]
31
+ if values["type"] == "integer"
32
+ properties[property] = properties[property].to_i
33
+ elsif values["type"] == "array" && MAPPING.keys.include?(property)
34
+ klass = Tatooine.const_get(MAPPING[property])
35
+ properties[property] = properties[property].map do |url|
36
+ klass.new("url" => url)
37
+ end
38
+ elsif MAPPING.keys.include?(property)
39
+ klass = Tatooine.const_get(MAPPING[property])
40
+ properties[property] = klass.new("url" => properties[property])
41
+ end
42
+ instance_variable_set("@#{property}", properties[property])
43
+ end
44
+ end
45
+ @url = @properties["url"] if @properties["url"]
46
+ end
47
+
48
+
49
+ module ClassMethods
50
+ def list(opts={})
51
+ load_schema
52
+ url = opts.delete("url") || ""
53
+ body = connection.get(url, opts).body
54
+ @count = body["count"]
55
+ @next = body["next"]
56
+ @previous = body["previous"]
57
+ body["results"].map { |result| new(result) }
58
+ end
59
+
60
+ def count
61
+ @count || get_count
62
+ end
63
+
64
+ def next
65
+ list "url" => @next if @next
66
+ end
67
+
68
+ def previous
69
+ list "url" => @previous if @previous
70
+ end
71
+
72
+ def get(id)
73
+ load_schema
74
+ result = connection.get("#{id}/")
75
+ new(result.body)
76
+ end
77
+
78
+ def schema
79
+ load_schema
80
+ end
81
+
82
+ def connection
83
+ options = {
84
+ headers: {
85
+ "User-Agent" => "Tatooine Ruby Gem - #{Tatooine::VERSION}"
86
+ },
87
+ url: "#{Tatooine::API_BASE}#{@resource_path}/"
88
+ }
89
+ @connection ||= Faraday.new(options) do |faraday|
90
+ faraday.response :raise_http_error
91
+ faraday.response :dates
92
+ faraday.response :json
93
+ faraday.adapter Faraday.default_adapter
94
+ end
95
+ end
96
+
97
+ private
98
+
99
+ def load_schema
100
+ if !@schema
101
+ @schema = connection.get("schema").body
102
+ @schema["properties"].keys.each do |property|
103
+ define_method property.to_sym do
104
+ if @properties.nil? || @properties.keys.length <= 1
105
+ result = self.class.connection.get(@url)
106
+ @properties = result.body
107
+ set_properties
108
+ end
109
+ instance_variable_get("@#{property}")
110
+ end
111
+ end
112
+ end
113
+ @schema
114
+ end
115
+
116
+ def get_count
117
+ @count = connection.get("").body["count"]
118
+ end
119
+
120
+ def resource_path(path)
121
+ @resource_path = path
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,6 @@
1
+ module Tatooine
2
+ class Species
3
+ include Resource
4
+ resource_path "species"
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Tatooine
2
+ class Starship
3
+ include Resource
4
+ resource_path "starships"
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Tatooine
2
+ class Vehicle
3
+ include Resource
4
+ resource_path "vehicles"
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ module Tatooine
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,21 @@
1
+ require "./spec/spec_helper"
2
+
3
+ describe "API errors" do
4
+ {
5
+ 400 => Tatooine::BadRequest,
6
+ 404 => Tatooine::NotFound,
7
+ 500 => Tatooine::InternalServerError,
8
+ 502 => Tatooine::BadGateway,
9
+ 503 => Tatooine::ServiceUnavailable,
10
+ 504 => Tatooine::GatewayTimeout,
11
+ 521 => Tatooine::OriginDown
12
+ }.each do |status, error|
13
+ it "should raise custom error for #{status}" do
14
+ stub_request(:get, "#{Tatooine::API_BASE}films/schema").to_return(
15
+ :body => File.new("./spec/fixtures/film_schema.json")
16
+ )
17
+ stub_request(:get, "#{Tatooine::API_BASE}films/11/").to_return(:status => status)
18
+ expect { Tatooine::Film.get(11) }.to raise_error(error)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,107 @@
1
+ require "./spec/spec_helper"
2
+
3
+ describe Tatooine::Film do
4
+ describe "#schema" do
5
+ before(:all) do
6
+ VCR.use_cassette("film schema") do
7
+ @schema = Tatooine::Film.schema
8
+ end
9
+ end
10
+
11
+ it "should get the description of the class" do
12
+ expect(@schema).to be_instance_of(Hash)
13
+ expect(@schema["description"]).to eq("A Star Wars film")
14
+ end
15
+ end
16
+
17
+ describe "#count" do
18
+ before(:all) do
19
+ VCR.use_cassette("film count") do
20
+ @count = Tatooine::Film.count
21
+ end
22
+ end
23
+
24
+ it "should return the correct count" do
25
+ expect(@count).to be(6)
26
+ end
27
+ end
28
+
29
+ describe "#list" do
30
+ before(:all) do
31
+ VCR.use_cassette("film list") do
32
+ @films = Tatooine::Film.list
33
+ end
34
+ end
35
+
36
+ it "gets a list of films" do
37
+ expect(@films).to be_instance_of(Array)
38
+ expect(@films.length).to be(6)
39
+ expect(@films.first).to be_instance_of(Tatooine::Film)
40
+ end
41
+ end
42
+
43
+ describe "#get" do
44
+ before(:all) do
45
+ VCR.use_cassette("film 1") do
46
+ @film = Tatooine::Film.get(1)
47
+ end
48
+ end
49
+
50
+ it "gets the url of the film" do
51
+ expect(@film.url).to eq("http://swapi.co/api/films/1/")
52
+ end
53
+
54
+ it "gets the attributes of the film" do
55
+ expect(@film.title).to eq("A New Hope")
56
+ expect(@film.episode_id).to eq(4)
57
+ end
58
+
59
+ it "gets a list of characters" do
60
+ expect(@film.characters).to be_instance_of(Array)
61
+ expect(@film.characters.first).to be_instance_of(Tatooine::Person)
62
+ end
63
+
64
+ it "gets a list of planets" do
65
+ expect(@film.planets).to be_instance_of(Array)
66
+ expect(@film.planets.first).to be_instance_of(Tatooine::Planet)
67
+ end
68
+
69
+ it "gets a list of species" do
70
+ expect(@film.species).to be_instance_of(Array)
71
+ expect(@film.species.first).to be_instance_of(Tatooine::Species)
72
+ end
73
+
74
+ it "gets a list of starships" do
75
+ expect(@film.starships).to be_instance_of(Array)
76
+ expect(@film.starships.first).to be_instance_of(Tatooine::Starship)
77
+ end
78
+
79
+ it "gets a list of vehicles" do
80
+ expect(@film.vehicles).to be_instance_of(Array)
81
+ expect(@film.vehicles.first).to be_instance_of(Tatooine::Vehicle)
82
+ end
83
+ end
84
+
85
+ describe "#new" do
86
+ it "primes an object with a url" do
87
+ VCR.use_cassette("film schema") do
88
+ @film = Tatooine::Film.new("url" => "#{Tatooine::API_BASE}films/1/")
89
+ expect(@film).to respond_to(:title)
90
+ end
91
+ end
92
+
93
+ it "gets the object when methods are called" do
94
+ VCR.use_cassette("film 1 and schema") do
95
+ @film = Tatooine::Film.new("url" => "#{Tatooine::API_BASE}films/1/")
96
+ expect(@film.title).to eq("A New Hope")
97
+ end
98
+ end
99
+
100
+ it "sets properties from an object" do
101
+ VCR.use_cassette("film schema") do
102
+ @film = Tatooine::Film.new("title" => "Anything, because I said so")
103
+ expect(@film.title).to eq("Anything, because I said so")
104
+ end
105
+ end
106
+ end
107
+ end