aus_post_api 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +37 -0
- data/LICENSE.txt +21 -0
- data/README.md +147 -0
- data/Rakefile +32 -0
- data/aus_post_api.gemspec +14 -0
- data/lib/aus_post_api.rb +7 -0
- data/lib/aus_post_api/dce.rb +15 -0
- data/lib/aus_post_api/dce/endpoint.rb +80 -0
- data/lib/aus_post_api/dce/validate_australian_address.rb +12 -0
- data/lib/aus_post_api/endpoint.rb +66 -0
- data/lib/aus_post_api/endpoint/attributes.rb +54 -0
- data/lib/aus_post_api/pac.rb +83 -0
- data/lib/aus_post_api/pac/country.rb +9 -0
- data/lib/aus_post_api/pac/domestic_letter_size.rb +9 -0
- data/lib/aus_post_api/pac/domestic_letter_thickness.rb +9 -0
- data/lib/aus_post_api/pac/domestic_letter_weight.rb +9 -0
- data/lib/aus_post_api/pac/domestic_parcel_size.rb +9 -0
- data/lib/aus_post_api/pac/domestic_parcel_type.rb +9 -0
- data/lib/aus_post_api/pac/domestic_parcel_weight.rb +9 -0
- data/lib/aus_post_api/pac/domestic_postcode_search.rb +12 -0
- data/lib/aus_post_api/pac/endpoint.rb +76 -0
- data/lib/aus_post_api/pac/international_letter_weight.rb +9 -0
- data/lib/aus_post_api/pac/international_parcel_weight.rb +9 -0
- data/lib/aus_post_api/pac/postage_letter_domestic_calculate.rb +12 -0
- data/lib/aus_post_api/pac/postage_letter_domestic_service.rb +11 -0
- data/lib/aus_post_api/pac/postage_letter_international_calculate.rb +12 -0
- data/lib/aus_post_api/pac/postage_letter_international_service.rb +11 -0
- data/lib/aus_post_api/pac/postage_parcel_domestic_calculate.rb +13 -0
- data/lib/aus_post_api/pac/postage_parcel_domestic_service.rb +12 -0
- data/lib/aus_post_api/pac/postage_parcel_international_calculate.rb +12 -0
- data/lib/aus_post_api/pac/postage_parcel_international_service.rb +11 -0
- data/lib/aus_post_api/uri_handler.rb +35 -0
- data/spec/aus_post_api/dce/endpoint_spec.rb +86 -0
- data/spec/aus_post_api/dce/validate_australian_address_spec.rb +18 -0
- data/spec/aus_post_api/dce_spec.rb +33 -0
- data/spec/aus_post_api/endpoint/attributes_spec.rb +35 -0
- data/spec/aus_post_api/endpoint_spec.rb +43 -0
- data/spec/aus_post_api/pac/country_spec.rb +8 -0
- data/spec/aus_post_api/pac/domestic_letter_size_spec.rb +8 -0
- data/spec/aus_post_api/pac/domestic_letter_thickness_spec.rb +8 -0
- data/spec/aus_post_api/pac/domestic_letter_weight_spec.rb +8 -0
- data/spec/aus_post_api/pac/domestic_parcel_size_spec.rb +8 -0
- data/spec/aus_post_api/pac/domestic_parcel_type_spec.rb +8 -0
- data/spec/aus_post_api/pac/domestic_parcel_weight_spec.rb +8 -0
- data/spec/aus_post_api/pac/endpoint_spec.rb +109 -0
- data/spec/aus_post_api/pac/international_letter_weight_spec.rb +8 -0
- data/spec/aus_post_api/pac/international_parcel_weight_spec.rb +8 -0
- data/spec/aus_post_api/pac/postage_letter_domestic_calculate_spec.rb +19 -0
- data/spec/aus_post_api/pac/postage_letter_domestic_service_spec.rb +15 -0
- data/spec/aus_post_api/pac/postage_letter_international_calculate_spec.rb +20 -0
- data/spec/aus_post_api/pac/postage_letter_international_service_spec.rb +13 -0
- data/spec/aus_post_api/pac/postage_parcel_domestic_calculate_spec.rb +24 -0
- data/spec/aus_post_api/pac/postage_parcel_domestic_service_spec.rb +17 -0
- data/spec/aus_post_api/pac/postage_parcel_international_calculate_spec.rb +15 -0
- data/spec/aus_post_api/pac/postage_parcel_international_service_spec.rb +8 -0
- data/spec/aus_post_api/pac/postcode_search_spec.rb +17 -0
- data/spec/aus_post_api/pac_spec.rb +101 -0
- data/spec/aus_post_api/uri_handler_spec.rb +14 -0
- data/spec/shared_examples/shared_examples_for_endpoint_classes.rb +37 -0
- data/spec/spec_helper.rb +41 -0
- metadata +136 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d5236689702f486d8307a74b0b01ca9c0b8b7733
|
4
|
+
data.tar.gz: aec946ad8ea44c0ea0358b819470328c658ec7dc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4aa3123f0a1231cb164673ca8d32f9a4c133f2fbe36669ac0ef4466391a9095957f87447485ff3e6c29cc71ef41bfab27ca5c869e179cf7272fa57c7814f6d77
|
7
|
+
data.tar.gz: df27ce56b45e696c99a44bfe78b8e0aab0d16b1d9ab24d08424d7e10885aedc91f94b88a78793658902fc23274df41ea13c86165e73f65e374fb959381b06990
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
addressable (2.3.8)
|
5
|
+
crack (0.4.2)
|
6
|
+
safe_yaml (~> 1.0.0)
|
7
|
+
diff-lcs (1.2.5)
|
8
|
+
rake (10.4.2)
|
9
|
+
require_all (1.3.2)
|
10
|
+
rspec (3.2.0)
|
11
|
+
rspec-core (~> 3.2.0)
|
12
|
+
rspec-expectations (~> 3.2.0)
|
13
|
+
rspec-mocks (~> 3.2.0)
|
14
|
+
rspec-core (3.2.3)
|
15
|
+
rspec-support (~> 3.2.0)
|
16
|
+
rspec-expectations (3.2.1)
|
17
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
18
|
+
rspec-support (~> 3.2.0)
|
19
|
+
rspec-mocks (3.2.1)
|
20
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
21
|
+
rspec-support (~> 3.2.0)
|
22
|
+
rspec-support (3.2.2)
|
23
|
+
safe_yaml (1.0.4)
|
24
|
+
vcr (2.9.3)
|
25
|
+
webmock (1.21.0)
|
26
|
+
addressable (>= 2.3.6)
|
27
|
+
crack (>= 0.3.2)
|
28
|
+
|
29
|
+
PLATFORMS
|
30
|
+
ruby
|
31
|
+
|
32
|
+
DEPENDENCIES
|
33
|
+
rake
|
34
|
+
require_all
|
35
|
+
rspec
|
36
|
+
vcr
|
37
|
+
webmock
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Jared Shay
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
# AusPostAPI
|
2
|
+
|
3
|
+
The AusPostAPI gem is a wrapper around the Australia Post Developer API. Currently only the Postage Assesment Calculator (PAC) API is implemented.
|
4
|
+
|
5
|
+
## Documentation
|
6
|
+
|
7
|
+
Full and up to date documentation can be found at https://developers.auspost.com.au/apis
|
8
|
+
|
9
|
+
*see below for full list of endpoints that can be called from this gem*
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
### Ruby
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
AusPostAPI::PAC.new(config).endpoint(params)
|
17
|
+
```
|
18
|
+
|
19
|
+
### Rails
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
# config/initializers/aus_post_api.rb
|
23
|
+
|
24
|
+
$aus_post_api = AusPostAPI::PAC.new(config)
|
25
|
+
|
26
|
+
# app/some_file.rb
|
27
|
+
|
28
|
+
$aus_post_api.endpoint(params)
|
29
|
+
```
|
30
|
+
|
31
|
+
## Configuration
|
32
|
+
The gem requires a `FORMAT` of either `'json'` or `'xml'`, and the `PAC_AUTH_KEY` to be set. You can get an auth key at https://developers.auspost.com.au/sign-up-for-an-API-key
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
{
|
36
|
+
FORMAT: 'xml',
|
37
|
+
PAC_API_KEY: 123-456
|
38
|
+
}
|
39
|
+
```
|
40
|
+
|
41
|
+
## Endpoints
|
42
|
+
|
43
|
+
There are no validations on any parameters. Consult the Australia Post documentation for what units and limitations apply.
|
44
|
+
|
45
|
+
```
|
46
|
+
domestic_postcode_search
|
47
|
+
Required Attributes:
|
48
|
+
- q
|
49
|
+
Optional Attributes:
|
50
|
+
- state
|
51
|
+
- excludepostboxflag
|
52
|
+
|
53
|
+
country
|
54
|
+
|
55
|
+
domestic_letter_thickness
|
56
|
+
|
57
|
+
domestic_letter_weight
|
58
|
+
|
59
|
+
domestic_letter_size
|
60
|
+
|
61
|
+
international_letter_weight
|
62
|
+
|
63
|
+
international_parcel_weight
|
64
|
+
|
65
|
+
domestic_parcel_weight
|
66
|
+
|
67
|
+
domestic_parcel_type
|
68
|
+
|
69
|
+
domestic_parcel_size
|
70
|
+
|
71
|
+
postage_letter_domestic_service
|
72
|
+
Required Attributes:
|
73
|
+
- length
|
74
|
+
- width
|
75
|
+
- thickness
|
76
|
+
- weight
|
77
|
+
|
78
|
+
postage_parcel_domestic_service
|
79
|
+
Required Attributes:
|
80
|
+
- from_postcode
|
81
|
+
- to_postcode
|
82
|
+
- length
|
83
|
+
- width
|
84
|
+
- height
|
85
|
+
- weight
|
86
|
+
|
87
|
+
postage_letter_international_service
|
88
|
+
Required Attributes:
|
89
|
+
- country_code
|
90
|
+
- weight
|
91
|
+
|
92
|
+
postage_parcel_international_service
|
93
|
+
Required Attributes:
|
94
|
+
- country_code
|
95
|
+
- weight
|
96
|
+
|
97
|
+
postage_parcel_domestic_calculate
|
98
|
+
Required Attributes:
|
99
|
+
- from_postcode
|
100
|
+
- to_postcode
|
101
|
+
- length
|
102
|
+
- width
|
103
|
+
- height
|
104
|
+
- weight
|
105
|
+
- service_code
|
106
|
+
Optional Attributes:
|
107
|
+
- option_code
|
108
|
+
- suboption_code
|
109
|
+
- extra_cover
|
110
|
+
|
111
|
+
postage_parcel_international_calculate
|
112
|
+
Required Attributes:
|
113
|
+
- country_code
|
114
|
+
- weight
|
115
|
+
- service_code
|
116
|
+
Optional Attributes:
|
117
|
+
- option_code
|
118
|
+
- suboption_code
|
119
|
+
- extra_cover
|
120
|
+
|
121
|
+
postage_letter_domestic_calculate
|
122
|
+
Required Attributes:
|
123
|
+
- service_code
|
124
|
+
- weight
|
125
|
+
Optional Attributes:
|
126
|
+
- option_code
|
127
|
+
- suboption_code
|
128
|
+
- extra_cover
|
129
|
+
|
130
|
+
postage_letter_international_calculate
|
131
|
+
Required Attributes:
|
132
|
+
- country_code
|
133
|
+
- service_code
|
134
|
+
Optional Attributes:
|
135
|
+
- weight
|
136
|
+
- option_code
|
137
|
+
- suboption_code
|
138
|
+
- extra_cover
|
139
|
+
```
|
140
|
+
|
141
|
+
## Testing
|
142
|
+
Set the config `TEST: true` for use in testing environments. This will use the Australia Post testing endpoint, and ignore any api keys provided.
|
143
|
+
|
144
|
+
*note: at the time of writing this the testing endpoint is unreliable. Consider getting additional api keys for testing purposes and not setting the TEST parameter in your tests*
|
145
|
+
|
146
|
+
## Contributing
|
147
|
+
Fork and PR!
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
desc "Open an irb session preloaded with the aus_post gem"
|
2
|
+
task :console do
|
3
|
+
sh "irb -I lib -r aus_post_api.rb"
|
4
|
+
end
|
5
|
+
|
6
|
+
desc "Display all PAC endpoints with a list of required values"
|
7
|
+
task :pac_docs do
|
8
|
+
require_relative 'lib/aus_post_api'
|
9
|
+
|
10
|
+
|
11
|
+
docs = ""
|
12
|
+
(AusPostAPI::PAC.instance_methods - Object.instance_methods).each do |method|
|
13
|
+
klass = "AusPostAPI::PAC::#{method.to_s.split("_").map(&:capitalize).join}"
|
14
|
+
required = Kernel.const_get(klass).required_attributes
|
15
|
+
optional = Kernel.const_get(klass).optional_attributes
|
16
|
+
|
17
|
+
docs << method.to_s
|
18
|
+
docs << "\n"
|
19
|
+
|
20
|
+
if !required.empty?
|
21
|
+
docs << " Required Attributes:\n"
|
22
|
+
required.each { |attr| docs << " - #{attr}\n"}
|
23
|
+
end
|
24
|
+
if !optional.empty?
|
25
|
+
docs << " Optional Attributes:\n"
|
26
|
+
optional.each { |attr| docs << " - #{attr}\n"}
|
27
|
+
end
|
28
|
+
docs << "\n"
|
29
|
+
end
|
30
|
+
|
31
|
+
puts docs.chomp!
|
32
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Gem::Specification.new do |gem|
|
2
|
+
gem.authors = ["JaredShay"]
|
3
|
+
gem.email = ["jared.shay@gmail.com"]
|
4
|
+
gem.description = %q{Wrapper for Australia Post's developer API}
|
5
|
+
gem.summary = %q{Wrapper for Australia Post's developer API}
|
6
|
+
gem.homepage = "https://github.com/jaredshay/aus_post_api"
|
7
|
+
|
8
|
+
gem.files = `git ls-files`.split("\n")
|
9
|
+
gem.test_files = `git ls-files -- spec/*`.split("\n")
|
10
|
+
gem.name = "aus_post_api"
|
11
|
+
gem.require_paths = ["lib"]
|
12
|
+
gem.version = '1.0.0'
|
13
|
+
gem.license = "MIT"
|
14
|
+
end
|
data/lib/aus_post_api.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module AusPostAPI
|
2
|
+
# The PAC class implements methods specified by the Australia Post Delivery
|
3
|
+
# Choices API. A config hash must be supplied that specifies a valid
|
4
|
+
# DCE_AUTH_KEY, and request format. Setting the key TEST to true in the config
|
5
|
+
# hash will ignore any supplied auth_key and use the test endpoint.
|
6
|
+
class DCE
|
7
|
+
def initialize(config)
|
8
|
+
@config = config
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate_australian_address(params = {})
|
12
|
+
AusPostAPI::DCE::ValidateAustralianAddress.new(params, @config).execute
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module AusPostAPI
|
4
|
+
class DCE
|
5
|
+
# Abstract class that defines a DCE::Endpoint. It implements the `uri` &
|
6
|
+
# `headers` methods and expects the `api_uri` method to be implemented.
|
7
|
+
class Endpoint < AusPostAPI::Endpoint
|
8
|
+
LIVE_URI = 'https://api.auspost.com.au/'
|
9
|
+
FORMATS = ['json', 'xml']
|
10
|
+
|
11
|
+
def uri
|
12
|
+
"#{LIVE_URI}/#{api_uri}.#{format}?#{params}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def headers
|
16
|
+
{ "Authorization" => "Basic #{basic_auth}" }
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def api_uri
|
22
|
+
raise ImplementationError.new('api_uri')
|
23
|
+
end
|
24
|
+
|
25
|
+
def format
|
26
|
+
if @config[:FORMAT].nil?
|
27
|
+
raise NoFormatProvidedError
|
28
|
+
else
|
29
|
+
if !FORMATS.include?(@config[:FORMAT])
|
30
|
+
raise InvalidFormatError
|
31
|
+
else
|
32
|
+
@config[:FORMAT]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def params
|
38
|
+
[].tap { |result|
|
39
|
+
@attributes.keys.each { |a| result << "#{a}=#{self.send(a).split.join('+')}" }
|
40
|
+
}.join('&')
|
41
|
+
end
|
42
|
+
|
43
|
+
def basic_auth
|
44
|
+
Base64.encode64("#{username}:#{password}")
|
45
|
+
end
|
46
|
+
|
47
|
+
def username
|
48
|
+
raise NoHeaderProvidedError.new('USERNAME') if @config[:USERNAME].nil?
|
49
|
+
|
50
|
+
@config[:USERNAME]
|
51
|
+
end
|
52
|
+
|
53
|
+
def password
|
54
|
+
raise NoHeaderProvidedError.new('PASSWORD') if @config[:PASSWORD].nil?
|
55
|
+
|
56
|
+
@config[:PASSWORD]
|
57
|
+
end
|
58
|
+
|
59
|
+
class NoHeaderProvidedError < StandardError
|
60
|
+
def initialize(header)
|
61
|
+
super("The called endpoint requires the #{header} config to be set")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class InvalidFormatError < StandardError
|
66
|
+
def initialize
|
67
|
+
super("Accepted formats are: #{AusPostAPI::DCE::Endpoint::FORMATS.join(', ')}")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class NoFormatProvidedError < InvalidFormatError; end
|
72
|
+
|
73
|
+
class ImplementationError < StandardError
|
74
|
+
def initialize(method)
|
75
|
+
super("No #{method} implemented")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module AusPostAPI
|
2
|
+
class DCE
|
3
|
+
class ValidateAustralianAddress < AusPostAPI::DCE::Endpoint
|
4
|
+
required_attributes :addressline1, :suburb, :postcode, :state, :country
|
5
|
+
optional_attributes :addressline2
|
6
|
+
|
7
|
+
def api_uri
|
8
|
+
"ValidateAddress"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require_relative "./endpoint/attributes"
|
2
|
+
|
3
|
+
# The Endpoint class is an abstract class that sets attributes and handles the
|
4
|
+
# execution of API endoint calls.
|
5
|
+
#
|
6
|
+
# It requires two instance methods to be defined.
|
7
|
+
# * `uri` - returns a valid uri string that represents the full api path
|
8
|
+
# * `headers` - returns a hash of key value pairs for the request
|
9
|
+
#
|
10
|
+
# The attributes module adds class level methods for specifying the attributes
|
11
|
+
# of the API call.
|
12
|
+
# * `required_attributes`
|
13
|
+
# * `optional_attributes`
|
14
|
+
module AusPostAPI
|
15
|
+
class Endpoint
|
16
|
+
include AusPostAPI::Endpoint::Attributes
|
17
|
+
|
18
|
+
def initialize(attributes, config, uri_handler = AusPostAPI::UriHandler)
|
19
|
+
@config = config
|
20
|
+
@attributes = attributes
|
21
|
+
@uri_handler = uri_handler
|
22
|
+
|
23
|
+
set_attributes
|
24
|
+
end
|
25
|
+
|
26
|
+
def execute
|
27
|
+
@uri_handler.call(uri, headers)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def uri
|
33
|
+
raise ImplementationError.new("uri")
|
34
|
+
end
|
35
|
+
|
36
|
+
def headers
|
37
|
+
raise ImplementationError.new("headers")
|
38
|
+
end
|
39
|
+
|
40
|
+
def set_attributes
|
41
|
+
required_param = -> (attr) { raise RequiredArgumentError.new(attr) }
|
42
|
+
|
43
|
+
required_attributes.each do |attr|
|
44
|
+
self.send("#{attr}=", @attributes.fetch(attr, &required_param))
|
45
|
+
end
|
46
|
+
|
47
|
+
optional_attributes.each do |attr|
|
48
|
+
if @attributes.has_key?(attr)
|
49
|
+
self.send("#{attr}=", @attributes.fetch(attr))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class RequiredArgumentError < StandardError
|
55
|
+
def initialize(attr)
|
56
|
+
super("#{attr} is a required argument")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class ImplementationError < StandardError
|
61
|
+
def initialize(method)
|
62
|
+
super("No #{method} implemented")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|