click_and_send 0.0.2
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/.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
|