ruby-trello 2.0.0 → 2.3.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 (142) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +106 -17
  3. data/lib/trello.rb +47 -34
  4. data/lib/trello/action.rb +10 -8
  5. data/lib/trello/association_builder/has_many.rb +17 -0
  6. data/lib/trello/association_builder/has_one.rb +16 -0
  7. data/lib/trello/association_fetcher/has_many.rb +26 -0
  8. data/lib/trello/association_fetcher/has_many/fetch.rb +47 -0
  9. data/lib/trello/association_fetcher/has_many/params.rb +56 -0
  10. data/lib/trello/association_fetcher/has_one.rb +25 -0
  11. data/lib/trello/association_fetcher/has_one/fetch.rb +49 -0
  12. data/lib/trello/association_fetcher/has_one/params.rb +36 -0
  13. data/lib/trello/association_infer_tool.rb +13 -0
  14. data/lib/trello/association_proxy.rb +1 -1
  15. data/lib/trello/attachment.rb +13 -10
  16. data/lib/trello/basic_data.rb +16 -49
  17. data/lib/trello/board.rb +17 -3
  18. data/lib/trello/card.rb +103 -38
  19. data/lib/trello/checklist.rb +14 -13
  20. data/lib/trello/client.rb +1 -1
  21. data/lib/trello/comment.rb +4 -4
  22. data/lib/trello/custom_field.rb +131 -0
  23. data/lib/trello/custom_field_item.rb +98 -0
  24. data/lib/trello/custom_field_option.rb +22 -0
  25. data/lib/trello/error.rb +12 -0
  26. data/lib/trello/item.rb +7 -7
  27. data/lib/trello/item_state.rb +3 -3
  28. data/lib/trello/label.rb +30 -12
  29. data/lib/trello/label_name.rb +10 -10
  30. data/lib/trello/list.rb +6 -6
  31. data/lib/trello/member.rb +10 -9
  32. data/lib/trello/notification.rb +6 -6
  33. data/lib/trello/organization.rb +11 -11
  34. data/lib/trello/plugin_datum.rb +6 -6
  35. data/lib/trello/register_attributes.rb +54 -0
  36. data/lib/trello/token.rb +6 -5
  37. data/lib/trello/webhook.rb +5 -5
  38. data/spec/action_spec.rb +22 -1
  39. data/spec/basic_data_spec.rb +58 -0
  40. data/spec/board_spec.rb +28 -2
  41. data/spec/card_spec.rb +183 -8
  42. data/spec/cassettes/can_add_a_file_from_url_on_a_card.yml +189 -0
  43. data/spec/cassettes/can_add_a_file_on_a_card.yml +200 -0
  44. data/spec/cassettes/can_add_a_member_to_a_card.yml +190 -0
  45. data/spec/cassettes/can_add_label_on_a_card.yml +281 -0
  46. data/spec/cassettes/can_close_bong_a_card.yml +189 -0
  47. data/spec/cassettes/can_get_actions.yml +196 -0
  48. data/spec/cassettes/can_get_attachments.yml +187 -0
  49. data/spec/cassettes/can_get_comments.yml +193 -0
  50. data/spec/cassettes/can_move_a_card_to_another_board_with_specific_list.yml +373 -0
  51. data/spec/cassettes/can_move_a_card_to_another_board_without_specific_list.yml +281 -0
  52. data/spec/cassettes/can_move_a_card_to_another_list.yml +281 -0
  53. data/spec/cassettes/can_remove_a_member_from_a_card.yml +185 -0
  54. data/spec/cassettes/can_remove_an_attachment_on_a_card.yml +277 -0
  55. data/spec/cassettes/can_remove_an_upvote_on_a_card.yml +465 -0
  56. data/spec/cassettes/can_remove_label_on_a_card.yml +279 -0
  57. data/spec/cassettes/can_success_add_comment_to_a_card.yml +196 -0
  58. data/spec/cassettes/can_success_copy_checklist_to_a_card.yml +281 -0
  59. data/spec/cassettes/can_success_copy_checklist_to_a_card_without_name_pos.yml +281 -0
  60. data/spec/cassettes/can_success_create_a_card.yml +99 -0
  61. data/spec/cassettes/can_success_create_new_checklist_to_a_card.yml +189 -0
  62. data/spec/cassettes/can_success_delete_card.yml +185 -0
  63. data/spec/cassettes/can_success_upate_a_card.yml +189 -0
  64. data/spec/cassettes/can_success_update_bong_a_board.yml +283 -0
  65. data/spec/cassettes/can_success_update_bong_a_card.yml +191 -0
  66. data/spec/cassettes/can_upvote_on_a_card.yml +469 -0
  67. data/spec/cassettes/card_find_with_id_and_get_all_fields.yml +95 -0
  68. data/spec/cassettes/card_find_with_id_and_get_specific_fields.yml +96 -0
  69. data/spec/cassettes/custom_field_item_save_1.yml +97 -0
  70. data/spec/cassettes/custom_field_item_save_2.yml +281 -0
  71. data/spec/cassettes/get_board_of_card.yml +187 -0
  72. data/spec/cassettes/get_check_item_states_of_card.yml +188 -0
  73. data/spec/cassettes/get_checklists_of_card.yml +187 -0
  74. data/spec/cassettes/get_cover_image_of_card.yml +187 -0
  75. data/spec/cassettes/get_custom_field_items_of_card.yml +187 -0
  76. data/spec/cassettes/get_list_of_card.yml +188 -0
  77. data/spec/cassettes/get_lists.yml +187 -0
  78. data/spec/cassettes/get_members_of_card.yml +189 -0
  79. data/spec/cassettes/get_plugin_data_of_card.yml +187 -0
  80. data/spec/cassettes/get_voters_of_card.yml +188 -0
  81. data/spec/cassettes/remove_upvote_on_a_card_when_have_not_voted.yml +366 -0
  82. data/spec/cassettes/revote_on_a_card.yml +464 -0
  83. data/spec/checklist_spec.rb +39 -3
  84. data/spec/client_spec.rb +5 -1
  85. data/spec/custom_field_item_spec.rb +192 -0
  86. data/spec/custom_field_option_spec.rb +49 -0
  87. data/spec/custom_field_spec.rb +261 -0
  88. data/spec/integration/basic_data/many_spec.rb +123 -0
  89. data/spec/integration/basic_data/one_spec.rb +84 -0
  90. data/spec/integration/basic_data/register_attributes_spec.rb +75 -0
  91. data/spec/integration/board/update!_spec.rb +31 -0
  92. data/spec/integration/board_lists_spec.rb +21 -0
  93. data/spec/integration/card/add_and_remove_attachment_spec.rb +45 -0
  94. data/spec/integration/card/add_and_remove_label_spec.rb +35 -0
  95. data/spec/integration/card/add_checklist_spec.rb +32 -0
  96. data/spec/integration/card/add_comment_spec.rb +19 -0
  97. data/spec/integration/card/add_memeber_spec.rb +19 -0
  98. data/spec/integration/card/associations/actions_spec.rb +17 -0
  99. data/spec/integration/card/associations/attachments_spec.rb +18 -0
  100. data/spec/integration/card/associations/board_spec.rb +17 -0
  101. data/spec/integration/card/associations/check_item_states_spec.rb +17 -0
  102. data/spec/integration/card/associations/checklists_spec.rb +18 -0
  103. data/spec/integration/card/associations/comments_spec.rb +19 -0
  104. data/spec/integration/card/associations/cover_image_spec.rb +18 -0
  105. data/spec/integration/card/associations/custom_field_items_spec.rb +18 -0
  106. data/spec/integration/card/associations/list_spec.rb +16 -0
  107. data/spec/integration/card/associations/members_spec.rb +18 -0
  108. data/spec/integration/card/associations/plugin_data_spec.rb +18 -0
  109. data/spec/integration/card/associations/voters_spec.rb +17 -0
  110. data/spec/integration/card/close!_spec.rb +16 -0
  111. data/spec/integration/card/create_new_check_list_spec.rb +19 -0
  112. data/spec/integration/card/create_spec.rb +50 -0
  113. data/spec/integration/card/delete_spec.rb +16 -0
  114. data/spec/integration/card/find_spec.rb +67 -0
  115. data/spec/integration/card/move_to_board_spec.rb +36 -0
  116. data/spec/integration/card/move_to_list_spec.rb +20 -0
  117. data/spec/integration/card/remove_member_spec.rb +19 -0
  118. data/spec/integration/card/save_spec.rb +61 -0
  119. data/spec/integration/card/update!_spec.rb +53 -0
  120. data/spec/integration/card/vote_spec.rb +50 -0
  121. data/spec/integration/custom_field_item_spec.rb +47 -0
  122. data/spec/item_spec.rb +20 -0
  123. data/spec/label_spec.rb +99 -0
  124. data/spec/list_spec.rb +21 -0
  125. data/spec/member_spec.rb +23 -0
  126. data/spec/notification_spec.rb +21 -0
  127. data/spec/organization_spec.rb +26 -0
  128. data/spec/spec_helper.rb +96 -23
  129. data/spec/token_spec.rb +19 -0
  130. data/spec/unit/trello/association_builder/has_many_spec.rb +36 -0
  131. data/spec/unit/trello/association_builder/has_one_spec.rb +36 -0
  132. data/spec/unit/trello/association_fetcher/has_many/fetch_spec.rb +38 -0
  133. data/spec/unit/trello/association_fetcher/has_many/params_spec.rb +107 -0
  134. data/spec/unit/trello/association_fetcher/has_many_spec.rb +50 -0
  135. data/spec/unit/trello/association_fetcher/has_one/fetch_spec.rb +51 -0
  136. data/spec/unit/trello/association_fetcher/has_one/params_spec.rb +81 -0
  137. data/spec/unit/trello/association_fetcher/has_one_spec.rb +49 -0
  138. data/spec/unit/trello/association_infer_tool_spec.rb +41 -0
  139. data/spec/unit/trello/basic_data_spec.rb +54 -0
  140. data/spec/unit/trello/card_spec.rb +103 -0
  141. data/spec/webhook_spec.rb +20 -0
  142. metadata +224 -30
