ruby-trello-czuger 2.0.0

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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +182 -0
  3. data/lib/trello.rb +163 -0
  4. data/lib/trello/action.rb +68 -0
  5. data/lib/trello/association.rb +14 -0
  6. data/lib/trello/association_proxy.rb +42 -0
  7. data/lib/trello/attachment.rb +40 -0
  8. data/lib/trello/authorization.rb +187 -0
  9. data/lib/trello/basic_data.rb +132 -0
  10. data/lib/trello/board.rb +211 -0
  11. data/lib/trello/card.rb +467 -0
  12. data/lib/trello/checklist.rb +143 -0
  13. data/lib/trello/client.rb +120 -0
  14. data/lib/trello/comment.rb +62 -0
  15. data/lib/trello/configuration.rb +68 -0
  16. data/lib/trello/core_ext/array.rb +6 -0
  17. data/lib/trello/core_ext/hash.rb +6 -0
  18. data/lib/trello/core_ext/string.rb +6 -0
  19. data/lib/trello/cover_image.rb +8 -0
  20. data/lib/trello/has_actions.rb +9 -0
  21. data/lib/trello/item.rb +37 -0
  22. data/lib/trello/item_state.rb +30 -0
  23. data/lib/trello/json_utils.rb +64 -0
  24. data/lib/trello/label.rb +108 -0
  25. data/lib/trello/label_name.rb +31 -0
  26. data/lib/trello/list.rb +114 -0
  27. data/lib/trello/member.rb +112 -0
  28. data/lib/trello/multi_association.rb +12 -0
  29. data/lib/trello/net.rb +39 -0
  30. data/lib/trello/notification.rb +61 -0
  31. data/lib/trello/organization.rb +68 -0
  32. data/lib/trello/plugin_datum.rb +34 -0
  33. data/lib/trello/token.rb +37 -0
  34. data/lib/trello/webhook.rb +103 -0
  35. data/spec/action_spec.rb +149 -0
  36. data/spec/array_spec.rb +13 -0
  37. data/spec/association_spec.rb +26 -0
  38. data/spec/basic_auth_policy_spec.rb +51 -0
  39. data/spec/board_spec.rb +442 -0
  40. data/spec/card_spec.rb +822 -0
  41. data/spec/checklist_spec.rb +296 -0
  42. data/spec/client_spec.rb +257 -0
  43. data/spec/configuration_spec.rb +95 -0
  44. data/spec/hash_spec.rb +15 -0
  45. data/spec/integration/how_to_authorize_spec.rb +53 -0
  46. data/spec/integration/how_to_use_boards_spec.rb +48 -0
  47. data/spec/integration/integration_test.rb +40 -0
  48. data/spec/item_spec.rb +75 -0
  49. data/spec/json_utils_spec.rb +73 -0
  50. data/spec/label_spec.rb +205 -0
  51. data/spec/list_spec.rb +253 -0
  52. data/spec/member_spec.rb +159 -0
  53. data/spec/notification_spec.rb +143 -0
  54. data/spec/oauth_policy_spec.rb +160 -0
  55. data/spec/organization_spec.rb +71 -0
  56. data/spec/spec_helper.rb +435 -0
  57. data/spec/string_spec.rb +55 -0
  58. data/spec/token_spec.rb +89 -0
  59. data/spec/trello_spec.rb +134 -0
  60. data/spec/webhook_spec.rb +130 -0
  61. metadata +200 -0
