ruby-trello 1.4.2 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -1
- data/lib/trello/card.rb +54 -4
- data/lib/trello/checklist.rb +9 -1
- data/lib/trello/item.rb +7 -5
- data/lib/trello/list.rb +18 -10
- data/lib/trello/net.rb +1 -0
- data/spec/board_spec.rb +2 -2
- data/spec/card_spec.rb +105 -23
- data/spec/checklist_spec.rb +11 -0
- data/spec/client_spec.rb +183 -130
- data/spec/list_spec.rb +18 -2
- data/spec/spec_helper.rb +9 -3
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3131b86cac06b4df9efe8f417ab1bd32de3cbdb1
|
4
|
+
data.tar.gz: 862affe69703265224606c58b6a72cb5e8c1f668
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f6f8f608417641e88c05ab8df2ebdb5e50bff8f522070be335d3be733d9eca5f6cd5470e34de6542875a9d1dfbb4b2dd78147403d035b2c4e931ebfb15c42e1
|
7
|
+
data.tar.gz: 1e53a8176105929d6c972888e3d6b218bbba627b92abcef67b6e660119ac5371dbca524c4fd2990751750a267ad318fc67803a95dc083dfc17b743053534c562
|
data/README.md
CHANGED
@@ -20,7 +20,10 @@ Seriously, [check it out](http://www.trello.com/).
|
|
20
20
|
Full Disclosure: This library is mostly complete, if you do find anything missing or not functioning as you expect it
|
21
21
|
to, please [let us know](https://trello.com/card/spot-a-bug-report-it/4f092b2ee23cb6fe6d1aaabd/17).
|
22
22
|
|
23
|
-
Supports Ruby 2.0 or newer.
|
23
|
+
Supports Ruby 2.1.0 or newer.
|
24
|
+
|
25
|
+
Use version 1.3.0 or earlier for ruby 1.9.3 support.
|
26
|
+
Use version 1.4.x or earlier for ruby 2.0.0 support.
|
24
27
|
|
25
28
|
## Configuration
|
26
29
|
|
data/lib/trello/card.rb
CHANGED
@@ -29,6 +29,8 @@ module Trello
|
|
29
29
|
# @return [Dateime]
|
30
30
|
# @!attribute [rw] card_labels
|
31
31
|
# @return [Array<Hash>]
|
32
|
+
# @!attribute [rw] labels
|
33
|
+
# @return [Array<Trello::Labels>]
|
32
34
|
# @!attribute [rw] cover_image_id
|
33
35
|
# @return [String] A 24-character hex string
|
34
36
|
# @!attribute [r] badges
|
@@ -42,7 +44,7 @@ module Trello
|
|
42
44
|
|
43
45
|
class Card < BasicData
|
44
46
|
register_attributes :id, :short_id, :name, :desc, :due, :closed, :url, :short_url,
|
45
|
-
:board_id, :member_ids, :list_id, :pos, :last_activity_date, :card_labels,
|
47
|
+
:board_id, :member_ids, :list_id, :pos, :last_activity_date, :labels, :card_labels,
|
46
48
|
:cover_image_id, :badges, :card_members, :source_card_id, :source_card_properties,
|
47
49
|
readonly: [ :id, :short_id, :url, :short_url, :last_activity_date, :badges, :card_members ]
|
48
50
|
validates_presence_of :id, :name, :list_id
|
@@ -67,6 +69,7 @@ module Trello
|
|
67
69
|
pos: 'pos',
|
68
70
|
last_activity_date: 'dateLastActivity',
|
69
71
|
card_labels: 'idLabels',
|
72
|
+
labels: 'labels',
|
70
73
|
badges: 'badges',
|
71
74
|
card_members: 'members',
|
72
75
|
source_card_id: "idCardSource",
|
@@ -151,6 +154,8 @@ module Trello
|
|
151
154
|
# (24-character hex strings).
|
152
155
|
# @option fields [String] :pos A position. `"top"`, `"bottom"`, or a
|
153
156
|
# positive number. Defaults to `"bottom"`.
|
157
|
+
# @option fields [Array] :labels An Array of Trello::Label objects
|
158
|
+
# derived from the JSON response
|
154
159
|
# @option fields [String] :card_labels A comma-separated list of
|
155
160
|
# objectIds (24-character hex strings).
|
156
161
|
# @option fields [Object] :cover_image_id
|
@@ -173,6 +178,7 @@ module Trello
|
|
173
178
|
attributes[:member_ids] = fields[SYMBOL_TO_STRING[:member_ids]]
|
174
179
|
attributes[:list_id] = fields[SYMBOL_TO_STRING[:list_id]]
|
175
180
|
attributes[:pos] = fields[SYMBOL_TO_STRING[:pos]]
|
181
|
+
attributes[:labels] = (fields[SYMBOL_TO_STRING[:labels]] || []).map { |lbl| Trello::Label.new(lbl) }
|
176
182
|
attributes[:card_labels] = fields[SYMBOL_TO_STRING[:card_labels]]
|
177
183
|
attributes[:last_activity_date] = Time.iso8601(fields[SYMBOL_TO_STRING[:last_activity_date]]) rescue nil
|
178
184
|
attributes[:cover_image_id] = fields[SYMBOL_TO_STRING[:cover_image_id]]
|
@@ -200,8 +206,6 @@ module Trello
|
|
200
206
|
MultiAssociation.new(self, states).proxy
|
201
207
|
end
|
202
208
|
|
203
|
-
many :labels
|
204
|
-
|
205
209
|
# Returns a reference to the list this card is currently in.
|
206
210
|
one :list, path: :lists, using: :list_id
|
207
211
|
|
@@ -215,6 +219,16 @@ module Trello
|
|
215
219
|
MultiAssociation.new(self, members).proxy
|
216
220
|
end
|
217
221
|
|
222
|
+
# Returns a list of members who have upvoted this card
|
223
|
+
# NOTE: this fetches a list each time it's called to avoid case where
|
224
|
+
# card is voted (or vote is removed) after card is fetched. Optimizing
|
225
|
+
# accuracy over network performance
|
226
|
+
#
|
227
|
+
# @return [Array<Trello::Member>]
|
228
|
+
def voters
|
229
|
+
Member.from_response client.get("/cards/#{id}/membersVoted")
|
230
|
+
end
|
231
|
+
|
218
232
|
# Saves a record.
|
219
233
|
#
|
220
234
|
# @raise [Trello::Error] if the card could not be saved
|
@@ -340,6 +354,30 @@ module Trello
|
|
340
354
|
client.delete("/cards/#{id}/members/#{member.id}")
|
341
355
|
end
|
342
356
|
|
357
|
+
# Current authenticated user upvotes a card
|
358
|
+
def upvote
|
359
|
+
begin
|
360
|
+
client.post("/cards/#{id}/membersVoted", {
|
361
|
+
value: me.id
|
362
|
+
})
|
363
|
+
rescue Trello::Error => e
|
364
|
+
fail e unless e.message =~ /has already voted/i
|
365
|
+
end
|
366
|
+
|
367
|
+
self
|
368
|
+
end
|
369
|
+
|
370
|
+
# Recind upvote. Noop if authenticated user hasn't previously voted
|
371
|
+
def remove_upvote
|
372
|
+
begin
|
373
|
+
client.delete("/cards/#{id}/membersVoted/#{me.id}")
|
374
|
+
rescue Trello::Error => e
|
375
|
+
fail e unless e.message =~ /has not voted/i
|
376
|
+
end
|
377
|
+
|
378
|
+
self
|
379
|
+
end
|
380
|
+
|
343
381
|
# Add a label
|
344
382
|
def add_label(label)
|
345
383
|
unless label.valid?
|
@@ -360,7 +398,8 @@ module Trello
|
|
360
398
|
|
361
399
|
# Add an attachment to this card
|
362
400
|
def add_attachment(attachment, name='')
|
363
|
-
|
401
|
+
# Is it a file object or a string (url)?
|
402
|
+
if attachment.respond_to?(:path) && attachment.respond_to?(:read)
|
364
403
|
client.post("/cards/#{id}/attachments", {
|
365
404
|
file: attachment,
|
366
405
|
name: name
|
@@ -393,5 +432,16 @@ module Trello
|
|
393
432
|
def comments
|
394
433
|
comments = Comment.from_response client.get("/cards/#{id}/actions", filter: "commentCard")
|
395
434
|
end
|
435
|
+
|
436
|
+
# Find the creation date
|
437
|
+
def created_at
|
438
|
+
@created_at ||= Time.at(id[0..7].to_i(16)) rescue nil
|
439
|
+
end
|
440
|
+
|
441
|
+
private
|
442
|
+
|
443
|
+
def me
|
444
|
+
@me ||= Member.find(:me)
|
445
|
+
end
|
396
446
|
end
|
397
447
|
end
|
data/lib/trello/checklist.rb
CHANGED
@@ -107,6 +107,14 @@ module Trello
|
|
107
107
|
client.post("/checklists/#{id}/checkItems", {name: name, checked: checked, pos: position})
|
108
108
|
end
|
109
109
|
|
110
|
+
# Update a checklist item's state, e.g.: "complete" or "incomplete"
|
111
|
+
def update_item_state(item_id, state)
|
112
|
+
client.put(
|
113
|
+
"/cards/#{card_id}/checklist/#{id}/checkItem/#{item_id}/state",
|
114
|
+
value: state,
|
115
|
+
)
|
116
|
+
end
|
117
|
+
|
110
118
|
# Delete a checklist item
|
111
119
|
def delete_checklist_item(item_id)
|
112
120
|
client.delete("/checklists/#{id}/checkItems/#{item_id}")
|
@@ -119,7 +127,7 @@ module Trello
|
|
119
127
|
|
120
128
|
# Copy a checklist (i.e., same attributes, items, etc.)
|
121
129
|
def copy
|
122
|
-
checklist_copy = self.class.create(name: self.name, board_id: self.board_id)
|
130
|
+
checklist_copy = self.class.create(name: self.name, board_id: self.board_id, card_id: self.card_id)
|
123
131
|
copy_items_to(checklist_copy)
|
124
132
|
return checklist_copy
|
125
133
|
end
|
data/lib/trello/item.rb
CHANGED
@@ -20,11 +20,13 @@ module Trello
|
|
20
20
|
# Supply a hash of string keyed data retrieved from the Trello API representing
|
21
21
|
# an item.
|
22
22
|
def update_fields(fields)
|
23
|
-
attributes[:id]
|
24
|
-
attributes[:
|
25
|
-
attributes[:
|
26
|
-
attributes[:
|
27
|
-
attributes[:
|
23
|
+
attributes[:id] = fields['id']
|
24
|
+
attributes[:card_id] = fields['idCard']
|
25
|
+
attributes[:checklist_id] = fields['idChecklist']
|
26
|
+
attributes[:name] = fields['name']
|
27
|
+
attributes[:type] = fields['type']
|
28
|
+
attributes[:state] = fields['state']
|
29
|
+
attributes[:pos] = fields['pos']
|
28
30
|
self
|
29
31
|
end
|
30
32
|
|
data/lib/trello/list.rb
CHANGED
@@ -12,7 +12,7 @@ module Trello
|
|
12
12
|
# @!attribute [rw] pos
|
13
13
|
# @return [Object]
|
14
14
|
class List < BasicData
|
15
|
-
register_attributes :id, :name, :closed, :board_id, :pos, readonly: [ :id, :board_id ]
|
15
|
+
register_attributes :id, :name, :closed, :board_id, :pos, :source_list_id, readonly: [ :id, :board_id ]
|
16
16
|
validates_presence_of :id, :name, :board_id
|
17
17
|
validates_length_of :name, in: 1..16384
|
18
18
|
|
@@ -29,9 +29,10 @@ module Trello
|
|
29
29
|
|
30
30
|
def create(options)
|
31
31
|
client.create(:list,
|
32
|
-
'name'
|
33
|
-
'idBoard'
|
34
|
-
'pos'
|
32
|
+
'name' => options[:name],
|
33
|
+
'idBoard' => options[:board_id],
|
34
|
+
'pos' => options[:pos],
|
35
|
+
'idListSource' => options[:source_list_id])
|
35
36
|
end
|
36
37
|
end
|
37
38
|
|
@@ -40,11 +41,12 @@ module Trello
|
|
40
41
|
# Supply a hash of string keyed data retrieved from the Trello API representing
|
41
42
|
# a List.
|
42
43
|
def update_fields(fields)
|
43
|
-
attributes[:id]
|
44
|
-
attributes[:name]
|
45
|
-
attributes[:closed]
|
46
|
-
attributes[:board_id]
|
47
|
-
attributes[:pos]
|
44
|
+
attributes[:id] = fields['id']
|
45
|
+
attributes[:name] = fields['name']
|
46
|
+
attributes[:closed] = fields['closed']
|
47
|
+
attributes[:board_id] = fields['idBoard']
|
48
|
+
attributes[:pos] = fields['pos']
|
49
|
+
attributes[:source_list_id] = fields['idListSource']
|
48
50
|
self
|
49
51
|
end
|
50
52
|
|
@@ -55,7 +57,8 @@ module Trello
|
|
55
57
|
name: name,
|
56
58
|
closed: closed || false,
|
57
59
|
idBoard: board_id,
|
58
|
-
pos: pos
|
60
|
+
pos: pos,
|
61
|
+
idListSource: source_list_id
|
59
62
|
})
|
60
63
|
end
|
61
64
|
|
@@ -98,6 +101,11 @@ module Trello
|
|
98
101
|
})
|
99
102
|
end
|
100
103
|
|
104
|
+
# Archives all the cards of the list
|
105
|
+
def archive_all_cards
|
106
|
+
client.post("/lists/#{id}/archiveAllCards")
|
107
|
+
end
|
108
|
+
|
101
109
|
# :nodoc:
|
102
110
|
def request_prefix
|
103
111
|
"/lists/#{id}"
|
data/lib/trello/net.rb
CHANGED
data/spec/board_spec.rb
CHANGED
@@ -130,12 +130,12 @@ module Trello
|
|
130
130
|
|
131
131
|
expect(labels.count).to eq(4)
|
132
132
|
expect(labels[2].color).to eq('red')
|
133
|
-
expect(labels[2].id).to eq('
|
133
|
+
expect(labels[2].id).to eq('cbcdef123456789123456789')
|
134
134
|
expect(labels[2].board_id).to eq('abcdef123456789123456789')
|
135
135
|
expect(labels[2].name).to eq('deploy')
|
136
136
|
expect(labels[2].uses).to eq(2)
|
137
137
|
expect(labels[3].color).to eq('blue')
|
138
|
-
expect(labels[3].id).to eq('
|
138
|
+
expect(labels[3].id).to eq('dbcdef123456789123456789')
|
139
139
|
expect(labels[3].board_id).to eq('abcdef123456789123456789')
|
140
140
|
expect(labels[3].name).to eq('on hold')
|
141
141
|
expect(labels[3].uses).to eq(6)
|
data/spec/card_spec.rb
CHANGED
@@ -94,7 +94,7 @@ module Trello
|
|
94
94
|
|
95
95
|
expect(card).to be_a Card
|
96
96
|
end
|
97
|
-
|
97
|
+
|
98
98
|
it 'creates a duplicate card with source due date and checklists and saves it on Trello', refactor: true do
|
99
99
|
payload = {
|
100
100
|
source_card_id: cards_details.first['id'],
|
@@ -220,6 +220,16 @@ module Trello
|
|
220
220
|
it "gets its pos" do
|
221
221
|
expect(card.pos).to_not be_nil
|
222
222
|
end
|
223
|
+
|
224
|
+
|
225
|
+
it 'gets its creation time' do
|
226
|
+
expect(card.created_at).to be_kind_of Time
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'get its correct creation time' do
|
230
|
+
expect(card.created_at).to eq(Time.parse('2061-05-04 04:40:18 +0200'))
|
231
|
+
end
|
232
|
+
|
223
233
|
end
|
224
234
|
|
225
235
|
context "actions" do
|
@@ -402,6 +412,76 @@ module Trello
|
|
402
412
|
end
|
403
413
|
end
|
404
414
|
|
415
|
+
context "add/remove votes" do
|
416
|
+
let(:authenticated_member) { double(id: '4ee7df3ce582acdec80000b2') }
|
417
|
+
|
418
|
+
before do
|
419
|
+
allow(card)
|
420
|
+
.to receive(:me)
|
421
|
+
.and_return(authenticated_member)
|
422
|
+
end
|
423
|
+
|
424
|
+
it 'upvotes a card with the currently authenticated member' do
|
425
|
+
expect(client)
|
426
|
+
.to receive(:post)
|
427
|
+
.with("/cards/abcdef123456789123456789/membersVoted", {
|
428
|
+
value: authenticated_member.id
|
429
|
+
})
|
430
|
+
|
431
|
+
card.upvote
|
432
|
+
end
|
433
|
+
|
434
|
+
it 'returns the card even if the user has already upvoted' do
|
435
|
+
expect(client)
|
436
|
+
.to receive(:post)
|
437
|
+
.with("/cards/abcdef123456789123456789/membersVoted", {
|
438
|
+
value: authenticated_member.id
|
439
|
+
})
|
440
|
+
.and_raise(Trello::Error, 'member has already voted')
|
441
|
+
expect(card.upvote).to be_kind_of Trello::Card
|
442
|
+
end
|
443
|
+
|
444
|
+
it 'removes an upvote from a card' do
|
445
|
+
expect(client)
|
446
|
+
.to receive(:delete)
|
447
|
+
.with("/cards/abcdef123456789123456789/membersVoted/#{authenticated_member.id}")
|
448
|
+
|
449
|
+
card.remove_upvote
|
450
|
+
end
|
451
|
+
|
452
|
+
it 'returns card after remove_upvote even if the user has not previously upvoted it' do
|
453
|
+
expect(client)
|
454
|
+
.to receive(:delete)
|
455
|
+
.with("/cards/abcdef123456789123456789/membersVoted/#{authenticated_member.id}")
|
456
|
+
.and_raise(Trello::Error, 'member has not voted on the card')
|
457
|
+
|
458
|
+
card.remove_upvote
|
459
|
+
end
|
460
|
+
|
461
|
+
end
|
462
|
+
|
463
|
+
context "return all voters" do
|
464
|
+
it 'returns members that have voted for the card' do
|
465
|
+
no_voters = JSON.generate([])
|
466
|
+
expect(client)
|
467
|
+
.to receive(:get)
|
468
|
+
.with("/cards/#{card.id}/membersVoted")
|
469
|
+
.and_return(no_voters)
|
470
|
+
|
471
|
+
card.voters
|
472
|
+
|
473
|
+
|
474
|
+
voters = JSON.generate([user_details])
|
475
|
+
expect(client)
|
476
|
+
.to receive(:get)
|
477
|
+
.with("/cards/#{card.id}/membersVoted")
|
478
|
+
.and_return(voters)
|
479
|
+
|
480
|
+
expect(card.voters.first).to be_kind_of Trello::Member
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
|
405
485
|
context "comments" do
|
406
486
|
it "posts a comment" do
|
407
487
|
expect(client)
|
@@ -415,37 +495,39 @@ module Trello
|
|
415
495
|
|
416
496
|
context "labels" do
|
417
497
|
before do
|
418
|
-
allow(client)
|
419
|
-
.to receive(:get)
|
420
|
-
.with("/cards/abcdef123456789123456789/labels")
|
421
|
-
.and_return label_payload
|
422
|
-
|
423
498
|
allow(client)
|
424
499
|
.to receive(:post)
|
425
|
-
.with("/cards/
|
500
|
+
.with("/cards/ebcdef123456789123456789/labels", { value: 'green' })
|
426
501
|
.and_return "not important"
|
427
502
|
|
428
503
|
allow(client)
|
429
504
|
.to receive(:delete)
|
430
|
-
.with("/cards/
|
505
|
+
.with("/cards/ebcdef123456789123456789/labels/green")
|
431
506
|
.and_return "not important"
|
432
507
|
end
|
433
|
-
|
434
|
-
|
435
|
-
with("/cards/abcdef123456789123456789/labels", {}).
|
436
|
-
and_return label_payload
|
508
|
+
|
509
|
+
it "includes card label objects list" do
|
437
510
|
labels = card.labels
|
438
|
-
expect(labels.size).to
|
439
|
-
expect(labels[0].color).to
|
440
|
-
expect(labels[0].id).to
|
441
|
-
expect(labels[0].board_id).to
|
442
|
-
expect(labels[0].name).to
|
443
|
-
expect(labels[0].uses).to
|
444
|
-
expect(labels[1].color).to
|
445
|
-
expect(labels[1].id).to
|
446
|
-
expect(labels[1].board_id).to
|
447
|
-
expect(labels[1].name).to
|
448
|
-
expect(labels[1].uses).to
|
511
|
+
expect(labels.size).to eq(4)
|
512
|
+
expect(labels[0].color).to eq('yellow')
|
513
|
+
expect(labels[0].id).to eq('abcdef123456789123456789')
|
514
|
+
expect(labels[0].board_id).to eq('abcdef123456789123456789')
|
515
|
+
expect(labels[0].name).to eq('iOS')
|
516
|
+
expect(labels[0].uses).to eq(3)
|
517
|
+
expect(labels[1].color).to eq('purple')
|
518
|
+
expect(labels[1].id).to eq('bbcdef123456789123456789')
|
519
|
+
expect(labels[1].board_id).to eq('abcdef123456789123456789')
|
520
|
+
expect(labels[1].name).to eq('Issue or bug')
|
521
|
+
expect(labels[1].uses).to eq(1)
|
522
|
+
end
|
523
|
+
|
524
|
+
it "includes label ids list" do
|
525
|
+
label_ids = card.card_labels
|
526
|
+
expect(label_ids.size).to eq(4)
|
527
|
+
expect(label_ids[0]).to eq('abcdef123456789123456789')
|
528
|
+
expect(label_ids[1]).to eq('bbcdef123456789123456789')
|
529
|
+
expect(label_ids[2]).to eq('cbcdef123456789123456789')
|
530
|
+
expect(label_ids[3]).to eq('dbcdef123456789123456789')
|
449
531
|
end
|
450
532
|
|
451
533
|
it "can remove a label" do
|
data/spec/checklist_spec.rb
CHANGED
@@ -139,6 +139,17 @@ module Trello
|
|
139
139
|
|
140
140
|
checklist.add_item(expected_item_name, expected_checked, expected_pos)
|
141
141
|
end
|
142
|
+
|
143
|
+
it "updates an item's state" do
|
144
|
+
expected_item_id = "1234"
|
145
|
+
expected_state = "incomplete"
|
146
|
+
expected_resource =
|
147
|
+
"/cards/abccardid/checklist/abcdef123456789123456789" \
|
148
|
+
"/checkItem/#{expected_item_id}/state"
|
149
|
+
payload = { value: expected_state }
|
150
|
+
expect(client).to receive(:put).once.with(expected_resource, payload)
|
151
|
+
checklist.update_item_state(expected_item_id, expected_state)
|
152
|
+
end
|
142
153
|
end
|
143
154
|
|
144
155
|
context "board" do
|
data/spec/client_spec.rb
CHANGED
@@ -3,202 +3,255 @@ require "spec_helper"
|
|
3
3
|
include Trello
|
4
4
|
include Trello::Authorization
|
5
5
|
|
6
|
-
describe Client
|
7
|
-
|
8
|
-
double "A fake OK response",
|
9
|
-
code: 200,
|
10
|
-
body: "A fake response body"
|
11
|
-
end
|
6
|
+
describe Client do
|
7
|
+
|
12
8
|
let(:client) { Client.new }
|
13
9
|
let(:auth_policy) { double }
|
14
10
|
|
15
11
|
before do
|
16
|
-
allow(TInternet)
|
17
|
-
.to receive(:execute)
|
18
|
-
.and_return fake_ok_response
|
19
|
-
|
20
12
|
allow(Authorization::AuthPolicy)
|
21
13
|
.to receive(:new)
|
22
|
-
|
14
|
+
.and_return(auth_policy)
|
23
15
|
|
24
16
|
allow(auth_policy)
|
25
17
|
.to receive(:authorize) { |request| request }
|
26
18
|
end
|
27
19
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
.once
|
32
|
-
.ordered
|
20
|
+
describe "and how it handles RestClient exceptions" do
|
21
|
+
context "with RestClient::Exception sans HTTP code" do
|
22
|
+
let(:rc_exception_without_http_code) { RestClient::Exception.new }
|
33
23
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
24
|
+
before do
|
25
|
+
allow(TInternet)
|
26
|
+
.to receive(:execute_core)
|
27
|
+
.and_raise(rc_exception_without_http_code)
|
28
|
+
end
|
38
29
|
|
39
|
-
|
40
|
-
|
30
|
+
it "bubbles up RestClient::Exception errors that don't contain an HTTP code" do
|
31
|
+
expect { client.get "/xxx" }.to raise_error rc_exception_without_http_code
|
32
|
+
end
|
33
|
+
end
|
41
34
|
|
42
|
-
|
43
|
-
|
44
|
-
|
35
|
+
context "with RestClient::Exception that contains HTTP code" do
|
36
|
+
let(:response_with_non_200_status) { double "A fake 404 response",
|
37
|
+
code: 404,
|
38
|
+
body: "404 error response"}
|
39
|
+
let(:rc_exception_with_http_code) { RestClient::Exception.new(double("404 error response", {:code => 404, :body => "404 error response"}))}
|
45
40
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
41
|
+
before do
|
42
|
+
allow(TInternet)
|
43
|
+
.to receive(:execute_core)
|
44
|
+
.and_raise(rc_exception_with_http_code)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "raises Error" do
|
48
|
+
|
49
|
+
expect(TInternet)
|
50
|
+
.to receive(:try_execute)
|
51
|
+
.and_return(response_with_non_200_status)
|
50
52
|
|
51
|
-
|
53
|
+
expect { client.get "/xxx" }.to raise_error(Error, "404 error response")
|
54
|
+
end
|
55
|
+
end
|
52
56
|
end
|
53
57
|
|
54
|
-
|
55
|
-
|
56
|
-
|
58
|
+
describe "and how it handles authorization" do
|
59
|
+
let (:fake_ok_response) do
|
60
|
+
double "A fake OK response",
|
61
|
+
code: 200,
|
62
|
+
body: "A fake response body"
|
63
|
+
end
|
64
|
+
let(:client) { Client.new }
|
65
|
+
let(:auth_policy) { double }
|
66
|
+
|
67
|
+
before do
|
68
|
+
allow(TInternet)
|
69
|
+
.to receive(:execute)
|
70
|
+
.and_return fake_ok_response
|
57
71
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
.with expected_request
|
72
|
+
allow(Authorization::AuthPolicy)
|
73
|
+
.to receive(:new)
|
74
|
+
.and_return(auth_policy)
|
62
75
|
|
63
|
-
|
64
|
-
|
76
|
+
allow(auth_policy)
|
77
|
+
.to receive(:authorize) { |request| request }
|
78
|
+
end
|
65
79
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
80
|
+
it "authorizes before it queries the internet" do
|
81
|
+
expect(auth_policy)
|
82
|
+
.to receive(:authorize)
|
83
|
+
.once
|
84
|
+
.ordered
|
71
85
|
|
72
|
-
|
73
|
-
|
74
|
-
|
86
|
+
expect(TInternet)
|
87
|
+
.to receive(:execute)
|
88
|
+
.once
|
89
|
+
.ordered
|
75
90
|
|
76
|
-
|
77
|
-
|
91
|
+
client.get "/xxx"
|
92
|
+
end
|
93
|
+
|
94
|
+
it "queries the internet with expanded earl and query parameters" do
|
95
|
+
expected_uri = Addressable::URI.parse("https://api.trello.com/1/xxx?a=1&b=2")
|
96
|
+
expected_request = Request.new :get, expected_uri, {}
|
97
|
+
|
98
|
+
expect(TInternet)
|
99
|
+
.to receive(:execute)
|
100
|
+
.once
|
101
|
+
.with expected_request
|
102
|
+
|
103
|
+
client.get "/xxx", a: "1", b: "2"
|
104
|
+
end
|
105
|
+
|
106
|
+
it "encodes parameters" do
|
107
|
+
expected_uri = Addressable::URI.parse("https://api.trello.com/1/xxx?name=Jazz%20Kang")
|
108
|
+
expected_request = Request.new :get, expected_uri, {}
|
109
|
+
|
110
|
+
expect(TInternet)
|
111
|
+
.to receive(:execute)
|
112
|
+
.once
|
113
|
+
.with expected_request
|
114
|
+
|
115
|
+
client.get "/xxx", name: "Jazz Kang"
|
116
|
+
end
|
78
117
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
118
|
+
it "raises an error when response has non-200 status" do
|
119
|
+
expected_error_message = "An error response"
|
120
|
+
response_with_non_200_status = double "A fake OK response",
|
121
|
+
code: 404,
|
122
|
+
body: expected_error_message
|
123
|
+
|
124
|
+
expect(TInternet)
|
125
|
+
.to receive(:execute)
|
126
|
+
.and_return response_with_non_200_status
|
127
|
+
|
128
|
+
expect { client.get "/xxx" }.to raise_error expected_error_message
|
129
|
+
end
|
130
|
+
|
131
|
+
it "uses version 1 of the API" do
|
132
|
+
expect(TInternet)
|
133
|
+
.to receive(:execute)
|
134
|
+
.once do |request|
|
83
135
|
expect(request.uri.to_s).to match(/1\//)
|
84
136
|
fake_ok_response
|
85
137
|
end
|
86
138
|
|
87
|
-
|
88
|
-
|
139
|
+
client.get "/"
|
140
|
+
end
|
89
141
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
142
|
+
it "omits the \"?\" when no parameters" do
|
143
|
+
expect(TInternet)
|
144
|
+
.to receive(:execute)
|
145
|
+
.once do |request|
|
94
146
|
expect(request.uri.to_s).not_to match(/\?$/)
|
95
147
|
fake_ok_response
|
96
148
|
end
|
97
149
|
|
98
|
-
|
99
|
-
|
150
|
+
client.get "/xxx"
|
151
|
+
end
|
100
152
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
153
|
+
it "supports post" do
|
154
|
+
expect(TInternet)
|
155
|
+
.to receive(:execute)
|
156
|
+
.once
|
157
|
+
.and_return fake_ok_response
|
106
158
|
|
107
|
-
|
108
|
-
|
159
|
+
client.post "/xxx", { phil: "T' north" }
|
160
|
+
end
|
109
161
|
|
110
|
-
|
111
|
-
|
162
|
+
it "supplies the body for a post" do
|
163
|
+
expected_body = { name: "Phil", nickname: "The Crack Fox" }
|
112
164
|
|
113
|
-
|
114
|
-
|
115
|
-
|
165
|
+
expect(TInternet)
|
166
|
+
.to receive(:execute)
|
167
|
+
.once do |request|
|
116
168
|
expect(request.body).to eq expected_body
|
117
169
|
fake_ok_response
|
118
170
|
end
|
119
171
|
|
120
|
-
|
121
|
-
|
172
|
+
client.post "/xxx", expected_body
|
173
|
+
end
|
122
174
|
|
123
|
-
|
124
|
-
|
175
|
+
it "supplies the path for a post" do
|
176
|
+
expected_path = "/xxx"
|
125
177
|
|
126
|
-
|
127
|
-
|
128
|
-
|
178
|
+
expect(TInternet)
|
179
|
+
.to receive(:execute)
|
180
|
+
.once do |request|
|
129
181
|
expect(request.uri.path).to match(/#{expected_path}$/)
|
130
182
|
fake_ok_response
|
131
183
|
end
|
132
184
|
|
133
|
-
|
134
|
-
|
185
|
+
client.post "/xxx", {}
|
186
|
+
end
|
135
187
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
188
|
+
it "supports put" do
|
189
|
+
expect(TInternet)
|
190
|
+
.to receive(:execute)
|
191
|
+
.once
|
192
|
+
.and_return fake_ok_response
|
141
193
|
|
142
|
-
|
143
|
-
|
194
|
+
client.put "/xxx", { phil: "T' north" }
|
195
|
+
end
|
144
196
|
|
145
|
-
|
146
|
-
|
197
|
+
it "supplies the body for a put" do
|
198
|
+
expected_body = { name: "Phil", nickname: "The Crack Fox" }
|
147
199
|
|
148
|
-
|
149
|
-
|
150
|
-
|
200
|
+
expect(TInternet)
|
201
|
+
.to receive(:execute)
|
202
|
+
.once do |request|
|
151
203
|
expect(request.body).to eq expected_body
|
152
204
|
fake_ok_response
|
153
205
|
end
|
154
206
|
|
155
|
-
|
156
|
-
|
207
|
+
client.put "/xxx", expected_body
|
208
|
+
end
|
157
209
|
|
158
|
-
|
159
|
-
|
210
|
+
it "supplies the path for a put" do
|
211
|
+
expected_path = "/xxx"
|
160
212
|
|
161
|
-
|
162
|
-
|
163
|
-
|
213
|
+
expect(TInternet)
|
214
|
+
.to receive(:execute)
|
215
|
+
.once do |request|
|
164
216
|
expect(request.uri.path).to match(/#{expected_path}$/)
|
165
217
|
fake_ok_response
|
166
218
|
end
|
167
219
|
|
168
|
-
|
169
|
-
end
|
170
|
-
|
171
|
-
context "initialize" do
|
172
|
-
let(:client) do
|
173
|
-
Client.new(
|
174
|
-
consumer_key: 'consumer_key',
|
175
|
-
consumer_secret: 'consumer_secret',
|
176
|
-
oauth_token: 'oauth_token',
|
177
|
-
oauth_token_secret: 'oauth_token_secret'
|
178
|
-
)
|
220
|
+
client.put "/xxx", {}
|
179
221
|
end
|
180
222
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
223
|
+
context "initialize" do
|
224
|
+
let(:client) do
|
225
|
+
Client.new(
|
226
|
+
consumer_key: 'consumer_key',
|
227
|
+
consumer_secret: 'consumer_secret',
|
228
|
+
oauth_token: 'oauth_token',
|
229
|
+
oauth_token_secret: 'oauth_token_secret'
|
230
|
+
)
|
231
|
+
end
|
188
232
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
config.oauth_token = 'oauth_token'
|
195
|
-
config.oauth_token_secret = 'oauth_token_secret'
|
233
|
+
it "is configurable" do
|
234
|
+
expect(client.consumer_key).to eq('consumer_key')
|
235
|
+
expect(client.consumer_secret).to eq('consumer_secret')
|
236
|
+
expect(client.oauth_token).to eq('oauth_token')
|
237
|
+
expect(client.oauth_token_secret).to eq('oauth_token_secret')
|
196
238
|
end
|
239
|
+
end
|
197
240
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
241
|
+
describe "configure" do
|
242
|
+
it "sets key attributes through config block" do
|
243
|
+
client.configure do |config|
|
244
|
+
config.consumer_key = 'consumer_key'
|
245
|
+
config.consumer_secret = 'consumer_secret'
|
246
|
+
config.oauth_token = 'oauth_token'
|
247
|
+
config.oauth_token_secret = 'oauth_token_secret'
|
248
|
+
end
|
249
|
+
|
250
|
+
expect(client.consumer_key).to eq('consumer_key')
|
251
|
+
expect(client.consumer_secret).to eq('consumer_secret')
|
252
|
+
expect(client.oauth_token).to eq('oauth_token')
|
253
|
+
expect(client.oauth_token_secret).to eq('oauth_token_secret')
|
254
|
+
end
|
202
255
|
end
|
203
256
|
end
|
204
257
|
end
|
data/spec/list_spec.rb
CHANGED
@@ -57,12 +57,19 @@ module Trello
|
|
57
57
|
payload = {
|
58
58
|
name: 'Test List',
|
59
59
|
board_id: 'abcdef123456789123456789',
|
60
|
-
pos: 42
|
60
|
+
pos: 42,
|
61
|
+
source_list_id: 'abcdef123456789'
|
61
62
|
}
|
62
63
|
|
63
64
|
result = JSON.generate(payload)
|
64
65
|
|
65
|
-
expected_payload = {
|
66
|
+
expected_payload = {
|
67
|
+
name: 'Test List',
|
68
|
+
closed: false,
|
69
|
+
idBoard: 'abcdef123456789123456789',
|
70
|
+
pos: 42,
|
71
|
+
idListSource: 'abcdef123456789'
|
72
|
+
}
|
66
73
|
|
67
74
|
expect(client)
|
68
75
|
.to receive(:post)
|
@@ -163,6 +170,15 @@ module Trello
|
|
163
170
|
|
164
171
|
expect(list.move_all_cards(other_list)).to eq cards_payload
|
165
172
|
end
|
173
|
+
|
174
|
+
it 'archives all cards' do
|
175
|
+
allow(client)
|
176
|
+
.to receive(:post)
|
177
|
+
.with('/lists/abcdef123456789123456789/archiveAllCards')
|
178
|
+
.and_return cards_payload
|
179
|
+
|
180
|
+
expect(list.archive_all_cards).to eq cards_payload
|
181
|
+
end
|
166
182
|
end
|
167
183
|
|
168
184
|
describe '#closed?' do
|
data/spec/spec_helper.rb
CHANGED
@@ -88,6 +88,7 @@ module Helpers
|
|
88
88
|
'position' => 16384,
|
89
89
|
'url' => 'https://trello.com/blah/blah',
|
90
90
|
'idBoard' => 'abcdef123456789123456789',
|
91
|
+
'idCard' => 'abccardid',
|
91
92
|
'idList' => 'abcdef123456789123456789',
|
92
93
|
'idMembers' => ['abcdef123456789123456789'],
|
93
94
|
'checkItems' => { 'id' => 'ghijk987654321' }
|
@@ -142,6 +143,11 @@ module Helpers
|
|
142
143
|
'idBoard' => 'abcdef123456789123456789',
|
143
144
|
'idAttachmentCover' => 'abcdef123456789123456789',
|
144
145
|
'idMembers' => ['abcdef123456789123456789'],
|
146
|
+
'labels' => label_details,
|
147
|
+
'idLabels' => [ 'abcdef123456789123456789',
|
148
|
+
'bbcdef123456789123456789',
|
149
|
+
'cbcdef123456789123456789',
|
150
|
+
'dbcdef123456789123456789' ],
|
145
151
|
'url' => 'https://trello.com/card/board/specify-the-type-and-scope-of-the-jit-in-a-lightweight-spec/abcdef123456789123456789/abcdef123456789123456789',
|
146
152
|
'shortUrl' => 'https://trello.com/c/abcdef12',
|
147
153
|
'pos' => 12,
|
@@ -310,9 +316,9 @@ module Helpers
|
|
310
316
|
def label_details
|
311
317
|
[
|
312
318
|
{'color' => 'yellow', 'name' => 'iOS', 'id' => 'abcdef123456789123456789', 'uses' => 3, 'idBoard' => 'abcdef123456789123456789'},
|
313
|
-
{'color' => 'purple', 'name' => 'Issue or bug', 'id' => '
|
314
|
-
{'color' => 'red', 'name' => 'deploy', 'id' => '
|
315
|
-
{'color' => 'blue', 'name' => 'on hold', 'id' => '
|
319
|
+
{'color' => 'purple', 'name' => 'Issue or bug', 'id' => 'bbcdef123456789123456789', 'uses' => 1, 'idBoard' => 'abcdef123456789123456789'},
|
320
|
+
{'color' => 'red', 'name' => 'deploy', 'id' => 'cbcdef123456789123456789', 'uses' => 2, 'idBoard' => 'abcdef123456789123456789'},
|
321
|
+
{'color' => 'blue', 'name' => 'on hold', 'id' => 'dbcdef123456789123456789', 'uses' => 6, 'idBoard' => 'abcdef123456789123456789'}
|
316
322
|
]
|
317
323
|
end
|
318
324
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-trello
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Tregunna
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-08-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -56,14 +56,14 @@ dependencies:
|
|
56
56
|
name: oauth
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: 0.4.5
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 0.4.5
|
69
69
|
- !ruby/object:Gem::Dependency
|
@@ -158,7 +158,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
158
158
|
requirements:
|
159
159
|
- - ">="
|
160
160
|
- !ruby/object:Gem::Version
|
161
|
-
version: 2.
|
161
|
+
version: 2.1.0
|
162
162
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
163
163
|
requirements:
|
164
164
|
- - ">="
|