@@ -0,0 +1,25 @@
1
+ module Trello
2
+ module AssociationFetcher
3
+ class HasOne
4
+ autoload :Params, 'trello/association_fetcher/has_one/params'
5
+ autoload :Fetch, 'trello/association_fetcher/has_one/fetch'
6
+
7
+ attr_reader :model, :name, :options
8
+
9
+ def initialize(model, name, options)
10
+ @model = model
11
+ @name = name
12
+ @options = options
13
+ end
14
+
15
+ def fetch
16
+ params = Params.new(
17
+ association_owner: model,
18
+ association_name: name,
19
+ association_options: options
20
+ )
21
+ Fetch.execute(params)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,49 @@
1
+ module Trello
2
+ module AssociationFetcher
3
+ class HasOne
4
+ class Fetch
5
+ class << self
6
+ def execute(params)
7
+ new(params).execute
8
+ end
9
+ end
10
+
11
+ def initialize(params)
12
+ @params = params
13
+ end
14
+
15
+ def execute
16
+ if association_restful_name
17
+ client.find(association_restful_name, association_restful_id)
18
+ else
19
+ association_class.find(association_restful_id)
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :params
26
+
27
+ def client
28
+ association_owner.client
29
+ end
30
+
31
+ def association_owner
32
+ params.association_owner
33
+ end
34
+
35
+ def association_restful_name
36
+ params.association_restful_name
37
+ end
38
+
39
+ def association_restful_id
40
+ params.association_restful_id
41
+ end
42
+
43
+ def association_class
44
+ params.association_class
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,36 @@
1
+ module Trello
2
+ module AssociationFetcher
3
+ class HasOne
4
+ class Params
5
+ attr_reader :association_owner
6
+
7
+ def initialize(association_owner:, association_name:, association_options:)
8
+ @association_owner = association_owner
9
+ @association_name = association_name
10
+ @association_options = association_options || {}
11
+ end
12
+
13
+ def association_class
14
+ association_options[:via] || infer_class_on(association_name)
15
+ end
16
+
17
+ def association_restful_name
18
+ association_options[:path]
19
+ end
20
+
21
+ def association_restful_id
22
+ id_field = association_options[:using] || :id
23
+ association_owner.send(id_field)
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :association_name, :association_options
29
+
30
+ def infer_class_on(name)
31
+ AssociationInferTool.infer_class_on_name(name)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,13 @@
1
+ module Trello
2
+ class AssociationInferTool
3
+ class << self
4
+ def infer_restful_resource_on_class(klass)
5
+ klass.to_s.split('::').last.downcase.pluralize
6
+ end
7
+
8
+ def infer_class_on_name(name)
9
+ Trello.const_get(name.to_s.singularize.camelize)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -31,7 +31,7 @@ module Trello
31
31
  end
