shippo 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0e17e4581ec82ca7bb1242373d0f42045696f9a8
4
+ data.tar.gz: b159bf9011321abef4e546f7a60b276de4b09d93
5
+ SHA512:
6
+ metadata.gz: 48150a7adda38b277e70b3e031229456086a74472e5836892c3d42158445fe1e747e7bc3aef25de24c9a078354c424f53dae96de3b92671b3905180297a3436b
7
+ data.tar.gz: 5d2a381f23176b9c45a8c772fee6588facf4134e00e974581fb93edec09fa000d3b0b7d3b8b5d11ea9855e23505bcd207b1a4ca8af226ad99240bac15738db8d
data/example.rb ADDED
@@ -0,0 +1,59 @@
1
+ require 'shippo'
2
+ Shippo::api_user = 'YOUR_API_USER'
3
+ Shippo::api_pass = 'YOUR_API_PASS'
4
+
5
+ from = Shippo::Address.create(
6
+ :object_purchase => 'QUOTE',
7
+ :name => 'Laura Behrens Wu',
8
+ :company => 'Shippo',
9
+ :street1 => 'Clayton St.',
10
+ :street_no => '215',
11
+ :street2 => '',
12
+ :city => 'San Francisco',
13
+ :state => 'CA',
14
+ :zip => '94117',
15
+ :country => 'US',
16
+ :phone => '+1 555 341 9393',
17
+ :email => 'laura@goshippo.com',
18
+ :ip => '',
19
+ :metadata => 'Customer ID 123456'
20
+ )
21
+ puts from
22
+ to = Shippo::Address.create(
23
+ :object_purchase => 'QUOTE',
24
+ :name => 'Mr. John Doe',
25
+ :company => 'ACME Inc.',
26
+ :street1 => nil,
27
+ :street_no => '',
28
+ :street2 => '',
29
+ :city => 'Berlin',
30
+ :country => 'DE',
31
+ :ip => '',
32
+ :metadata => ''
33
+ )
34
+ puts to
35
+ parcel = Shippo::Parcel.create(
36
+ :length => 5,
37
+ :width => 1,
38
+ :height => 5.555,
39
+ :distance_unit => :cm,
40
+ :weight => '2.122',
41
+ :mass_unit => :lb,
42
+ :metadata => 'Customer ID 123456'
43
+ )
44
+ puts parcel
45
+ shipment = Shippo::Shipment.create(
46
+ :object_purpose => 'QUOTE',
47
+ # you can also put in the object_id directly, but this is more convenient
48
+ :address_from => from,
49
+ :address_to => to,
50
+ :parcel => parcel,
51
+ :metadata => 'Quote Shipment'
52
+ )
53
+ puts shipment
54
+ # never do this in real life
55
+ sleep(5)
56
+
57
+ rates = shipment.rates
58
+
59
+ puts "Shipment #{shipment[:object_id]} has the following rates:\n\n#{rates}"
data/lib/shippo.rb ADDED
@@ -0,0 +1,86 @@
1
+ require 'rest_client'
2
+ require 'json'
3
+ require 'set'
4
+
5
+ require 'shippo/error.rb'
6
+ require 'shippo/container_object.rb'
7
+ require 'shippo/api_object.rb'
8
+ require 'shippo/list.rb'
9
+ require 'shippo/create.rb'
10
+ require 'shippo/resource.rb'
11
+ require 'shippo/address.rb'
12
+ require 'shippo/parcel.rb'
13
+ require 'shippo/shipment.rb'
14
+ require 'shippo/transaction.rb'
15
+ require 'shippo/rate.rb'
16
+
17
+ module Shippo
18
+ @api_base = 'https://api.goshippo.com/v1'
19
+ @api_version = 1.0
20
+ @api_user = ''
21
+ @api_pass = ''
22
+
23
+ class << self
24
+ attr_accessor :api_base, :api_version, :api_user, :api_pass
25
+ end
26
+
27
+ def self.api_url(url='')
28
+ @api_base + url
29
+ end
30
+
31
+ def self.request(method, url, params = {}, headers = {})
32
+ unless @api_user && @api_pass
33
+ raise AuthError.new("API credentials missing! Make sure to set Shippo.api_user, Shippo.api_Pass")
34
+ end
35
+ begin
36
+ payload = {}
37
+ url = api_url(url)
38
+ case method
39
+ when :get
40
+ pairs = []
41
+ params.each { |k, v|
42
+ pairs.push "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"
43
+ }
44
+ url += "?#{pairs.join('&')}" unless pairs.empty?
45
+ when :post
46
+ payload = params
47
+ end
48
+ opts = { :headers => headers,
49
+ :method => method,
50
+ :payload => payload,
51
+ :url => url,
52
+ :open_timeout => 15,
53
+ :timeout => 30,
54
+ :user => @api_user,
55
+ :password => @api_pass
56
+ }
57
+ res = make_request(opts)
58
+ rescue => e
59
+ case e
60
+ when RestClient::ServerBrokeConnection, RestClient::RequestTimeout
61
+ msg = "Could not connect to the Shippo API at #{@api_base}. " +
62
+ "Please proceed to check your connection, try again and " +
63
+ "contact Shippo support should the issue persist."
64
+ when SocketError
65
+ msg = "Unexpected error connecting to the Shippo API at #{@api_base}."
66
+ else
67
+ msg = "Connection error"
68
+ end
69
+ raise ConnectionError.new msg + "\n\n(e.message)"
70
+ end
71
+ parse(res)
72
+ end
73
+ def self.parse(response)
74
+ JSON::parse(response.body, { :symbolize_names => true })
75
+ end
76
+ end
77
+ def make_request(opts)
78
+ RestClient::Request.execute(opts){ |response, request, result, &block|
79
+ if [301, 302, 307].include? response.code
80
+ response.follow_redirection(request, result, &block)
81
+ else
82
+ response.return!(request, result, &block)
83
+ end
84
+ }
85
+ end
86
+
@@ -0,0 +1,6 @@
1
+ module Shippo
2
+ class Address < Resource
3
+ include Shippo::Operations::List
4
+ include Shippo::Operations::Create
5
+ end
6
+ end
@@ -0,0 +1,89 @@
1
+ module Shippo
2
+ class ApiObject < ContainerObject
3
+ include Enumerable
4
+
5
+ def initialize(id=nil)
6
+ # parameter overloading!
7
+ if id.kind_of?(Hash)
8
+ id = id[:id]
9
+ end
10
+
11
+ @values = {}
12
+ @values[:id] = id if id
13
+ end
14
+
15
+ def self.construct_from(values)
16
+ # recursive on arrays
17
+ case values
18
+ when Array
19
+ values.map { |v| self.construct_from(v) }
20
+ when Hash
21
+ obj = self.new(values[:id])
22
+ obj.refresh_from(values)
23
+ obj
24
+ else
25
+ # on scalar types, just identity
26
+ values
27
+ end
28
+ end
29
+
30
+ def to_s(*args)
31
+ JSON.pretty_generate @values
32
+ end
33
+
34
+ def inspect()
35
+ id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
36
+ "#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}> JSON: " + self.to_s
37
+ end
38
+
39
+ def refresh_from(values)
40
+ values.each do |k, v|
41
+ @values[k.to_sym] = v
42
+ end
43
+ instance_eval do
44
+ add_accessors(@values.keys)
45
+ end
46
+
47
+ end
48
+ def [](k)
49
+ @values[k.to_sym]
50
+ end
51
+
52
+ def []=(k, v)
53
+ send(:"#{k}=", v)
54
+ end
55
+ def keys
56
+ @values.keys
57
+ end
58
+
59
+ def values
60
+ @values.values
61
+ end
62
+
63
+ def to_json(*a)
64
+ JSON.dump(@values)
65
+ end
66
+
67
+ def as_json(*a)
68
+ @values.as_json(*a)
69
+ end
70
+
71
+ def to_hash
72
+ @values
73
+ end
74
+
75
+ def each(&blk)
76
+ @values.each(&blk)
77
+ end
78
+
79
+ if RUBY_VERSION < '1.9.2'
80
+ def respond_to?(symbol)
81
+ @values.has_key?(symbol) || super
82
+ end
83
+ end
84
+
85
+
86
+ end
87
+
88
+
89
+ end
@@ -0,0 +1,28 @@
1
+ module Shippo
2
+ class ContainerObject
3
+ protected
4
+ def create_accessor(k_name, k_index)
5
+ metaclass.instance_eval do
6
+ define_method(k_name) { @values[k_index] }
7
+ define_method(:"#{k_name}=") do |v|
8
+ @values[k_index] = v unless k_index == ''
9
+ end
10
+ end
11
+ end
12
+ def add_accessors(keys)
13
+ keys.each do |k|
14
+ #TODO raise something here, should filter this before
15
+ orig_k = k
16
+ while respond_to?(k) do
17
+ k = "_#{k}".to_sym
18
+ end
19
+ create_accessor(k, orig_k)
20
+ end
21
+ end
22
+ def metaclass
23
+ class << self
24
+ self
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,19 @@
1
+ module Shippo
2
+ module Operations
3
+ module Create
4
+ module ClassMethods
5
+ def create(params={})
6
+ params.each do |k, v|
7
+ params[k] = v[:object_id] if v.is_a?(ApiObject)
8
+ end
9
+ response = Shippo.request(:post, "#{self.url}/", params)
10
+ self.construct_from(response)
11
+ end
12
+ end
13
+
14
+ def self.included(base)
15
+ base.extend(ClassMethods)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ module Shippo
2
+ class APIError < StandardError
3
+ attr_reader :message
4
+ def initialize(message=nil)
5
+ @message = message
6
+ end
7
+
8
+ def to_s
9
+ @message
10
+ end
11
+ end
12
+ class ConnectionError < APIError
13
+ end
14
+ class MissingDataError < APIError
15
+ end
16
+ class AuthError < APIError
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ module Shippo
2
+ module Operations
3
+ module List
4
+ module ClassMethods
5
+ # return all items
6
+ def all(params={})
7
+ response = Shippo.request(:get, "#{url}/", params)
8
+ self.construct_from(response[:results] || [])
9
+ end
10
+ # return a specific item
11
+ def get(id, params={})
12
+ response = Shippo.request(:get, "#{url}/#{CGI.escape(id)}/", params)
13
+ self.construct_from(response)
14
+ end
15
+ end
16
+
17
+ def self.included(base)
18
+ base.extend(ClassMethods)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,6 @@
1
+ module Shippo
2
+ class Parcel < Resource
3
+ include Shippo::Operations::List
4
+ include Shippo::Operations::Create
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Shippo
2
+ class Rate < Resource
3
+ include Shippo::Operations::List
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ module Shippo
2
+ class Resource < ApiObject
3
+ def self.class_name
4
+ self.name.split('::')[-1]
5
+ end
6
+ def self.url()
7
+ dc = class_name.downcase
8
+ "/" + dc + (dc[-1] == 's' ? 'es' : 's')
9
+ end
10
+ def url
11
+ unless id = self['object_id']
12
+ raise MissingDataError.new("#{self.class} has no object_id")
13
+ end
14
+ "#{self.class.url}/#{CGI.escape(id)}"
15
+ end
16
+
17
+ def refresh
18
+ response, api_key = Shippo.request(:get, url, @retrieve_options)
19
+ refresh_from(response)
20
+ self
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,10 @@
1
+ module Shippo
2
+ class Shipment < Resource
3
+ include Shippo::Operations::List
4
+ include Shippo::Operations::Create
5
+ def rates(params={})
6
+ response = Shippo.request(:get, "#{url}/rates/", params)
7
+ Shippo::Rate.construct_from(response[:results])
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,6 @@
1
+ module Shippo
2
+ class Transaction < Resource
3
+ include Shippo::Operations::List
4
+ include Shippo::Operations::Create
5
+ end
6
+ end
data/test/test.rb ADDED
@@ -0,0 +1,26 @@
1
+ require 'rubygems'
2
+ gem 'mocha'
3
+ require 'test/unit'
4
+ require 'mocha/test_unit'
5
+ require 'shippo'
6
+ def parcel_list_result
7
+ JSON::parse File.open('./parcel_list.json').read
8
+ end
9
+
10
+ module Shippo
11
+ class ParcelTest < Test::Unit::TestCase
12
+ def test_import
13
+ # get an array of test parcels from JSON
14
+ ret = Shippo::Parcel.construct_from(parcel_list_result)
15
+ # test the various options of accessing the parcel data
16
+ assert_equal ret[:metadata], "Customer ID 123456"
17
+ ret["metadata"] = "Customer ID 007"
18
+ assert_equal ret.metadata, "Customer ID 007"
19
+ assert_equal ret.object_owner, "tobias.schottdorf@gmail.com"
20
+ ret[:object_owner] = "hans.wurst@gmail.com"
21
+ assert_equal ret["object_owner"], "hans.wurst@gmail.com"
22
+ assert_equal ret.object_state, "VALID"
23
+
24
+ end
25
+ end
26
+ end
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shippo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Tobias Schottdorf
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-03 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Quick and easy access to the Shippo API
14
+ email: tobias.schottdorf@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - example.rb
20
+ - test/test.rb
21
+ - lib/shippo.rb
22
+ - ./lib/shippo/address.rb
23
+ - ./lib/shippo/api_object.rb
24
+ - ./lib/shippo/container_object.rb
25
+ - ./lib/shippo/create.rb
26
+ - ./lib/shippo/error.rb
27
+ - ./lib/shippo/list.rb
28
+ - ./lib/shippo/parcel.rb
29
+ - ./lib/shippo/rate.rb
30
+ - ./lib/shippo/resource.rb
31
+ - ./lib/shippo/shipment.rb
32
+ - ./lib/shippo/transaction.rb
33
+ homepage: http://goshippo.com
34
+ licenses:
35
+ - MIT
36
+ metadata: {}
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ requirements: []
52
+ rubyforge_project:
53
+ rubygems_version: 2.0.14
54
+ signing_key:
55
+ specification_version: 4
56
+ summary: Shippo API
57
+ test_files: []