postmates 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +3 -0
- data/.travis.yml +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +137 -0
- data/Rakefile +9 -0
- data/lib/faraday/raise_http_exception.rb +28 -0
- data/lib/postmates.rb +19 -0
- data/lib/postmates/client.rb +81 -0
- data/lib/postmates/configuration.rb +30 -0
- data/lib/postmates/connection.rb +25 -0
- data/lib/postmates/delivery.rb +33 -0
- data/lib/postmates/error.rb +9 -0
- data/lib/postmates/quote.rb +23 -0
- data/lib/postmates/request.rb +28 -0
- data/lib/postmates/response.rb +27 -0
- data/lib/postmates/utils.rb +14 -0
- data/lib/postmates/version.rb +14 -0
- data/postmates.gemspec +29 -0
- data/spec/client_spec.rb +116 -0
- data/spec/error_spec.rb +82 -0
- data/spec/fixtures/create_params.json +14 -0
- data/spec/fixtures/deliveries.json +242 -0
- data/spec/fixtures/delivery.json +39 -0
- data/spec/fixtures/forbidden.json +5 -0
- data/spec/fixtures/invalid_params.json +14 -0
- data/spec/fixtures/not_found.json +5 -0
- data/spec/fixtures/quote.json +10 -0
- data/spec/fixtures/quote_params.json +4 -0
- data/spec/fixtures/server_error.json +5 -0
- data/spec/fixtures/service_unavailable.json +5 -0
- data/spec/fixtures/unauthorized.json +5 -0
- data/spec/postmates_spec.rb +19 -0
- data/spec/spec_helper.rb +57 -0
- metadata +218 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 7cf00b57dc6151452b0a344584891a6428e8bb25
|
|
4
|
+
data.tar.gz: 8639fcb84ca1e2c4d15ab8703848c37027be5239
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f603412097d99cb59f93589e0a9fa43939241e7626c646606be5c5b4b4377b6f46818dfdce7de4f238f86528d397445ad9348a35bd0286b85fa898202fc30b80
|
|
7
|
+
data.tar.gz: 221fd57a328ef694ebd35a978cfcabb3cf81fb2ae418fe022cddf7009567e192c323f100d78ce304ad808d58f79c2cc5d3c7c1d81f208edf92d96a32b8bff0f6
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
language: ruby
|
|
2
|
+
|
|
3
|
+
rvm:
|
|
4
|
+
- 1.9.3
|
|
5
|
+
- 2.0.0
|
|
6
|
+
- 2.1
|
|
7
|
+
- 2.2
|
|
8
|
+
- jruby-19mode
|
|
9
|
+
- jruby-head
|
|
10
|
+
- rbx-2
|
|
11
|
+
- ruby-head
|
|
12
|
+
|
|
13
|
+
env:
|
|
14
|
+
global:
|
|
15
|
+
- JRUBY_OPTS="$JRUBY_OPTS --debug"
|
|
16
|
+
|
|
17
|
+
matrix:
|
|
18
|
+
allow_failures:
|
|
19
|
+
- rvm: jruby-head
|
|
20
|
+
- rvm: rbx-2
|
|
21
|
+
- rvm: ruby-head
|
|
22
|
+
fast_finish: true
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2014 Rahul Horé
|
|
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,137 @@
|
|
|
1
|
+
# Postmates
|
|
2
|
+
[](https://travis-ci.org/O-I/postmates)
|
|
3
|
+
[](https://coveralls.io/r/O-I/postmates?branch=master)
|
|
4
|
+
|
|
5
|
+
Ruby wrapper for the [Postmates API](https://postmates.com/developer/docs).
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
`gem install 'postmates'` or add `gem 'postmates'` to your Gemfile and `bundle`.
|
|
10
|
+
|
|
11
|
+
## Configuration
|
|
12
|
+
|
|
13
|
+
You'll need an API key and your customer ID. You can sign up to register your app [here](https://postmates.com/developer/register). Just want to test things out? Postmates has you covered with a [test key and customer account](https://postmates.com/developer/testing).
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
# Create a new Postmates client
|
|
17
|
+
@client = Postmates.new
|
|
18
|
+
|
|
19
|
+
# Set basic config variables
|
|
20
|
+
@client.configure do |config|
|
|
21
|
+
config.api_key = 'YOUR_API_KEY'
|
|
22
|
+
config.customer_id = 'YOUR_CUSTOMER_ID'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Or do some more advanced stuff
|
|
26
|
+
@client.configure do |config|
|
|
27
|
+
# Get full Faraday responses instead of Ruby objects representing the body
|
|
28
|
+
config.raw_response = true
|
|
29
|
+
# Ensure consistent fields & formats by specifying a version in the header
|
|
30
|
+
config.headers.merge!('X-Postmates-Version' => 20140825)
|
|
31
|
+
# Work with a possible future version of the API
|
|
32
|
+
config.api_url = 'https://api.postmates.com/v2/'
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# And switch back to the defaults easily
|
|
36
|
+
@client.reset
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
Descriptions and examples of the supported actions are below. For a more detailed explanation of available endpoints and an exhaustive list of the properties each response returns, check out the official Postmates developer [documentation](https://postmates.com/developer/docs).
|
|
42
|
+
|
|
43
|
+
### Customer Deliveries Endpoints
|
|
44
|
+
|
|
45
|
+
#### POST /v1/customers/:customer_id/delivery_quotes
|
|
46
|
+
|
|
47
|
+
Get a delivery quote. Returns a `Postmates::Quote` object.
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
from = "20 McAllister St, San Francisco, CA"
|
|
51
|
+
to = "101 Market St, San Francisco, CA"
|
|
52
|
+
quote = @client.quote(pickup_address: from, dropoff_address: to)
|
|
53
|
+
|
|
54
|
+
quote.fee # => 1350
|
|
55
|
+
quote.currency # => "usd"
|
|
56
|
+
format = '%m/%d/%Y %I:%M:%S%p' # all times are returned in UTC
|
|
57
|
+
quote.expires_at.strftime(format) # => "01/05/2015 09:35:28PM"
|
|
58
|
+
quote.expired? # => false
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
#### POST /v1/customers/:customer_id/deliveries
|
|
62
|
+
|
|
63
|
+
Create a delivery. Returns a `Postmates::Delivery` object.
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
# all fields required except where noted
|
|
67
|
+
package = {
|
|
68
|
+
manifest: "a box of kittens",
|
|
69
|
+
pickup_name: "The Warehouse",
|
|
70
|
+
pickup_address: "20 McAllister St, San Francisco, CA",
|
|
71
|
+
pickup_phone_number: "555-555-5555",
|
|
72
|
+
pickup_business_name: "Optional Pickup Business Name, Inc.",
|
|
73
|
+
pickup_notes: "Optional note that this is Invoice #123",
|
|
74
|
+
dropoff_name: "Alice",
|
|
75
|
+
dropoff_address: "101 Market St, San Francisco, CA",
|
|
76
|
+
dropoff_phone_number: "415-555-1234",
|
|
77
|
+
dropoff_business_name: "Optional Dropoff Business Name, Inc.",
|
|
78
|
+
dropoff_notes: "Optional note to ring the bell",
|
|
79
|
+
quote_id: "dqt_K9LFfpSZCdAJsk" # optional
|
|
80
|
+
}
|
|
81
|
+
delivery = @client.create(package)
|
|
82
|
+
|
|
83
|
+
delivery.id # => "del_K9gEsDNuPJ-lLV"
|
|
84
|
+
delivery.status # => "pending"
|
|
85
|
+
delivery.delivered? # => false
|
|
86
|
+
delivery.pickup # a hash representing pickup information
|
|
87
|
+
delivery.dropoff # a hash representing dropoff information
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### GET /v1/customers/:customer_id/deliveries
|
|
91
|
+
|
|
92
|
+
Fetch a list of all deliveries for a customer. Returns an array of `Postmates::Delivery` objects.
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
my_deliveries = @client.list
|
|
96
|
+
my_ongoing_deliveries = @client.list(filter: 'pending')
|
|
97
|
+
|
|
98
|
+
# If the result is too large for one response
|
|
99
|
+
# there are a few meta-attributes you can call on the returned array
|
|
100
|
+
# Here's a simulated example of a paginated response:
|
|
101
|
+
deliveries = @client.list(limit: 2)
|
|
102
|
+
deliveries.size # => 2 (number of deliveries in the returned array)
|
|
103
|
+
deliveries.total_count # => 6 (number of total deliveries)
|
|
104
|
+
deliveries.next_href # a URI object representing the path to the next page
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### GET /v1/customers/:customer_id/deliveries/:delivery_id
|
|
108
|
+
|
|
109
|
+
Fetch a single delivery by id. Returns a `Postmates::Delivery` object.
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
@client.retrieve('del_K9gEsDNuPJ-lLV')
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
#### POST /v1/customers/:customer_id/deliveries/:delivery_id/cancel
|
|
116
|
+
|
|
117
|
+
Cancel an ongoing delivery prior to pickup. Returns a `Postmates::Delivery` object.
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
@client.cancel('del_K9gEsDNuPJ-lLV')
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
#### POST /v1/customers/:customer_id/deliveries/:delivery_id/return
|
|
124
|
+
|
|
125
|
+
Cancel an ongoing delivery that was already picked up and create a delivery that is a reverse of the original. The items will get returned to the original pickup location. Returns a `Postmates::Delivery` object.
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
@client.cancel('del_K9gEsDNuPJ-lLV')
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Contributing
|
|
132
|
+
|
|
133
|
+
1. Fork it ( https://github.com/[my-github-username]/postmates/fork )
|
|
134
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
135
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
136
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
137
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'faraday'
|
|
3
|
+
require_relative '../postmates/error'
|
|
4
|
+
|
|
5
|
+
module FaradayMiddleware
|
|
6
|
+
class RaiseHTTPException < Faraday::Middleware
|
|
7
|
+
def call(env)
|
|
8
|
+
@app.call(env).on_complete do |response|
|
|
9
|
+
response_hash = JSON.parse(response.body)
|
|
10
|
+
msg = "#{response[:status]} #{response_hash['message']}"
|
|
11
|
+
|
|
12
|
+
case response[:status]
|
|
13
|
+
when 400 ; raise Postmates::BadRequest, msg
|
|
14
|
+
when 401 ; raise Postmates::Unauthorized, msg
|
|
15
|
+
when 403 ; raise Postmates::Forbidden, msg
|
|
16
|
+
when 404 ; raise Postmates::NotFound, msg
|
|
17
|
+
when 500 ; raise Postmates::InternalServerError, msg
|
|
18
|
+
when 503 ; raise Postmates::ServiceUnavailable, msg
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def initialize(app)
|
|
24
|
+
super app
|
|
25
|
+
@parser = nil
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/postmates.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require_relative 'postmates/client'
|
|
2
|
+
|
|
3
|
+
module Postmates
|
|
4
|
+
class << self
|
|
5
|
+
|
|
6
|
+
def new
|
|
7
|
+
@client ||= Postmates::Client.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def method_missing(method, *args, &block)
|
|
11
|
+
return super unless new.respond_to?(method)
|
|
12
|
+
new.send(method, *args, &block)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def respond_to?(method, include_private = false)
|
|
16
|
+
new.respond_to?(method, include_private) || super
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
require_relative 'request'
|
|
2
|
+
require_relative 'connection'
|
|
3
|
+
require_relative 'configuration'
|
|
4
|
+
|
|
5
|
+
module Postmates
|
|
6
|
+
class Client
|
|
7
|
+
include Postmates::Request
|
|
8
|
+
include Postmates::Connection
|
|
9
|
+
include Postmates::Configuration
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
reset
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# POST /v1/customers/:customer_id/delivery_quotes
|
|
16
|
+
#
|
|
17
|
+
# pickup_address="20 McAllister St, San Francisco, CA"
|
|
18
|
+
# dropoff_address="101 Market St, San Francisco, CA"
|
|
19
|
+
#
|
|
20
|
+
# Returns a Quote object or the raw Faraday response
|
|
21
|
+
# if raw_response = true
|
|
22
|
+
def quote(options = {})
|
|
23
|
+
post("customers/#{customer_id}/delivery_quotes", options)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# POST /v1/customers/:customer_id/deliveries
|
|
27
|
+
#
|
|
28
|
+
# manifest="a box of kittens"
|
|
29
|
+
# pickup_name="The Warehouse"
|
|
30
|
+
# pickup_address="20 McAllister St, San Francisco, CA"
|
|
31
|
+
# pickup_phone_number="555-555-5555"
|
|
32
|
+
# pickup_business_name="Optional Pickup Business Name, Inc."
|
|
33
|
+
# pickup_notes="Optional note that this is Invoice #123"
|
|
34
|
+
# dropoff_name="Alice"
|
|
35
|
+
# dropoff_address="101 Market St, San Francisco, CA"
|
|
36
|
+
# dropoff_phone_number="415-555-1234"
|
|
37
|
+
# dropoff_business_name="Optional Dropoff Business Name, Inc."
|
|
38
|
+
# dropoff_notes="Optional note to ring the bell"
|
|
39
|
+
# quote_id=qUdje83jhdk
|
|
40
|
+
#
|
|
41
|
+
# Returns a Delivery object or the raw Faraday response
|
|
42
|
+
# if raw_response = true
|
|
43
|
+
def create(options = {})
|
|
44
|
+
post("customers/#{customer_id}/deliveries", options)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# GET /v1/customers/:customer_id/deliveries
|
|
48
|
+
#
|
|
49
|
+
# ?filter=ongoing
|
|
50
|
+
#
|
|
51
|
+
# Returns a list of Delivery objects or the raw Faraday response
|
|
52
|
+
# if raw_response = true
|
|
53
|
+
def list(options = {})
|
|
54
|
+
get("customers/#{customer_id}/deliveries", options)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# GET /v1/customers/:customer_id/deliveries/:delivery_id
|
|
58
|
+
#
|
|
59
|
+
# Returns a Delivery object or the raw Faraday response
|
|
60
|
+
# if raw_response = true
|
|
61
|
+
def retrieve(delivery_id)
|
|
62
|
+
get("customers/#{customer_id}/deliveries/#{delivery_id}")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# POST /v1/customers/:customer_id/deliveries/:delivery_id/cancel
|
|
66
|
+
#
|
|
67
|
+
# Returns a Delivery object or the raw Faraday response
|
|
68
|
+
# if raw_response = true
|
|
69
|
+
def cancel(delivery_id)
|
|
70
|
+
post("customers/#{customer_id}/deliveries/#{delivery_id}/cancel")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# POST /v1/customers/:customer_id/deliveries/:delivery_id/return
|
|
74
|
+
#
|
|
75
|
+
# Returns a Delivery object or the raw Faraday response
|
|
76
|
+
# if raw_response = true
|
|
77
|
+
def return(delivery_id)
|
|
78
|
+
post("customers/#{customer_id}/deliveries/#{delivery_id}/return")
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require_relative 'version'
|
|
2
|
+
|
|
3
|
+
module Postmates
|
|
4
|
+
module Configuration
|
|
5
|
+
|
|
6
|
+
VALID_CONFIGURATION_KEYS = [:headers, :api_url, :api_key,
|
|
7
|
+
:customer_id, :raw_response]
|
|
8
|
+
|
|
9
|
+
attr_accessor *VALID_CONFIGURATION_KEYS
|
|
10
|
+
|
|
11
|
+
DEFAULT_API_URL = 'https://api.postmates.com/v1/'
|
|
12
|
+
DEFAULT_HEADERS = {
|
|
13
|
+
accept: 'application/json',
|
|
14
|
+
user_agent: "postmates gem #{Postmates::Version}"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
def configure
|
|
18
|
+
yield self
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def reset
|
|
22
|
+
self.headers = DEFAULT_HEADERS
|
|
23
|
+
self.api_url = DEFAULT_API_URL
|
|
24
|
+
self.api_key = nil
|
|
25
|
+
self.customer_id = nil
|
|
26
|
+
self.raw_response = false
|
|
27
|
+
self
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'faraday_middleware'
|
|
2
|
+
require_relative '../faraday/raise_http_exception'
|
|
3
|
+
|
|
4
|
+
module Postmates
|
|
5
|
+
module Connection
|
|
6
|
+
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def connection
|
|
10
|
+
options = {
|
|
11
|
+
headers: headers,
|
|
12
|
+
ssl: { verify: false },
|
|
13
|
+
url: api_url
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
Faraday.new(options) do |connection|
|
|
17
|
+
connection.use Faraday::Request::UrlEncoded
|
|
18
|
+
connection.use Faraday::Request::BasicAuthentication, api_key, ''
|
|
19
|
+
connection.use Faraday::Response::ParseJson
|
|
20
|
+
connection.use FaradayMiddleware::RaiseHTTPException
|
|
21
|
+
connection.adapter(Faraday.default_adapter)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require_relative 'utils'
|
|
2
|
+
|
|
3
|
+
module Postmates
|
|
4
|
+
class Delivery
|
|
5
|
+
include Postmates::Utils
|
|
6
|
+
attr_reader :id, :created_at, :updated_at, :status, :complete,
|
|
7
|
+
:pickup_eta, :dropoff_eta, :dropoff_deadline,
|
|
8
|
+
:quote_id, :fee, :currency, :manifest, :pickup,
|
|
9
|
+
:dropoff, :courier, :image_url
|
|
10
|
+
|
|
11
|
+
def initialize(hash)
|
|
12
|
+
@id = hash['id']
|
|
13
|
+
@status = hash['status']
|
|
14
|
+
@complete = hash['complete']
|
|
15
|
+
@quote_id = hash['quote_id']
|
|
16
|
+
@fee = hash['fee']
|
|
17
|
+
@currency = hash['currency']
|
|
18
|
+
@manifest = hash['manifest']
|
|
19
|
+
@pickup = hash['pickup']
|
|
20
|
+
@dropoff = hash['dropoff']
|
|
21
|
+
@courier = hash['courier']
|
|
22
|
+
@image_url = urlify hash['image_href']
|
|
23
|
+
@created_at = timeify hash['created']
|
|
24
|
+
@updated_at = timeify hash['updated']
|
|
25
|
+
@pickup_eta = timeify hash['pickup_eta']
|
|
26
|
+
@dropoff_eta = timeify hash['dropoff_eta']
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def delivered?
|
|
30
|
+
status == 'delivered'
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|