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.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +64 -0
- data/Rakefile +13 -0
- data/lib/temando.rb +27 -0
- data/lib/temando/api/base.rb +40 -0
- data/lib/temando/api/entities/anything.rb +26 -0
- data/lib/temando/api/entities/anywhere.rb +29 -0
- data/lib/temando/api/entities/quote.rb +30 -0
- data/lib/temando/api/exceptions/soap_error.rb +14 -0
- data/lib/temando/api/get_quotes_by_request.rb +48 -0
- data/lib/temando/api/soap_client.rb +64 -0
- data/lib/temando/delivery/base.rb +27 -0
- data/lib/temando/delivery/door_to_door.rb +16 -0
- data/lib/temando/item/base.rb +29 -0
- data/lib/temando/item/general_goods.rb +22 -0
- data/lib/temando/location.rb +13 -0
- data/lib/temando/quote.rb +7 -0
- data/lib/temando/request.rb +49 -0
- data/lib/temando/rspec.rb +15 -0
- data/lib/temando/version.rb +3 -0
- data/spec/fixtures/xml/exceptions/failed_auth.xml +10 -0
- data/spec/fixtures/xml/get_quotes_by_request/request.xml +47 -0
- data/spec/fixtures/xml/get_quotes_by_request/response.xml +80 -0
- data/spec/integration/get_quotes_spec.rb +41 -0
- data/spec/remote/remote_request_spec.rb +29 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/default_disable_remote.rb +14 -0
- data/spec/support/factories/item_factory.rb +9 -0
- data/spec/support/xml_fixtures.rb +9 -0
- data/spec/units/api/entities/quote_spec.rb +23 -0
- data/spec/units/api/get_quotes_by_request_spec.rb +29 -0
- data/spec/units/api/soap_client_spec.rb +26 -0
- data/spec/units/delivery/door_to_door_spec.rb +21 -0
- data/spec/units/item/general_goods_spec.rb +21 -0
- data/spec/units/request_spec.rb +32 -0
- data/temando.gemspec +25 -0
- metadata +194 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
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.
|
data/README.md
ADDED
@@ -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
|
data/Rakefile
ADDED
@@ -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
|
data/lib/temando.rb
ADDED
@@ -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,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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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,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
|
data/temando.gemspec
ADDED
@@ -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
|