click_and_send 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +10 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +41 -0
- data/Rakefile +5 -0
- data/click_and_send.gemspec +25 -0
- data/lib/click_and_send.rb +92 -0
- data/lib/click_and_send/configuration.rb +18 -0
- data/lib/click_and_send/encryption.rb +44 -0
- data/lib/click_and_send/errors.rb +6 -0
- data/lib/click_and_send/request.rb +100 -0
- data/lib/click_and_send/request/check_services.rb +16 -0
- data/lib/click_and_send/request/create_items.rb +16 -0
- data/lib/click_and_send/request/delete_items.rb +16 -0
- data/lib/click_and_send/request/item_summary.rb +16 -0
- data/lib/click_and_send/request/print_item.rb +16 -0
- data/lib/click_and_send/version.rb +3 -0
- data/lib/click_and_send/xml.rb +11 -0
- data/spec/acceptance_spec.rb +74 -0
- data/spec/click_and_send/request_spec.rb +95 -0
- data/spec/click_and_send/xml_spec.rb +16 -0
- data/spec/click_and_send_spec.rb +157 -0
- data/spec/fixtures/config.example.yml +5 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/factories.rb +128 -0
- data/spec/support/helpers.rb +25 -0
- data/spec/support/open_uri.rb +2 -0
- data/spec/support/shared_examples.rb +19 -0
- metadata +157 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Zubin Henner
|
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,41 @@
|
|
1
|
+
# ClickAndSend
|
2
|
+
|
3
|
+
Ruby adapter for Australia Post's [ClickAndSend](http://www.clickandsend.com.au/) API.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'click_and_send'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install click_and_send
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Configure like this:
|
22
|
+
|
23
|
+
ClickAndSend.configure do |c|
|
24
|
+
c.account_number = '1234567'
|
25
|
+
c.api_key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
|
26
|
+
c.user_name = 'acme'
|
27
|
+
c.wsdl = 'https://auspost-staging.oss.neopost-id.com/modules/oss/api/ship/wsdl?sz_Client=AustraliaPost'
|
28
|
+
end
|
29
|
+
|
30
|
+
Note that the WSDL URL above is for ClickAndSend's staging environment.
|
31
|
+
|
32
|
+
You should only need to call methods on the `ClickAndSend` class.
|
33
|
+
Refer to the docs and acceptance tests for usage examples.
|
34
|
+
|
35
|
+
## Contributing
|
36
|
+
|
37
|
+
1. Fork it
|
38
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
39
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
40
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
41
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/click_and_send/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Zubin Henner"]
|
6
|
+
gem.email = ["zubin.henner@gmail.com"]
|
7
|
+
gem.description = %q{Ruby adapter for Australia Post's ClickAndSend API}
|
8
|
+
gem.summary = %q{Ruby adapter for Australia Post's ClickAndSend API}
|
9
|
+
gem.homepage = "https://github.com/zubin/click_and_send"
|
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 = "click_and_send"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = ClickAndSend::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency 'nori'
|
19
|
+
gem.add_dependency 'savon'
|
20
|
+
|
21
|
+
gem.add_development_dependency 'rake'
|
22
|
+
gem.add_development_dependency 'rspec'
|
23
|
+
gem.add_development_dependency 'faker'
|
24
|
+
gem.add_development_dependency 'pry'
|
25
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'click_and_send/version'
|
2
|
+
require 'click_and_send/configuration'
|
3
|
+
require 'click_and_send/encryption'
|
4
|
+
require 'click_and_send/errors'
|
5
|
+
require 'click_and_send/request'
|
6
|
+
require 'click_and_send/request/check_services'
|
7
|
+
require 'click_and_send/request/create_items'
|
8
|
+
require 'click_and_send/request/delete_items'
|
9
|
+
require 'click_and_send/request/item_summary'
|
10
|
+
require 'click_and_send/request/print_item'
|
11
|
+
require 'click_and_send/xml'
|
12
|
+
|
13
|
+
module ClickAndSend
|
14
|
+
class << self
|
15
|
+
attr_accessor :configuration
|
16
|
+
|
17
|
+
# Example use:
|
18
|
+
#
|
19
|
+
# ClickAndSend.configure do |c|
|
20
|
+
# c.account_number = '1234567'
|
21
|
+
# c.api_key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
|
22
|
+
# c.user_name = 'acme'
|
23
|
+
# c.wsdl = 'https://auspost-staging.oss.neopost-id.com/modules/oss/api/ship/wsdl?sz_Client=AustraliaPost'
|
24
|
+
# end
|
25
|
+
def configure
|
26
|
+
self.configuration ||= Configuration.new
|
27
|
+
yield(configuration)
|
28
|
+
end
|
29
|
+
|
30
|
+
def check_services(options)
|
31
|
+
ensure_keys_present(options, %w(Sender Receiver ItemDetails))
|
32
|
+
Request::CheckServices.new(options).result.fetch(:product_found)
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_items(options)
|
36
|
+
ensure_keys_present(options, %w(Transactions))
|
37
|
+
Request::CreateItems.new(options).result.fetch(:item_created)
|
38
|
+
end
|
39
|
+
|
40
|
+
def delete_items_by_ref(refs)
|
41
|
+
items = find_tracking_numbers(refs).inject([]) do |carrier, (ref, tracking_numbers)|
|
42
|
+
tracking_numbers.each { |t| carrier << {'DeleteItem' => {'CustTransactionID' => ref, 'TrackingNumber' => t}} }
|
43
|
+
carrier
|
44
|
+
end
|
45
|
+
delete_items('DeleteItems' => items)
|
46
|
+
end
|
47
|
+
|
48
|
+
def delete_items(options)
|
49
|
+
ensure_keys_present(options, %w(DeleteItems))
|
50
|
+
Request::DeleteItems.new(options).result
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_tracking_numbers(refs)
|
54
|
+
item_summary.inject({}) do |hash,item|
|
55
|
+
if refs.include?(item[:cust_transaction_id])
|
56
|
+
hash[item[:cust_transaction_id]] ||= []
|
57
|
+
hash[item[:cust_transaction_id]] << item[:tracking_number]
|
58
|
+
end
|
59
|
+
hash
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def item_summary
|
64
|
+
Request::ItemSummary.new.result.fetch(:item_summary_transaction)
|
65
|
+
end
|
66
|
+
|
67
|
+
def pdf_url(ref)
|
68
|
+
tracking_numbers = find_tracking_numbers([ref]).fetch(ref)
|
69
|
+
case tracking_numbers.length
|
70
|
+
when 0 then raise(Errors::APIError, "Not found: '#{ref}'")
|
71
|
+
when 1 then tracking_number = tracking_numbers.first
|
72
|
+
else
|
73
|
+
raise(Errors::APIError, "Expected 1 but found #{tracking_numbers.length} tracking numbers for 'REF': #{tracking_numbers.join(', ')}")
|
74
|
+
end
|
75
|
+
options = {
|
76
|
+
'PrintFormat' => 'LINK',
|
77
|
+
'CustTransactionID' => ref,
|
78
|
+
'TrackingNumber' => tracking_number
|
79
|
+
}
|
80
|
+
Request::PrintItem.new(options).result
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def ensure_keys_present(hash, required_keys)
|
86
|
+
missing_keys = required_keys - hash.keys
|
87
|
+
if missing_keys.any?
|
88
|
+
raise(Errors::InvalidInput, "Missing: #{missing_keys.join(', ')}")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ClickAndSend
|
2
|
+
class Configuration
|
3
|
+
ATTRIBUTES = [:account_number, :api_version, :api_key, :user_name, :wsdl]
|
4
|
+
attr_accessor *ATTRIBUTES
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@api_version = 10
|
8
|
+
end
|
9
|
+
|
10
|
+
def errors
|
11
|
+
Hash[ATTRIBUTES.collect { |attribute| [attribute, ("is required" if send(attribute).nil?)] }]
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid?
|
15
|
+
errors.values.compact.empty?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'openssl'
|
3
|
+
|
4
|
+
module ClickAndSend
|
5
|
+
class Encryption
|
6
|
+
def initialize(data)
|
7
|
+
@data = data
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
Base64.encode64(encrypted).chomp.gsub(/\={1,2}$/, '') unless @data.nil?
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def cipher
|
17
|
+
@cipher ||= begin
|
18
|
+
cipher = OpenSSL::Cipher::Cipher.new(cipher_type)
|
19
|
+
cipher.encrypt
|
20
|
+
cipher.key = key
|
21
|
+
cipher.iv = key[0..15]
|
22
|
+
cipher
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def cipher_type
|
27
|
+
'AES-256-CBC'
|
28
|
+
end
|
29
|
+
|
30
|
+
def encrypted
|
31
|
+
cipher.update(padded_data)
|
32
|
+
end
|
33
|
+
|
34
|
+
def key
|
35
|
+
ClickAndSend.configuration.api_key
|
36
|
+
end
|
37
|
+
|
38
|
+
# Data must be padded to nearest multiple of 16
|
39
|
+
def padded_data
|
40
|
+
desired_length = (@data.length.to_f / 16).ceil * 16
|
41
|
+
@data.ljust(desired_length, ' ')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'nori'
|
2
|
+
require 'savon'
|
3
|
+
|
4
|
+
module ClickAndSend
|
5
|
+
module Request
|
6
|
+
attr_accessor :data
|
7
|
+
|
8
|
+
def initialize(data = {})
|
9
|
+
self.data = data
|
10
|
+
end
|
11
|
+
|
12
|
+
def result
|
13
|
+
raise_on_error
|
14
|
+
response.fetch(key(:result))
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def client
|
20
|
+
raise ClickAndSend::Errors::InvalidConfiguration unless config.valid?
|
21
|
+
@client ||= Savon.client do |wsdl, http|
|
22
|
+
wsdl.document = config.wsdl
|
23
|
+
http.auth.ssl.verify_mode = :none
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def config
|
28
|
+
ClickAndSend.configuration
|
29
|
+
end
|
30
|
+
|
31
|
+
def encrypt(data)
|
32
|
+
ClickAndSend::Encryption.new(data).to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
def errors
|
36
|
+
response.values_at(*key(:errors)).compact
|
37
|
+
end
|
38
|
+
|
39
|
+
def errors?
|
40
|
+
errors.any?
|
41
|
+
end
|
42
|
+
|
43
|
+
def default_keys
|
44
|
+
{ :errors => [:item_errors, :errors_found] }
|
45
|
+
end
|
46
|
+
|
47
|
+
def extra_request_xml
|
48
|
+
ClickAndSend::XML.new(data).to_xml if data
|
49
|
+
end
|
50
|
+
|
51
|
+
def header
|
52
|
+
<<-XML
|
53
|
+
<Header>
|
54
|
+
<CnSUserName>#{encrypt(config.user_name)}</CnSUserName>
|
55
|
+
<APIAccessKey>#{encrypt(config.api_key)}</APIAccessKey>
|
56
|
+
<RequestDate>#{encrypt(request_date)}</RequestDate>
|
57
|
+
<APIVersion>#{config.api_version}</APIVersion>
|
58
|
+
</Header>
|
59
|
+
XML
|
60
|
+
end
|
61
|
+
|
62
|
+
def key(name)
|
63
|
+
default_keys.merge(keys).fetch(name)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Hash with keys :answer, :request, :response, :result
|
67
|
+
def keys
|
68
|
+
raise(ClickAndSend::Errors::NotImplemented, "#{klass_name}#keys")
|
69
|
+
end
|
70
|
+
|
71
|
+
def klass_name
|
72
|
+
self.class.name
|
73
|
+
end
|
74
|
+
|
75
|
+
def raise_on_error
|
76
|
+
raise(ClickAndSend::Errors::APIError, errors) if errors?
|
77
|
+
end
|
78
|
+
|
79
|
+
def request_date
|
80
|
+
Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
|
81
|
+
end
|
82
|
+
|
83
|
+
def request_xml
|
84
|
+
"<#{key(:request)}Request>%s</#{key(:request)}Request>" % CGI::escapeHTML(<<-XML.strip.gsub(/\n\s+/, "\n"))
|
85
|
+
<#{key(:request)}>
|
86
|
+
#{header}
|
87
|
+
#{extra_request_xml}
|
88
|
+
</#{key(:request)}>
|
89
|
+
XML
|
90
|
+
end
|
91
|
+
|
92
|
+
def response
|
93
|
+
@response ||= Nori.parse(request.to_hash[key(:response)][key(:response)]).fetch(key(:answer))
|
94
|
+
end
|
95
|
+
|
96
|
+
def request
|
97
|
+
client.request(:ind, key(:request), :body => request_xml)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ClickAndSend
|
2
|
+
module Request
|
3
|
+
class CheckServices
|
4
|
+
include ClickAndSend::Request
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def keys
|
9
|
+
{ :answer => :ws_check_services_answer,
|
10
|
+
:request => 'WSCheckServices',
|
11
|
+
:response => :ws_check_services_response,
|
12
|
+
:result => :products_found }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ClickAndSend
|
2
|
+
module Request
|
3
|
+
class CreateItems
|
4
|
+
include ClickAndSend::Request
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def keys
|
9
|
+
{ :answer => :ws_create_items_answer,
|
10
|
+
:request => 'WSCreateItems',
|
11
|
+
:response => :ws_create_items_response,
|
12
|
+
:result => :items_created }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ClickAndSend
|
2
|
+
module Request
|
3
|
+
class DeleteItems
|
4
|
+
include ClickAndSend::Request
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def keys
|
9
|
+
{ :answer => :ws_delete_items_answer,
|
10
|
+
:request => 'WSDeleteItems',
|
11
|
+
:response => :ws_delete_items_response,
|
12
|
+
:result => :delete_items }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ClickAndSend
|
2
|
+
module Request
|
3
|
+
class ItemSummary
|
4
|
+
include ClickAndSend::Request
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def keys
|
9
|
+
{ :answer => :ws_item_summary_answer,
|
10
|
+
:request => 'WSItemSummary',
|
11
|
+
:response => :ws_item_summary_response,
|
12
|
+
:result => :item_summary_transactions }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ClickAndSend
|
2
|
+
module Request
|
3
|
+
class PrintItem
|
4
|
+
include ClickAndSend::Request
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def keys
|
9
|
+
{ :answer => :ws_print_item_answer,
|
10
|
+
:request => 'WSPrintItem',
|
11
|
+
:response => :ws_print_item_response,
|
12
|
+
:result => :label_http_link }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ClickAndSend, :acceptance do
|
4
|
+
let(:item_details) { }
|
5
|
+
before { load_config(:acceptance) }
|
6
|
+
|
7
|
+
describe '.check_services' do
|
8
|
+
subject { ClickAndSend.check_services(attributes_for(%w(Sender Receiver ItemDetails))) }
|
9
|
+
specify { should be_a(Array) }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '.create_items' do
|
13
|
+
subject { ClickAndSend.create_items(options) }
|
14
|
+
|
15
|
+
context "when one transaction" do
|
16
|
+
let(:options) { {'Transactions' => attributes_for('Transaction')} }
|
17
|
+
specify { should be_a(Hash) }
|
18
|
+
end
|
19
|
+
|
20
|
+
context "when two transactions" do
|
21
|
+
let(:options) { attributes_for('Transactions') }
|
22
|
+
specify { should be_a(Array) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when item uploaded" do
|
27
|
+
let(:create_item_attributes) { attributes_for('Transaction') }
|
28
|
+
let(:ref) { create_item_attributes.values.first.fetch('CustTransactionID') }
|
29
|
+
before { ClickAndSend.create_items('Transactions' => create_item_attributes) }
|
30
|
+
|
31
|
+
describe '.find_tracking_numbers' do
|
32
|
+
subject { ClickAndSend.find_tracking_numbers([ref]) }
|
33
|
+
specify do
|
34
|
+
should be_a(Hash)
|
35
|
+
subject.keys.should == [ref]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '.pdf_url' do
|
40
|
+
subject { ClickAndSend.pdf_url(ref) }
|
41
|
+
specify do
|
42
|
+
subject.should be_a(String)
|
43
|
+
open(subject).content_type.should == 'application/pdf'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '.delete_items_by_ref' do
|
48
|
+
specify do
|
49
|
+
expect do
|
50
|
+
ClickAndSend.delete_items_by_ref([ref])
|
51
|
+
end.to change { ClickAndSend.find_tracking_numbers([ref]).empty? }.to(true)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '.delete_items' do
|
56
|
+
let(:items) do
|
57
|
+
tracking_numbers.collect do |tracking_number|
|
58
|
+
{'DeleteItem' => {'CustTransactionID' => ref, 'TrackingNumber' => tracking_number}}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
let(:tracking_numbers) { ClickAndSend.find_tracking_numbers([ref]).fetch(ref) }
|
62
|
+
specify do
|
63
|
+
expect do
|
64
|
+
ClickAndSend.delete_items('DeleteItems' => items)
|
65
|
+
end.to change { ClickAndSend.find_tracking_numbers(ref).empty? }.to(true)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '.item_summary' do
|
71
|
+
subject { ClickAndSend.item_summary }
|
72
|
+
specify { should be_a(Array) }
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ClickAndSend::Request do
|
4
|
+
subject { klass.new }
|
5
|
+
let(:klass) { Class.new.tap { |klass| klass.send(:include, ClickAndSend::Request) } }
|
6
|
+
|
7
|
+
describe '.new' do
|
8
|
+
it "defaults data to an empty hash" do
|
9
|
+
subject.data.should == {}
|
10
|
+
end
|
11
|
+
|
12
|
+
it "sets data" do
|
13
|
+
klass.new('foo').data.should == 'foo'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#result' do
|
18
|
+
subject { klass.new(data).result }
|
19
|
+
let(:data) { stub(:data) }
|
20
|
+
before { load_config }
|
21
|
+
|
22
|
+
context "valid keys" do
|
23
|
+
let(:client) { stub(:client) }
|
24
|
+
let(:config) { ClickAndSend.configuration }
|
25
|
+
let(:expected_xml) do
|
26
|
+
"<WSSomethingRequest>%s</WSSomethingRequest>" % CGI::escapeHTML(<<-XML.strip.gsub(/\n\s+/, "\n"))
|
27
|
+
<WSSomething>
|
28
|
+
<Header>
|
29
|
+
<CnSUserName>encrypted_#{config.user_name}</CnSUserName>
|
30
|
+
<APIAccessKey>encrypted_#{config.api_key}</APIAccessKey>
|
31
|
+
<RequestDate>encrypted_#{Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')}</RequestDate>
|
32
|
+
<APIVersion>#{config.api_version}</APIVersion>
|
33
|
+
</Header>
|
34
|
+
<ExtraRequestXml></ExtraRequestXml>
|
35
|
+
</WSSomething>
|
36
|
+
XML
|
37
|
+
end
|
38
|
+
let(:parsed_response) { {:ws_something_answer => {:something_result => 'the result'}} }
|
39
|
+
let(:request) { stub.as_null_object }
|
40
|
+
before do
|
41
|
+
klass.send(:define_method, :keys) do
|
42
|
+
{ :answer => :ws_something_answer,
|
43
|
+
:request => 'WSSomething',
|
44
|
+
:response => :ws_something_response,
|
45
|
+
:result => :something_result }
|
46
|
+
end
|
47
|
+
Savon.stub(:client) { client }
|
48
|
+
ClickAndSend::Encryption.stub(:new) { |arg| stub(:to_s => "encrypted_#{arg}") }
|
49
|
+
ClickAndSend::XML.stub(:new).with(data) { stub(:to_xml => "<ExtraRequestXml></ExtraRequestXml>") }
|
50
|
+
client.stub(:request) { request }
|
51
|
+
Nori.stub(:parse).with(request.to_hash[:ws_something_response][:ws_something_response]) do
|
52
|
+
parsed_response
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
it "calls service with :request key" do
|
58
|
+
client.should_receive(:request).with(:ind, 'WSSomething', :body => expected_xml)
|
59
|
+
subject
|
60
|
+
end
|
61
|
+
|
62
|
+
it "parses response" do
|
63
|
+
Nori.should_receive(:parse).with(request.to_hash[:ws_something_response][:ws_something_response])
|
64
|
+
subject
|
65
|
+
end
|
66
|
+
|
67
|
+
it "uses :response and :answer keys" do
|
68
|
+
request = stub.as_null_object
|
69
|
+
client.should_receive(:request) { request }
|
70
|
+
Nori.should_receive(:parse).with(request.to_hash[:ws_something_response][:ws_something_response])
|
71
|
+
subject.should == 'the result'
|
72
|
+
end
|
73
|
+
|
74
|
+
context "when error key present in response" do
|
75
|
+
let(:parsed_response) { {:ws_something_answer => {:errors_found => "Oops!"}} }
|
76
|
+
it "raises on API error" do
|
77
|
+
expect { subject }.to raise_error(ClickAndSend::Errors::APIError)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "keys undefined" do
|
83
|
+
it "raises NotImplemented" do
|
84
|
+
expect { subject }.to raise_error(ClickAndSend::Errors::NotImplemented)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "missing key" do
|
89
|
+
before { klass.send(:define_method, :keys) { {} } }
|
90
|
+
it "raises KeyError" do
|
91
|
+
expect { subject }.to raise_error(KeyError)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ClickAndSend::XML do
|
4
|
+
describe '#to_xml' do
|
5
|
+
subject { ClickAndSend::XML.new(input).to_xml }
|
6
|
+
context "hash" do
|
7
|
+
let(:input) { {:a => 1, "b" => 2} }
|
8
|
+
it { should == "<a>1</a><b>2</b>" }
|
9
|
+
end
|
10
|
+
|
11
|
+
context "nested hash" do
|
12
|
+
let(:input) { {:a => {:b => 2}} }
|
13
|
+
it { should == "<a><b>2</b></a>" }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ClickAndSend do
|
4
|
+
before { ClickAndSend.configuration = nil }
|
5
|
+
|
6
|
+
describe '.configuration' do
|
7
|
+
subject { ClickAndSend.configuration }
|
8
|
+
|
9
|
+
context "unconfigured" do
|
10
|
+
it { should be_nil }
|
11
|
+
end
|
12
|
+
|
13
|
+
context "configured" do
|
14
|
+
before { ClickAndSend.configure {} }
|
15
|
+
it { should be_a(ClickAndSend::Configuration) }
|
16
|
+
its(:api_version) { should == 10 }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '.configure' do
|
21
|
+
before { ClickAndSend.configure {} }
|
22
|
+
|
23
|
+
[:account_number, :api_key, :user_name, :wsdl].each do |attribute|
|
24
|
+
it "requires #{attribute}" do
|
25
|
+
expect do
|
26
|
+
ClickAndSend.configure { |c| c.send(:"#{attribute}=", "foo") }
|
27
|
+
end.to change { ClickAndSend.configuration.errors[attribute] }.to(nil)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "unrecognised attribute" do
|
32
|
+
specify do
|
33
|
+
expect { ClickAndSend.configure { |c| c.foo = "bar" } }.to raise_error(NoMethodError)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "requests" do
|
39
|
+
describe '.check_services' do
|
40
|
+
subject { ClickAndSend.check_services(options) }
|
41
|
+
let(:required_inputs) { %w(Sender Receiver ItemDetails) }
|
42
|
+
it_behaves_like "data required"
|
43
|
+
|
44
|
+
context "when input has valid keys" do
|
45
|
+
let(:options) { {'Sender' => stub, 'Receiver' => stub, 'ItemDetails' => stub} }
|
46
|
+
it "fetches :product_found from CheckServices result" do
|
47
|
+
ClickAndSend::Request::CheckServices.should_receive(:new) { stub(:result => {:product_found => 'services'}) }
|
48
|
+
subject.should == 'services'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '.create_items' do
|
54
|
+
subject { ClickAndSend.create_items(options) }
|
55
|
+
let(:options) { {'Transactions' => stub} }
|
56
|
+
let(:required_inputs) { %w(Transactions) }
|
57
|
+
it_behaves_like "data required"
|
58
|
+
it "fetches :item_created from CreateItems result" do
|
59
|
+
ClickAndSend::Request::CreateItems.should_receive(:new) { stub(:result => {:item_created => 'created'}) }
|
60
|
+
subject.should == 'created'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '.delete_items_by_ref' do
|
65
|
+
subject { ClickAndSend.delete_items_by_ref(['my_ref']) }
|
66
|
+
it "calls delete_items with ref, tracking_number" do
|
67
|
+
ClickAndSend.stub(:find_tracking_numbers).with(['my_ref']) { {'my_ref' => ['t1', 't2']} }
|
68
|
+
ClickAndSend.should_receive(:delete_items).with('DeleteItems' => [
|
69
|
+
{'DeleteItem' => {'CustTransactionID' => 'my_ref', 'TrackingNumber' => 't1'}},
|
70
|
+
{'DeleteItem' => {'CustTransactionID' => 'my_ref', 'TrackingNumber' => 't2'}}
|
71
|
+
])
|
72
|
+
subject
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '.delete_items' do
|
77
|
+
subject { ClickAndSend.delete_items(options) }
|
78
|
+
let(:options) { {'DeleteItems' => stub} }
|
79
|
+
let(:required_inputs) { %w(DeleteItems) }
|
80
|
+
it_behaves_like "data required"
|
81
|
+
it "calls DeleteItems result" do
|
82
|
+
ClickAndSend::Request::DeleteItems.should_receive(:new) { stub(:result => 'deleted') }
|
83
|
+
subject.should == 'deleted'
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe '.find_tracking_numbers' do
|
88
|
+
subject { ClickAndSend.find_tracking_numbers(['REF']) }
|
89
|
+
let(:item_summary) do
|
90
|
+
[ {:cust_transaction_id => 'REF', :tracking_number => 'TRACK-REF-1'},
|
91
|
+
{:cust_transaction_id => 'REF', :tracking_number => 'TRACK-REF-2'},
|
92
|
+
{:cust_transaction_id => 'OTHER_REF', :tracking_number => 'TRACK-OTHER-REF'} ]
|
93
|
+
end
|
94
|
+
before { ClickAndSend.stub(:item_summary) { item_summary } }
|
95
|
+
it { should be_a(Hash) }
|
96
|
+
its(:keys) { should == ['REF'] }
|
97
|
+
it "includes tracking numbers for REF" do
|
98
|
+
subject.values.first.should include('TRACK-REF-1')
|
99
|
+
subject.values.first.should include('TRACK-REF-2')
|
100
|
+
end
|
101
|
+
it "excludes other tracking number" do
|
102
|
+
subject.values.first.should_not include('TRACK-OTHER-REF')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe '.item_summary' do
|
107
|
+
subject { ClickAndSend.item_summary }
|
108
|
+
it "fetches :item_summary_transaction from ItemSummary result" do
|
109
|
+
ClickAndSend::Request::ItemSummary.should_receive(:new) do
|
110
|
+
stub(:result => {:item_summary_transaction => 'items'})
|
111
|
+
end
|
112
|
+
subject.should == 'items'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe '.pdf_url' do
|
117
|
+
subject { ClickAndSend.pdf_url('REF') }
|
118
|
+
let(:options) do
|
119
|
+
{
|
120
|
+
'PrintFormat' => 'LINK',
|
121
|
+
'CustTransactionID' => 'REF',
|
122
|
+
'TrackingNumber' => tracking_numbers.first
|
123
|
+
}
|
124
|
+
end
|
125
|
+
let(:pdf_url) { 'http://example.com/paperwork.pdf' }
|
126
|
+
before do
|
127
|
+
ClickAndSend.stub(:find_tracking_numbers).with(['REF']) do
|
128
|
+
{'REF' => tracking_numbers}
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context "one tracking number" do
|
133
|
+
let(:tracking_numbers) { ['xyz'] }
|
134
|
+
specify "requests URL from PrintItem" do
|
135
|
+
ClickAndSend::Request::PrintItem.should_receive(:new).with(options) do
|
136
|
+
stub(:result => pdf_url)
|
137
|
+
end
|
138
|
+
subject.should == pdf_url
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context "no matching tracking numbers" do
|
143
|
+
let(:tracking_numbers) { [] }
|
144
|
+
it "raises API error" do
|
145
|
+
expect { subject }.to raise_error(ClickAndSend::Errors::APIError, "Not found: 'REF'")
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context "two tracking numbers" do
|
150
|
+
let(:tracking_numbers) { ['uvw', 'xyz'] }
|
151
|
+
it "raises API error" do
|
152
|
+
expect { subject }.to raise_error(ClickAndSend::Errors::APIError, "Expected 1 but found 2 tracking numbers for 'REF': uvw, xyz")
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
# To run the acceptance test, copy this file to config.yml and change these values to your test account's credentials.
|
2
|
+
:account_number: 1234567
|
3
|
+
:api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
4
|
+
:user_name: acme
|
5
|
+
:wsdl: https://auspost-staging.oss.neopost-id.com/modules/oss/api/ship/wsdl?sz_Client=AustraliaPost
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'click_and_send'
|
2
|
+
Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each { |path| require path }
|
3
|
+
|
4
|
+
RSpec.configure do |config|
|
5
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
6
|
+
config.run_all_when_everything_filtered = true
|
7
|
+
config.filter_run :focus
|
8
|
+
|
9
|
+
# Run specs in random order to surface order dependencies. If you find an
|
10
|
+
# order dependency and want to debug it, you can fix the order by providing
|
11
|
+
# the seed, which is printed after each run.
|
12
|
+
# --seed 1234
|
13
|
+
# Disabled random order because some acceptance tests need to run in order.
|
14
|
+
# config.order = 'random'
|
15
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'faker'
|
2
|
+
|
3
|
+
module ClickAndSend
|
4
|
+
module Test
|
5
|
+
module Factories
|
6
|
+
def attributes_for(*keys)
|
7
|
+
keys.flatten.inject({}) do |hash, key|
|
8
|
+
value = case key
|
9
|
+
when 'ItemDetails' then item_details_attributes
|
10
|
+
when 'Receiver' then us_address_attributes
|
11
|
+
when 'Sender' then au_address_attributes
|
12
|
+
when 'Transaction' then transaction_attributes
|
13
|
+
when 'Transactions'
|
14
|
+
[{'Transaction' => [transaction_attributes, transaction_attributes]}]
|
15
|
+
else raise UnknownDataKey, key
|
16
|
+
end
|
17
|
+
hash.merge(key => value)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def address_attributes
|
24
|
+
{
|
25
|
+
'CompanyName' => '',
|
26
|
+
'ContactName' => name,
|
27
|
+
'Telephone' => phone_number,
|
28
|
+
'Email' => email,
|
29
|
+
'AddressLine1' => street_address,
|
30
|
+
'AddressLine2' => '',
|
31
|
+
'AddressLine3' => '',
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def au_address_attributes
|
36
|
+
address_attributes.merge(
|
37
|
+
'Town' => "Sydney",
|
38
|
+
'StateCode' => "NSW",
|
39
|
+
'PostCode' => "2000",
|
40
|
+
'CountryCode' => "AU"
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def us_address_attributes
|
45
|
+
address_attributes.merge(
|
46
|
+
'Town' => city,
|
47
|
+
'StateCode' => "NY",
|
48
|
+
'PostCode' => "10001",
|
49
|
+
'CountryCode' => "US",
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def city
|
54
|
+
Faker::Address.city
|
55
|
+
end
|
56
|
+
|
57
|
+
def customs_data_attributes
|
58
|
+
{
|
59
|
+
'CustomsData' => {
|
60
|
+
# 'CustomsItemType' => '',
|
61
|
+
# 'CustomsQuantity' => '',
|
62
|
+
# 'ExportDeclarationNumber' => '',
|
63
|
+
# 'ReasonForExport' => '',
|
64
|
+
'NameOfPersonLodging' => name,
|
65
|
+
# 'HSTariffNumber' => '',
|
66
|
+
# 'CountryOfOrigin' => '',
|
67
|
+
# 'NonDeliverySenderInstructions' => '',
|
68
|
+
# 'ReturnFollowingAddress' => '',
|
69
|
+
}
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def dimensions
|
74
|
+
{ 'Length' => '12.00', 'Width' => '12.00', 'Height' => '5.00' }
|
75
|
+
end
|
76
|
+
|
77
|
+
def email
|
78
|
+
Faker::Internet.email
|
79
|
+
end
|
80
|
+
|
81
|
+
def item_details_attributes
|
82
|
+
{
|
83
|
+
'PackagingType' => 'M', # Merchandise
|
84
|
+
'Dimensions' => dimensions,
|
85
|
+
'ItemWeight' => '0.500', # Float Format 999.999 Kg
|
86
|
+
'ExtraCover' => 0, # Boolean
|
87
|
+
'ExtraCoverAmountOrDeclaredValue' => '100.00',
|
88
|
+
'ItemDescriptionOrDescriptionOfGoods' => "stuff",
|
89
|
+
'InternationalItemDetails' => customs_data_attributes
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
def name
|
94
|
+
Faker::Name.name
|
95
|
+
end
|
96
|
+
|
97
|
+
def payment_details_attributes
|
98
|
+
{
|
99
|
+
'PaymentType' => '01',
|
100
|
+
'PaymentData' => ClickAndSend.configuration.account_number
|
101
|
+
}
|
102
|
+
end
|
103
|
+
|
104
|
+
def phone_number
|
105
|
+
Faker::PhoneNumber.phone_number.gsub(/[^\d]/, '')
|
106
|
+
end
|
107
|
+
|
108
|
+
def street_address
|
109
|
+
Faker::Address.street_address
|
110
|
+
end
|
111
|
+
|
112
|
+
def transaction_attributes
|
113
|
+
{
|
114
|
+
'CustTransactionID' => rand(10**10).to_s,
|
115
|
+
'Sender' => au_address_attributes,
|
116
|
+
'Receiver' => us_address_attributes,
|
117
|
+
'ItemDetails' => item_details_attributes,
|
118
|
+
'ProductCode' => '1',
|
119
|
+
'PaymentDetails' => payment_details_attributes,
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
class UnknownDataKey < StandardError; end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
RSpec.configure { |c| c.include ClickAndSend::Test::Factories }
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module ClickAndSend
|
4
|
+
module Test
|
5
|
+
module Helpers
|
6
|
+
def load_config(mode = :test)
|
7
|
+
yaml_config_path = case mode
|
8
|
+
when :test
|
9
|
+
File.expand_path(File.join(File.dirname(__FILE__), '../fixtures/config.example.yml'))
|
10
|
+
when :acceptance
|
11
|
+
File.expand_path(File.join(File.dirname(__FILE__), '../fixtures/config.yml'))
|
12
|
+
end
|
13
|
+
if File.exists?(yaml_config_path)
|
14
|
+
ClickAndSend.configure do |config|
|
15
|
+
YAML::load_file(yaml_config_path).each { |k,v| config.send(:"#{k}=", v) }
|
16
|
+
end
|
17
|
+
else
|
18
|
+
pending "Can't run acceptance tests because #{yaml_config_path} is missing. See fixtures/config.example.yml."
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
RSpec.configure { |c| c.include ClickAndSend::Test::Helpers }
|
@@ -0,0 +1,19 @@
|
|
1
|
+
shared_examples_for "data required" do
|
2
|
+
context "empty input" do
|
3
|
+
let(:options) { {} }
|
4
|
+
it "raises InvalidInput" do
|
5
|
+
required_inputs.each do |required_input|
|
6
|
+
expect { subject }.to raise_error(ClickAndSend::Errors::InvalidInput) do |e|
|
7
|
+
e.message.should include(required_input)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "required keys present" do
|
14
|
+
let(:options) { required_inputs.inject({}) { |hash, key| hash.merge(key => stub) } }
|
15
|
+
it "doesn't raise InvalidInput" do
|
16
|
+
expect { subject }.not_to raise_error(ClickAndSend::Errors::InvalidInput)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: click_and_send
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Zubin Henner
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-14 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: nori
|
16
|
+
requirement: &70235421689060 !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: *70235421689060
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: savon
|
27
|
+
requirement: &70235421676680 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70235421676680
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rake
|
38
|
+
requirement: &70235421675840 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70235421675840
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec
|
49
|
+
requirement: &70235421675300 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70235421675300
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: faker
|
60
|
+
requirement: &70235421674500 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70235421674500
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: &70235421673020 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *70235421673020
|
80
|
+
description: Ruby adapter for Australia Post's ClickAndSend API
|
81
|
+
email:
|
82
|
+
- zubin.henner@gmail.com
|
83
|
+
executables: []
|
84
|
+
extensions: []
|
85
|
+
extra_rdoc_files: []
|
86
|
+
files:
|
87
|
+
- .gitignore
|
88
|
+
- .rspec
|
89
|
+
- .travis.yml
|
90
|
+
- Gemfile
|
91
|
+
- LICENSE
|
92
|
+
- README.md
|
93
|
+
- Rakefile
|
94
|
+
- click_and_send.gemspec
|
95
|
+
- lib/click_and_send.rb
|
96
|
+
- lib/click_and_send/configuration.rb
|
97
|
+
- lib/click_and_send/encryption.rb
|
98
|
+
- lib/click_and_send/errors.rb
|
99
|
+
- lib/click_and_send/request.rb
|
100
|
+
- lib/click_and_send/request/check_services.rb
|
101
|
+
- lib/click_and_send/request/create_items.rb
|
102
|
+
- lib/click_and_send/request/delete_items.rb
|
103
|
+
- lib/click_and_send/request/item_summary.rb
|
104
|
+
- lib/click_and_send/request/print_item.rb
|
105
|
+
- lib/click_and_send/version.rb
|
106
|
+
- lib/click_and_send/xml.rb
|
107
|
+
- spec/acceptance_spec.rb
|
108
|
+
- spec/click_and_send/request_spec.rb
|
109
|
+
- spec/click_and_send/xml_spec.rb
|
110
|
+
- spec/click_and_send_spec.rb
|
111
|
+
- spec/fixtures/config.example.yml
|
112
|
+
- spec/spec_helper.rb
|
113
|
+
- spec/support/factories.rb
|
114
|
+
- spec/support/helpers.rb
|
115
|
+
- spec/support/open_uri.rb
|
116
|
+
- spec/support/shared_examples.rb
|
117
|
+
homepage: https://github.com/zubin/click_and_send
|
118
|
+
licenses: []
|
119
|
+
post_install_message:
|
120
|
+
rdoc_options: []
|
121
|
+
require_paths:
|
122
|
+
- lib
|
123
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
124
|
+
none: false
|
125
|
+
requirements:
|
126
|
+
- - ! '>='
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
segments:
|
130
|
+
- 0
|
131
|
+
hash: 1242808500940722472
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
none: false
|
134
|
+
requirements:
|
135
|
+
- - ! '>='
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
segments:
|
139
|
+
- 0
|
140
|
+
hash: 1242808500940722472
|
141
|
+
requirements: []
|
142
|
+
rubyforge_project:
|
143
|
+
rubygems_version: 1.8.6
|
144
|
+
signing_key:
|
145
|
+
specification_version: 3
|
146
|
+
summary: Ruby adapter for Australia Post's ClickAndSend API
|
147
|
+
test_files:
|
148
|
+
- spec/acceptance_spec.rb
|
149
|
+
- spec/click_and_send/request_spec.rb
|
150
|
+
- spec/click_and_send/xml_spec.rb
|
151
|
+
- spec/click_and_send_spec.rb
|
152
|
+
- spec/fixtures/config.example.yml
|
153
|
+
- spec/spec_helper.rb
|
154
|
+
- spec/support/factories.rb
|
155
|
+
- spec/support/helpers.rb
|
156
|
+
- spec/support/open_uri.rb
|
157
|
+
- spec/support/shared_examples.rb
|