32
32
 
33
33
  def to_ary
34
- proxy_assocation.dup
34
+ proxy_assocation.target.dup
35
35
  end
36
36
  alias_method :to_a, :to_ary
37
37
 
@@ -7,6 +7,8 @@ module Trello
7
7
  # @return [String]
8
8
  # @!attribute url
9
9
  # @return [String]
10
+ # @!attribute pos
11
+ # @return [Float]
10
12
  # @!attribute bytes
11
13
  # @return [Fixnum]
12
14
  # @!attribute date
@@ -16,21 +18,22 @@ module Trello
16
18
  # @!attribute mime_type
17
19
  # @return [String]
18
20
  class Attachment < BasicData
19
- register_attributes :name, :id, :url, :bytes, :member_id, :date, :is_upload, :mime_type, :previews
21
+ register_attributes :name, :id, :pos, :url, :bytes, :member_id, :date, :is_upload, :mime_type, :previews
20
22
  # Update the fields of an attachment.
21
23
  #
22
24
  # Supply a hash of stringkeyed data retrieved from the Trello API representing
23
25
  # an attachment.
24
26
  def update_fields(fields)
25
- attributes[:name] = fields['name']
26
- attributes[:id] = fields['id']
27
- attributes[:url] = fields['url']
28
- attributes[:bytes] = fields['bytes'].to_i
29
- attributes[:member_id] = fields['idMember']
30
- attributes[:date] = Time.parse(fields['date'])
31
- attributes[:is_upload] = fields['isUpload']
32
- attributes[:mime_type] = fields['mimeType']
33
- attributes[:previews] = fields['previews']
27
+ attributes[:name] = fields['name'] || attributes[:name]
28
+ attributes[:id] = fields['id'] || attributes[:id]
29
+ attributes[:pos] = fields['pos'] || attributes[:pos]
30
+ attributes[:url] = fields['url'] || attributes[:url]
31
+ attributes[:bytes] = fields['bytes'].to_i || attributes[:bytes]
32
+ attributes[:member_id] = fields['idMember'] || attributes[:member_id]
33
+ attributes[:date] = Time.parse(fields['date']).presence || attributes[:date]
34
+ attributes[:is_upload] = fields['isUpload'] if fields.has_key?('isUpload')
35
+ attributes[:mime_type] = fields['mimeType'] || attributes[:mime_type]
36
+ attributes[:previews] = fields['previews'] if fields.has_key?('previews')
34
37
  self
