asana 0.0.6 → 0.1.1
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 +9 -9
- data/.codeclimate.yml +4 -0
- data/.gitignore +12 -20
- data/.rspec +4 -0
- data/.rubocop.yml +18 -0
- data/.travis.yml +12 -0
- data/.yardopts +5 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +17 -0
- data/Guardfile +85 -4
- data/LICENSE.txt +21 -0
- data/README.md +264 -135
- data/Rakefile +62 -7
- data/asana.gemspec +27 -21
- data/examples/Gemfile +6 -0
- data/examples/Gemfile.lock +56 -0
- data/examples/api_token.rb +21 -0
- data/examples/cli_app.rb +25 -0
- data/examples/events.rb +38 -0
- data/examples/omniauth_integration.rb +54 -0
- data/lib/asana.rb +8 -11
- data/lib/asana/authentication.rb +8 -0
- data/lib/asana/authentication/oauth2.rb +42 -0
- data/lib/asana/authentication/oauth2/access_token_authentication.rb +51 -0
- data/lib/asana/authentication/oauth2/bearer_token_authentication.rb +32 -0
- data/lib/asana/authentication/oauth2/client.rb +50 -0
- data/lib/asana/authentication/token_authentication.rb +20 -0
- data/lib/asana/client.rb +124 -0
- data/lib/asana/client/configuration.rb +165 -0
- data/lib/asana/errors.rb +90 -0
- data/lib/asana/http_client.rb +155 -0
- data/lib/asana/http_client/environment_info.rb +53 -0
- data/lib/asana/http_client/error_handling.rb +103 -0
- data/lib/asana/http_client/response.rb +32 -0
- data/lib/asana/resources.rb +11 -0
- data/lib/asana/resources/attachment.rb +44 -0
- data/lib/asana/resources/attachment_uploading.rb +33 -0
- data/lib/asana/resources/collection.rb +68 -0
- data/lib/asana/resources/event.rb +49 -0
- data/lib/asana/resources/event_subscription.rb +12 -0
- data/lib/asana/resources/events.rb +101 -0
- data/lib/asana/resources/project.rb +145 -19
- data/lib/asana/resources/registry.rb +62 -0
- data/lib/asana/resources/resource.rb +103 -0
- data/lib/asana/resources/response_helper.rb +14 -0
- data/lib/asana/resources/story.rb +58 -7
- data/lib/asana/resources/tag.rb +111 -19
- data/lib/asana/resources/task.rb +284 -57
- data/lib/asana/resources/team.rb +55 -0
- data/lib/asana/resources/user.rb +65 -10
- data/lib/asana/resources/workspace.rb +79 -34
- data/lib/asana/ruby2_0_0_compatibility.rb +3 -0
- data/lib/asana/version.rb +3 -1
- data/lib/templates/index.js +8 -0
- data/lib/templates/resource.ejs +225 -0
- data/package.json +7 -0
- metadata +91 -51
- data/LICENSE +0 -22
- data/lib/asana/config.rb +0 -23
- data/lib/asana/resource.rb +0 -52
- data/spec/asana/resources/project_spec.rb +0 -63
- data/spec/asana/resources/story_spec.rb +0 -39
- data/spec/asana/resources/tag_spec.rb +0 -63
- data/spec/asana/resources/task_spec.rb +0 -95
- data/spec/asana/resources/user_spec.rb +0 -64
- data/spec/asana/resources/workspace_spec.rb +0 -108
- data/spec/spec_helper.rb +0 -9
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
require 'faraday_middleware/multi_json'
|
4
|
+
|
5
|
+
require_relative 'http_client/error_handling'
|
6
|
+
require_relative 'http_client/environment_info'
|
7
|
+
require_relative 'http_client/response'
|
8
|
+
|
9
|
+
module Asana
|
10
|
+
# Internal: Wrapper over Faraday that abstracts authentication, request
|
11
|
+
# parsing and common options.
|
12
|
+
class HttpClient
|
13
|
+
# Internal: The API base URI.
|
14
|
+
BASE_URI = 'https://app.asana.com/api/1.0'
|
15
|
+
|
16
|
+
# Public: Initializes an HttpClient to make requests to the Asana API.
|
17
|
+
#
|
18
|
+
# authentication - [Asana::Authentication] An authentication strategy.
|
19
|
+
# adapter - [Symbol, Proc] A Faraday adapter, eiter a Symbol for
|
20
|
+
# registered adapters or a Proc taking a builder for a
|
21
|
+
# custom one. Defaults to Faraday.default_adapter.
|
22
|
+
# user_agent - [String] The user agent. Defaults to "ruby-asana vX.Y.Z".
|
23
|
+
# config - [Proc] An optional block that yields the Faraday builder
|
24
|
+
# object for customization.
|
25
|
+
def initialize(authentication: required('authentication'),
|
26
|
+
adapter: nil,
|
27
|
+
user_agent: nil,
|
28
|
+
debug_mode: false,
|
29
|
+
&config)
|
30
|
+
@authentication = authentication
|
31
|
+
@adapter = adapter || Faraday.default_adapter
|
32
|
+
@environment_info = EnvironmentInfo.new(user_agent)
|
33
|
+
@debug_mode = debug_mode
|
34
|
+
@config = config
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: Performs a GET request against the API.
|
38
|
+
#
|
39
|
+
# resource_uri - [String] the resource URI relative to the base Asana API
|
40
|
+
# URL, e.g "/users/me".
|
41
|
+
# params - [Hash] the request parameters
|
42
|
+
# options - [Hash] the request I/O options
|
43
|
+
#
|
44
|
+
# Returns an [Asana::HttpClient::Response] if everything went well.
|
45
|
+
# Raises [Asana::Errors::APIError] if anything went wrong.
|
46
|
+
def get(resource_uri, params: {}, options: {})
|
47
|
+
opts = options.reduce({}) do |acc, (k, v)|
|
48
|
+
acc.tap do |hash|
|
49
|
+
hash[:"opt_#{k}"] = v.is_a?(Array) ? v.join(',') : v
|
50
|
+
end
|
51
|
+
end
|
52
|
+
perform_request(:get, resource_uri, params.merge(opts))
|
53
|
+
end
|
54
|
+
|
55
|
+
# Public: Performs a PUT request against the API.
|
56
|
+
#
|
57
|
+
# resource_uri - [String] the resource URI relative to the base Asana API
|
58
|
+
# URL, e.g "/users/me".
|
59
|
+
# body - [Hash] the body to PUT.
|
60
|
+
# options - [Hash] the request I/O options
|
61
|
+
#
|
62
|
+
# Returns an [Asana::HttpClient::Response] if everything went well.
|
63
|
+
# Raises [Asana::Errors::APIError] if anything went wrong.
|
64
|
+
def put(resource_uri, body: {}, options: {})
|
65
|
+
params = { data: body }.merge(options.empty? ? {} : { options: options })
|
66
|
+
perform_request(:put, resource_uri, params)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Public: Performs a POST request against the API.
|
70
|
+
#
|
71
|
+
# resource_uri - [String] the resource URI relative to the base Asana API
|
72
|
+
# URL, e.g "/tags".
|
73
|
+
# body - [Hash] the body to POST.
|
74
|
+
# upload - [Faraday::UploadIO] an upload object to post as multipart.
|
75
|
+
# Defaults to nil.
|
76
|
+
# options - [Hash] the request I/O options
|
77
|
+
#
|
78
|
+
# Returns an [Asana::HttpClient::Response] if everything went well.
|
79
|
+
# Raises [Asana::Errors::APIError] if anything went wrong.
|
80
|
+
def post(resource_uri, body: {}, upload: nil, options: {})
|
81
|
+
params = { data: body }.merge(options.empty? ? {} : { options: options })
|
82
|
+
if upload
|
83
|
+
perform_request(:post, resource_uri, params.merge(file: upload)) do |c|
|
84
|
+
c.request :multipart
|
85
|
+
end
|
86
|
+
else
|
87
|
+
perform_request(:post, resource_uri, params)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Public: Performs a DELETE request against the API.
|
92
|
+
#
|
93
|
+
# resource_uri - [String] the resource URI relative to the base Asana API
|
94
|
+
# URL, e.g "/tags".
|
95
|
+
#
|
96
|
+
# Returns an [Asana::HttpClient::Response] if everything went well.
|
97
|
+
# Raises [Asana::Errors::APIError] if anything went wrong.
|
98
|
+
def delete(resource_uri)
|
99
|
+
perform_request(:delete, resource_uri)
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def connection(&request_config)
|
105
|
+
Faraday.new do |builder|
|
106
|
+
@authentication.configure(builder)
|
107
|
+
@environment_info.configure(builder)
|
108
|
+
request_config.call(builder) if request_config
|
109
|
+
configure_format(builder)
|
110
|
+
add_middleware(builder)
|
111
|
+
@config.call(builder) if @config
|
112
|
+
use_adapter(builder, @adapter)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def perform_request(method, resource_uri, body = {}, &request_config)
|
117
|
+
handling_errors do
|
118
|
+
url = BASE_URI + resource_uri
|
119
|
+
log_request(method, url, body) if @debug_mode
|
120
|
+
Response.new(connection(&request_config).public_send(method, url, body))
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def configure_format(builder)
|
125
|
+
builder.request :multi_json
|
126
|
+
builder.response :multi_json
|
127
|
+
end
|
128
|
+
|
129
|
+
def add_middleware(builder)
|
130
|
+
builder.use Faraday::Response::RaiseError
|
131
|
+
builder.use FaradayMiddleware::FollowRedirects
|
132
|
+
end
|
133
|
+
|
134
|
+
def use_adapter(builder, adapter)
|
135
|
+
case adapter
|
136
|
+
when Symbol
|
137
|
+
builder.adapter(adapter)
|
138
|
+
when Proc
|
139
|
+
adapter.call(builder)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def handling_errors(&request)
|
144
|
+
ErrorHandling.handle(&request)
|
145
|
+
end
|
146
|
+
|
147
|
+
def log_request(method, url, body)
|
148
|
+
STDERR.puts format('[%s] %s %s (%s)',
|
149
|
+
self.class,
|
150
|
+
method.to_s.upcase,
|
151
|
+
url,
|
152
|
+
body.inspect)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require_relative '../version'
|
2
|
+
require 'openssl'
|
3
|
+
|
4
|
+
module Asana
|
5
|
+
class HttpClient
|
6
|
+
# Internal: Adds environment information to a Faraday request.
|
7
|
+
class EnvironmentInfo
|
8
|
+
# Internal: The default user agent to use in all requests to the API.
|
9
|
+
USER_AGENT = "ruby-asana v#{Asana::VERSION}"
|
10
|
+
|
11
|
+
def initialize(user_agent = nil)
|
12
|
+
@user_agent = user_agent || USER_AGENT
|
13
|
+
@openssl_version = OpenSSL::OPENSSL_VERSION
|
14
|
+
@client_version = Asana::VERSION
|
15
|
+
@os = os
|
16
|
+
end
|
17
|
+
|
18
|
+
# Public: Augments a Faraday connection with information about the
|
19
|
+
# environment.
|
20
|
+
def configure(builder)
|
21
|
+
builder.headers[:user_agent] = @user_agent
|
22
|
+
builder.headers[:"X-Asana-Client-Lib"] = header
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def header
|
28
|
+
{ os: @os,
|
29
|
+
language: 'ruby',
|
30
|
+
language_version: RUBY_VERSION,
|
31
|
+
version: @client_version,
|
32
|
+
openssl_version: @openssl_version }
|
33
|
+
.map { |k, v| "#{k}=#{v}" }.join('&')
|
34
|
+
end
|
35
|
+
|
36
|
+
# rubocop:disable Metrics/MethodLength
|
37
|
+
def os
|
38
|
+
if RUBY_PLATFORM =~ /win32/ || RUBY_PLATFORM =~ /mingw/
|
39
|
+
'windows'
|
40
|
+
elsif RUBY_PLATFORM =~ /linux/
|
41
|
+
'linux'
|
42
|
+
elsif RUBY_PLATFORM =~ /darwin/
|
43
|
+
'darwin'
|
44
|
+
elsif RUBY_PLATFORM =~ /freebsd/
|
45
|
+
'freebsd'
|
46
|
+
else
|
47
|
+
'unknown'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
# rubocop:enable Metrics/MethodLength
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
3
|
+
require_relative '../errors'
|
4
|
+
|
5
|
+
module Asana
|
6
|
+
class HttpClient
|
7
|
+
# Internal: Handles errors from the API and re-raises them as proper
|
8
|
+
# exceptions.
|
9
|
+
module ErrorHandling
|
10
|
+
include Errors
|
11
|
+
|
12
|
+
module_function
|
13
|
+
|
14
|
+
# Public: Perform a request handling any API errors correspondingly.
|
15
|
+
#
|
16
|
+
# request - [Proc] a block that will execute the request.
|
17
|
+
#
|
18
|
+
# Returns a [Faraday::Response] object.
|
19
|
+
#
|
20
|
+
# Raises [Asana::Errors::InvalidRequest] for invalid requests.
|
21
|
+
# Raises [Asana::Errors::NotAuthorized] for unauthorized requests.
|
22
|
+
# Raises [Asana::Errors::Forbidden] for forbidden requests.
|
23
|
+
# Raises [Asana::Errors::NotFound] when a resource can't be found.
|
24
|
+
# Raises [Asana::Errors::RateLimitEnforced] when the API is throttling.
|
25
|
+
# Raises [Asana::Errors::ServerError] when there's a server problem.
|
26
|
+
# Raises [Asana::Errors::APIError] when the API returns an unknown error.
|
27
|
+
#
|
28
|
+
# rubocop:disable all
|
29
|
+
def handle(&request)
|
30
|
+
request.call
|
31
|
+
rescue Faraday::ClientError => e
|
32
|
+
raise e unless e.response
|
33
|
+
case e.response[:status]
|
34
|
+
when 400 then raise invalid_request(e.response)
|
35
|
+
when 401 then raise not_authorized(e.response)
|
36
|
+
when 403 then raise forbidden(e.response)
|
37
|
+
when 404 then raise not_found(e.response)
|
38
|
+
when 412 then recover_response(e.response)
|
39
|
+
when 429 then raise rate_limit_enforced(e.response)
|
40
|
+
when 500 then raise server_error(e.response)
|
41
|
+
else raise api_error(e.response)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
# rubocop:enable all
|
45
|
+
|
46
|
+
# Internal: Returns an InvalidRequest exception including a list of
|
47
|
+
# errors.
|
48
|
+
def invalid_request(response)
|
49
|
+
errors = body(response).fetch('errors', []).map { |e| e['message'] }
|
50
|
+
InvalidRequest.new(errors).tap do |exception|
|
51
|
+
exception.response = response
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Internal: Returns a NotAuthorized exception.
|
56
|
+
def not_authorized(response)
|
57
|
+
NotAuthorized.new.tap { |exception| exception.response = response }
|
58
|
+
end
|
59
|
+
|
60
|
+
# Internal: Returns a Forbidden exception.
|
61
|
+
def forbidden(response)
|
62
|
+
Forbidden.new.tap { |exception| exception.response = response }
|
63
|
+
end
|
64
|
+
|
65
|
+
# Internal: Returns a NotFound exception.
|
66
|
+
def not_found(response)
|
67
|
+
NotFound.new.tap { |exception| exception.response = response }
|
68
|
+
end
|
69
|
+
|
70
|
+
# Internal: Returns a RateLimitEnforced exception with a retry after
|
71
|
+
# field.
|
72
|
+
def rate_limit_enforced(response)
|
73
|
+
retry_after_seconds = response[:headers]['Retry-After']
|
74
|
+
RateLimitEnforced.new(retry_after_seconds).tap do |exception|
|
75
|
+
exception.response = response
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Internal: Returns a ServerError exception with a unique phrase.
|
80
|
+
def server_error(response)
|
81
|
+
phrase = body(response).fetch('errors', []).first['phrase']
|
82
|
+
ServerError.new(phrase).tap do |exception|
|
83
|
+
exception.response = response
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Internal: Returns an APIError exception.
|
88
|
+
def api_error(response)
|
89
|
+
APIError.new.tap { |exception| exception.response = response }
|
90
|
+
end
|
91
|
+
|
92
|
+
# Internal: Parser a response body from JSON.
|
93
|
+
def body(response)
|
94
|
+
MultiJson.load(response[:body])
|
95
|
+
end
|
96
|
+
|
97
|
+
def recover_response(response)
|
98
|
+
r = response.dup.tap { |res| res[:body] = body(response) }
|
99
|
+
Response.new(OpenStruct.new(env: OpenStruct.new(r)))
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Asana
|
2
|
+
class HttpClient
|
3
|
+
# Internal: Represents a response from the Asana API.
|
4
|
+
class Response
|
5
|
+
# Public:
|
6
|
+
# Returns a [Faraday::Env] object for debugging.
|
7
|
+
attr_reader :faraday_env
|
8
|
+
# Public:
|
9
|
+
# Returns the [Integer] status code of the response.
|
10
|
+
attr_reader :status
|
11
|
+
# Public:
|
12
|
+
# Returns the [Hash] representing the parsed JSON body.
|
13
|
+
attr_reader :body
|
14
|
+
|
15
|
+
# Public: Wraps a Faraday response.
|
16
|
+
#
|
17
|
+
# faraday_response - [Faraday::Response] the Faraday response to wrap.
|
18
|
+
def initialize(faraday_response)
|
19
|
+
@faraday_env = faraday_response.env
|
20
|
+
@status = faraday_env.status
|
21
|
+
@body = faraday_env.body
|
22
|
+
end
|
23
|
+
|
24
|
+
# Public:
|
25
|
+
# Returns a [String] representation of the response.
|
26
|
+
def to_s
|
27
|
+
"#<Asana::HttpClient::Response status=#{@status} body=#{@body}>"
|
28
|
+
end
|
29
|
+
alias_method :inspect, :to_s
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require_relative 'resources/resource'
|
2
|
+
require_relative 'resources/collection'
|
3
|
+
|
4
|
+
Dir[File.join(File.dirname(__FILE__), 'resources', '*.rb')]
|
5
|
+
.each { |resource| require resource }
|
6
|
+
|
7
|
+
module Asana
|
8
|
+
# Public: Contains all the resources that the Asana API can return.
|
9
|
+
module Resources
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
### WARNING: This file is auto-generated by the asana-api-meta repo. Do not
|
2
|
+
### edit it manually.
|
3
|
+
|
4
|
+
module Asana
|
5
|
+
module Resources
|
6
|
+
# An _attachment_ object represents any file attached to a task in Asana,
|
7
|
+
# whether it's an uploaded file or one associated via a third-party service
|
8
|
+
# such as Dropbox or Google Drive.
|
9
|
+
class Attachment < Resource
|
10
|
+
|
11
|
+
|
12
|
+
attr_reader :id
|
13
|
+
|
14
|
+
class << self
|
15
|
+
# Returns the plural name of the resource.
|
16
|
+
def plural_name
|
17
|
+
'attachments'
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns the full record for a single attachment.
|
21
|
+
#
|
22
|
+
# id - [Id] Globally unique identifier for the attachment.
|
23
|
+
#
|
24
|
+
# options - [Hash] the request I/O options.
|
25
|
+
def find_by_id(client, id, options: {})
|
26
|
+
|
27
|
+
self.new(parse(client.get("/attachments/#{id}", options: options)).first, client: client)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the compact records for all attachments on the task.
|
31
|
+
#
|
32
|
+
# task - [Id] Globally unique identifier for the task.
|
33
|
+
#
|
34
|
+
# per_page - [Integer] the number of records to fetch per page.
|
35
|
+
# options - [Hash] the request I/O options.
|
36
|
+
def find_by_task(client, task: required("task"), per_page: 20, options: {})
|
37
|
+
params = { limit: per_page }.reject { |_,v| v.nil? || Array(v).empty? }
|
38
|
+
Collection.new(parse(client.get("/tasks/#{task}/attachments", params: params, options: options)), type: self, client: client)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Asana
|
2
|
+
module Resources
|
3
|
+
# Internal: Mixin to add the ability to upload an attachment to a specific
|
4
|
+
# Asana resource (a Task, really).
|
5
|
+
module AttachmentUploading
|
6
|
+
# Uploads a new attachment to the resource.
|
7
|
+
#
|
8
|
+
# filename - [String] the absolute path of the file to upload.
|
9
|
+
# mime - [String] the MIME type of the file
|
10
|
+
# options - [Hash] the request I/O options
|
11
|
+
# data - [Hash] extra attributes to post
|
12
|
+
#
|
13
|
+
# rubocop:disable Metrics/AbcSize
|
14
|
+
# rubocop:disable Metrics/MethodLength
|
15
|
+
def attach(filename: required('filename'),
|
16
|
+
mime: required('mime'),
|
17
|
+
options: {}, **data)
|
18
|
+
path = File.expand_path(filename)
|
19
|
+
unless File.exist?(path)
|
20
|
+
fail ArgumentError, "file #{filename} doesn't exist"
|
21
|
+
end
|
22
|
+
upload = Faraday::UploadIO.new(path, mime)
|
23
|
+
response = client.post("/#{self.class.plural_name}/#{id}/attachments",
|
24
|
+
body: data,
|
25
|
+
upload: upload,
|
26
|
+
options: options)
|
27
|
+
Attachment.new(parse(response).first, client: client)
|
28
|
+
end
|
29
|
+
# rubocop:enable Metrics/MethodLength
|
30
|
+
# rubocop:enable Metrics/AbcSize
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative 'response_helper'
|
2
|
+
|
3
|
+
module Asana
|
4
|
+
module Resources
|
5
|
+
# Public: Represents a paginated collection of Asana resources.
|
6
|
+
class Collection
|
7
|
+
include Enumerable
|
8
|
+
include ResponseHelper
|
9
|
+
|
10
|
+
attr_reader :elements
|
11
|
+
|
12
|
+
# Public: Initializes a collection representing a page of resources of a
|
13
|
+
# given type.
|
14
|
+
#
|
15
|
+
# (elements, extra) - [Array] an (String, Hash) tuple coming from the
|
16
|
+
# response parser.
|
17
|
+
# type - [Class] the type of resource that the collection
|
18
|
+
# contains. Defaults to the generic Resource.
|
19
|
+
# client - [Asana::Client] the client to perform requests.
|
20
|
+
def initialize((elements, extra),
|
21
|
+
type: Resource,
|
22
|
+
client: required('client'))
|
23
|
+
@elements = elements.map { |elem| type.new(elem, client: client) }
|
24
|
+
@type = type
|
25
|
+
@next_page_data = extra['next_page']
|
26
|
+
@client = client
|
27
|
+
end
|
28
|
+
|
29
|
+
# Public: Iterates over the elements of the collection.
|
30
|
+
def each(&block)
|
31
|
+
if block
|
32
|
+
@elements.each(&block)
|
33
|
+
(next_page || []).each(&block)
|
34
|
+
else
|
35
|
+
to_enum
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Public: Returns the size of the collection.
|
40
|
+
def size
|
41
|
+
to_a.size
|
42
|
+
end
|
43
|
+
alias_method :length, :size
|
44
|
+
|
45
|
+
# Public: Returns a String representation of the collection.
|
46
|
+
def to_s
|
47
|
+
"#<Asana::Collection<#{@type}> " \
|
48
|
+
"[#{@elements.map(&:inspect).join(', ')}" +
|
49
|
+
(@next_page_data ? ', ...' : '') + ']>'
|
50
|
+
end
|
51
|
+
|
52
|
+
alias_method :inspect, :to_s
|
53
|
+
|
54
|
+
# Public: Returns a new Asana::Resources::Collection with the next page
|
55
|
+
# or nil if there are no more pages. Caches the result.
|
56
|
+
def next_page
|
57
|
+
if defined?(@next_page)
|
58
|
+
@next_page
|
59
|
+
else
|
60
|
+
@next_page = if @next_page_data
|
61
|
+
response = parse(@client.get(@next_page_data['path']))
|
62
|
+
self.class.new(response, type: @type, client: @client)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|