tatooine 1.0.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 (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