35
38
  end
36
39
  end
@@ -42,61 +42,20 @@ module Trello
42
42
  end
43
43
  end
44
44
 
45
- def self.register_attributes(*names)
46
- options = { readonly: [] }
47
- options.merge!(names.pop) if names.last.kind_of? Hash
48
-
49
- # Defines the attribute getter and setters.
50
- class_eval do
51
- define_method :attributes do
52
- @attributes ||= names.reduce({}) { |hash, k| hash.merge(k.to_sym => nil) }
53
- end
54
-
55
- names.each do |key|
56
- define_method(:"#{key}") { @attributes[key] }
45
+ def self.register_attributes(*names_and_options)
46
+ has_opts = names_and_options.last.kind_of?(Hash)
47
+ readonly_attributes = has_opts ? names_and_options.pop[:readonly] : []
48
+ attributes = names_and_options
57
49
 
58
- unless options[:readonly].include?(key.to_sym)
59
- define_method :"#{key}=" do |val|
60
- send(:"#{key}_will_change!") unless val == @attributes[key]
61
- @attributes[key] = val
62
- end
63
- end
64
- end
65
-
66
- define_attribute_methods names
67
- end
50
+ RegisterAttributes.execute(self, attributes, readonly_attributes)
68
51
  end
69
52
 
70
53
  def self.one(name, opts = {})
71
- class_eval do
72
- define_method(:"#{name}") do |*args|
73
- options = opts.dup
74
- klass = options.delete(:via) || Trello.const_get(name.to_s.camelize)
75
- ident = options.delete(:using) || :id
76
- path = options.delete(:path)
77
-
78
- if path
79
- client.find(path, self.send(ident))
80
- else
81
- klass.find(self.send(ident))
82
- end
83
- end
84
- end
54
+ AssociationBuilder::HasOne.build(self, name, opts)
85
55
  end