@@ -0,0 +1,467 @@
1
+ module Trello
2
+ # A Card is a container that can house checklists and comments; it resides inside a List.
3
+ #
4
+ # @!attribute [r] id
5
+ # @return [String]
6
+ # @!attribute [r] short_id
7
+ # @return [Fixnum]
8
+ # @!attribute [rw] name
9
+ # @return [String]
10
+ # @!attribute [rw] desc
11
+ # @return [String]
12
+ # @!attribute [rw] due
13
+ # @return [Datetime]
14
+ # @!attribute [rw] closed
15
+ # @return [Boolean]
16
+ # @!attribute [r] url
17
+ # @return [String]
18
+ # @!attribute [r] short_url
19
+ # @return [String]
20
+ # @!attribute [rw] board_id
21
+ # @return [String] A 24-character hex string
22
+ # @!attribute [rw] member_ids
23
+ # @return [Array<String>] An Array of 24-character hex strings
24
+ # @!attribute [rw] list_id
25
+ # @return [String] A 24-character hex string
26
+ # @!attribute [rw] pos
27
+ # @return [Float]
28
+ # @!attribute [r] last_activity_date
29
+ # @return [Dateime]
30
+ # @!attribute [rw] card_labels
31
+ # @return [Array<Hash>]
32
+ # @!attribute [rw] labels
33
+ # @return [Array<Trello::Labels>]
34
+ # @!attribute [rw] cover_image_id
35
+ # @return [String] A 24-character hex string
36
+ # @!attribute [r] badges
37
+ # @return [Hash]
38
+ # @!attribute [r] card_members
39
+ # @return [Object]
40
+ # @!attribute [rw] source_card_id
41
+ # @return [String] A 24-character hex string
42
+ # @!attribute [rw] source_card_properties
43
+ # @return [Array<String>] Array of strings
44
+
45
+ class Card < BasicData
46
+ register_attributes :id, :short_id, :name, :desc, :due, :due_complete, :closed, :url, :short_url,
47
+ :board_id, :member_ids, :list_id, :pos, :last_activity_date, :labels, :card_labels,
48
+ :cover_image_id, :badges, :card_members, :source_card_id, :source_card_properties,
49
+ readonly: [ :id, :short_id, :url, :short_url, :last_activity_date, :badges, :card_members ]
50
+ validates_presence_of :id, :name, :list_id
51
+ validates_length_of :name, in: 1..16384
52
+ validates_length_of :desc, in: 0..16384
53
+
54
+ include HasActions
55
+
56
+ SYMBOL_TO_STRING = {
57
+ id: 'id',
58
+ short_id: 'idShort',
59
+ name: 'name',
60
+ desc: 'desc',
61
+ due: 'due',
62
+ due_complete: 'dueComplete',
63
+ closed: 'closed',
64
+ url: 'url',
65
+ short_url: 'shortUrl',
66
+ board_id: 'idBoard',
67
+ member_ids: 'idMembers',
68
+ cover_image_id: 'idAttachmentCover',
69
+ list_id: 'idList',
70
+ pos: 'pos',
71
+ last_activity_date: 'dateLastActivity',
72
+ card_labels: 'idLabels',
73
+ labels: 'labels',
74
+ badges: 'badges',
75
+ card_members: 'members',
76
+ source_card_id: "idCardSource",
77
+ source_card_properties: "keepFromSource"
78
+ }
79
+
80
+ class << self
81
+ # Find a specific card by its id.
82
+ #
83
+ # @raise [Trello::Error] if the card could not be found.
84
+ #
85
+ # @return [Trello::Card]
86
+ def find(id, params = {})
87
+ client.find(:card, id, params)
88
+ end
89
+
90
+ # Create a new card and save it on Trello.
91
+ #
92
+ # If using source_card_id to duplicate a card, make sure to save
93
+ # the source card to Trello before calling this method to assure
94
+ # the correct data is used in the duplication.
95
+ #
96
+ # @param [Hash] options
97
+ # @option options [String] :name The name of the new card.
98
+ # @option options [String] :list_id ID of the list that the card should
99
+ # be added to.
100
+ # @option options [String] :desc A string with a
101
+ # length from 0 to 16384.
102
+ # @option options [String] :member_ids A comma-separated list of
103
+ # objectIds (24-character hex strings).
104
+ # @option options [String] :card_labels A comma-separated list of
105
+ # objectIds (24-character hex strings).
106
+ # @option options [Date] :due A date, or `nil`.
107
+ # @option options [String] :pos A position. `"top"`, `"bottom"`, or a
108
+ # positive number. Defaults to `"bottom"`.
109
+ # @option options [String] :source_card_id ID of the card to copy
110
+ # @option options [String] :source_card_properties A single, or array of,
111
+ # string properties to copy from source card.
112
+ # `"all"`, `"checklists"`, `"due"`, `"members"`, or `nil`.
113
+ # Defaults to `"all"`.
114
+ #
115
+ # @raise [Trello::Error] if the card could not be created.
116
+ #
117
+ # @return [Trello::Card]
118
+ def create(options)
119
+ client.create(:card,
120
+ 'name' => options[:name],
121
+ 'idList' => options[:list_id],
122
+ 'desc' => options[:desc],
123
+ 'idMembers' => options[:member_ids],
124
+ 'idLabels' => options[:card_labels],
125
+ 'due' => options[:due],
126
+ 'due_complete' => options[:due_complete] || false,
127
+ 'pos' => options[:pos],
128
+ 'idCardSource' => options[:source_card_id],
129
+ 'keepFromSource' => options.key?(:source_card_properties) ? options[:source_card_properties] : 'all'
130
+ )
131
+ end
132
+ end
133
+
134
+ # Update the fields of a card.
135
+ #
136
+ # Supply a hash of string keyed data retrieved from the Trello API representing
137
+ # a card.
138
+ #
139
+ # Note that this this method does not save anything new to the Trello API,
140
+ # it just assigns the input attributes to your local object. If you use
141
+ # this method to assign attributes, call `save` or `update!` afterwards if
142
+ # you want to persist your changes to Trello.
143
+ #
144
+ # @param [Hash] fields
145
+ # @option fields [String] :id
146
+ # @option fields [String] :short_id
147
+ # @option fields [String] :name The new name of the card.
148
+ # @option fields [String] :desc A string with a length from 0 to
149
+ # 16384.
150
+ # @option fields [Date] :due A date, or `nil`.
151
+ # @option fields [Boolean] :due_complete
152
+ # @option fields [Boolean] :closed
153
+ # @option fields [String] :url
154
+ # @option fields [String] :short_url
155
+ # @option fields [String] :board_id
156
+ # @option fields [String] :member_ids A comma-separated list of objectIds
157
+ # (24-character hex strings).
158
+ # @option fields [String] :pos A position. `"top"`, `"bottom"`, or a
159
+ # positive number. Defaults to `"bottom"`.
160
+ # @option fields [Array] :labels An Array of Trello::Label objects
161
+ # derived from the JSON response
162
+ # @option fields [String] :card_labels A comma-separated list of
163
+ # objectIds (24-character hex strings).
164
+ # @option fields [Object] :cover_image_id
165
+ # @option fields [Object] :badges
166
+ # @option fields [Object] :card_members
167
+ # @option fields [String] :source_card_id
168
+ # @option fields [Array] :source_card_properties
169
+ #
170
+ # @return [Trello::Card] self
171
+ def update_fields(fields)
172
+ attributes[:id] = fields[SYMBOL_TO_STRING[:id]] || attributes[:id]
173
+ attributes[:short_id] = fields[SYMBOL_TO_STRING[:short_id]] || attributes[:short_id]
174
+ attributes[:name] = fields[SYMBOL_TO_STRING[:name]] || fields[:name] || attributes[:name]
175
+ attributes[:desc] = fields[SYMBOL_TO_STRING[:desc]] || fields[:desc] || attributes[:desc]
176
+ attributes[:due] = Time.iso8601(fields[SYMBOL_TO_STRING[:due]]) rescue nil if fields.has_key?(SYMBOL_TO_STRING[:due])
177
+ attributes[:due] = fields[:due] if fields.has_key?(:due)
178
+ attributes[:due_complete] = fields[SYMBOL_TO_STRING[:due_complete]] if fields.has_key?(SYMBOL_TO_STRING[:due_complete])
179
+ attributes[:due_complete] ||= false
180
+ attributes[:closed] = fields[SYMBOL_TO_STRING[:closed]] if fields.has_key?(SYMBOL_TO_STRING[:closed])
181
+ attributes[:url] = fields[SYMBOL_TO_STRING[:url]] || attributes[:url]
182
+ attributes[:short_url] = fields[SYMBOL_TO_STRING[:short_url]] || attributes[:short_url]
183
+ attributes[:board_id] = fields[SYMBOL_TO_STRING[:board_id]] || attributes[:board_id]
184
+ attributes[:member_ids] = fields[SYMBOL_TO_STRING[:member_ids]] || fields[:member_ids] || attributes[:member_ids]
185
+ attributes[:list_id] = fields[SYMBOL_TO_STRING[:list_id]] || fields[:list_id] || attributes[:list_id]
186
+ attributes[:pos] = fields[SYMBOL_TO_STRING[:pos]] || fields[:pos] || attributes[:pos]
187
+ attributes[:labels] = (fields[SYMBOL_TO_STRING[:labels]] || []).map { |lbl| Trello::Label.new(lbl) }.presence || attributes[:labels].presence || []
188
+ attributes[:card_labels] = fields[SYMBOL_TO_STRING[:card_labels]] || fields[:card_labels] || attributes[:card_labels]
189
+ attributes[:last_activity_date] = Time.iso8601(fields[SYMBOL_TO_STRING[:last_activity_date]]) rescue nil if fields.has_key?(SYMBOL_TO_STRING[:last_activity_date])
190
+ attributes[:cover_image_id] = fields[SYMBOL_TO_STRING[:cover_image_id]] || attributes[:cover_image_id]
191
+ attributes[:badges] = fields[SYMBOL_TO_STRING[:badges]] || attributes[:badges]
192
+ attributes[:card_members] = fields[SYMBOL_TO_STRING[:card_members]] || attributes[:card_members]
193
+ attributes[:source_card_id] = fields[SYMBOL_TO_STRING[:source_card_id]] || fields[:source_card_id] || attributes[:source_card_id]
194
+ attributes[:source_card_properties] = fields[SYMBOL_TO_STRING[:source_card_properties]] || fields[:source_card_properties] || attributes[:source_card_properties]
195
+ self
196
+ end
197
+
198
+ # Returns a reference to the board this card is part of.
199
+ one :board, path: :boards, using: :board_id
200
+ # Returns a reference to the cover image attachment
201
+ one :cover_image, path: :attachments, using: :cover_image_id
202
+
203
+ # Returns a list of checklists associated with the card.
204
+ #
205
+ # The options hash may have a filter key which can have its value set as any
206
+ # of the following values:
207
+ # :filter => [ :none, :all ] # default :all
208
+ many :checklists, filter: :all
209
+
210
+ # Returns a list of plugins associated with the card
211
+ many :plugin_data, path: "pluginData"
212
+
213
+ def check_item_states
214
+ states = CheckItemState.from_response client.get("/cards/#{self.id}/checkItemStates")
215
+ MultiAssociation.new(self, states).proxy
216
+ end
217
+
218
+ # Returns a reference to the list this card is currently in.
219
+ one :list, path: :lists, using: :list_id
220
+
221
+ # Returns a list of members who are assigned to this card.
222
+ #
223
+ # @return [Array<Trello::Member>]
224
+ def members
225
+ members = member_ids.map do |member_id|
226
+ Member.from_response client.get("/members/#{member_id}")
227
+ end
228
+ MultiAssociation.new(self, members).proxy
229
+ end
230
+
231
+ # Returns a list of members who have upvoted this card
232
+ # NOTE: this fetches a list each time it's called to avoid case where
233
+ # card is voted (or vote is removed) after card is fetched. Optimizing
234
+ # accuracy over network performance
235
+ #
236
+ # @return [Array<Trello::Member>]
237
+ def voters
238
+ Member.from_response client.get("/cards/#{id}/membersVoted")
239
+ end
240
+
241
+ # Saves a record.
242
+ #
243
+ # @raise [Trello::Error] if the card could not be saved
244
+ #
245
+ # @return [String] The JSON representation of the saved card returned by
246
+ # the Trello API.
247
+ def save
248
+ # If we have an id, just update our fields.
249
+ return update! if id
250
+
251
+ from_response client.post("/cards", {
252
+ name: name,
253
+ desc: desc,
254
+ idList: list_id,
255
+ idMembers: member_ids,
256
+ idLabels: card_labels,
257
+ pos: pos,
258
+ due: due,
259
+ dueComplete: due_complete,
260
+ idCardSource: source_card_id,
261
+ keepFromSource: source_card_properties
262
+ })
263
+ end
264
+
265
+ # Update an existing record.
266
+ #
267
+ # Warning: this updates all fields using values already in memory. If
268
+ # an external resource has updated these fields, you should refresh!
269
+ # this object before making your changes, and before updating the record.
270
+ #
271
+ # @raise [Trello::Error] if the card could not be updated.
272
+ #
273
+ # @return [String] The JSON representation of the updated card returned by
274
+ # the Trello API.
275
+ def update!
276
+ @previously_changed = changes
277
+ # extract only new values to build payload
278
+ payload = Hash[changes.map { |key, values| [SYMBOL_TO_STRING[key.to_sym].to_sym, values[1]] }]
279
+ @changed_attributes.clear
280
+
281
+ client.put("/cards/#{id}", payload)
282
+ end
283
+
284
+ # Delete this card
285
+ #
286
+ # @return [String] the JSON response from the Trello API
287
+ def delete
288
+ client.delete("/cards/#{id}")
289
+ end
290
+
291
+ # Check if the card is not active anymore.
292
+ def closed?
293
+ closed
294
+ end
295
+
296
+ # Close the card.
297
+ #
298
+ # This only marks your local copy card as closed. Use `close!` if you
299
+ # want to close the card and persist the change to the Trello API.
300
+ #
301
+ # @return [Boolean] always returns true
302
+ #
303
+ # @return [String] The JSON representation of the closed card returned by
304
+ # the Trello API.
305
+ def close
306
+ self.closed = true
307
+ end
308
+
309
+ def close!
310
+ close
311
+ save
312
+ end
313
+
314
+ # Is the record valid?
315
+ def valid?
316
+ name && list_id
317
+ end
318
+
319
+ # Add a comment with the supplied text.
320
+ def add_comment(text)
321
+ client.post("/cards/#{id}/actions/comments", text: text)
322
+ end
323
+
324
+ # Add a checklist to this card
325
+ def add_checklist(checklist)
326
+ client.post("/cards/#{id}/checklists", {
327
+ value: checklist.id
328
+ })
329
+ end
330
+
331
+ # create a new checklist and add it to this card
332
+ def create_new_checklist(name)
333
+ client.post("/cards/#{id}/checklists", { name: name })
334
+ end
335
+
336
+ # Move this card to the given list
337
+ def move_to_list(list)
338
+ list_number = list.is_a?(String) ? list : list.id
339
+ unless list_id == list_number
340
+ client.put("/cards/#{id}/idList", {
341
+ value: list_number
342
+ })
343
+ end
344
+ end
345
+
346
+ # Moves this card to the given list no matter which board it is on
347
+ def move_to_list_on_any_board(list_id)
348
+ list = List.find(list_id)
349
+ if board.id == list.board_id
350
+ move_to_list(list_id)
351
+ else
352
+ move_to_board(Board.find(list.board_id), list)
353
+ end
354
+ end
355
+
356
+ # Move this card to the given board (and optional list on this board)
357
+ def move_to_board(new_board, new_list = nil)
358
+ unless board_id == new_board.id
359
+ payload = { value: new_board.id }
360
+ payload[:idList] = new_list.id if new_list
361
+ client.put("/cards/#{id}/idBoard", payload)
362
+ end
363
+ end
364
+
365
+ # Add a member to this card
366
+ def add_member(member)
367
+ client.post("/cards/#{id}/members", {
368
+ value: member.id
369
+ })
370
+ end
371
+
372
+ # Remove a member from this card
373
+ def remove_member(member)
374
+ client.delete("/cards/#{id}/members/#{member.id}")
375
+ end
376
+
377
+ # Current authenticated user upvotes a card
378
+ def upvote
379
+ begin
380
+ client.post("/cards/#{id}/membersVoted", {
381
+ value: me.id
382
+ })
383
+ rescue Trello::Error => e
384
+ fail e unless e.message =~ /has already voted/i
385
+ end
386
+
387
+ self
388
+ end
389
+
390
+ # Recind upvote. Noop if authenticated user hasn't previously voted
391
+ def remove_upvote
392
+ begin
393
+ client.delete("/cards/#{id}/membersVoted/#{me.id}")
394
+ rescue Trello::Error => e
395
+ fail e unless e.message =~ /has not voted/i
396
+ end
397
+
398
+ self
399
+ end
400
+
401
+ # Add a label
402
+ def add_label(label)
403
+ unless label.valid?
404
+ errors.add(:label, "is not valid.")
405
+ return Trello.logger.warn "Label is not valid." unless label.valid?
406
+ end
407
+ client.post("/cards/#{id}/idLabels", {value: label.id})
408
+ end
409
+
410
+ # Remove a label
411
+ def remove_label(label)
412
+ unless label.valid?
413
+ errors.add(:label, "is not valid.")
414
+ return Trello.logger.warn "Label is not valid." unless label.valid?
415
+ end
416
+ client.delete("/cards/#{id}/idLabels/#{label.id}")
417
+ end
418
+
419
+ # Add an attachment to this card
420
+ def add_attachment(attachment, name = '')
421
+ # Is it a file object or a string (url)?
422
+ if attachment.respond_to?(:path) && attachment.respond_to?(:read)
423
+ client.post("/cards/#{id}/attachments", {
424
+ file: attachment,
425
+ name: name
426
+ })
427
+ else
428
+ client.post("/cards/#{id}/attachments", {
429
+ url: attachment,
430
+ name: name
431
+ })
432
+ end
433
+ end
434
+
435
+ # Retrieve a list of attachments
436
+ def attachments
437
+ attachments = Attachment.from_response client.get("/cards/#{id}/attachments")
438
+ MultiAssociation.new(self, attachments).proxy
439
+ end
440
+
441
+ # Remove an attachment from this card
442
+ def remove_attachment(attachment)
443
+ client.delete("/cards/#{id}/attachments/#{attachment.id}")
444
+ end
445
+
446
+ # :nodoc:
447
+ def request_prefix
448
+ "/cards/#{id}"
449
+ end
450
+
451
+ # Retrieve a list of comments
452
+ def comments
453
+ comments = Comment.from_response client.get("/cards/#{id}/actions", filter: "commentCard")
454
+ end
455
+
456
+ # Find the creation date
457
+ def created_at
458
+ @created_at ||= Time.at(id[0..7].to_i(16)) rescue nil
459
+ end
460
+
461
+ private
462
+
463
+ def me
464
+ @me ||= Member.find(:me)
465
+ end
466
+ end
467
+ end