Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
philnash committed Feb 17, 2015
0 parents commit 233ec3c
Show file tree
Hide file tree
Showing 66 changed files with 3,972 additions and 0 deletions.
22 changes: 22 additions & 0 deletions .gitignore
@@ -0,0 +1,22 @@
*.gem
*.rbc
.bundle
.config
.yardoc
Gemfile.lock
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp
*.bundle
*.so
*.o
*.a
mkmf.log
4 changes: 4 additions & 0 deletions Gemfile
@@ -0,0 +1,4 @@
source 'https://rubygems.org'

# Specify your gem's dependencies in tatooine.gemspec
gemspec
22 changes: 22 additions & 0 deletions LICENSE.txt
@@ -0,0 +1,22 @@
Copyright (c) 2014 Phil Nash

MIT License

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
48 changes: 48 additions & 0 deletions README.md
@@ -0,0 +1,48 @@
# Tatooine

A Ruby interface to [SWAPI](http://swapi.co/) (the Star Wars API).

## Installation

Add this line to your application's Gemfile:

gem 'tatooine'

And then execute:

$ bundle

Or install it yourself as:

$ gem install tatooine

## Usage

There are 6 resources available: Planets, Starships, Vehicles, People, Films and Species. They all have a similar interface.

```ruby
planets = Tatooine::Planet.all
Tatooine::Planet.count
# => 60
planets.length
# => 10 # resources are paginated
planets.concat Tatooine::Planet.next
planets.length
# => 20

tatooine = Tatooine::Planet.get(1)
tatooine.name
# => "Tatooine"
tatooine.residents
# => [<Tatooine::Person>, ...]
tatooine.residents.first.name
# => "Luke Skywalker" # Will make the request to get the resource
```

## Contributing

1. Fork it ( https://github.com/philnash/tatooine/fork )
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request
8 changes: 8 additions & 0 deletions Rakefile
@@ -0,0 +1,8 @@
require "bundler/gem_tasks"

require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:spec) do |task|
task.rspec_opts = %w[--color]
end

task :default => :spec
17 changes: 17 additions & 0 deletions lib/tatooine.rb
@@ -0,0 +1,17 @@
require "faraday"
require "faraday_middleware"

require "tatooine/version"
require "tatooine/error"
require "tatooine/middleware/raise_http_error"
require "tatooine/resource"
require "tatooine/film"
require "tatooine/person"
require "tatooine/planet"
require "tatooine/species"
require "tatooine/starship"
require "tatooine/vehicle"

module Tatooine
API_BASE = "http://swapi.co/api/"
end
25 changes: 25 additions & 0 deletions lib/tatooine/error.rb
@@ -0,0 +1,25 @@
module Tatooine
# Custom error class for rescuing from all client errors
class Error < StandardError; end

# Raised when Swapi returns the HTTP status code 400
class BadRequest < Error; end

# Raised when Swapi returns the HTTP status code 404
class NotFound < Error; end

# Raised when Swapi returns the HTTP status code 500
class InternalServerError < Error; end

# Raised when Swapi returns the HTTP status code 502
class BadGateway < Error; end

# Raised when Swapi returns the HTTP status code 503
class ServiceUnavailable < Error; end

# Raised when Swapi returns the HTTP status code 504
class GatewayTimeout < Error; end

# Raised when Swapi returns the HTTP status code 521
class OriginDown < Error; end
end
6 changes: 6 additions & 0 deletions lib/tatooine/film.rb
@@ -0,0 +1,6 @@
module Tatooine
class Film
include Resource
resource_path "films"
end
end
34 changes: 34 additions & 0 deletions lib/tatooine/middleware/raise_http_error.rb
@@ -0,0 +1,34 @@
module Tatooine
module Middleware
class RaiseHttpError < Faraday::Response::Middleware
def on_complete(env)
case env[:status].to_i
when 400
raise Tatooine::BadRequest, response_values(env)
when 404
raise Tatooine::NotFound, response_values(env)
when 500
raise Tatooine::InternalServerError, response_values(env)
when 502
raise Tatooine::BadGateway, response_values(env)
when 503
raise Tatooine::ServiceUnavailable, response_values(env)
when 504
raise Tatooine::GatewayTimeout, response_values(env)
when 521
raise Tatooine::OriginDown, response_values(env)
end
end

def response_values(env)
{:status => env.status, :headers => env.response_headers, :body => env.body}
end
end
end
end

module Faraday
class Response
register_middleware :raise_http_error => Tatooine::Middleware::RaiseHttpError
end
end
6 changes: 6 additions & 0 deletions lib/tatooine/person.rb
@@ -0,0 +1,6 @@
module Tatooine
class Person
include Resource
resource_path "people"
end
end
6 changes: 6 additions & 0 deletions lib/tatooine/planet.rb
@@ -0,0 +1,6 @@
module Tatooine
class Planet
include Resource
resource_path "planets"
end
end
125 changes: 125 additions & 0 deletions lib/tatooine/resource.rb
@@ -0,0 +1,125 @@
module Tatooine
module Resource
MAPPING = {
"films" => :Film,
"people" => :Person,
"planets" => :Planet,
"species" => :Species,
"starships" => :Starship,
"vehicles" => :Vehicle,
"homeworld" => :Planet,
"characters" => :Person,
"pilots" => :Person,
"residents" => :Person
}

def self.included(base)
base.extend(ClassMethods)
end

def initialize(opts={})
@properties = opts
set_properties
end

private

def set_properties
self.class.schema["properties"].each do |property, values|
properties = @properties.dup
if properties[property]
if values["type"] == "integer"
properties[property] = properties[property].to_i
elsif values["type"] == "array" && MAPPING.keys.include?(property)
klass = Tatooine.const_get(MAPPING[property])
properties[property] = properties[property].map do |url|
klass.new("url" => url)
end
elsif MAPPING.keys.include?(property)
klass = Tatooine.const_get(MAPPING[property])
properties[property] = klass.new("url" => properties[property])
end
instance_variable_set("@#{property}", properties[property])
end
end
@url = @properties["url"] if @properties["url"]
end


module ClassMethods
def list(opts={})
load_schema
url = opts.delete("url") || ""
body = connection.get(url, opts).body
@count = body["count"]
@next = body["next"]
@previous = body["previous"]
body["results"].map { |result| new(result) }
end

def count
@count || get_count
end

def next
list "url" => @next if @next
end

def previous
list "url" => @previous if @previous
end

def get(id)
load_schema
result = connection.get("#{id}/")
new(result.body)
end

def schema
load_schema
end

def connection
options = {
headers: {
"User-Agent" => "Tatooine Ruby Gem - #{Tatooine::VERSION}"
},
url: "#{Tatooine::API_BASE}#{@resource_path}/"
}
@connection ||= Faraday.new(options) do |faraday|
faraday.response :raise_http_error
faraday.response :dates
faraday.response :json
faraday.adapter Faraday.default_adapter
end
end

private

def load_schema
if !@schema
@schema = connection.get("schema").body
@schema["properties"].keys.each do |property|
define_method property.to_sym do
if @properties.nil? || @properties.keys.length <= 1
result = self.class.connection.get(@url)
@properties = result.body
set_properties
end
instance_variable_get("@#{property}")
end
end
end
@schema
end

def get_count
@count = connection.get("").body["count"]
end

def resource_path(path)
@resource_path = path
end
end
end
end
6 changes: 6 additions & 0 deletions lib/tatooine/species.rb
@@ -0,0 +1,6 @@
module Tatooine
class Species
include Resource
resource_path "species"
end
end
6 changes: 6 additions & 0 deletions lib/tatooine/starship.rb
@@ -0,0 +1,6 @@
module Tatooine
class Starship
include Resource
resource_path "starships"
end
end
6 changes: 6 additions & 0 deletions lib/tatooine/vehicle.rb
@@ -0,0 +1,6 @@
module Tatooine
class Vehicle
include Resource
resource_path "vehicles"
end
end
3 changes: 3 additions & 0 deletions lib/tatooine/version.rb
@@ -0,0 +1,3 @@
module Tatooine
VERSION = "1.0.0"
end
21 changes: 21 additions & 0 deletions spec/error_spec.rb
@@ -0,0 +1,21 @@
require "./spec/spec_helper"

describe "API errors" do
{
400 => Tatooine::BadRequest,
404 => Tatooine::NotFound,
500 => Tatooine::InternalServerError,
502 => Tatooine::BadGateway,
503 => Tatooine::ServiceUnavailable,
504 => Tatooine::GatewayTimeout,
521 => Tatooine::OriginDown
}.each do |status, error|
it "should raise custom error for #{status}" do
stub_request(:get, "#{Tatooine::API_BASE}films/schema").to_return(
:body => File.new("./spec/fixtures/film_schema.json")
)
stub_request(:get, "#{Tatooine::API_BASE}films/11/").to_return(:status => status)
expect { Tatooine::Film.get(11) }.to raise_error(error)
end
end
end

0 comments on commit 233ec3c

Please sign in to comment.