86
56
 
87
57
  def self.many(name, opts = {})
88
- class_eval do
89
- define_method(:"#{name}") do |*args|
90
- options = opts.dup
91
- resource = options.delete(:in) || self.class.to_s.split("::").last.downcase.pluralize
92
- klass = options.delete(:via) || Trello.const_get(name.to_s.singularize.camelize)
93
- path = options.delete(:path) || name
94
- params = options.merge(args[0] || {})
95
-
96
- resources = client.find_many(klass, "/#{resource}/#{id}/#{path}", params)
97
- MultiAssociation.new(self, resources).proxy
98
- end
99
- end
58
+ AssociationBuilder::HasMany.build(self, name, opts)
100
59
  end
101
60
 
102
61
  def self.client
@@ -122,7 +81,15 @@ module Trello
122
81
 
123
82
  # Two objects are equal if their _id_ methods are equal.
124
83
  def ==(other)
125
- id == other.id
84
+ self.class == other.class && id == other.id
85
+ end
86
+
87
+ # Alias hash equality to equality
88
+ alias eql? ==
89
+
90
+ # Delegate hash key computation to class and id pair
91
+ def hash
92
+ [self.class, id].hash
126
93
  end
127
94
 
128
95
  def client
@@ -72,11 +72,12 @@ module Trello
72
72
  fail "Cannot save new instance." unless self.id
73
73
 
74
74
  @previously_changed = changes
75
- @changed_attributes.clear
75
+ @changed_attributes.clear if @changed_attributes.respond_to?(:clear)
76
+ changes_applied if respond_to?(:changes_applied)
76
77
 
