kanbantastic 0.1.5
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 +3 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +42 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +0 -0
- data/Rakefile +9 -0
- data/kanbantastic.gemspec +35 -0
- data/lib/kanbantastic.rb +8 -0
- data/lib/kanbantastic/base.rb +140 -0
- data/lib/kanbantastic/column.rb +45 -0
- data/lib/kanbantastic/config.rb +15 -0
- data/lib/kanbantastic/task.rb +130 -0
- data/lib/kanbantastic/user.rb +15 -0
- data/lib/kanbantastic/version.rb +3 -0
- data/spec/cassettes/base/get.yml +166 -0
- data/spec/cassettes/base/post.yml +164 -0
- data/spec/cassettes/base/put.yml +154 -0
- data/spec/cassettes/base/rectify_time.yml +30 -0
- data/spec/cassettes/cassette2.yml +63 -0
- data/spec/cassettes/cassette3.yml +63 -0
- data/spec/cassettes/cassette6.yml +123 -0
- data/spec/cassettes/task/all.yml +123 -0
- data/spec/cassettes/task/archive1.yml +557 -0
- data/spec/cassettes/task/archive2.yml +371 -0
- data/spec/cassettes/task/archived1.yml +185 -0
- data/spec/cassettes/task/archived2.yml +619 -0
- data/spec/cassettes/task/create.yml +123 -0
- data/spec/cassettes/task/find.yml +125 -0
- data/spec/cassettes/task/move_to_first_column.yml +722 -0
- data/spec/cassettes/task/move_to_last_column.yml +846 -0
- data/spec/cassettes/task/move_to_next_column.yml +350 -0
- data/spec/cassettes/task/move_to_previous_column.yml +412 -0
- data/spec/cassettes/task/move_to_second_column.yml +722 -0
- data/spec/cassettes/task/owner.yml +185 -0
- data/spec/cassettes/task/update.yml +185 -0
- data/spec/cassettes/task/update_column_id.yml +247 -0
- data/spec/cassettes/workspaces_with_projects.yml +34 -0
- data/spec/kanbantastic/base_spec.rb +179 -0
- data/spec/kanbantastic/column_spec.rb +83 -0
- data/spec/kanbantastic/config_spec.rb +12 -0
- data/spec/kanbantastic/kanbanery_spec.rb +16 -0
- data/spec/kanbantastic/task_spec.rb +380 -0
- data/spec/spec_helper.rb +22 -0
- metadata +194 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rvm --create ruby-1.9.2-p180@kanbantastic
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
kanbantastic (0.1.1)
|
|
5
|
+
activemodel
|
|
6
|
+
httparty
|
|
7
|
+
|
|
8
|
+
GEM
|
|
9
|
+
remote: http://rubygems.org/
|
|
10
|
+
specs:
|
|
11
|
+
activemodel (3.0.5)
|
|
12
|
+
activesupport (= 3.0.5)
|
|
13
|
+
builder (~> 2.1.2)
|
|
14
|
+
i18n (~> 0.4)
|
|
15
|
+
activesupport (3.0.5)
|
|
16
|
+
builder (2.1.2)
|
|
17
|
+
crack (0.1.8)
|
|
18
|
+
diff-lcs (1.1.2)
|
|
19
|
+
fakeweb (1.3.0)
|
|
20
|
+
httparty (0.7.4)
|
|
21
|
+
crack (= 0.1.8)
|
|
22
|
+
i18n (0.5.0)
|
|
23
|
+
mocha (0.9.12)
|
|
24
|
+
rspec (2.5.0)
|
|
25
|
+
rspec-core (~> 2.5.0)
|
|
26
|
+
rspec-expectations (~> 2.5.0)
|
|
27
|
+
rspec-mocks (~> 2.5.0)
|
|
28
|
+
rspec-core (2.5.1)
|
|
29
|
+
rspec-expectations (2.5.0)
|
|
30
|
+
diff-lcs (~> 1.1.2)
|
|
31
|
+
rspec-mocks (2.5.0)
|
|
32
|
+
vcr (1.6.0)
|
|
33
|
+
|
|
34
|
+
PLATFORMS
|
|
35
|
+
ruby
|
|
36
|
+
|
|
37
|
+
DEPENDENCIES
|
|
38
|
+
fakeweb
|
|
39
|
+
kanbantastic!
|
|
40
|
+
mocha
|
|
41
|
+
rspec
|
|
42
|
+
vcr
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2010 Jagdeep Singh
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
|
File without changes
|
data/Rakefile
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
require "kanbantastic/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = "kanbantastic"
|
|
7
|
+
s.version = Kanbantastic::VERSION
|
|
8
|
+
s.platform = Gem::Platform::RUBY
|
|
9
|
+
s.authors = ["Jagdeep Singh"]
|
|
10
|
+
s.email = ["jagdeepkh@gmail.com"]
|
|
11
|
+
s.homepage = "https://github.com/Ennova/Kanbantastic"
|
|
12
|
+
s.summary = %q{Provides a ruby interface to manage your kanbanery resources like a project's tasks, columns and more}
|
|
13
|
+
s.description = %q{Use this gem to interact with kanbanery API}
|
|
14
|
+
|
|
15
|
+
s.rubyforge_project = "kanbantastic"
|
|
16
|
+
|
|
17
|
+
s.files = `git ls-files`.split("\n")
|
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
20
|
+
s.require_paths = ["lib"]
|
|
21
|
+
s.licenses = ["MIT"]
|
|
22
|
+
s.rubygems_version = %q{1.3.7}
|
|
23
|
+
s.extra_rdoc_files = [
|
|
24
|
+
"LICENSE.txt",
|
|
25
|
+
"README.rdoc"
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
s.add_runtime_dependency('activemodel')
|
|
29
|
+
s.add_runtime_dependency('httparty')
|
|
30
|
+
|
|
31
|
+
s.add_development_dependency('rspec')
|
|
32
|
+
s.add_development_dependency('fakeweb')
|
|
33
|
+
s.add_development_dependency('vcr')
|
|
34
|
+
s.add_development_dependency('mocha')
|
|
35
|
+
end
|
data/lib/kanbantastic.rb
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
require 'active_model'
|
|
2
|
+
require 'httparty'
|
|
3
|
+
|
|
4
|
+
require File.join(File.dirname(__FILE__), 'kanbantastic/base')
|
|
5
|
+
require File.join(File.dirname(__FILE__), 'kanbantastic/task')
|
|
6
|
+
require File.join(File.dirname(__FILE__), 'kanbantastic/column')
|
|
7
|
+
require File.join(File.dirname(__FILE__), 'kanbantastic/config')
|
|
8
|
+
require File.join(File.dirname(__FILE__), 'kanbantastic/user')
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
module Kanbantastic
|
|
2
|
+
|
|
3
|
+
class Base
|
|
4
|
+
include HTTParty
|
|
5
|
+
include ActiveModel::Validations
|
|
6
|
+
validates_presence_of :config
|
|
7
|
+
|
|
8
|
+
RECTIFY_TIME_FOR = [:updated_at, :created_at, :moved_at]
|
|
9
|
+
|
|
10
|
+
attr_accessor :config
|
|
11
|
+
|
|
12
|
+
def initialize(config)
|
|
13
|
+
@config = config
|
|
14
|
+
self.valid?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def get(url, options={})
|
|
18
|
+
check_parameter_format(:url => [url, String], :options => [options, Hash])
|
|
19
|
+
|
|
20
|
+
self.class.setup_headers(config.api_key)
|
|
21
|
+
response = self.class.get(self.class.base_uri(config.workspace) + url, options)
|
|
22
|
+
if response.code == 200
|
|
23
|
+
self.class.parse_response(response)
|
|
24
|
+
else
|
|
25
|
+
raise response.headers['status']
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def post(url, options={})
|
|
30
|
+
check_parameter_format(:url => [url, String], :options => [options, Hash])
|
|
31
|
+
|
|
32
|
+
self.class.setup_headers(config.api_key)
|
|
33
|
+
response = self.class.post(self.class.base_uri(config.workspace) + url, options)
|
|
34
|
+
if response.code == 201
|
|
35
|
+
self.class.parse_response(response)
|
|
36
|
+
else
|
|
37
|
+
raise response.headers['status']
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def put(url, options={})
|
|
42
|
+
check_parameter_format(:url => [url, String], :options => [options, Hash])
|
|
43
|
+
|
|
44
|
+
self.class.setup_headers(config.api_key)
|
|
45
|
+
response = self.class.put(self.class.base_uri(config.workspace) + url, options)
|
|
46
|
+
if response.code == 200
|
|
47
|
+
self.class.parse_response(response)
|
|
48
|
+
else
|
|
49
|
+
raise response.headers['status']
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def project_id
|
|
54
|
+
config.project_id
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def assign_attributes(params={})
|
|
58
|
+
params.each do |key, value|
|
|
59
|
+
method_name = key.to_s+"="
|
|
60
|
+
self.send(method_name, value) if self.respond_to?(method_name)
|
|
61
|
+
end
|
|
62
|
+
return self
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.find_project_id(project_name, workspace_name, api_key)
|
|
66
|
+
setup_headers(api_key)
|
|
67
|
+
workspaces = get(base_uri(workspace_name) + "/user/workspaces.json").parsed_response
|
|
68
|
+
if workspace_name
|
|
69
|
+
workspace = workspaces.select{|w| w['name'] == workspace_name }.first
|
|
70
|
+
project = workspace['projects'].select{|p| p['name'] == project_name }.first if workspace
|
|
71
|
+
end
|
|
72
|
+
return project['id'] if project
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def self.request(method, config, url, options={})
|
|
76
|
+
new(config).send(method.to_s, url, options)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.invalid_response_error(msg, response)
|
|
80
|
+
begin
|
|
81
|
+
text = response.map{|k,v| v.map{|e| "#{k} #{e}" }.join(', ')}.join(', ')
|
|
82
|
+
rescue
|
|
83
|
+
raise msg
|
|
84
|
+
end
|
|
85
|
+
raise "#{msg} #{text}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def self.parse_response response
|
|
91
|
+
rectify_time(symbolize_keys(response.parsed_response))
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def self.rectify_time response
|
|
95
|
+
diff = server_time_difference
|
|
96
|
+
if response.class == Array
|
|
97
|
+
response.each{|r| RECTIFY_TIME_FOR.each{|k| (r[k] += diff) if r[k]}}
|
|
98
|
+
else
|
|
99
|
+
RECTIFY_TIME_FOR.each{|k| (response[k] += diff) if response[k]}
|
|
100
|
+
end
|
|
101
|
+
return response
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def self.server_time_difference
|
|
105
|
+
diff = (Time.now.utc.to_i - Time.parse(head("https://kanbanery.com/api/v1/test.json").headers['date']).utc.to_i)
|
|
106
|
+
if diff > 1
|
|
107
|
+
raise "Kanbanery server has a time difference of #{diff} seconds"
|
|
108
|
+
else
|
|
109
|
+
return diff
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def check_parameter_format options={}
|
|
114
|
+
raise "options must be a Hash" unless options.instance_of?(Hash)
|
|
115
|
+
options.each do |key, value|
|
|
116
|
+
raise "options Hash must have each key as a Symbol or a String" unless key.instance_of?(Symbol) or key.instance_of?(String)
|
|
117
|
+
if value.class != Array or value.length != 2 or value[1].class != Class
|
|
118
|
+
raise "each value of options Hash must be an Array of a parameter and its expected class."
|
|
119
|
+
end
|
|
120
|
+
raise "#{key} must be a #{value[1]}." unless value[0].instance_of? value[1]
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def self.base_uri workspace
|
|
125
|
+
"https://#{workspace}.kanbanery.com/api/v1"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def self.setup_headers api_key
|
|
129
|
+
headers['X-Kanbanery-ApiToken'] = api_key
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def self.symbolize_keys(hsh)
|
|
133
|
+
if hsh.class == Array
|
|
134
|
+
hsh.map{|x| x.inject({}){|h,(k,v)| h[k.to_sym] = v; h}}
|
|
135
|
+
else
|
|
136
|
+
hsh.inject({}){|h,(k,v)| h[k.to_sym] = v; h}
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Kanbantastic
|
|
2
|
+
|
|
3
|
+
class Column < Base
|
|
4
|
+
attr_reader :id, :name, :position
|
|
5
|
+
validates_presence_of :id, :name, :position
|
|
6
|
+
|
|
7
|
+
def initialize(config, options={})
|
|
8
|
+
super(config)
|
|
9
|
+
@id = options[:id]
|
|
10
|
+
@name = options[:name]
|
|
11
|
+
@position = options[:position]
|
|
12
|
+
self.valid?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def first?
|
|
16
|
+
self.position == 1
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def second?
|
|
20
|
+
self.position == 2
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def last?
|
|
24
|
+
Kanbantastic::Column.all(config).last.id == id
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def ==(column)
|
|
28
|
+
id == column.id
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.find(config, id)
|
|
32
|
+
Kanbantastic::Column.all(config).select{|c| c.id == id }[0]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.all(config)
|
|
36
|
+
collection = []
|
|
37
|
+
response = request(:get, config, "/projects/#{config.project_id}/columns.json")
|
|
38
|
+
response.each do |data|
|
|
39
|
+
column = new(config, data)
|
|
40
|
+
collection << column if column.valid?
|
|
41
|
+
end
|
|
42
|
+
return collection
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Kanbantastic
|
|
2
|
+
|
|
3
|
+
class Config
|
|
4
|
+
include ActiveModel::Validations
|
|
5
|
+
attr_reader :api_key, :workspace, :project_id
|
|
6
|
+
validates_presence_of :api_key, :workspace, :project_id
|
|
7
|
+
|
|
8
|
+
def initialize(api_key, workspace, project_id)
|
|
9
|
+
@api_key = api_key
|
|
10
|
+
@workspace = workspace
|
|
11
|
+
@project_id = project_id
|
|
12
|
+
self.valid?
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
module Kanbantastic
|
|
2
|
+
|
|
3
|
+
class Task < Base
|
|
4
|
+
attr_accessor :id, :title, :column_id, :updated_at, :created_at, :moved_at, :task_type_id, :owner_id, :position
|
|
5
|
+
validates_presence_of :config, :id, :title, :column_id, :updated_at, :task_type_id
|
|
6
|
+
|
|
7
|
+
def initialize(config, options = {})
|
|
8
|
+
super(config)
|
|
9
|
+
assign_attributes(options)
|
|
10
|
+
@task_type_id = options[:task_type_id] || find_task_type_id(options[:task_type_name])
|
|
11
|
+
valid?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def find_task_type_id(task_type_name)
|
|
15
|
+
@task_type_ids ||= {}
|
|
16
|
+
@task_type_ids[task_type_name] ||= if !task_type_name.blank?
|
|
17
|
+
response = get("/projects/#{project_id}/task_types.json")
|
|
18
|
+
task_type = response.select{|t| t[:name] == task_type_name}.first || Hash.new
|
|
19
|
+
task_type[:id]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def update(params={})
|
|
24
|
+
params[:owner_id] ||= self.owner_id
|
|
25
|
+
begin
|
|
26
|
+
response = put("/tasks/#{id}.json", :body => {:task => params})
|
|
27
|
+
rescue Exception => e
|
|
28
|
+
self.class.invalid_response_error("Unable to update task. #{e.message}", response)
|
|
29
|
+
end
|
|
30
|
+
if response[:id]
|
|
31
|
+
assign_attributes(response)
|
|
32
|
+
return valid?
|
|
33
|
+
else
|
|
34
|
+
self.class.invalid_response_error("Unable to update task.", response)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.create(config, params={})
|
|
39
|
+
# raise exception if title and task_type_name is not present
|
|
40
|
+
unless params[:title] and params[:task_type_name]
|
|
41
|
+
raise "Kanbanery task can't be created without title and task_type_name."
|
|
42
|
+
end
|
|
43
|
+
response = request(:post, config, "/projects/#{config.project_id}/tasks.json", :body => {:task => params})
|
|
44
|
+
task = new(config, response)
|
|
45
|
+
task.valid? ? task : invalid_response_error("Unable to create task.", response)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def column
|
|
49
|
+
@columns ||= {}
|
|
50
|
+
@columns[column_id] ||= Kanbantastic::Column.find(config, column_id)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def move_to_next_column
|
|
54
|
+
update(:location => 'next_column')
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def move_to_previous_column
|
|
58
|
+
update(:location => 'prev_column')
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def move_to_first_column
|
|
62
|
+
column = Kanbantastic::Column.all(config).first
|
|
63
|
+
update(:column_id => column.id, :position => nil)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def move_to_second_column
|
|
67
|
+
column = Kanbantastic::Column.all(config)[1]
|
|
68
|
+
update(:column_id => column.id, :position => nil)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def move_to_last_column
|
|
72
|
+
column = Kanbantastic::Column.all(config).last
|
|
73
|
+
update(:column_id => column.id, :position => nil)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def archive
|
|
77
|
+
if Kanbantastic::Task.archived?(config, id)
|
|
78
|
+
return true
|
|
79
|
+
else
|
|
80
|
+
# raise an exception as only tasks which are in last column can be archived.
|
|
81
|
+
unless column.last?
|
|
82
|
+
raise "Kanbanery tasks can be archived only from the last column."
|
|
83
|
+
end
|
|
84
|
+
update(:location => "archive")
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def owner
|
|
89
|
+
@owner ||= if owner_id
|
|
90
|
+
response = get("/projects/#{project_id}/users.json")
|
|
91
|
+
user = response.select{|r| r[:id] == owner_id}[0]
|
|
92
|
+
Kanbantastic::User.new("#{user[:first_name]} #{user[:last_name]}", user[:email], user[:gravatar_url])
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def ==(task)
|
|
97
|
+
id == task.id
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def self.archived?(config, id)
|
|
101
|
+
response = request(:get, config, "/projects/#{config.project_id}/archive/tasks.json")
|
|
102
|
+
archived_task = response.select{|t| t[:id] == id}[0]
|
|
103
|
+
archived_task ? true : false
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def self.find(config, id)
|
|
107
|
+
begin
|
|
108
|
+
response = request(:get, config, "/tasks/#{id}.json")
|
|
109
|
+
rescue
|
|
110
|
+
return nil
|
|
111
|
+
end
|
|
112
|
+
task = new(config, response)
|
|
113
|
+
if task.valid?
|
|
114
|
+
return task
|
|
115
|
+
else
|
|
116
|
+
return nil
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def self.all(config)
|
|
121
|
+
collection = []
|
|
122
|
+
response = request(:get, config, "/projects/#{config.project_id}/tasks.json")
|
|
123
|
+
response.each do |data|
|
|
124
|
+
task = self.new(config, data)
|
|
125
|
+
collection << task if task.valid?
|
|
126
|
+
end
|
|
127
|
+
return collection
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|