ruby-trello 1.4.2 → 1.5.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 +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
|
- - ">="
|