77
78
  fields = {
78
79
  name: attributes[:name],
79
- description: attributes[:description],
80
+ desc: attributes[:description],
80
81
  closed: attributes[:closed],
81
82
  starred: attributes[:starred],
82
83
  idOrganization: attributes[:organization_id]
@@ -151,12 +152,25 @@ module Trello
151
152
  #
152
153
  # This method, when called, can take a hash table with a filter key containing any
153
154
  # of the following values:
154
- # :filter => [ :none, :normal, :owners, :all ] # default :all
155
+ # :filter => [ :none, :normal, :owners, :admins, :all ] # default :all
155
156
  many :members, filter: :all
156
157
 
158
+ # Returns a list of checklists associated with the board.
159
+ #
160
+ # The options hash may have a filter key which can have its value set as any
161
+ # of the following values:
162
+ # :filter => [ :none, :all ] # default :all
163
+ many :checklists, filter: :all
164
+
157
165
  # Returns a reference to the organization this board belongs to.
158
166
  one :organization, path: :organizations, using: :organization_id
159
167
 
168
+ # Returns a list of plugins associated with the board
169
+ many :plugin_data, path: "pluginData"
170
+
171
+ # Returns custom fields activated on this board
172
+ many :custom_fields, path: "customFields"
173
+
160
174
  def labels(params = {})
161
175
  # Set the limit to as high as possible given there is no pagination in this API.
162
176
  params[:limit] = 1000 unless params[:limit]
@@ -169,35 +169,29 @@ module Trello
169
169
  #
170
170
  # @return [Trello::Card] self
171
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
172
+ %i[
173
+ name desc due due_complete closed
174
+ board_id member_ids list_id pos
175
+ card_labels cover_image_id
176
+ ].each do |key|
177
+ send("#{key}_will_change!") if fields_has_key?(fields, key)
178
+ end
179
+
180
+ initialize_fields(fields)
181
+ end
182
+
183
+ def initialize(fields = {})
184
+ initialize_fields(fields)
195
185
  end
196
186
 
197
187
  # Returns a reference to the board this card is part of.
198
188
  one :board, path: :boards, using: :board_id
189
+
199
190
  # Returns a reference to the cover image attachment
200
- one :cover_image, path: :attachments, using: :cover_image_id
191
+ def cover_image(params = {})
192
+ response = client.get("/cards/#{id}/attachments/#{cover_image_id}", params)
193
+ CoverImage.from_response(response)
194
+ end
201
195
 
202
196
  # Returns a list of checklists associated with the card.
203
197
  #
@@ -209,6 +203,9 @@ module Trello
209
203
  # Returns a list of plugins associated with the card
210
204
  many :plugin_data, path: "pluginData"
211
205
 
206
+ # List of custom field values on the card, only the ones that have been set
207
+ many :custom_field_items, path: 'customFieldItems'
208
+
212
209
  def check_item_states
213
210
  states = CheckItemState.from_response client.get("/cards/#{self.id}/checkItemStates")
214
211
  MultiAssociation.new(self, states).proxy
@@ -221,9 +218,7 @@ module Trello
221
218
  #
222
219
  # @return [Array<Trello::Member>]
223
220
  def members
224
- members = member_ids.map do |member_id|
225
- Member.from_response client.get("/members/#{member_id}")
226
- end
221
+ members = Member.from_response client.get("/cards/#{self.id}/members")
227
222
  MultiAssociation.new(self, members).proxy
228
223
  end
229
224
 
@@ -269,15 +264,41 @@ module Trello
269
264
  #
270
265
  # @raise [Trello::Error] if the card could not be updated.
271
266
  #
272
- # @return [String] The JSON representation of the updated card returned by
273
- # the Trello API.
267
+ # @return [Trello::Card] updated self
274
268
  def update!
275
269
  @previously_changed = changes
276
270
  # extract only new values to build payload
277
271
  payload = Hash[changes.map { |key, values| [SYMBOL_TO_STRING[key.to_sym].to_sym, values[1]] }]
278
- @changed_attributes.clear
279
272
 
280
- client.put("/cards/#{id}", payload)
273
+ response = client.put("/cards/#{id}", payload)
274
+ updated_card = from_response(response)
275
+
276
+ @changed_attributes.clear if @changed_attributes.respond_to?(:clear)
277
+ changes_applied if respond_to?(:changes_applied)
278
+
279
+ attributes[:id] = updated_card.id
280
+ attributes[:short_id] = updated_card.short_id
281
+ attributes[:name] = updated_card.name
282
+ attributes[:desc] = updated_card.desc
283
+ attributes[:due] = updated_card.due
284
+ attributes[:due_complete] = updated_card.due_complete
285
+ attributes[:closed] = updated_card.closed
286
+ attributes[:url] = updated_card.url
287
+ attributes[:short_url] = updated_card.short_url
288
+ attributes[:board_id] = updated_card.board_id
289
+ attributes[:member_ids] = updated_card.member_ids
290
+ attributes[:list_id] = updated_card.list_id
291
+ attributes[:pos] = updated_card.pos
292
+ attributes[:labels] = updated_card.labels
293
+ attributes[:card_labels] = updated_card.card_labels
294
+ attributes[:last_activity_date] = updated_card.last_activity_date
295
+ attributes[:cover_image_id] = updated_card.cover_image_id
296
+ attributes[:badges] = updated_card.badges
297
+ attributes[:card_members] = updated_card.card_members
298
+ attributes[:source_card_id] = updated_card.source_card_id
299
+ attributes[:source_card_properties] = updated_card.source_card_properties
300
+
301
+ self
281
302
  end
282
303
 
283
304
  # Delete this card
@@ -312,7 +333,7 @@ module Trello
312
333
 
313
334
  # Is the record valid?
314
335
  def valid?
315
- name && list_id
336
+ !(name && list_id).nil?
316
337
  end
317
338
 
318
339
  # Add a comment with the supplied text.
@@ -321,10 +342,12 @@ module Trello
321
342
  end
322
343
 
323
344
  # Add a checklist to this card
324
- def add_checklist(checklist)
325
- client.post("/cards/#{id}/checklists", {
326
- value: checklist.id
327
- })
345
+ def add_checklist(checklist, name: nil, position: nil)
346
+ payload = { idChecklistSource: checklist.id }
347
+ payload[:name] = name if name
348
+ payload[:pos] = position if position
349
+
350
+ client.post("/cards/#{id}/checklists", payload)
328
351
  end
329
352
 
330
353
  # create a new checklist and add it to this card
@@ -342,6 +365,16 @@ module Trello
342
365
  end
343
366
  end
344
367
 
368
+ # Moves this card to the given list no matter which board it is on
369
+ def move_to_list_on_any_board(list_id)
370
+ list = List.find(list_id)
371
+ if board.id == list.board_id
372
+ move_to_list(list_id)
373
+ else
374
+ move_to_board(Board.find(list.board_id), list)
375
+ end
376
+ end
377
+
345
378
  # Move this card to the given board (and optional list on this board)
346
379
  def move_to_board(new_board, new_list = nil)
347
380
  unless board_id == new_board.id
@@ -353,14 +386,14 @@ module Trello
353
386
 
354
387
  # Add a member to this card
355
388
  def add_member(member)
356
- client.post("/cards/#{id}/members", {
389
+ client.post("/cards/#{id}/idMembers", {
357
390
  value: member.id
358
391
  })
359
392
  end
360
393
 
361
394
  # Remove a member from this card
362
395
  def remove_member(member)
363
- client.delete("/cards/#{id}/members/#{member.id}")
396
+ client.delete("/cards/#{id}/idMembers/#{member.id}")
364
397
  end
365
398
 
366
399
  # Current authenticated user upvotes a card
@@ -452,5 +485,37 @@ module Trello
452
485
  def me
453
486
  @me ||= Member.find(:me)
454
487
  end
488
+
489
+ def fields_has_key?(fields, key)
490
+ fields.key?(SYMBOL_TO_STRING[key]) || fields.key?(key)
491
+ end
492
+
493
+ def initialize_fields(fields)
494
+ attributes[:id] = fields[SYMBOL_TO_STRING[:id]] || attributes[:id]
495
+ attributes[:short_id] = fields[SYMBOL_TO_STRING[:short_id]] || attributes[:short_id]
496
+ attributes[:name] = fields[SYMBOL_TO_STRING[:name]] || fields[:name] || attributes[:name]
497
+ attributes[:desc] = fields[SYMBOL_TO_STRING[:desc]] || fields[:desc] || attributes[:desc]
498
+ attributes[:due] = Time.iso8601(fields[SYMBOL_TO_STRING[:due]]) rescue nil if fields.has_key?(SYMBOL_TO_STRING[:due])
499
+ attributes[:due] = fields[:due] if fields.has_key?(:due)
500
+ attributes[:due_complete] = fields[SYMBOL_TO_STRING[:due_complete]] if fields.has_key?(SYMBOL_TO_STRING[:due_complete])
501
+ attributes[:due_complete] ||= false
502
+ attributes[:closed] = fields[SYMBOL_TO_STRING[:closed]] if fields.has_key?(SYMBOL_TO_STRING[:closed])
503
+ attributes[:url] = fields[SYMBOL_TO_STRING[:url]] || attributes[:url]
504
+ attributes[:short_url] = fields[SYMBOL_TO_STRING[:short_url]] || attributes[:short_url]
505
+ attributes[:board_id] = fields[SYMBOL_TO_STRING[:board_id]] || attributes[:board_id]
506
+ attributes[:member_ids] = fields[SYMBOL_TO_STRING[:member_ids]] || fields[:member_ids] || attributes[:member_ids]
507
+ attributes[:list_id] = fields[SYMBOL_TO_STRING[:list_id]] || fields[:list_id] || attributes[:list_id]
508
+ attributes[:pos] = fields[SYMBOL_TO_STRING[:pos]] || fields[:pos] || attributes[:pos]
509
+ attributes[:labels] = (fields[SYMBOL_TO_STRING[:labels]] || []).map { |lbl| Trello::Label.new(lbl) }.presence || attributes[:labels].presence || []
510
+ attributes[:card_labels] = fields[SYMBOL_TO_STRING[:card_labels]] || fields[:card_labels] || attributes[:card_labels]
511
+ 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])
512
+ attributes[:cover_image_id] = fields[SYMBOL_TO_STRING[:cover_image_id]] || attributes[:cover_image_id]
513
+ attributes[:badges] = fields[SYMBOL_TO_STRING[:badges]] || attributes[:badges]
514
+ attributes[:card_members] = fields[SYMBOL_TO_STRING[:card_members]] || attributes[:card_members]
515
+ attributes[:source_card_id] = fields[SYMBOL_TO_STRING[:source_card_id]] || fields[:source_card_id] || attributes[:source_card_id]
516
+ attributes[:source_card_properties] = fields[SYMBOL_TO_STRING[:source_card_properties]] || fields[:source_card_properties] || attributes[:source_card_properties]
517
+ self
518
+ end
519
+
455
520
  end
456
521
  end