temando 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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