openc-asana 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +4 -0
- data/.gitignore +13 -0
- data/.rspec +4 -0
- data/.rubocop.yml +11 -0
- data/.travis.yml +12 -0
- data/.yardopts +5 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +21 -0
- data/Guardfile +86 -0
- data/LICENSE.txt +21 -0
- data/README.md +355 -0
- data/Rakefile +65 -0
- data/examples/Gemfile +6 -0
- data/examples/Gemfile.lock +59 -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 +12 -0
- 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 +92 -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/resource_includes/attachment_uploading.rb +33 -0
- data/lib/asana/resource_includes/collection.rb +68 -0
- data/lib/asana/resource_includes/event.rb +51 -0
- data/lib/asana/resource_includes/event_subscription.rb +14 -0
- data/lib/asana/resource_includes/events.rb +103 -0
- data/lib/asana/resource_includes/registry.rb +63 -0
- data/lib/asana/resource_includes/resource.rb +103 -0
- data/lib/asana/resource_includes/response_helper.rb +14 -0
- data/lib/asana/resources.rb +14 -0
- data/lib/asana/resources/attachment.rb +44 -0
- data/lib/asana/resources/project.rb +154 -0
- data/lib/asana/resources/story.rb +64 -0
- data/lib/asana/resources/tag.rb +120 -0
- data/lib/asana/resources/task.rb +300 -0
- data/lib/asana/resources/team.rb +55 -0
- data/lib/asana/resources/user.rb +72 -0
- data/lib/asana/resources/workspace.rb +91 -0
- data/lib/asana/ruby2_0_0_compatibility.rb +3 -0
- data/lib/asana/version.rb +5 -0
- data/lib/templates/index.js +8 -0
- data/lib/templates/resource.ejs +225 -0
- data/openc-asana.gemspec +32 -0
- data/package.json +7 -0
- metadata +200 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5e8b344a69d1e1f4e4179036f6ea1f8fbd0683e9
|
4
|
+
data.tar.gz: c563fc621a50385012b203543ad8e4abd37f4069
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 13d6487fa1e320009acb725ee360e917ca9f27ac95c3d37dab7076a81ac711aa696d9ad3f9827b826cbd139c486e8465bd18466a3ef2fb10705cce55c47fb897
|
7
|
+
data.tar.gz: 3d734aa235a08302f979644bec4d0230784346449ec165b6f98ad4035cad459cd8ca3e7b653b88a420aff9e90a59a194bc5e7ab046d68b2d1d92db44a9d2040f
|
data/.codeclimate.yml
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.2.2
|
4
|
+
- 2.0.0
|
5
|
+
deploy:
|
6
|
+
provider: rubygems
|
7
|
+
api_key:
|
8
|
+
secure: ZFzExKt6auBOcQyg8955GwlZW2OaS64Q+XHGSay2MzjALw1iiy5uuMdwkueCKrGWSv6/eBe9jjsmYBe3OfkUIpIBbUacnbEYeaC70AyucNjvFrrl0YVYHb7neojarJUmKz9bz9Pkju/jdxksaYaj58xfq5YPQDfjFtdmylvuNEYpujT6goPEbxG4U4PpIhhQOZRDRXXAPS+f7jHejTSK06kvJjiJw0d51VJtBbp+0TKNKL6BDKdOKjKeHuebuUmSw8crDyaYdnwYwmNg1cJrGOv2t76M08zoKkkIO2lwPMHisi1/+cbVcZfxM4SfdHJeU6cQuRdb0uCUbbj6GsGwT8vWP2mGUrLe4UV/GfZDmvK3MKeKIlkgig31a3Qny9yjn8EjSnKHYuHBbJvPQDPPpFUfgEneUxn2t4P6m+epkd1gldWqTWf8mhMR/6xAFT4s+BaxnMMJsTC3Ea+dZZ30EqCw/kx5B2Z1KVLgsxHeMN/Q+AeOcbOvlGDsFL0Mjk/PqDTW1AWKLs/D1ohcxjSmlNJGWR6JHa/Ei0GqjDE2+/ZGsKsRfcDD4kU5qnKdqdzDlbL3cL4tChzuWVcguYdrg1yZzqPrCPzmy+2D7Hphyaj9CPKEh7qwT+IQU5o/V2peOJUjKrMlJS4gFq6MvTDh5U59J88Kkg72DXhcEUcySkU=
|
9
|
+
gem: asana
|
10
|
+
on:
|
11
|
+
tags: true
|
12
|
+
repo: Asana/ruby-asana
|
data/.yardopts
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in asana.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :tools do
|
7
|
+
gem 'rubocop'
|
8
|
+
gem 'rubocop-rspec'
|
9
|
+
|
10
|
+
gem 'guard'
|
11
|
+
gem 'guard-rspec'
|
12
|
+
gem 'guard-rubocop'
|
13
|
+
gem 'guard-yard'
|
14
|
+
|
15
|
+
gem 'yard'
|
16
|
+
gem 'yard-tomdoc'
|
17
|
+
|
18
|
+
gem 'byebug'
|
19
|
+
|
20
|
+
gem 'simplecov', require: false
|
21
|
+
end
|
data/Guardfile
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
# directories %w(app lib config test spec features)
|
6
|
+
|
7
|
+
## Uncomment to clear the screen before every task
|
8
|
+
# clearing :on
|
9
|
+
|
10
|
+
## Guard internally checks for changes in the Guardfile and exits.
|
11
|
+
## If you want Guard to automatically start up again, run guard in a
|
12
|
+
## shell loop, e.g.:
|
13
|
+
##
|
14
|
+
## $ while bundle exec guard; do echo "Restarting Guard..."; done
|
15
|
+
##
|
16
|
+
## Note: if you are using the `directories` clause above and you are not
|
17
|
+
## watching the project directory ('.'), then you will want to move
|
18
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
19
|
+
#
|
20
|
+
# $ mkdir config
|
21
|
+
# $ mv Guardfile config/
|
22
|
+
# $ ln -s config/Guardfile .
|
23
|
+
#
|
24
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
25
|
+
|
26
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
27
|
+
# rspec may be run, below are examples of the most common uses.
|
28
|
+
# * bundler: 'bundle exec rspec'
|
29
|
+
# * bundler binstubs: 'bin/rspec'
|
30
|
+
# * spring: 'bin/rspec' (This will use spring if running and you have
|
31
|
+
# installed the spring binstubs per the docs)
|
32
|
+
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
33
|
+
# * 'just' rspec: 'rspec'
|
34
|
+
|
35
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
36
|
+
require "guard/rspec/dsl"
|
37
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
38
|
+
|
39
|
+
# Feel free to open issues for suggestions and improvements
|
40
|
+
|
41
|
+
# RSpec files
|
42
|
+
rspec = dsl.rspec
|
43
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
44
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
45
|
+
watch(rspec.spec_files)
|
46
|
+
|
47
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
48
|
+
|
49
|
+
# Rails files
|
50
|
+
rails = dsl.rails(view_extensions: %w(erb haml slim))
|
51
|
+
dsl.watch_spec_files_for(rails.app_files)
|
52
|
+
dsl.watch_spec_files_for(rails.views)
|
53
|
+
|
54
|
+
watch(rails.controllers) do |m|
|
55
|
+
[
|
56
|
+
rspec.spec.("routing/#{m[1]}_routing"),
|
57
|
+
rspec.spec.("controllers/#{m[1]}_controller"),
|
58
|
+
rspec.spec.("acceptance/#{m[1]}")
|
59
|
+
]
|
60
|
+
end
|
61
|
+
|
62
|
+
# Rails config changes
|
63
|
+
watch(rails.spec_helper) { rspec.spec_dir }
|
64
|
+
watch(rails.routes) { "#{rspec.spec_dir}/routing" }
|
65
|
+
watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
|
66
|
+
|
67
|
+
# Capybara features specs
|
68
|
+
watch(rails.view_dirs) { |m| rspec.spec.("features/#{m[1]}") }
|
69
|
+
|
70
|
+
# Turnip features and steps
|
71
|
+
watch(%r{^spec/acceptance/(.+)\.feature$})
|
72
|
+
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
|
73
|
+
Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
guard :rubocop do
|
78
|
+
watch(%r{.+\.rb$})
|
79
|
+
watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
|
80
|
+
end
|
81
|
+
|
82
|
+
guard 'yard' do
|
83
|
+
watch(%r{app/.+\.rb})
|
84
|
+
watch(%r{lib/.+\.rb})
|
85
|
+
watch(%r{ext/.+\.c})
|
86
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Asana, Inc.
|
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,355 @@
|
|
1
|
+
# Asana
|
2
|
+
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/asana.svg)](http://badge.fury.io/rb/asana)
|
4
|
+
[![Build Status](https://travis-ci.org/Asana/ruby-asana.svg?branch=master)](https://travis-ci.org/Asana/ruby-asana)
|
5
|
+
[![Code Climate](https://codeclimate.com/github/Asana/ruby-asana/badges/gpa.svg)](https://codeclimate.com/github/Asana/ruby-asana)
|
6
|
+
[![Dependency Status](https://gemnasium.com/Asana/ruby-asana.svg)](https://gemnasium.com/Asana/ruby-asana)
|
7
|
+
|
8
|
+
|
9
|
+
A Ruby client for the 1.0 version of the Asana API.
|
10
|
+
|
11
|
+
Supported rubies:
|
12
|
+
|
13
|
+
* MRI 2.0.0 up to 2.2.x stable
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'asana'
|
21
|
+
```
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
$ bundle
|
26
|
+
|
27
|
+
Or install it yourself as:
|
28
|
+
|
29
|
+
$ gem install asana
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
To do anything, you'll need always an instance of `Asana::Client` configured
|
34
|
+
with your preferred authentication method (see the Authentication section below
|
35
|
+
for more complex scenarios) and other options.
|
36
|
+
|
37
|
+
The most minimal example would be as follows:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
require 'asana'
|
41
|
+
|
42
|
+
client = Asana::Client.new do |c|
|
43
|
+
c.authentication :api_token, 'my_api_token'
|
44
|
+
end
|
45
|
+
|
46
|
+
client.workspaces.find_all.first
|
47
|
+
```
|
48
|
+
|
49
|
+
A full-blown customized client using OAuth2 wih a previously obtained refresh
|
50
|
+
token, Typhoeus as a Faraday adapter, a custom user agent and custom Faraday
|
51
|
+
middleware:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
require 'asana'
|
55
|
+
|
56
|
+
client = Asana::Client.new do |c|
|
57
|
+
c.authentication :oauth2,
|
58
|
+
refresh_token: 'abc',
|
59
|
+
client_id: 'bcd',
|
60
|
+
client_secret: 'cde',
|
61
|
+
redirect_uri: 'http://example.org/auth'
|
62
|
+
c.faraday_adapter :typhoeus
|
63
|
+
c.configure_faraday { |conn| conn.use SomeFaradayMiddleware }
|
64
|
+
end
|
65
|
+
|
66
|
+
workspace = client.workspaces.find_by_id(12)
|
67
|
+
workspace.users
|
68
|
+
# => #<Asana::Collection<User> ...>
|
69
|
+
client.tags.create_in_workspace(workspace: workspace.id, name: 'foo')
|
70
|
+
# => #<Asana::Tag id: ..., name: "foo">
|
71
|
+
```
|
72
|
+
|
73
|
+
All resources are exposed as methods on the `Asana::Client` instance. Check out
|
74
|
+
the [documentation for each of them][docs].
|
75
|
+
|
76
|
+
### Authentication
|
77
|
+
|
78
|
+
This gem supports authenticating against the Asana API with either an API token or through OAuth2.
|
79
|
+
|
80
|
+
#### API Token
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
Asana::Client.new do |c|
|
84
|
+
c.authentication :api_token, 'my_api_token'
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
#### OAuth2
|
89
|
+
|
90
|
+
Authenticating through OAuth2 is preferred. There are many ways you can do this.
|
91
|
+
|
92
|
+
##### With a plain bearer token (doesn't support auto-refresh)
|
93
|
+
|
94
|
+
If you have a plain bearer token obtained somewhere else and you don't mind not
|
95
|
+
having your token auto-refresh, you can authenticate with it as follows:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
Asana::Client.new do |c|
|
99
|
+
c.authentication :oauth2, bearer_token: 'my_bearer_token'
|
100
|
+
end
|
101
|
+
```
|
102
|
+
|
103
|
+
##### With a refresh token and client credentials
|
104
|
+
|
105
|
+
If you obtained a refresh token, you can use it together with your client
|
106
|
+
credentials to authenticate:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
Asana::Client.new do |c|
|
110
|
+
c.authentication :oauth2,
|
111
|
+
refresh_token: 'abc',
|
112
|
+
client_id: 'bcd',
|
113
|
+
client_secret: 'cde',
|
114
|
+
redirect_uri: 'http://example.org/auth'
|
115
|
+
end
|
116
|
+
```
|
117
|
+
|
118
|
+
##### With an ::OAuth2::AccessToken object (from `omniauth-asana` for example)
|
119
|
+
|
120
|
+
If you use `omniauth-asana` or a browser-based OAuth2 authentication strategy in
|
121
|
+
general, possibly because your application is a web application, you can reuse
|
122
|
+
those credentials to authenticate with this API client. Here's how to do it from
|
123
|
+
the callback method:
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
# assuming we're using Sinatra and omniauth-asana
|
127
|
+
get '/auth/:name/callback' do
|
128
|
+
creds = request.env["omniauth.auth"]["credentials"].tap { |h| h.delete('expires') }
|
129
|
+
strategy = request.env["omniauth.strategy"]
|
130
|
+
|
131
|
+
# We need to refresh the omniauth OAuth2 token
|
132
|
+
access_token = OAuth2::AccessToken.from_hash(strategy.client, creds).refresh!
|
133
|
+
|
134
|
+
$client = Asana::Client.new do |c|
|
135
|
+
c.authentication :oauth2, access_token
|
136
|
+
end
|
137
|
+
|
138
|
+
redirect '/'
|
139
|
+
end
|
140
|
+
```
|
141
|
+
|
142
|
+
See `examples/omniauth_integration.rb` for a working example of this.
|
143
|
+
|
144
|
+
##### Using an OAuth2 offline authentication flow (for CLI applications)
|
145
|
+
|
146
|
+
If your application can't receive HTTP requests and thus you can't use
|
147
|
+
`omniauth-asana`, for example if it's a CLI application, you can authenticate as
|
148
|
+
follows:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
access_token = Asana::Authentication::OAuth2.offline_flow(client_id: ...,
|
152
|
+
client_secret: ...)
|
153
|
+
client = Asana::Client.new do |c|
|
154
|
+
c.authentication :oauth2, access_token
|
155
|
+
end
|
156
|
+
|
157
|
+
client.tasks.find_by_id(12)
|
158
|
+
```
|
159
|
+
|
160
|
+
This will print an authorization URL on STDOUT, and block until you paste in the
|
161
|
+
authorization code, which you can get by visiting that URL and granting the
|
162
|
+
necessary permissions.
|
163
|
+
|
164
|
+
### Pagination
|
165
|
+
|
166
|
+
Whenever you ask for a collection of resources, you can provide a number of
|
167
|
+
results per page to fetch, between 1 and 100. If you don't provide any, it
|
168
|
+
defaults to 20.
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
my_tasks = client.tasks.find_by_tag(tag: tag_id, per_page: 5)
|
172
|
+
# => #<Asana::Collection<Task> ...>
|
173
|
+
```
|
174
|
+
|
175
|
+
An `Asana::Collection` is a paginated collection -- it holds the first
|
176
|
+
`per_page` results, and a reference to the next page if any.
|
177
|
+
|
178
|
+
When you iterate an `Asana::Collection`, it'll transparently keep fetching all
|
179
|
+
the pages, and caching them along the way:
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
my_tasks.size # => 23, not 5
|
183
|
+
my_tasks.take(14)
|
184
|
+
# => [#<Asana::Task ...>, #<Asana::Task ...>, ... until 14]
|
185
|
+
```
|
186
|
+
|
187
|
+
#### Manual pagination
|
188
|
+
|
189
|
+
If you only want to deal with one page at a time and manually paginate, you can
|
190
|
+
get the elements of the current page with `#elements` and ask for the next page
|
191
|
+
with `#next_page`, which will return an `Asana::Collection` with the next page
|
192
|
+
of elements:
|
193
|
+
|
194
|
+
```ruby
|
195
|
+
my_tasks.elements # => [#<Asana::Task ...>, #<Asana::Task ...>, ... until 5]
|
196
|
+
my_tasks.next_page # => #<Asana::Collection ...>
|
197
|
+
```
|
198
|
+
|
199
|
+
#### Lazy pagination
|
200
|
+
|
201
|
+
Because an `Asana::Collection` represents the entire collection, it is often
|
202
|
+
handy to just take what you need from it, rather than let it fetch all its
|
203
|
+
contents from the network. You can accomplish this by turning it into a lazy
|
204
|
+
collection with `#lazy`:
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
# let my_tasks be an Asana::Collection of 10 pages of 100 elements each
|
208
|
+
my_tasks.lazy.drop(120).take(15).to_a
|
209
|
+
# Fetches only 2 pages, enough to get elements 120 to 135
|
210
|
+
# => [#<Asana::Task ...>, #<Asana::Task ...>, ...]
|
211
|
+
```
|
212
|
+
|
213
|
+
### Error handling
|
214
|
+
|
215
|
+
In any request against the Asana API, there a number of errors that could
|
216
|
+
arise. Those are well documented in the [Asana API Documentation][apidocs], and
|
217
|
+
are represented as exceptions under the namespace `Asana::Errors`.
|
218
|
+
|
219
|
+
All errors are subclasses of `Asana::Errors::APIError`, so make sure to rescue
|
220
|
+
instances of this class if you want to handle them yourself.
|
221
|
+
|
222
|
+
### I/O options
|
223
|
+
|
224
|
+
All requests (except `DELETE`) accept extra I/O options
|
225
|
+
[as documented in the API docs][io]. Just pass an extra `options` hash to any
|
226
|
+
request:
|
227
|
+
|
228
|
+
```ruby
|
229
|
+
client.tasks.find_by_id(12, options: { expand: ['workspace'] })
|
230
|
+
```
|
231
|
+
|
232
|
+
### Attachment uploading
|
233
|
+
|
234
|
+
To attach a file to a task or a project, you just need its absolute path on your
|
235
|
+
filesystem and its MIME type, and the file will be uploaded for you:
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
task = client.tasks.find_by_id(12)
|
239
|
+
attachment = task.attach(filename: '/absolute/path/to/my/file.png',
|
240
|
+
mime: 'image/png')
|
241
|
+
attachment.name # => 'file.png'
|
242
|
+
```
|
243
|
+
|
244
|
+
### Event streams
|
245
|
+
|
246
|
+
To subscribe to an event stream of a task or a project, just call `#events` on
|
247
|
+
it:
|
248
|
+
|
249
|
+
```ruby
|
250
|
+
task = client.tasks.find_by_id(12)
|
251
|
+
task.events # => #<Asana::Events ...>
|
252
|
+
|
253
|
+
# You can do the same with only the task id:
|
254
|
+
events = client.events.for(task.id)
|
255
|
+
```
|
256
|
+
|
257
|
+
An `Asana::Events` object is an infinite collection of `Asana::Event`
|
258
|
+
instances. Be warned that if you call `#each` on it, it will block forever!
|
259
|
+
|
260
|
+
Note that, by default, an event stream will wait at least 1 second between
|
261
|
+
polls, but that's configurable with the `wait` parameter:
|
262
|
+
|
263
|
+
```ruby
|
264
|
+
# wait at least 3 and a half seconds between each poll to the API
|
265
|
+
task.events(wait: 3.5) # => #<Asana::Events ...>
|
266
|
+
```
|
267
|
+
|
268
|
+
There are some interesting things you can do with an event stream, as it is a
|
269
|
+
normal Ruby Enumerable. Read below to get some ideas.
|
270
|
+
|
271
|
+
#### Subscribe to the event stream with a callback, polling every 2 seconds
|
272
|
+
|
273
|
+
```ruby
|
274
|
+
# Run this in another thread so that we don't block forever
|
275
|
+
events = client.tasks.find_by_id(12).events(wait: 2)
|
276
|
+
Thread.new do
|
277
|
+
events.each do |event|
|
278
|
+
notify_someone "New event arrived! #{event}"
|
279
|
+
end
|
280
|
+
end
|
281
|
+
```
|
282
|
+
|
283
|
+
#### Make the stream lazy and filter it by a specific pattern
|
284
|
+
|
285
|
+
To do that we need to call `#lazy` on the `Events` instance, just like with any
|
286
|
+
other `Enumerable`.
|
287
|
+
|
288
|
+
```ruby
|
289
|
+
events = client.tasks.find_by_id(12).events
|
290
|
+
only_change_events = events.lazy.select { |event| event.action == 'changed' }
|
291
|
+
Thread.new do
|
292
|
+
only_change_events.each do |event|
|
293
|
+
notify_someone "New change event arrived! #{event}"
|
294
|
+
end
|
295
|
+
end
|
296
|
+
```
|
297
|
+
|
298
|
+
## Development
|
299
|
+
|
300
|
+
You'll need Ruby 2.1+ and Node v0.10.26+ / NPM 1.4.3+ installed.
|
301
|
+
|
302
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
303
|
+
`bin/console` for an interactive prompt that will allow you to experiment.
|
304
|
+
|
305
|
+
Run the build with `rake`. This is equivalent to:
|
306
|
+
|
307
|
+
$ rake spec && rake rubocop && rake yard
|
308
|
+
|
309
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
310
|
+
|
311
|
+
## Releasing a new version
|
312
|
+
|
313
|
+
To release a new version, run either of these commands:
|
314
|
+
|
315
|
+
rake bump:patch
|
316
|
+
rake bump:minor
|
317
|
+
rake bump:major
|
318
|
+
|
319
|
+
This will: update `lib/asana/version.rb`, commit and tag the commit. Then you
|
320
|
+
just need to `push --tags` to let Travis build and release the new version to
|
321
|
+
Rubygems:
|
322
|
+
|
323
|
+
git push --tags
|
324
|
+
|
325
|
+
### Code generation
|
326
|
+
|
327
|
+
The specific Asana resource classes (`Tag`, `Workspace`, `Task`, etc) are
|
328
|
+
generated code, hence they shouldn't be modified by hand. The code that
|
329
|
+
generates it lives in `lib/templates/resource.ejs`, and is tested by generating
|
330
|
+
`spec/templates/unicorn.rb` and running `spec/templates/unicorn_spec.rb` as part
|
331
|
+
of the build.
|
332
|
+
|
333
|
+
If you wish to make changes on the code generation script:
|
334
|
+
|
335
|
+
1. Add/modify a spec on `spec/templates/unicorn_spec.rb`
|
336
|
+
2. Add your new feature or change to `lib/templates/resource.ejs`
|
337
|
+
3. Run `rake` or, more granularly, `rake codegen && rspec
|
338
|
+
spec/templates/unicorn_spec.rb`
|
339
|
+
|
340
|
+
Once you're sure your code works, submit a pull request and ask the maintainer
|
341
|
+
to make a release, as they'll need to run a release script from the
|
342
|
+
[asana-api-meta][meta] repository.
|
343
|
+
|
344
|
+
## Contributing
|
345
|
+
|
346
|
+
1. Fork it ( https://github.com/[my-github-username]/asana/fork )
|
347
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
348
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
349
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
350
|
+
5. Create a new Pull Request
|
351
|
+
|
352
|
+
[apidocs]: https://asana.com/developers
|
353
|
+
[io]: https://asana.com/developers/documentation/getting-started/input-output-options
|
354
|
+
[docs]: https://asana.github.com/ruby-asana
|
355
|
+
[meta]: https://github.com/asana/asana-api-meta
|