temando 0.1.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 (39) hide show
  1. data/.gitignore +18 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +22 -0
  4. data/README.md +64 -0
  5. data/Rakefile +13 -0
  6. data/lib/temando.rb +27 -0
  7. data/lib/temando/api/base.rb +40 -0
  8. data/lib/temando/api/entities/anything.rb +26 -0
  9. data/lib/temando/api/entities/anywhere.rb +29 -0
  10. data/lib/temando/api/entities/quote.rb +30 -0
  11. data/lib/temando/api/exceptions/soap_error.rb +14 -0
  12. data/lib/temando/api/get_quotes_by_request.rb +48 -0
  13. data/lib/temando/api/soap_client.rb +64 -0
  14. data/lib/temando/delivery/base.rb +27 -0
  15. data/lib/temando/delivery/door_to_door.rb +16 -0
  16. data/lib/temando/item/base.rb +29 -0
  17. data/lib/temando/item/general_goods.rb +22 -0
  18. data/lib/temando/location.rb +13 -0
  19. data/lib/temando/quote.rb +7 -0
  20. data/lib/temando/request.rb +49 -0
  21. data/lib/temando/rspec.rb +15 -0
  22. data/lib/temando/version.rb +3 -0
  23. data/spec/fixtures/xml/exceptions/failed_auth.xml +10 -0
  24. data/spec/fixtures/xml/get_quotes_by_request/request.xml +47 -0
  25. data/spec/fixtures/xml/get_quotes_by_request/response.xml +80 -0
  26. data/spec/integration/get_quotes_spec.rb +41 -0
  27. data/spec/remote/remote_request_spec.rb +29 -0
  28. data/spec/spec_helper.rb +29 -0
  29. data/spec/support/default_disable_remote.rb +14 -0
  30. data/spec/support/factories/item_factory.rb +9 -0
  31. data/spec/support/xml_fixtures.rb +9 -0
  32. data/spec/units/api/entities/quote_spec.rb +23 -0
  33. data/spec/units/api/get_quotes_by_request_spec.rb +29 -0
  34. data/spec/units/api/soap_client_spec.rb +26 -0
  35. data/spec/units/delivery/door_to_door_spec.rb +21 -0
  36. data/spec/units/item/general_goods_spec.rb +21 -0
  37. data/spec/units/request_spec.rb +32 -0
  38. data/temando.gemspec +25 -0
  39. metadata +194 -0
