trelloapi 0.1.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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.gitignore +12 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +5 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +11 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/lib/.DS_Store +0 -0
  13. data/lib/trello.rb +163 -0
  14. data/lib/trello/.DS_Store +0 -0
  15. data/lib/trello/action.rb +68 -0
  16. data/lib/trello/association.rb +14 -0
  17. data/lib/trello/association_proxy.rb +42 -0
  18. data/lib/trello/attachment.rb +40 -0
  19. data/lib/trello/authorization.rb +187 -0
  20. data/lib/trello/basic_data.rb +132 -0
  21. data/lib/trello/board.rb +201 -0
  22. data/lib/trello/card.rb +456 -0
  23. data/lib/trello/checklist.rb +142 -0
  24. data/lib/trello/client.rb +120 -0
  25. data/lib/trello/comment.rb +62 -0
  26. data/lib/trello/configuration.rb +68 -0
  27. data/lib/trello/core_ext/array.rb +6 -0
  28. data/lib/trello/core_ext/hash.rb +6 -0
  29. data/lib/trello/core_ext/string.rb +6 -0
  30. data/lib/trello/cover_image.rb +8 -0
  31. data/lib/trello/has_actions.rb +9 -0
  32. data/lib/trello/item.rb +37 -0
  33. data/lib/trello/item_state.rb +30 -0
  34. data/lib/trello/json_utils.rb +64 -0
  35. data/lib/trello/label.rb +108 -0
  36. data/lib/trello/label_name.rb +31 -0
  37. data/lib/trello/list.rb +114 -0
  38. data/lib/trello/member.rb +112 -0
  39. data/lib/trello/multi_association.rb +12 -0
  40. data/lib/trello/net.rb +39 -0
  41. data/lib/trello/notification.rb +61 -0
  42. data/lib/trello/organization.rb +68 -0
  43. data/lib/trello/plugin_datum.rb +34 -0
  44. data/lib/trello/token.rb +36 -0
  45. data/lib/trello/webhook.rb +103 -0
  46. data/trello.gemspec +41 -0
  47. metadata +161 -0
