ruby-trello 0.4.4.3 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +81 -8
- data/lib/trello.rb +25 -9
- data/lib/trello/action.rb +5 -5
- data/lib/trello/association_proxy.rb +3 -3
- data/lib/trello/authorization.rb +105 -49
- data/lib/trello/basic_data.rb +48 -4
- data/lib/trello/board.rb +10 -9
- data/lib/trello/card.rb +38 -35
- data/lib/trello/checklist.rb +10 -8
- data/lib/trello/client.rb +98 -29
- data/lib/trello/configuration.rb +68 -0
- data/lib/trello/has_actions.rb +1 -1
- data/lib/trello/list.rb +9 -7
- data/lib/trello/member.rb +3 -3
- data/lib/trello/multi_association.rb +4 -2
- data/lib/trello/notification.rb +8 -8
- data/lib/trello/organization.rb +3 -3
- data/lib/trello/token.rb +4 -4
- data/spec/action_spec.rb +30 -18
- data/spec/basic_auth_policy_spec.rb +1 -0
- data/spec/board_spec.rb +141 -120
- data/spec/card_spec.rb +104 -82
- data/spec/checklist_spec.rb +35 -6
- data/spec/client_spec.rb +56 -19
- data/spec/configuration_spec.rb +114 -0
- data/spec/integration/how_to_authorize_spec.rb +1 -1
- data/spec/list_spec.rb +74 -20
- data/spec/member_spec.rb +37 -23
- data/spec/notification_spec.rb +28 -24
- data/spec/oauth_policy_spec.rb +55 -9
- data/spec/organization_spec.rb +18 -7
- data/spec/spec_helper.rb +5 -0
- data/spec/token_spec.rb +25 -8
- data/spec/trello_spec.rb +73 -0
- metadata +10 -6
data/lib/trello/board.rb
CHANGED
@@ -11,17 +11,18 @@ module Trello
|
|
11
11
|
class << self
|
12
12
|
# Finds a board.
|
13
13
|
def find(id)
|
14
|
-
|
14
|
+
client.find(:board, id)
|
15
15
|
end
|
16
16
|
|
17
17
|
def create(fields)
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
client.create(:board,
|
19
|
+
'name' => fields[:name],
|
20
|
+
'desc' => fields[:description],
|
21
|
+
'closed' => fields[:closed] || false)
|
21
22
|
end
|
22
23
|
|
23
24
|
def all
|
24
|
-
|
25
|
+
client.get("/members/#{Member.find(:me).username}/boards").json_into(self)
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
@@ -32,7 +33,7 @@ module Trello
|
|
32
33
|
fields.merge!(:desc => description) if description
|
33
34
|
fields.merge!(:idOrganization => organization_id) if organization_id
|
34
35
|
|
35
|
-
|
36
|
+
client.post("/boards", fields).json_into(self)
|
36
37
|
end
|
37
38
|
|
38
39
|
def update!
|
@@ -41,7 +42,7 @@ module Trello
|
|
41
42
|
@previously_changed = changes
|
42
43
|
@changed_attributes.clear
|
43
44
|
|
44
|
-
|
45
|
+
client.put("/boards/#{self.id}/", {
|
45
46
|
:name => attributes[:name],
|
46
47
|
:description => attributes[:description],
|
47
48
|
:closed => attributes[:closed]
|
@@ -68,7 +69,7 @@ module Trello
|
|
68
69
|
end
|
69
70
|
|
70
71
|
def find_card(card_id)
|
71
|
-
|
72
|
+
client.get("/boards/#{self.id}/cards/#{card_id}").json_into(Card)
|
72
73
|
end
|
73
74
|
|
74
75
|
# Return all the cards on this board.
|
@@ -93,7 +94,7 @@ module Trello
|
|
93
94
|
many :members, :filter => :all
|
94
95
|
|
95
96
|
# Returns a reference to the organization this board belongs to.
|
96
|
-
one :organization, :using => :organization_id
|
97
|
+
one :organization, :path => :organizations, :using => :organization_id
|
97
98
|
|
98
99
|
# :nodoc:
|
99
100
|
def request_prefix
|
data/lib/trello/card.rb
CHANGED
@@ -12,14 +12,15 @@ module Trello
|
|
12
12
|
class << self
|
13
13
|
# Find a specific card by its id.
|
14
14
|
def find(id)
|
15
|
-
|
15
|
+
client.find(:card, id)
|
16
16
|
end
|
17
17
|
|
18
18
|
# Create a new card and save it on Trello.
|
19
19
|
def create(options)
|
20
|
-
|
20
|
+
client.create(:card,
|
21
|
+
'name' => options[:name],
|
21
22
|
'idList' => options[:list_id],
|
22
|
-
'desc' => options[:description])
|
23
|
+
'desc' => options[:description])
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
@@ -43,7 +44,7 @@ module Trello
|
|
43
44
|
end
|
44
45
|
|
45
46
|
# Returns a reference to the board this card is part of.
|
46
|
-
one :board, :using => :board_id
|
47
|
+
one :board, :path => :boards, :using => :board_id
|
47
48
|
|
48
49
|
# Returns a list of checklists associated with the card.
|
49
50
|
#
|
@@ -53,18 +54,18 @@ module Trello
|
|
53
54
|
many :checklists, :filter => :all
|
54
55
|
|
55
56
|
def check_item_states
|
56
|
-
states =
|
57
|
+
states = client.get("/cards/#{self.id}/checkItemStates").json_into(CheckItemState)
|
57
58
|
MultiAssociation.new(self, states).proxy
|
58
59
|
end
|
59
60
|
|
60
61
|
|
61
62
|
# Returns a reference to the list this card is currently in.
|
62
|
-
one :list, :using => :list_id
|
63
|
+
one :list, :path => :lists, :using => :list_id
|
63
64
|
|
64
65
|
# Returns a list of members who are assigned to this card.
|
65
66
|
def members
|
66
67
|
members = member_ids.map do |member_id|
|
67
|
-
|
68
|
+
client.get("/members/#{member_id}").json_into(Member)
|
68
69
|
end
|
69
70
|
MultiAssociation.new(self, members).proxy
|
70
71
|
end
|
@@ -74,7 +75,7 @@ module Trello
|
|
74
75
|
# If we have an id, just update our fields.
|
75
76
|
return update! if id
|
76
77
|
|
77
|
-
|
78
|
+
client.post("/cards", {
|
78
79
|
:name => name,
|
79
80
|
:desc => description,
|
80
81
|
:idList => list_id
|
@@ -87,18 +88,16 @@ module Trello
|
|
87
88
|
# this object before making your changes, and before updating the record.
|
88
89
|
def update!
|
89
90
|
@previously_changed = changes
|
91
|
+
# extract only new values to build payload
|
92
|
+
payload = Hash[changes.map { |key, values| [key.to_sym, values[1]] }]
|
90
93
|
@changed_attributes.clear
|
91
94
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
:idBoard => board_id,
|
99
|
-
:idMembers => member_ids,
|
100
|
-
:pos => pos
|
101
|
-
})
|
95
|
+
client.put("/cards/#{id}", payload)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Delete this card
|
99
|
+
def delete
|
100
|
+
client.delete("/cards/#{id}")
|
102
101
|
end
|
103
102
|
|
104
103
|
# Check if the card is not active anymore.
|
@@ -122,45 +121,49 @@ module Trello
|
|
122
121
|
|
123
122
|
# Add a comment with the supplied text.
|
124
123
|
def add_comment(text)
|
125
|
-
|
124
|
+
client.post("/cards/#{id}/actions/comments", :text => text)
|
126
125
|
end
|
127
126
|
|
128
127
|
# Add a checklist to this card
|
129
128
|
def add_checklist(checklist)
|
130
|
-
|
129
|
+
client.post("/cards/#{id}/checklists", {
|
131
130
|
:value => checklist.id
|
132
131
|
})
|
133
132
|
end
|
134
133
|
|
135
134
|
# Move this card to the given list
|
136
135
|
def move_to_list(list)
|
137
|
-
|
138
|
-
|
139
|
-
|
136
|
+
unless list_id == list.id
|
137
|
+
client.put("/cards/#{id}/idList", {
|
138
|
+
:value => list.id
|
139
|
+
})
|
140
|
+
end
|
140
141
|
end
|
141
142
|
|
142
143
|
# Move this card to the given board (and optional list on this board)
|
143
144
|
def move_to_board(new_board, new_list = nil)
|
144
|
-
|
145
|
-
|
146
|
-
|
145
|
+
unless board_id == new_board.id
|
146
|
+
payload = { :value => new_board.id }
|
147
|
+
payload[:idList] = new_list.id if new_list
|
148
|
+
client.put("/cards/#{id}/idBoard", payload)
|
149
|
+
end
|
147
150
|
end
|
148
151
|
|
149
152
|
# Add a member to this card
|
150
153
|
def add_member(member)
|
151
|
-
|
154
|
+
client.post("/cards/#{id}/members", {
|
152
155
|
:value => member.id
|
153
156
|
})
|
154
157
|
end
|
155
158
|
|
156
159
|
# Remove a member from this card
|
157
160
|
def remove_member(member)
|
158
|
-
|
161
|
+
client.delete("/cards/#{id}/members/#{member.id}")
|
159
162
|
end
|
160
163
|
|
161
164
|
# Retrieve a list of labels
|
162
165
|
def labels
|
163
|
-
labels =
|
166
|
+
labels = client.get("/cards/#{id}/labels").json_into(Label)
|
164
167
|
MultiAssociation.new(self, labels).proxy
|
165
168
|
end
|
166
169
|
|
@@ -170,7 +173,7 @@ module Trello
|
|
170
173
|
errors.add(:label, "colour '#{colour}' does not exist")
|
171
174
|
return Trello.logger.warn "The label colour '#{colour}' does not exist."
|
172
175
|
end
|
173
|
-
|
176
|
+
client.post("/cards/#{id}/labels", { :value => colour })
|
174
177
|
end
|
175
178
|
|
176
179
|
# Remove a label
|
@@ -179,18 +182,18 @@ module Trello
|
|
179
182
|
errors.add(:label, "colour '#{colour}' does not exist")
|
180
183
|
return Trello.logger.warn "The label colour '#{colour}' does not exist." unless %w{green yellow orange red purple blue}.include? colour
|
181
184
|
end
|
182
|
-
|
185
|
+
client.delete("/cards/#{id}/labels/#{colour}")
|
183
186
|
end
|
184
187
|
|
185
188
|
# Add an attachment to this card
|
186
189
|
def add_attachment(attachment, name='')
|
187
190
|
if attachment.is_a? File
|
188
|
-
|
191
|
+
client.post("/cards/#{id}/attachments", {
|
189
192
|
:file => attachment,
|
190
193
|
:name => name
|
191
194
|
})
|
192
195
|
else
|
193
|
-
|
196
|
+
client.post("/cards/#{id}/attachments", {
|
194
197
|
:url => attachment,
|
195
198
|
:name => name
|
196
199
|
})
|
@@ -199,13 +202,13 @@ module Trello
|
|
199
202
|
|
200
203
|
# Retrieve a list of attachments
|
201
204
|
def attachments
|
202
|
-
attachments =
|
205
|
+
attachments = client.get("/cards/#{id}/attachments").json_into(Attachment)
|
203
206
|
MultiAssociation.new(self, attachments).proxy
|
204
207
|
end
|
205
208
|
|
206
209
|
# Remove an attachment from this card
|
207
210
|
def remove_attachment(attachment)
|
208
|
-
|
211
|
+
client.delete("/cards/#{id}/attachments/#{attachment.id}")
|
209
212
|
end
|
210
213
|
|
211
214
|
# :nodoc:
|
data/lib/trello/checklist.rb
CHANGED
@@ -9,12 +9,13 @@ module Trello
|
|
9
9
|
class << self
|
10
10
|
# Locate a specific checklist by its id.
|
11
11
|
def find(id)
|
12
|
-
|
12
|
+
client.find(:checklist, id)
|
13
13
|
end
|
14
14
|
|
15
15
|
def create(options)
|
16
|
-
|
17
|
-
|
16
|
+
client.create(:checklist,
|
17
|
+
'name' => options[:name],
|
18
|
+
'idBoard' => options[:board_id])
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
@@ -30,6 +31,7 @@ module Trello
|
|
30
31
|
attributes[:url] = fields['url']
|
31
32
|
attributes[:check_items] = fields['checkItems']
|
32
33
|
attributes[:board_id] = fields['idBoard']
|
34
|
+
attributes[:list_id] = fields['idList']
|
33
35
|
attributes[:member_ids] = fields['idMembers']
|
34
36
|
self
|
35
37
|
end
|
@@ -43,14 +45,14 @@ module Trello
|
|
43
45
|
def save
|
44
46
|
return update! if id
|
45
47
|
|
46
|
-
|
48
|
+
client.post("/checklists", {
|
47
49
|
:name => name,
|
48
50
|
:idBoard => board_id
|
49
51
|
}).json_into(self)
|
50
52
|
end
|
51
53
|
|
52
54
|
def update!
|
53
|
-
|
55
|
+
client.put("/checklists/#{id}", { :name => name }).json_into(self)
|
54
56
|
end
|
55
57
|
|
56
58
|
# Return a list of items on the checklist.
|
@@ -61,10 +63,10 @@ module Trello
|
|
61
63
|
end
|
62
64
|
|
63
65
|
# Return a reference to the board the checklist is on.
|
64
|
-
one :board, :using => :board_id
|
66
|
+
one :board, :path => :checklists, :using => :board_id
|
65
67
|
|
66
68
|
# Return a reference to the list the checklist is on.
|
67
|
-
one :list, :using => :list_id
|
69
|
+
one :list, :path => :lists, :using => :list_id
|
68
70
|
|
69
71
|
# Return a list of members active in this checklist.
|
70
72
|
def members
|
@@ -76,7 +78,7 @@ module Trello
|
|
76
78
|
|
77
79
|
# Add an item to the checklist
|
78
80
|
def add_item(name)
|
79
|
-
|
81
|
+
client.post("/checklists/#{id}/checkItems", { :name => name })
|
80
82
|
end
|
81
83
|
end
|
82
84
|
end
|
data/lib/trello/client.rb
CHANGED
@@ -2,48 +2,117 @@ require 'addressable/uri'
|
|
2
2
|
|
3
3
|
module Trello
|
4
4
|
class Client
|
5
|
-
extend
|
5
|
+
extend Forwardable
|
6
|
+
include Authorization
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
def_delegators :configuration, :credentials, *Configuration.configurable_attributes
|
9
|
+
|
10
|
+
def initialize(attrs = {})
|
11
|
+
self.configuration.attributes = attrs
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(path, params = {})
|
15
|
+
uri = Addressable::URI.parse("https://api.trello.com/#{API_VERSION}#{path}")
|
16
|
+
uri.query_values = params unless params.empty?
|
17
|
+
invoke_verb(:get, uri)
|
18
|
+
end
|
19
|
+
|
20
|
+
def post(path, body = {})
|
21
|
+
uri = Addressable::URI.parse("https://api.trello.com/#{API_VERSION}#{path}")
|
22
|
+
invoke_verb(:post, uri, body)
|
23
|
+
end
|
13
24
|
|
14
|
-
|
15
|
-
|
16
|
-
|
25
|
+
def put(path, body = {})
|
26
|
+
uri = Addressable::URI.parse("https://api.trello.com/#{API_VERSION}#{path}")
|
27
|
+
invoke_verb(:put, uri, body)
|
28
|
+
end
|
29
|
+
|
30
|
+
def delete(path)
|
31
|
+
uri = Addressable::URI.parse("https://api.trello.com/#{API_VERSION}#{path}")
|
32
|
+
invoke_verb(:delete, uri)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Finds given resource by id
|
36
|
+
#
|
37
|
+
# Examples:
|
38
|
+
# client.find(:board, "board1234")
|
39
|
+
# client.find(:member, "user1234")
|
40
|
+
#
|
41
|
+
def find(path, id)
|
42
|
+
response = get("/#{path.to_s.pluralize}/#{id}")
|
43
|
+
trello_class = class_from_path(path)
|
44
|
+
trello_class.parse response do |data|
|
45
|
+
data.client = self
|
17
46
|
end
|
47
|
+
end
|
18
48
|
|
19
|
-
|
20
|
-
|
21
|
-
|
49
|
+
# Finds given resource by path with params
|
50
|
+
def find_many(trello_class, path, params = {})
|
51
|
+
response = get(path, params)
|
52
|
+
trello_class.parse_many response do |data|
|
53
|
+
data.client = self
|
22
54
|
end
|
55
|
+
end
|
23
56
|
|
24
|
-
|
25
|
-
|
26
|
-
|
57
|
+
# Creates resource with given options (attributes)
|
58
|
+
#
|
59
|
+
# Examples:
|
60
|
+
# client.create(:member, options)
|
61
|
+
# client.create(:board, options)
|
62
|
+
#
|
63
|
+
def create(path, options)
|
64
|
+
trello_class = class_from_path(path)
|
65
|
+
trello_class.save options do |data|
|
66
|
+
data.client = self
|
27
67
|
end
|
68
|
+
end
|
28
69
|
|
29
|
-
|
30
|
-
|
31
|
-
|
70
|
+
def configure
|
71
|
+
yield configuration if block_given?
|
72
|
+
end
|
73
|
+
|
74
|
+
def configuration
|
75
|
+
@configuration ||= Configuration.new
|
76
|
+
end
|
32
77
|
|
33
|
-
|
78
|
+
def auth_policy
|
79
|
+
@auth_policy ||= auth_policy_class.new(credentials)
|
80
|
+
end
|
34
81
|
|
35
|
-
|
36
|
-
Trello.logger.error("[401 #{name.to_s.upcase} #{uri}]: Your access token has expired.")
|
37
|
-
raise InvalidAccessToken, response.body
|
38
|
-
end
|
82
|
+
private
|
39
83
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
84
|
+
def invoke_verb(name, uri, body = nil)
|
85
|
+
request = Request.new name, uri, {}, body
|
86
|
+
response = TInternet.execute auth_policy.authorize(request)
|
87
|
+
|
88
|
+
return '' unless response
|
89
|
+
|
90
|
+
if response.code.to_i == 401 && response.body =~ /expired token/
|
91
|
+
Trello.logger.error("[401 #{name.to_s.upcase} #{uri}]: Your access token has expired.")
|
92
|
+
raise InvalidAccessToken, response.body
|
93
|
+
end
|
44
94
|
|
45
|
-
|
95
|
+
unless [200, 201].include? response.code
|
96
|
+
Trello.logger.error("[#{response.code} #{name.to_s.upcase} #{uri}]: #{response.body}")
|
97
|
+
raise Error, response.body
|
46
98
|
end
|
99
|
+
|
100
|
+
response.body
|
101
|
+
end
|
102
|
+
|
103
|
+
def auth_policy_class
|
104
|
+
if configuration.oauth?
|
105
|
+
OAuthPolicy
|
106
|
+
elsif configuration.basic?
|
107
|
+
BasicAuthPolicy
|
108
|
+
else
|
109
|
+
AuthPolicy
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def class_from_path(path_or_class)
|
114
|
+
return path_or_class if path_or_class.is_a?(Class)
|
115
|
+
Trello.const_get(path_or_class.to_s.singularize.camelize)
|
47
116
|
end
|
48
117
|
end
|
49
118
|
end
|