@@ -0,0 +1,18 @@
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
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in temando.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Jason Stirk
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,64 @@
1
+ # Temando
2
+
3
+ The `temando` gem provides a Ruby interface to the
4
+ [Temando](https://www.temando.com/) shipping fulfilment provider.
5
+
6
+ Currently, it only supports fetching quotes from the API and returning
7
+ them.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'temando'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install temando
22
+
23
+ ## Usage
24
+
25
+ Temando authentication details should be set before calling any methods :
26
+
27
+ ```ruby
28
+ Temando::Api::Base.config.username = 'myuser@example.com'
29
+ Temando::Api::Base.config.password = 'sekrit'
30
+ ```
31
+
32
+ Example:
33
+
34
+ ``ruby
35
+ request = Temando::Request.new
36
+
37
+ # Add the items to be shipped
38
+ request.items << Temando::Item::GeneralGoods.new(...)
39
+ request.items << Temando::Item::GeneralGoods.new(...)
40
+
41
+ # Add the details for the actual shipment method and its locations
42
+ anywhere = Temando::Delivery::DoorToDoor.new
43
+ anywhere.origin = Temando::Location.new(...)
44
+ anywhere.destination = Temando::Location.new(...)
45
+
46
+ # Ask the server for the quotes
47
+ quotes = request.quotes_for(anywhere)
48
+
49
+ quotes.first # => #<Temando::Quote>
50
+ ```
51
+
52
+ ## Tests
53
+
54
+ `rake spec` to run the tests.
55
+
56
+ `rake spec:remote` to run the remote specs.
57
+
58
+ ## Contributing
59
+
60
+ 1. Fork it
61
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
62
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
63
+ 4. Push to the branch (`git push origin my-new-feature`)
64
+ 5. Create new Pull Request
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new('spec') do |t|
6
+ t.rspec_opts = '-t ~remote'
7
+ end
8
+
9
+ RSpec::Core::RakeTask.new('spec:remote') do |t|
10
+ t.rspec_opts = '-t remote'
11
+ end
12
+
13
+ task :default => :spec
@@ -0,0 +1,27 @@
1
+ require "typhoeus"
2
+
3
+ require "temando/version"
4
+
5
+ require "temando/location"
6
+
7
+ require "temando/item/base"
8
+ require "temando/item/general_goods"
9
+
10
+ require "temando/delivery/base"
11
+ require "temando/delivery/door_to_door"
12
+
13
+ require "temando/quote"
14
+
15
+ require "temando/api/soap_client"
16
+ require "temando/api/base"
17
+ require "temando/api/entities/anything"
18
+ require "temando/api/entities/anywhere"
19
+ require "temando/api/entities/quote"
20
+ require "temando/api/get_quotes_by_request"
21
+
22
+ require "temando/api/exceptions/soap_error"
23
+
24
+ require "temando/request"
25
+
26
+ module Temando
27
+ end
@@ -0,0 +1,40 @@
1
+ require 'active_support/configurable'
2
+ require 'nokogiri'
3
+
4
+ module Temando
5
+ module Api
6
+ # Provides the low-level SOAP formatting functionality.
7
+ class Base
8
+ include ActiveSupport::Configurable
9
+
10
+ config_accessor :username, :password, :logger
11
+
12
+ TEMANDO_NAMESPACE = "http://api.temando.com/schema/2009_06/server.xsd"
13
+
14
+ def soap_boilerplate(&block)
15
+ data = Nokogiri::XML::Builder.new do |xml|
16
+ xml.Envelope("xmlns:soapenv" => "http://schemas.xmlsoap.org/soap/envelope/",
17
+ "xmlns:xsd" => "http://www.w3.org/2001/XMLSchema",
18
+ "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance") do
19
+
20
+ # Ensure that the "Envelope" namespace absolutely must be soapenv, otherwise we will get soap:VersionMismatch errors
21
+ xml.parent.namespace = xml.parent.namespace_definitions.find { |x| x.prefix == 'soapenv' }
22
+ xml['soapenv'].Header do
23
+ xml.Security("xmlns:wsse" => "http://schemas.xmlsoap.org/ws/2002/04/secext") do
24
+ xml.parent.namespace = xml.parent.namespace_definitions.find { |x| x.prefix == 'wsse' }
25
+ xml['wsse'].UsernameToken do
26
+ xml['wsse'].Username Temando::Api::Base.username
27
+ xml['wsse'].Password Temando::Api::Base.password
28
+ end
29
+ end
30
+ end
31
+ xml['soapenv'].Body(&block)
32
+ end
33
+ end
34
+
35
+ data.to_xml(:indent => 2, :encoding => 'UTF-8')
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,26 @@
1
+ module Temando::Api
2
+ module Entities
3
+ class Anything
4
+ def initialize(anything)
5
+ @anything = anything
6
+ end
7
+
8
+ def build_xml(xml)
9
+ xml.anything do
10
+ xml.class_ @anything.shipping_class
11
+ xml.subclass @anything.shipping_subclass
12
+ xml.packaging @anything.shipping_packaging
13
+ xml.qualifierFreightGeneralFragile(@anything.fragile ? 'Y' : 'N')
14
+ xml.distanceMeasurementType 'Centimetres'
15
+ xml.weightMeasurementType 'Kilograms'
16
+ xml.length((@anything.length.to_f * 100).ceil)
17
+ xml.width((@anything.width.to_f * 100).ceil)
18
+ xml.height((@anything.height.to_f * 100).ceil)
19
+ xml.weight @anything.weight.ceil
20
+ xml.quantity @anything.quantity
21
+ xml.description @anything.description
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ module Temando::Api
2
+ module Entities
3
+ class Anywhere
4
+ def initialize(anywhere)
5
+ @anywhere = anywhere
6
+ end
7
+
8
+ def build_xml(xml)
9
+ xml.anywhere do
10
+ xml.parent.namespace = nil
11
+ xml.itemNature @anywhere.shipping_nature
12
+ xml.itemMethod @anywhere.shipping_method
13
+ xml.originCountry @anywhere.origin.country
14
+ xml.originCode @anywhere.origin.postcode
15
+ xml.originSuburb @anywhere.origin.suburb
16
+ xml.destinationCountry @anywhere.destination.country
17
+ xml.destinationCode @anywhere.destination.postcode
18
+ xml.destinationSuburb @anywhere.destination.suburb
19
+ xml.destinationIs 'Residence'
20
+ xml.destinationResNotifyBefore 'N'
21
+ xml.destinationResLimitedAccess 'N'
22
+ xml.originIs 'Business'
23
+ xml.originBusNotifyBefore 'Y'
24
+ xml.originBusLimitedAccess 'N'
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,30 @@
1
+ require 'active_support/core_ext/hash/conversions'
2
+ require 'bigdecimal'
3
+
4
+ module Temando::Api
5
+ module Entities
6
+ # Handles turning a SOAP <quote> element into a Temando::Quote and
7
+ # back again.
8
+ class Quote
9
+ def self.parse_xml(quote_xml)
10
+ hash = Hash.from_xml(quote_xml)['quote']
11
+ quote = Temando::Quote.new
12
+
13
+ quote.total_price = BigDecimal.new(hash['totalPrice'])
14
+ quote.base_price = BigDecimal.new(hash['basePrice'])
15
+ quote.tax = BigDecimal.new(hash['tax'])
16
+ quote.currency = hash['currency']
17
+
18
+ quote.name = hash['deliveryMethod']
19
+
20
+ quote.minimum_eta = hash['etaFrom'].to_i
21
+ quote.maximum_eta = hash['etaTo'].to_i
22
+ quote.guaranteed_eta = (hash['guaranteedEta'] == 'Y')
23
+
24
+ quote.carrier_id = hash['carrier']['id']
25
+
26
+ quote
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,14 @@
1
+ module Temando::Api
2
+ module Exceptions
3
+ # Raised when the ASI returns a SOAP Fault object.
4
+ class SoapError < StandardError
5
+ attr_reader :last_request, :last_response
6
+
7
+ def initialize(message, last_request, last_response)
8
+ @last_request = last_request
9
+ @last_response = last_response
10
+ super(message)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,48 @@
1
+ module Temando
2
+ module Api
3
+ # Abstracts generating the request for the SOAP API's
4
+ # getQuotesByRequest method, and parsing the response.
5
+ class GetQuotesByRequest < Temando::Api::Base
6
+
7
+ def initialize(items, delivery)
8
+ @items = items
9
+ @delivery = delivery
10
+
11
+ @items.each { |item| validate_item(item) }
12
+ end
13
+
14
+ def request_xml
15
+ soap_boilerplate do |xml|
16
+ xml.getQuotesByRequest('xmlns:tem' => TEMANDO_NAMESPACE) do
17
+ xml.parent.namespace = xml.parent.namespace_definitions.find { |x| x.prefix == 'tem' }
18
+
19
+ xml.anythings do
20
+ xml.parent.namespace = nil
21
+ @items.each do |anything|
22
+ Temando::Api::Entities::Anything.new(anything).build_xml(xml)
23
+ end
24
+ end
25
+
26
+ Temando::Api::Entities::Anywhere.new(@delivery).build_xml(xml)
27
+
28
+ # TODO: anytime
29
+ # TODO: general
30
+ end
31
+ end
32
+ end
33
+
34
+ def parse_response(response_xml)
35
+ xml = Nokogiri::XML(response_xml)
36
+ quotes = xml.xpath('//quote').collect do |result|
37
+ Temando::Api::Entities::Quote.parse_xml(result.to_xml)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def validate_item(item)
44
+ raise ArgumentError, "#{item.inspect} is not valid" unless item.valid?
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,64 @@
1
+ require 'active_support/configurable'
2
+
3
+ module Temando
4
+ module Api
5
+ class SoapClient
6
+ include ActiveSupport::Configurable
7
+
8
+ attr_reader :last_request, :last_response
9
+ config_accessor :service_url
10
+
11
+ config.service_url = "https://api.temando.com/soapServer.html"
12
+
13
+ def dispatch(request_xml)
14
+ response = ''
15
+
16
+ @last_request = request_xml
17
+
18
+ process_response(perform_request(request_xml))
19
+ end
20
+
21
+ private
22
+
23
+ def logger
24
+ Temando::Api::Base.logger
25
+ end
26
+
27
+ def perform_request(request_xml)
28
+ logger.debug request_xml if logger.present?
29
+ Typhoeus::Request.post(service_url,
30
+ :body => request_xml,
31
+ :headers => {'Content-Type' => "text/xml; charset=utf-8"}
32
+ )
33
+ end
34
+
35
+ # Processes the response and decides whether to handle an error or whether to return the content
36
+ def process_response(response)
37
+ @last_response = response.body
38
+
39
+ logger.debug @last_response if logger.present?
40
+
41
+ if response.body =~ /:Fault>/ then
42
+ handle_error(response)
43
+ else
44
+ response.body
45
+ end
46
+ end
47
+
48
+ # Parses a soap:Fault error and raises it as a Temando::SoapError
49
+ def handle_error(response)
50
+ xml = Nokogiri::XML(response.body)
51
+ xpath = '/SOAP-ENV:Envelope/SOAP-ENV:Body/SOAP-ENV:Fault//faultstring'
52
+ msg = xml.xpath(xpath).text
53
+
54
+ if msg.to_s.strip == '' then
55
+ xpath = '/SOAP-ENV:Envelope/SOAP-ENV:Body/SOAP-ENV:Fault//faultcode'
56
+ msg = xml.xpath(xpath).text
57
+ end
58
+
59
+ # It was a real error - raise the exception
60
+ raise Temando::Api::Exceptions::SoapError.new("Error from server: #{msg}", self.last_request, self.last_response)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,27 @@
1
+ module Temando
2
+ module Delivery
3
+ # A Temando::Delivery::Base represents ways of shipping
4
+ # items.
5
+ #
6
+ # The Temando API refers to a delivery action as an "Anywhere".
7
+ #
8
+ # This is a base class for the delivery mechanisms,
9
+ # Temando::Delivery::DoorToDoor and Temando::Delivery::DepotToDepot
10
+ class Base
11
+ attr_reader :origin, :destination
12
+
13
+ def initialize(origin = nil, destination = nil)
14
+ self.origin = origin
15
+ self.destination = destination
16
+ end
17
+
18
+ def origin=(location)
19
+ @origin = location
20
+ end
21
+
22
+ def destination=(location)
23
+ @destination = location
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,16 @@
1
+ module Temando
2
+ module Delivery
3
+ # A Temando::Delivery::DoorToDoor represents the collection of a
4
+ # shipment from physical premises (eg. home or office) and shipping
5
+ # them to other physical premises.
6
+ class DoorToDoor < Temando::Delivery::Base
7
+ def shipping_nature
8
+ 'Domestic'
9
+ end
10
+
11
+ def shipping_method
12
+ 'Door to Door'
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,29 @@
1
+ require 'active_model'
2
+
3
+ module Temando
4
+ module Item
5
+ # A Temando::Item::Base is a physical item (or a quantity of the
6
+ # same item) which are to be shipped.
7
+ #
8
+ # The Temando API refers to these as "Anythings".
9
+ #
10
+ # We don't ship a Temando::Item::Base itself, but rather its
11
+ # subclasses, Temando::Item::GeneralGoods, Temando::Item::Freight,
12
+ # etc.
13
+ class Base
14
+
15
+ include ActiveModel::Validations
16
+
17
+ attr_accessor :length, :width, :height, :weight, :quantity, :description
18
+
19
+ validates_numericality_of :length, :width, :height
20
+ validates_numericality_of :quantity, :greater_than => 0, :only_integer => true
21
+
22
+ def initialize(attributes={})
23
+ attributes.each do |key, value|
24
+ self.send("#{key}=".intern, value)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,22 @@
1
+ module Temando
2
+ module Item
3
+ # A GeneralGood is a type of item that can be shipped by Temando.
4
+ class GeneralGoods < Temando::Item::Base
5
+ def shipping_class
6
+ 'General Goods'
7
+ end
8
+
9
+ def shipping_subclass
10
+ 'Household Goods'
11
+ end
12
+
13
+ def shipping_packaging
14
+ 'Parcel'
15
+ end
16
+
17
+ def fragile
18
+ false
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ module Temando
2
+ # A Temando::Location represents address and contact details for
3
+ # pickup or delivery of a shipment.
4
+ class Location
5
+ attr_accessor :country, :postcode, :suburb, :contact, :state, :street, :phone1, :phone2, :email, :company
6
+
7
+ def initialize(attributes={})
8
+ attributes.each do |key, value|
9
+ self.send("#{key}=".intern, value)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ module Temando
2
+ # A Quote is a representation of the shipping quote provided by one of
3
+ # Temando's suppliers.
4
+ class Quote
5
+ attr_accessor :total_price, :tax, :currency, :minimum_eta, :maximum_eta, :name, :base_price, :guaranteed_eta, :carrier_id
6
+ end
7
+ end
@@ -0,0 +1,49 @@
1
+ module Temando
2
+ # Temando::Request represents a request for a quotation from the
3
+ # Temando API.
4
+ #
5
+ # It is used to construct the parameters for a quotation, send them
6
+ # off to the API and return the available quotes.
7
+ #
8
+ # Example:
9
+ # request = Temando::Request.new
10
+ #
11
+ # # Add the items to be shipped
12
+ # request.items << Temando::Item::GeneralGoods.new(...)
13
+ # request.items << Temando::Item::GeneralGoods.new(...)
14
+ #
15
+ # # Add the details for the actual shipment method and its locations
16
+ # anywhere = Temando::Delivery::DoorToDoor.new
17
+ # anywhere.origin = Temando::Location.new(...)
18
+ # anywhere.destination = Temando::Location.new(...)
19
+ #
20
+ # # Ask the server for the quotes
21
+ # quotes = request.quotes_for(anywhere)
22
+ #
23
+ # quotes.first # => #<Temando::Quote>
24
+ #
25
+ # Temando authentication details should be set before calling these methods :
26
+ #
27
+ # Temando::Api::Base.config.username = 'myuser@example.com'
28
+ # Temando::Api::Base.config.password = 'sekrit'
29
+ class Request
30
+
31
+ attr_accessor :items
32
+
33
+ def initialize
34
+ @items = []
35
+ end
36
+
37
+ def quotes_for(delivery)
38
+ # Construct the request, dispatch it off to the server and return
39
+ # the result.
40
+ formatter = Temando::Api::GetQuotesByRequest.new(@items, delivery)
41
+ response = client.dispatch(formatter.request_xml)
42
+ formatter.parse_response(response)
43
+ end
44
+
45
+ def client
46
+ @client ||= Temando::Api::SoapClient.new
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,15 @@
1
+ # RSpec helpers for Temando.
2
+ module Temando
3
+ module RSpecHelpers
4
+ # Stubs the SOAP response data for a get_quotes request for a
5
+ # Temando::Response object.
6
+ def stub_temando_request(request, fixture_content)
7
+ request.client.should_receive(:dispatch).with(anything()).and_return(fixture_content)
8
+ end
9
+ end
10
+ end
11
+ if defined?(RSpec) then
12
+ RSpec.configure do |c|
13
+ c.include Temando::RSpecHelpers
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module Temando
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
3
+ <SOAP-ENV:Body>
4
+ <SOAP-ENV:Fault>
5
+ <faultcode>Err_Access03</faultcode>
6
+ <faultstring>The login details you have provided are either not a valid Username and Password combination or an invalid id.</faultstring>
7
+ <faultactor>https://api.temando.com/soapServer.html</faultactor>
8
+ </SOAP-ENV:Fault>
9
+ </SOAP-ENV:Body>
10
+ </SOAP-ENV:Envelope>
@@ -0,0 +1,47 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
3
+ <soapenv:Header>
4
+ <wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/04/secext">
5
+ <wsse:UsernameToken>
6
+ <wsse:Username>username</wsse:Username>
7
+ <wsse:Password>password</wsse:Password>
8
+ </wsse:UsernameToken>
9
+ </wsse:Security>
10
+ </soapenv:Header>
11
+ <soapenv:Body>
12
+ <tem:getQuotesByRequest xmlns:tem="http://api.temando.com/schema/2009_06/server.xsd">
13
+ <anythings>
14
+ <anything>
15
+ <class>General Goods</class>
16
+ <subclass>Household Goods</subclass>
17
+ <packaging>Parcel</packaging>
18
+ <qualifierFreightGeneralFragile>N</qualifierFreightGeneralFragile>
19
+ <distanceMeasurementType>Centimetres</distanceMeasurementType>
20
+ <weightMeasurementType>Kilograms</weightMeasurementType>
21
+ <length>10</length>
22
+ <width>10</width>
23
+ <height>10</height>
24
+ <weight>1</weight>
25
+ <quantity>2</quantity>
26
+ <description>Contains hats.</description>
27
+ </anything>
28
+ </anythings>
29
+ <anywhere>
30
+ <itemNature>Domestic</itemNature>
31
+ <itemMethod>Door to Door</itemMethod>
32
+ <originCountry>AU</originCountry>
33
+ <originCode>4000</originCode>
34
+ <originSuburb>Brisbane</originSuburb>
35
+ <destinationCountry>AU</destinationCountry>
36
+ <destinationCode>2000</destinationCode>
37
+ <destinationSuburb>Sydney</destinationSuburb>
38
+ <destinationIs>Residence</destinationIs>
39
+ <destinationResNotifyBefore>N</destinationResNotifyBefore>
40
+ <destinationResLimitedAccess>N</destinationResLimitedAccess>
41
+ <originIs>Business</originIs>
42
+ <originBusNotifyBefore>Y</originBusNotifyBefore>
43
+ <originBusLimitedAccess>N</originBusLimitedAccess>
44
+ </anywhere>
45
+ </tem:getQuotesByRequest>
46
+ </soapenv:Body>
47
+ </soapenv:Envelope>
@@ -0,0 +1,80 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
3
+ <SOAP-ENV:Header/>
4
+ <SOAP-ENV:Body>
5
+ <ns1:getQuotesByRequestResponse xmlns:ns1="http://api.temando.com/schema/2009_06/server.xsd">
6
+ <quote>
7
+ <generated>Automatically</generated>
8
+ <accepted>N</accepted>
9
+ <totalPrice>11.00</totalPrice>
10
+ <basePrice>10.00</basePrice>
11
+ <tax>1.00</tax>
12
+ <currency>AUD</currency>
13
+ <deliveryMethod>Off-peak</deliveryMethod>
14
+ <usingGeneralRail>N</usingGeneralRail>
15
+ <usingGeneralRoad>Y</usingGeneralRoad>
16
+ <usingGeneralSea>N</usingGeneralSea>
17
+ <usingExpressAir>N</usingExpressAir>
18
+ <usingExpressRoad>N</usingExpressRoad>
19
+ <etaFrom>2</etaFrom>
20
+ <etaTo>3</etaTo>
21
+ <guaranteedEta>Y</guaranteedEta>
22
+ <adjustments>
23
+ <adjustment>
24
+ <description>Credit Card Payment Adjustment</description>
25
+ <amount>0.20</amount>
26
+ <tax>0.02</tax>
27
+ </adjustment>
28
+ <adjustment>
29
+ <description>Credit Payment Adjustment</description>
30
+ <amount>-0.20</amount>
31
+ <tax>-0.02</tax>
32
+ </adjustment>
33
+ </adjustments>
34
+ <inclusions><inclusion><summary>Tracking available</summary><details>Tracking information will be available for this quote.
35
+ </details></inclusion>
36
+  <inclusion><summary>Automatic Booking</summary><details>Automatic booking generated.</details></inclusion>
37
+ <inclusion><summary>Automatic Consignment</summary><details>Automatic consignment note generated.</details></inclusion>
38
+ </inclusions>
39
+ <extras>
40
+ <extra>
41
+ <summary>Insurance</summary>
42
+ <details>Insurance cover up to $5,000.</details>
43
+ <totalPrice>2.20</totalPrice>
44
+ <basePrice>2.00</basePrice>
45
+ <tax>0.20</tax>
46
+ <mandatory>Y</mandatory>
47
+ <adjustments>
48
+ <adjustment>
49
+ <description>Credit Card Payment Adjustment</description>
50
+ <amount>0.04</amount>
51
+ <tax>0.00</tax>
52
+ </adjustment>
53
+ </adjustments>
54
+ </extra>
55
+ </extras>
56
+ <carrier>
57
+ <id>654321</id>
58
+ <companyName>ZYX Transport</companyName>
59
+ <companyContact>John Doe</companyContact>
60
+ <streetAddress>1 Transport Street</streetAddress>
61
+ <streetSuburb>Brisbane</streetSuburb>
62
+ <streetCity>Brisbane</streetCity>
63
+ <streetState>QLD</streetState>
64
+ <streetCode>4000</streetCode>
65
+ <streetCountry>AU</streetCountry>
66
+ <postalAddress>PO Box 1111</postalAddress>
67
+ <postalSuburb>Brisbane</postalSuburb>
68
+ <postalCity>Brisbane</postalCity>
69
+ <postalState>QLD</postalState>
70
+ <postalCode>4001</postalCode>
71
+ <postalCountry>AU</postalCountry>
72
+ <phone1>(07) 3333 3333</phone1>
73
+ <email>john.doe@zyxtransport.com.au</email>
74
+ <website>https://www.zyxtransport.com.au</website>
75
+ <conditions>Our conditions include: lots of text.</conditions>
76
+ </carrier>
77
+ </quote>
78
+ </ns1:getQuotesByRequestResponse>
79
+ </SOAP-ENV:Body>
80
+ </SOAP-ENV:Envelope>
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Get Quotes" do
4
+
5
+ it "can get the quotes from Temando" do
6
+ request = Temando::Request.new
7
+ request.items << valid_temando_item
8
+
9
+ anywhere = Temando::Delivery::DoorToDoor.new
10
+ anywhere.origin = Temando::Location.new(:contact => "Joe Bloggs",
11
+ :company => "Bloggs Inc.",
12
+ :street => "123 Test Street",
13
+ :suburb => "Sydney",
14
+ :state => "NSW",
15
+ :postcode => "2000",
16
+ :phone1 => Faker::PhoneNumber.phone_number,
17
+ :email => Faker::Internet.email,
18
+ :country => "AU"
19
+ )
20
+
21
+ anywhere.destination = Temando::Location.new(:contact => "Joe Bloggs",
22
+ :company => "Bloggs Inc.",
23
+ :street => "123 Test Street",
24
+ :suburb => "Sydney",
25
+ :state => "NSW",
26
+ :postcode => "2000",
27
+ :phone1 => Faker::PhoneNumber.phone_number,
28
+ :email => Faker::Internet.email,
29
+ :country => "AU"
30
+ )
31
+
32
+ stub_temando_request(request, xml_fixture('get_quotes_by_request/response'))
33
+
34
+ quotes = request.quotes_for(anywhere)
35
+ quotes.should be_a(Array)
36
+ quotes.count.should == 1
37
+
38
+ quote = quotes.first
39
+ quote.should be_a(Temando::Quote)
40
+ end
41
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Temando::Request, :remote => true do
4
+
5
+ describe ".quotes_for" do
6
+ let(:item) { valid_temando_item }
7
+ let(:origin) { Temando::Location.new(:country => 'AU',
8
+ :suburb => 'Brisbane',
9
+ :postcode => '4000'
10
+ ) }
11
+ let(:destination) { Temando::Location.new(:country => 'AU',
12
+ :suburb => 'Sydney',
13
+ :postcode => '2000'
14
+ ) }
15
+ let(:delivery) { Temando::Delivery::DoorToDoor.new(origin, destination) }
16
+ let(:request) { subject.items << item; subject }
17
+
18
+ it "dispatches the data in and out from Temando::Api::GetQuotesByRequest" do
19
+ pending "provide real API details"
20
+ Temando::Api::Base.config.username = 'username'
21
+ Temando::Api::Base.config.password = 'password'
22
+
23
+ response = request.quotes_for(delivery)
24
+
25
+ response.should be_a(Array)
26
+ response.first.should be_a(Temando::Quote)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ require 'bundler/setup'
2
+ require 'temando'
3
+ require 'temando/rspec'
4
+ require 'faker'
5
+
6
+ Dir[File.join(File.dirname(__FILE__), "/support/**/*.rb")].each {|f| require f}
7
+
8
+ # This file was generated by the `rspec --init` command. Conventionally, all
9
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
10
+ # Require this file using `require "spec_helper"` to ensure that it is only
11
+ # loaded once.
12
+ #
13
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
14
+ RSpec.configure do |config|
15
+ config.treat_symbols_as_metadata_keys_with_true_values = true
16
+ config.run_all_when_everything_filtered = true
17
+ config.filter_run :focus
18
+
19
+ # Run specs in random order to surface order dependencies. If you find an
20
+ # order dependency and want to debug it, you can fix the order by providing
21
+ # the seed, which is printed after each run.
22
+ # --seed 1234
23
+ config.order = 'random'
24
+
25
+ config.before(:each) do
26
+ Temando::Api::Base.config.username = 'username'
27
+ Temando::Api::Base.config.password = 'password'
28
+ end
29
+ end
@@ -0,0 +1,14 @@
1
+ module DefaultDisableRemote
2
+ def stub_remote!
3
+ Temando::Api::SoapClient.any_instance.stub(:perform_request).and_return { raise "External requests are disabled, but have not been correctly stubbed" }
4
+ end
5
+ end
6
+
7
+ RSpec.configure do |config|
8
+ config.include DefaultDisableRemote
9
+ config.before(:each) do
10
+ unless example.metadata[:remote] || (example.metadata[:disable_remote] == false)
11
+ stub_remote!
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ module ItemFactory
2
+ def valid_temando_item(options={})
3
+ Temando::Item::GeneralGoods.new({:length => 0.1, :width => 0.1, :height => 0.1, :weight => 1.0, :quantity => 1, :description => 'Hats'}.merge(options))
4
+ end
5
+ end
6
+
7
+ RSpec.configure do |c|
8
+ c.include ItemFactory
9
+ end
@@ -0,0 +1,9 @@
1
+ module XmlFixtures
2
+ def xml_fixture(name)
3
+ File.read(File.join(File.dirname(__FILE__), '..', 'fixtures', 'xml', name.to_s + '.xml'))
4
+ end
5
+ end
6
+
7
+ RSpec.configure do |c|
8
+ c.include XmlFixtures
9
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Temando::Api::Entities::Quote do
4
+
5
+ describe ".parse_xml" do
6
+ it "parses a <quote> entity into a Temando::Quote object" do
7
+ xml = Nokogiri::XML(xml_fixture('get_quotes_by_request/response'))
8
+ input_xml = xml.xpath('//quote').first.to_xml
9
+
10
+ quote = Temando::Api::Entities::Quote.parse_xml(input_xml)
11
+ quote.should be_a(Temando::Quote)
12
+ quote.total_price.should == 11.00
13
+ quote.tax.should == 1.0
14
+ quote.base_price.should == 10.00
15
+ quote.currency.should == 'AUD'
16
+ quote.minimum_eta.should == 2
17
+ quote.maximum_eta.should == 3
18
+ quote.guaranteed_eta.should be_true
19
+ quote.carrier_id.should == '654321'
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Temando::Api::GetQuotesByRequest do
4
+ let(:subject) { Temando::Api::GetQuotesByRequest.new([item], delivery) }
5
+ let(:item) { valid_temando_item(:description => 'Contains hats.', :quantity => 2) }
6
+ let(:origin) { Temando::Location.new(:country => 'AU',
7
+ :suburb => 'Brisbane',
8
+ :postcode => '4000'
9
+ ) }
10
+ let(:destination) { Temando::Location.new(:country => 'AU',
11
+ :suburb => 'Sydney',
12
+ :postcode => '2000'
13
+ ) }
14
+
15
+ let(:delivery) { Temando::Delivery::DoorToDoor.new(origin, destination) }
16
+
17
+ describe ".request_xml" do
18
+ it "generates a SOAP request" do
19
+ subject.request_xml.should == xml_fixture('get_quotes_by_request/request')
20
+ end
21
+ end
22
+
23
+ describe ".parse_response" do
24
+ it "decodes the SOAP response" do
25
+ Temando::Api::Entities::Quote.should_receive(:parse_xml).with(anything()).exactly(:once)
26
+ result = subject.parse_response(xml_fixture('get_quotes_by_request/response'))
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Temando::Api::SoapClient do
4
+
5
+ describe ".dispatch", :disable_remote => false do
6
+ let(:ok_response) { r = mock(Typhoeus::Response); r.stub(:body).and_return(xml_fixture('get_quotes_by_request/response')); r }
7
+ let(:bad_response) { r = mock(Typhoeus::Response); r.stub(:body).and_return(xml_fixture('exceptions/failed_auth')); r }
8
+
9
+ it "sends a request to the server" do
10
+ Typhoeus::Request.should_receive(:post).and_return(ok_response)
11
+
12
+ subject.dispatch('XML')
13
+ end
14
+
15
+ it "raises exceptions on soapfault responses" do
16
+ subject.should_receive(:perform_request).and_return(bad_response)
17
+ lambda { subject.dispatch('XML') }.should raise_exception(Temando::Api::Exceptions::SoapError)
18
+ end
19
+
20
+ it "returns the XML data on successful responses" do
21
+ subject.should_receive(:perform_request).and_return(ok_response)
22
+ subject.dispatch('XML').should =~ /getQuotesByRequestResponse/
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Temando::Delivery::DoorToDoor do
4
+
5
+ describe ".origin" do
6
+ it { should respond_to(:origin) }
7
+
8
+ it "accepts Temando::Location" do
9
+ lambda { subject.origin = Temando::Location.new }.should_not raise_exception
10
+ end
11
+ end
12
+
13
+ describe ".destination" do
14
+ it { should respond_to(:destination) }
15
+
16
+ it "accepts Temando::Location" do
17
+ lambda { subject.destination = Temando::Location.new }.should_not raise_exception
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Temando::Item::GeneralGoods do
4
+ let(:valid_item) { valid_temando_item }
5
+
6
+ describe ".valid?" do
7
+ it "should be valid" do
8
+ valid_item.should be_valid
9
+ end
10
+
11
+ it "is invalid with a blank quantity" do
12
+ valid_item.quantity = nil
13
+ valid_item.should_not be_valid
14
+ end
15
+
16
+ it "is invalid with a blank length" do
17
+ valid_item.length = nil
18
+ valid_item.should_not be_valid
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Temando::Request do
4
+
5
+ describe ".items" do
6
+ it { should respond_to(:items) }
7
+ end
8
+
9
+ describe ".quotes_for" do
10
+ let(:item) { valid_temando_item }
11
+ let(:delivery) { Temando::Delivery::DoorToDoor.new }
12
+ let(:request) { subject.items << item; subject }
13
+
14
+ it "dispatches the data in and out from Temando::Api::GetQuotesByRequest" do
15
+ format = mock(Temando::Api::GetQuotesByRequest)
16
+ Temando::Api::GetQuotesByRequest.should_receive(:new).with([item], delivery).and_return(format)
17
+ format.should_receive(:request_xml).and_return('REQUEST XML')
18
+
19
+ response = [ Temando::Quote.new ]
20
+
21
+ format.should_receive(:parse_response).with('RESPONSE XML').and_return(response)
22
+ request.client.should_receive(:dispatch).with('REQUEST XML').and_return('RESPONSE XML')
23
+
24
+ request.quotes_for(delivery).should == response
25
+ end
26
+ end
27
+
28
+ describe ".dispatch_request" do
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/temando/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Jason Stirk"]
6
+ gem.email = ["jason@reinteractive.net"]
7
+ gem.description = %q{Ruby API to Temando - an Australian transport fulfilment broker}
8
+ gem.summary = %q{Ruby API to Temando - an Australian transport fulfilment broker}
9
+ gem.homepage = "http://github.com/reInteractive-open/temando"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "temando"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Temando::VERSION
17
+
18
+ gem.add_dependency 'nokogiri'
19
+ gem.add_dependency 'typhoeus', '~> 0.4.2'
20
+ gem.add_dependency 'activesupport'
21
+ gem.add_dependency 'activemodel'
22
+
23
+ gem.add_development_dependency 'rspec', '~> 2.11.0'
24
+ gem.add_development_dependency 'faker'
25
+ end
metadata ADDED
@@ -0,0 +1,194 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: temando
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jason Stirk
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: nokogiri
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: typhoeus
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.4.2
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.4.2
46
+ - !ruby/object:Gem::Dependency
47
+ name: activesupport
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: activemodel
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rspec
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 2.11.0
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 2.11.0
94
+ - !ruby/object:Gem::Dependency
95
+ name: faker
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: Ruby API to Temando - an Australian transport fulfilment broker
111
+ email:
112
+ - jason@reinteractive.net
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - .gitignore
118
+ - Gemfile
119
+ - LICENSE
120
+ - README.md
121
+ - Rakefile
122
+ - lib/temando.rb
123
+ - lib/temando/api/base.rb
124
+ - lib/temando/api/entities/anything.rb
125
+ - lib/temando/api/entities/anywhere.rb
126
+ - lib/temando/api/entities/quote.rb
127
+ - lib/temando/api/exceptions/soap_error.rb
128
+ - lib/temando/api/get_quotes_by_request.rb
129
+ - lib/temando/api/soap_client.rb
130
+ - lib/temando/delivery/base.rb
131
+ - lib/temando/delivery/door_to_door.rb
132
+ - lib/temando/item/base.rb
133
+ - lib/temando/item/general_goods.rb
134
+ - lib/temando/location.rb
135
+ - lib/temando/quote.rb
136
+ - lib/temando/request.rb
137
+ - lib/temando/rspec.rb
138
+ - lib/temando/version.rb
139
+ - spec/fixtures/xml/exceptions/failed_auth.xml
140
+ - spec/fixtures/xml/get_quotes_by_request/request.xml
141
+ - spec/fixtures/xml/get_quotes_by_request/response.xml
142
+ - spec/integration/get_quotes_spec.rb
143
+ - spec/remote/remote_request_spec.rb
144
+ - spec/spec_helper.rb
145
+ - spec/support/default_disable_remote.rb
146
+ - spec/support/factories/item_factory.rb
147
+ - spec/support/xml_fixtures.rb
148
+ - spec/units/api/entities/quote_spec.rb
149
+ - spec/units/api/get_quotes_by_request_spec.rb
150
+ - spec/units/api/soap_client_spec.rb
151
+ - spec/units/delivery/door_to_door_spec.rb
152
+ - spec/units/item/general_goods_spec.rb
153
+ - spec/units/request_spec.rb
154
+ - temando.gemspec
155
+ homepage: http://github.com/reInteractive-open/temando
156
+ licenses: []
157
+ post_install_message:
158
+ rdoc_options: []
159
+ require_paths:
160
+ - lib
161
+ required_ruby_version: !ruby/object:Gem::Requirement
162
+ none: false
163
+ requirements:
164
+ - - ! '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ required_rubygems_version: !ruby/object:Gem::Requirement
168
+ none: false
169
+ requirements:
170
+ - - ! '>='
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ requirements: []
174
+ rubyforge_project:
175
+ rubygems_version: 1.8.19
176
+ signing_key:
177
+ specification_version: 3
178
+ summary: Ruby API to Temando - an Australian transport fulfilment broker
179
+ test_files:
180
+ - spec/fixtures/xml/exceptions/failed_auth.xml
181
+ - spec/fixtures/xml/get_quotes_by_request/request.xml
182
+ - spec/fixtures/xml/get_quotes_by_request/response.xml
183
+ - spec/integration/get_quotes_spec.rb
184
+ - spec/remote/remote_request_spec.rb
185
+ - spec/spec_helper.rb
186
+ - spec/support/default_disable_remote.rb
187
+ - spec/support/factories/item_factory.rb
188
+ - spec/support/xml_fixtures.rb
189
+ - spec/units/api/entities/quote_spec.rb
190
+ - spec/units/api/get_quotes_by_request_spec.rb
191
+ - spec/units/api/soap_client_spec.rb
192
+ - spec/units/delivery/door_to_door_spec.rb
193
+ - spec/units/item/general_goods_spec.rb
194
+ - spec/units/request_spec.rb