@@ -0,0 +1,456 @@
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]]
173
+ attributes[:short_id] = fields[SYMBOL_TO_STRING[:short_id]]
174
+ attributes[:name] = fields[SYMBOL_TO_STRING[:name]] || fields[:name]
175
+ attributes[:desc] = fields[SYMBOL_TO_STRING[:desc]] || fields[:desc]
176
+ attributes[:due] = Time.iso8601(fields[SYMBOL_TO_STRING[:due]]) rescue nil
177
+ attributes[:due] ||= fields[:due]
178
+ attributes[:due_complete] = fields[SYMBOL_TO_STRING[:due_complete]] || false
179
+ attributes[:closed] = fields[SYMBOL_TO_STRING[:closed]]
180
+ attributes[:url] = fields[SYMBOL_TO_STRING[:url]]
181
+ attributes[:short_url] = fields[SYMBOL_TO_STRING[:short_url]]
182
+ attributes[:board_id] = fields[SYMBOL_TO_STRING[:board_id]]
183
+ attributes[:member_ids] = fields[SYMBOL_TO_STRING[:member_ids]] || fields[:member_ids]
184
+ attributes[:list_id] = fields[SYMBOL_TO_STRING[:list_id]] || fields[:list_id]
185
+ attributes[:pos] = fields[SYMBOL_TO_STRING[:pos]] || fields[:pos]
186
+ attributes[:labels] = (fields[SYMBOL_TO_STRING[:labels]] || []).map { |lbl| Trello::Label.new(lbl) }
187
+ attributes[:card_labels] = fields[SYMBOL_TO_STRING[:card_labels]] || fields[:card_labels]
188
+ attributes[:last_activity_date] = Time.iso8601(fields[SYMBOL_TO_STRING[:last_activity_date]]) rescue nil
189
+ attributes[:cover_image_id] = fields[SYMBOL_TO_STRING[:cover_image_id]]
190
+ attributes[:badges] = fields[SYMBOL_TO_STRING[:badges]]
191
+ attributes[:card_members] = fields[SYMBOL_TO_STRING[:card_members]]
192
+ attributes[:source_card_id] = fields[SYMBOL_TO_STRING[:source_card_id]] || fields[:source_card_id]
193
+ attributes[:source_card_properties] = fields[SYMBOL_TO_STRING[:source_card_properties]] || fields[:source_card_properties]
194
+ self
195
+ end
196
+
197
+ # Returns a reference to the board this card is part of.
198
+ one :board, path: :boards, using: :board_id
199
+ # Returns a reference to the cover image attachment
200
+ one :cover_image, path: :attachments, using: :cover_image_id
201
+
202
+ # Returns a list of checklists associated with the card.
203
+ #
204
+ # The options hash may have a filter key which can have its value set as any
205
+ # of the following values:
206
+ # :filter => [ :none, :all ] # default :all
207
+ many :checklists, filter: :all
208
+
209
+ # Returns a list of plugins associated with the card
210
+ many :plugin_data, path: "pluginData"
211
+
212
+ def check_item_states
213
+ states = CheckItemState.from_response client.get("/cards/#{self.id}/checkItemStates")
214
+ MultiAssociation.new(self, states).proxy
215
+ end
216
+
217
+ # Returns a reference to the list this card is currently in.
218
+ one :list, path: :lists, using: :list_id
219
+
220
+ # Returns a list of members who are assigned to this card.
221
+ #
222
+ # @return [Array<Trello::Member>]
223
+ def members
224
+ members = member_ids.map do |member_id|
225
+ Member.from_response client.get("/members/#{member_id}")
226
+ end
227
+ MultiAssociation.new(self, members).proxy
228
+ end
229
+
230
+ # Returns a list of members who have upvoted this card
231
+ # NOTE: this fetches a list each time it's called to avoid case where
232
+ # card is voted (or vote is removed) after card is fetched. Optimizing
233
+ # accuracy over network performance
234
+ #
235
+ # @return [Array<Trello::Member>]
236
+ def voters
237
+ Member.from_response client.get("/cards/#{id}/membersVoted")
238
+ end
239
+
240
+ # Saves a record.
241
+ #
242
+ # @raise [Trello::Error] if the card could not be saved
243
+ #
244
+ # @return [String] The JSON representation of the saved card returned by
245
+ # the Trello API.
246
+ def save
247
+ # If we have an id, just update our fields.
248
+ return update! if id
249
+
250
+ from_response client.post("/cards", {
251
+ name: name,
252
+ desc: desc,
253
+ idList: list_id,
254
+ idMembers: member_ids,
255
+ idLabels: card_labels,
256
+ pos: pos,
257
+ due: due,
258
+ dueComplete: due_complete,
259
+ idCardSource: source_card_id,
260
+ keepFromSource: source_card_properties
261
+ })
262
+ end
263
+
264
+ # Update an existing record.
265
+ #
266
+ # Warning: this updates all fields using values already in memory. If
267
+ # an external resource has updated these fields, you should refresh!
268
+ # this object before making your changes, and before updating the record.
269
+ #
270
+ # @raise [Trello::Error] if the card could not be updated.
271
+ #
272
+ # @return [String] The JSON representation of the updated card returned by
273
+ # the Trello API.
274
+ def update!
275
+ @previously_changed = changes
276
+ # extract only new values to build payload
277
+ payload = Hash[changes.map { |key, values| [SYMBOL_TO_STRING[key.to_sym].to_sym, values[1]] }]
278
+ @changed_attributes.clear
279
+
280
+ client.put("/cards/#{id}", payload)
281
+ end
282
+
283
+ # Delete this card
284
+ #
285
+ # @return [String] the JSON response from the Trello API
286
+ def delete
287
+ client.delete("/cards/#{id}")
288
+ end
289
+
290
+ # Check if the card is not active anymore.
291
+ def closed?
292
+ closed
293
+ end
294
+
295
+ # Close the card.
296
+ #
297
+ # This only marks your local copy card as closed. Use `close!` if you
298
+ # want to close the card and persist the change to the Trello API.
299
+ #
300
+ # @return [Boolean] always returns true
301
+ #
302
+ # @return [String] The JSON representation of the closed card returned by
303
+ # the Trello API.
304
+ def close
305
+ self.closed = true
306
+ end
307
+
308
+ def close!
309
+ close
310
+ save
311
+ end
312
+
313
+ # Is the record valid?
314
+ def valid?
315
+ name && list_id
316
+ end
317
+
318
+ # Add a comment with the supplied text.
319
+ def add_comment(text)
320
+ client.post("/cards/#{id}/actions/comments", text: text)
321
+ end
322
+
323
+ # Add a checklist to this card
324
+ def add_checklist(checklist)
325
+ client.post("/cards/#{id}/checklists", {
326
+ value: checklist.id
327
+ })
328
+ end
329
+
330
+ # create a new checklist and add it to this card
331
+ def create_new_checklist(name)
332
+ client.post("/cards/#{id}/checklists", { name: name })
333
+ end
334
+
335
+ # Move this card to the given list
336
+ def move_to_list(list)
337
+ list_number = list.is_a?(String) ? list : list.id
338
+ unless list_id == list_number
339
+ client.put("/cards/#{id}/idList", {
340
+ value: list_number
341
+ })
342
+ end
343
+ end
344
+
345
+ # Move this card to the given board (and optional list on this board)
346
+ def move_to_board(new_board, new_list = nil)
347
+ unless board_id == new_board.id
348
+ payload = { value: new_board.id }
349
+ payload[:idList] = new_list.id if new_list
350
+ client.put("/cards/#{id}/idBoard", payload)
351
+ end
352
+ end
353
+
354
+ # Add a member to this card
355
+ def add_member(member)
356
+ client.post("/cards/#{id}/members", {
357
+ value: member.id
358
+ })
359
+ end
360
+
361
+ # Remove a member from this card
362
+ def remove_member(member)
363
+ client.delete("/cards/#{id}/members/#{member.id}")
364
+ end
365
+
366
+ # Current authenticated user upvotes a card
367
+ def upvote
368
+ begin
369
+ client.post("/cards/#{id}/membersVoted", {
370
+ value: me.id
371
+ })
372
+ rescue Trello::Error => e
373
+ fail e unless e.message =~ /has already voted/i
374
+ end
375
+
376
+ self
377
+ end
378
+
379
+ # Recind upvote. Noop if authenticated user hasn't previously voted
380
+ def remove_upvote
381
+ begin
382
+ client.delete("/cards/#{id}/membersVoted/#{me.id}")
383
+ rescue Trello::Error => e
384
+ fail e unless e.message =~ /has not voted/i
385
+ end
386
+
387
+ self
388
+ end
389
+
390
+ # Add a label
391
+ def add_label(label)
392
+ unless label.valid?
393
+ errors.add(:label, "is not valid.")
394
+ return Trello.logger.warn "Label is not valid." unless label.valid?
395
+ end
396
+ client.post("/cards/#{id}/idLabels", {value: label.id})
397
+ end
398
+
399
+ # Remove a label
400
+ def remove_label(label)
401
+ unless label.valid?
402
+ errors.add(:label, "is not valid.")
403
+ return Trello.logger.warn "Label is not valid." unless label.valid?
404
+ end
405
+ client.delete("/cards/#{id}/idLabels/#{label.id}")
406
+ end
407
+
408
+ # Add an attachment to this card
409
+ def add_attachment(attachment, name = '')
410
+ # Is it a file object or a string (url)?
411
+ if attachment.respond_to?(:path) && attachment.respond_to?(:read)
412
+ client.post("/cards/#{id}/attachments", {
413
+ file: attachment,
414
+ name: name
415
+ })
416
+ else
417
+ client.post("/cards/#{id}/attachments", {
418
+ url: attachment,
419
+ name: name
420
+ })
421
+ end
422
+ end
423
+
424
+ # Retrieve a list of attachments
425
+ def attachments
426
+ attachments = Attachment.from_response client.get("/cards/#{id}/attachments")
427
+ MultiAssociation.new(self, attachments).proxy
428
+ end
429
+
430
+ # Remove an attachment from this card
431
+ def remove_attachment(attachment)
432
+ client.delete("/cards/#{id}/attachments/#{attachment.id}")
433
+ end
434
+
435
+ # :nodoc:
436
+ def request_prefix
437
+ "/cards/#{id}"
438
+ end
439
+
440
+ # Retrieve a list of comments
441
+ def comments
442
+ comments = Comment.from_response client.get("/cards/#{id}/actions", filter: "commentCard")
443
+ end
444
+
445
+ # Find the creation date
446
+ def created_at
447
+ @created_at ||= Time.at(id[0..7].to_i(16)) rescue nil
448
+ end
449
+
450
+ private
451
+
452
+ def me
453
+ @me ||= Member.find(:me)
454
+ end
455
+ end
456
+ end