ruby-trello 1.3.0 → 1.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 129649aadb5faffaa3dd64361d4eeffc6f46dd8d
4
- data.tar.gz: 2ec4ff6b8555542d5006e24557c776cf28aca1fa
3
+ metadata.gz: 15e12149591973e1e515f0b8c510dc3718bddd7a
4
+ data.tar.gz: 7702c5ee26e57d7684581a4ccf465d3a99df3214
5
5
  SHA512:
6
- metadata.gz: 3b3388b34a9830bf26cb48a705b45bb0e67a8c4314108b2013a389cf71ae607aba2a7bdde48cdf8a095f126c01eacf545f15177cc39a3e3c99b59d46a2086071
7
- data.tar.gz: 7c5dc7a800406537edbbaab5f79eab36f11f73e84137562125775b8173896887d0762ecc3fb27f101e70d5ddb360e5a6ba9c35af8913a32d62f4c80bacbdfa56
6
+ metadata.gz: e689031f702357536e227cfe2d806af95f9222ce61baecc1548654d0d5255f25384f5d0adedc0205fceb164fc88e1c397bb00c7420ef02b28ac5ff521c401642
7
+ data.tar.gz: 58a9f2cf4a51385de17a23e60ba9b168c514e52d98b28eb0e8eb282e260f501ccba622c9639866420ac60c5bfcf5117a1f4659ba859ba7c03146e2b84044ca91
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # Ruby Trello API
2
2
 
3
- [![Stories in Ready](http://badge.waffle.io/jeremytregunna/ruby-trello.png)](http://waffle.io/jeremytregunna/ruby-trello)
3
+ [![Stories in Ready](http://badge.waffle.io/jeremytregunna/ruby-trello.png)](http://waffle.io/jeremytregunna/ruby-trello)
4
4
  [![Build Status](https://secure.travis-ci.org/jeremytregunna/ruby-trello.png)](http://travis-ci.org/jeremytregunna/ruby-trello) [![Dependency Status](https://gemnasium.com/jeremytregunna/ruby-trello.png)](https://gemnasium.com/jeremytregunna/ruby-trello.png)
5
+ [![Code Climate](https://codeclimate.com/github/jeremytregunna/ruby-trello/badges/gpa.svg)](https://codeclimate.com/github/jeremytregunna/ruby-trello)
5
6
 
6
7
  This library implements the [Trello](http://www.trello.com/) [API](http://trello.com/api).
7
8
 
@@ -19,23 +20,23 @@ Seriously, [check it out](http://www.trello.com/).
19
20
  Full Disclosure: This library is mostly complete, if you do find anything missing or not functioning as you expect it
20
21
  to, please [let us know](https://trello.com/card/spot-a-bug-report-it/4f092b2ee23cb6fe6d1aaabd/17).
21
22
 
22
- While this library still does function on ruby 1.8 as of version 1.0.2 with activemodel < 4.0, this notice services to
23
- illustrate that future versions may include ruby 1.9.3+ specific features.
23
+ Supports Ruby 2.0 or newer. Version 1.3.0 is the last version that supports Ruby that is older than 1.9.3.
24
24
 
25
25
  ## Configuration
26
26
 
27
27
  ####Basic authorization:
28
28
 
29
- 1. Get your API keys from [trello.com/app-key](https://trello.com/app-key).
30
- 2. Visit the URL [trello.com/1/authorize], with the following GET parameters:
31
- - `key`: the API key you got in step 1.
32
- - `response_type`: "token"
33
- - `expiration`: "never" if you don't want your token to ever expire. If you leave this blank,
34
- your generated token will expire after 30 days.
35
- - `scope`: "read,write" (optional) by default the API key will only give read access. You must set the scope to "read,write" if you would like ruby-trello to have permissions to create and delete.
36
- - The URL will look like this:
37
- `https://trello.com/1/authorize?key=YOURAPIKEY&response_type=token&expiration=never&scope=read,write`
38
- 3. You should see a page asking you to authorize your Trello application. Click "allow" and you should see a second page with a long alphanumeric string. This is your member token.
29
+ 1. Get your API public key from Trello via the irb console:
30
+
31
+ ```
32
+ $ gem install ruby-trello
33
+ $ irb -rubygems
34
+ irb> require 'trello'
35
+ irb> Trello.open_public_key_url # copy your public key
36
+ irb> Trello.open_authorization_url key: 'yourpublickey' # copy your member token
37
+ ```
38
+
39
+ 2. You can now use the public key and member token in your app code:
39
40
 
40
41
  ```ruby
41
42
  require 'trello'
data/lib/trello.rb CHANGED
@@ -1,8 +1,8 @@
1
-
2
1
  require 'oauth'
3
2
  require 'json'
4
3
  require 'logger'
5
4
  require 'active_model'
5
+ require 'addressable/uri'
6
6
 
7
7
  # Ruby wrapper around the Trello[http://trello.com] API
8
8
  #
@@ -62,6 +62,7 @@ module Trello
62
62
  autoload :TInternet, 'trello/net'
63
63
  autoload :Token, 'trello/token'
64
64
  autoload :Webhook, 'trello/webhook'
65
+ autoload :JsonUtils, 'trello/json_utils'
65
66
 
66
67
  module Authorization
67
68
  autoload :AuthPolicy, 'trello/authorization'
@@ -104,4 +105,57 @@ module Trello
104
105
 
105
106
  def self.auth_policy; client.auth_policy; end
106
107
  def self.configuration; client.configuration; end
108
+
109
+ # Url to Trello API public key page
110
+ def self.public_key_url
111
+ 'https://trello.com/app-key'
112
+ end
113
+
114
+ # Url to token for making authorized requests to the Trello API
115
+ #
116
+ # @param [String, #read] contents the contents to reverse
117
+ # @param options [Hash] Repository information to update
118
+ # @option options [String] :name Name of the application
119
+ # @option options [String] :key Application key
120
+ # @option options [String] :response_type 'token'
121
+ # @option options [String] :callback_method 'postMessage' or 'fragment'
122
+ # @option options [String] :return_url URL the token should be returned to
123
+ # @option options [String] :scope Comma-separated list of one or more of 'read', 'write', 'account'
124
+ # @option options [String] :expiration '1hour', '1day', '30days', 'never'
125
+ # @see https://developers.trello.com/authorize
126
+ def self.authorize_url(options = {})
127
+ params = options.dup
128
+ params[:key] ||= configuration.developer_public_key or
129
+ raise ArgumentError, 'Please configure your Trello public key'
130
+ params[:name] ||= 'Ruby Trello'
131
+ params[:scope] = 'read,write,account'
132
+ params[:expiration] ||= 'never'
133
+ params[:response_type] ||= 'token'
134
+ uri = Addressable::URI.parse 'https://trello.com/1/authorize'
135
+ uri.query_values = params
136
+ uri
137
+ end
138
+
139
+ # Visit the Trello API public key page
140
+ #
141
+ # @see https://trello.com/app-key
142
+ def self.open_public_key_url
143
+ open_url public_key_url
144
+ end
145
+
146
+ # Visit the Trello authorized token page
147
+ #
148
+ # @see https://developers.trello.com/authorize
149
+ def self.open_authorization_url(options = {})
150
+ open_url authorize_url(options)
151
+ end
152
+
153
+ # @private
154
+ def self.open_url(url)
155
+ require 'launchy'
156
+ Launchy.open(url.to_s)
157
+ rescue LoadError
158
+ warn 'Please install the launchy gem to open the url automatically.'
159
+ url
160
+ end
107
161
  end
data/lib/trello/action.rb CHANGED
@@ -26,8 +26,9 @@ module Trello
26
26
 
27
27
  def search(query, opts={})
28
28
  response = client.get("/search/", { query: query }.merge(opts))
29
- JSON.parse(response).except("options").inject({}) do |res, key|
30
- res.merge({ key.first => key.last.jsoned_into("Trello::#{key.first.singularize.capitalize}".constantize) })
29
+ parse_json(response).except("options").each_with_object({}) do |(key, data), result|
30
+ klass = "Trello::#{key.singularize.capitalize}".constantize
31
+ result[key] = klass.from_json(data)
31
32
  end
32
33
  end
33
34
  end
@@ -48,17 +49,17 @@ module Trello
48
49
 
49
50
  # Returns the board this action occurred on.
50
51
  def board
51
- client.get("/actions/#{id}/board").json_into(Board)
52
+ Board.from_response client.get("/actions/#{id}/board")
52
53
  end
53
54
 
54
55
  # Returns the card the action occurred on.
55
56
  def card
56
- client.get("/actions/#{id}/card").json_into(Card)
57
+ Card.from_response client.get("/actions/#{id}/card")
57
58
  end
58
59
 
59
60
  # Returns the list the action occurred on.
60
61
  def list
61
- client.get("/actions/#{id}/list").json_into(List)
62
+ List.from_response client.get("/actions/#{id}/list")
62
63
  end
63
64
 
64
65
  # Returns the member who created the action.
@@ -16,20 +16,21 @@ module Trello
16
16
  # @!attribute mime_type
17
17
  # @return [String]
18
18
  class Attachment < BasicData
19
- register_attributes :name, :id, :url, :bytes, :member_id, :date, :is_upload, :mime_type
19
+ register_attributes :name, :id, :url, :bytes, :member_id, :date, :is_upload, :mime_type, :previews
20
20
  # Update the fields of an attachment.
21
21
  #
22
22
  # Supply a hash of stringkeyed data retrieved from the Trello API representing
23
23
  # an attachment.
24
24
  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
25
+ attributes[:name] = fields['name']
26
+ attributes[:id] = fields['id']
27
+ attributes[:url] = fields['url']
28
+ attributes[:bytes] = fields['bytes'].to_i
29
29
  attributes[:member_id] = fields['idMember']
30
- attributes[:date] = Time.parse(fields['date'])
30
+ attributes[:date] = Time.parse(fields['date'])
31
31
  attributes[:is_upload] = fields['isUpload']
32
32
  attributes[:mime_type] = fields['mimeType']
33
+ attributes[:previews] = fields['previews']
33
34
  self
34
35
  end
35
36
  end
@@ -1,6 +1,3 @@
1
- require 'trello/core_ext/array'
2
- require 'trello/core_ext/hash'
3
- require 'trello/core_ext/string'
4
1
  require 'active_support/inflector'
5
2
 
6
3
  module Trello
@@ -9,6 +6,8 @@ module Trello
9
6
  include ActiveModel::Dirty
10
7
  include ActiveModel::Serializers::JSON
11
8
 
9
+ include Trello::JsonUtils
10
+
12
11
  class << self
13
12
  def path_name
14
13
  name.split("::").last.underscore
@@ -29,13 +28,13 @@ module Trello
29
28
  end
30
29
 
31
30
  def parse(response)
32
- response.json_into(self).tap do |basic_data|
31
+ from_response(response).tap do |basic_data|
33
32
  yield basic_data if block_given?
34
33
  end
35
34
  end
36
35
 
37
36
  def parse_many(response)
38
- response.json_into(self).map do |data|
37
+ from_response(response).map do |data|
39
38
  data.tap do |d|
40
39
  yield d if block_given?
41
40
  end
data/lib/trello/board.rb CHANGED
@@ -17,8 +17,8 @@ module Trello
17
17
  # @!attribute [r] prefs
18
18
  # @return [Hash] A 24-character hex string
19
19
  class Board < BasicData
20
- register_attributes :id, :name, :description, :closed, :starred, :url, :organization_id, :prefs,
21
- readonly: [ :id, :url ]
20
+ register_attributes :id, :name, :description, :closed, :starred, :url, :organization_id, :prefs, :last_activity_date,
21
+ readonly: [ :id, :url, :last_activity_date ]
22
22
  validates_presence_of :id, :name
23
23
  validates_length_of :name, in: 1..16384
24
24
  validates_length_of :description, maximum: 16384
@@ -53,7 +53,7 @@ module Trello
53
53
 
54
54
  # @return [Array<Trello::Board>] all boards for the current user
55
55
  def all
56
- client.get("/members/#{Member.find(:me).username}/boards").json_into(self)
56
+ from_response client.get("/members/#{Member.find(:me).username}/boards")
57
57
  end
58
58
  end
59
59
 
@@ -65,7 +65,7 @@ module Trello
65
65
  fields.merge!(idOrganization: organization_id) if organization_id
66
66
  fields.merge!(flat_prefs)
67
67
 
68
- client.post("/boards", fields).json_into(self)
68
+ from_response(client.post("/boards", fields))
69
69
  end
70
70
 
71
71
  def update!
@@ -83,7 +83,7 @@ module Trello
83
83
  }
84
84
  fields.merge!(flat_prefs)
85
85
 
86
- client.put("/boards/#{self.id}/", fields).json_into(self)
86
+ from_response client.put("/boards/#{self.id}/", fields)
87
87
  end
88
88
 
89
89
  def update_fields(fields)
@@ -95,6 +95,7 @@ module Trello
95
95
  attributes[:url] = fields['url'] if fields['url']
96
96
  attributes[:organization_id] = fields['idOrganization'] if fields['idOrganization']
97
97
  attributes[:prefs] = fields['prefs'] || {}
98
+ attributes[:last_activity_date] = Time.iso8601(fields['dateLastActivity']) rescue nil
98
99
  self
99
100
  end
100
101
 
@@ -107,7 +108,7 @@ module Trello
107
108
  def starred?
108
109
  attributes[:starred]
109
110
  end
110
-
111
+
111
112
  # @return [Boolean]
112
113
  def has_lists?
113
114
  lists.size > 0
@@ -116,7 +117,7 @@ module Trello
116
117
  # Find a card on this Board with the given ID.
117
118
  # @return [Trello::Card]
118
119
  def find_card(card_id)
119
- client.get("/boards/#{self.id}/cards/#{card_id}").json_into(Card)
120
+ Card.from_response client.get("/boards/#{self.id}/cards/#{card_id}")
120
121
  end
121
122
 
122
123
  # Add a member to this Board.
@@ -154,10 +155,15 @@ module Trello
154
155
  # Returns a reference to the organization this board belongs to.
155
156
  one :organization, path: :organizations, using: :organization_id
156
157
 
157
- many :labels
158
+ def labels(params={})
159
+ # Set the limit to as high as possible given there is no pagination in this API.
160
+ params[:limit] = 1000 unless params[:limit]
161
+ labels = Label.from_response client.get("/boards/#{id}/labels", params)
162
+ MultiAssociation.new(self, labels).proxy
163
+ end
158
164
 
159
165
  def label_names
160
- label_names = client.get("/boards/#{id}/labelnames").json_into(LabelName)
166
+ label_names = LabelName.from_response client.get("/boards/#{id}/labelnames")
161
167
  MultiAssociation.new(self, label_names).proxy
162
168
  end
163
169
 
data/lib/trello/card.rb CHANGED
@@ -78,7 +78,8 @@ module Trello
78
78
 
79
79
  # Create a new card and save it on Trello.
80
80
  #
81
- # @param [Hash] options # @option options [String] :name The name of the new card.
81
+ # @param [Hash] options
82
+ # @option options [String] :name The name of the new card.
82
83
  # @option options [String] :list_id ID of the list that the card should
83
84
  # be added to.
84
85
  # @option options [String] :desc A string with a
@@ -157,6 +158,7 @@ module Trello
157
158
  attributes[:cover_image_id] = fields[SYMBOL_TO_STRING[:cover_image_id]]
158
159
  attributes[:badges] = fields[SYMBOL_TO_STRING[:badges]]
159
160
  attributes[:card_members] = fields[SYMBOL_TO_STRING[:card_members]]
161
+
160
162
  self
161
163
  end
162
164
 
@@ -173,12 +175,12 @@ module Trello
173
175
  many :checklists, filter: :all
174
176
 
175
177
  def check_item_states
176
- states = client.get("/cards/#{self.id}/checkItemStates").json_into(CheckItemState)
178
+ states = CheckItemState.from_response client.get("/cards/#{self.id}/checkItemStates")
177
179
  MultiAssociation.new(self, states).proxy
178
180
  end
179
181
 
180
182
  many :labels
181
-
183
+
182
184
  # Returns a reference to the list this card is currently in.
183
185
  one :list, path: :lists, using: :list_id
184
186
 
@@ -187,7 +189,7 @@ module Trello
187
189
  # @return [Array<Trello::Member>]
188
190
  def members
189
191
  members = member_ids.map do |member_id|
190
- client.get("/members/#{member_id}").json_into(Member)
192
+ Member.from_response client.get("/members/#{member_id}")
191
193
  end
192
194
  MultiAssociation.new(self, members).proxy
193
195
  end
@@ -202,7 +204,7 @@ module Trello
202
204
  # If we have an id, just update our fields.
203
205
  return update! if id
204
206
 
205
- client.post("/cards", {
207
+ from_response client.post("/cards", {
206
208
  name: name,
207
209
  desc: desc,
208
210
  idList: list_id,
@@ -210,7 +212,7 @@ module Trello
210
212
  labels: card_labels,
211
213
  pos: pos,
212
214
  due: due
213
- }).json_into(self)
215
+ })
214
216
  end
215
217
 
216
218
  # Update an existing record.
@@ -232,7 +234,6 @@ module Trello
232
234
  client.put("/cards/#{id}", payload)
233
235
  end
234
236
 
235
-
236
237
  # Delete this card
237
238
  #
238
239
  # @return [String] the JSON response from the Trello API
@@ -315,7 +316,7 @@ module Trello
315
316
  def remove_member(member)
316
317
  client.delete("/cards/#{id}/members/#{member.id}")
317
318
  end
318
-
319
+
319
320
  # Add a label
320
321
  def add_label(label)
321
322
  unless label.valid?
@@ -327,11 +328,11 @@ module Trello
327
328
 
328
329
  # Remove a label
329
330
  def remove_label(label)
330
- unless label.valid?
331
- errors.add(:label, "is not valid.")
332
- return Trello.logger.warn "Label is not valid." unless label.valid?
333
- end
334
- client.delete("/cards/#{id}/idLabels/#{label.id}")
331
+ unless label.valid?
332
+ errors.add(:label, "is not valid.")
333
+ return Trello.logger.warn "Label is not valid." unless label.valid?
334
+ end
335
+ client.delete("/cards/#{id}/idLabels/#{label.id}")
335
336
  end
336
337
 
337
338
  # Add an attachment to this card
@@ -351,7 +352,7 @@ module Trello
351
352
 
352
353
  # Retrieve a list of attachments
353
354
  def attachments
354
- attachments = client.get("/cards/#{id}/attachments").json_into(Attachment)
355
+ attachments = Attachment.from_response client.get("/cards/#{id}/attachments")
355
356
  MultiAssociation.new(self, attachments).proxy
356
357
  end
357
358
 
@@ -22,8 +22,8 @@ module Trello
22
22
  # @!attribute [r] member_ids
23
23
  # @return [Array<String>] An array of 24-character hex strings
24
24
  class Checklist < BasicData
25
- register_attributes :id, :name, :description, :closed, :position, :url, :check_items, :board_id, :list_id, :member_ids,
26
- readonly: [:id, :description, :closed, :url, :check_items, :board_id, :list_id, :member_ids]
25
+ register_attributes :id, :name, :description, :closed, :position, :url, :check_items, :board_id, :list_id, :card_id, :member_ids,
26
+ readonly: [:id, :description, :closed, :url, :check_items, :board_id, :list_id, :card_id, :member_ids]
27
27
  validates_presence_of :id, :board_id, :list_id
28
28
  validates_length_of :name, in: 1..16384
29
29
 
@@ -36,7 +36,7 @@ module Trello
36
36
  def create(options)
37
37
  client.create(:checklist,
38
38
  'name' => options[:name],
39
- 'idBoard' => options[:board_id])
39
+ 'idCard' => options[:card_id])
40
40
  end
41
41
  end
42
42
 
@@ -53,6 +53,7 @@ module Trello
53
53
  attributes[:check_items] = fields['checkItems']
54
54
  attributes[:position] = fields['pos']
55
55
  attributes[:board_id] = fields['idBoard']
56
+ attributes[:card_id] = fields['idCard']
56
57
  attributes[:list_id] = fields['idList']
57
58
  attributes[:member_ids] = fields['idMembers']
58
59
  self
@@ -67,14 +68,14 @@ module Trello
67
68
  def save
68
69
  return update! if id
69
70
 
70
- client.post("/checklists", {
71
- name: name,
72
- idBoard: board_id
73
- }).json_into(self)
71
+ from_response(client.post("/checklists", {
72
+ name: name,
73
+ idCard: card_id
74
+ }))
74
75
  end
75
76
 
76
77
  def update!
77
- client.put("/checklists/#{id}", {name: name, pos: position}).json_into(self)
78
+ from_response(client.put("/checklists/#{id}", {name: name, pos: position}))
78
79
  end
79
80
 
80
81
  # Return a list of items on the checklist.
@@ -87,6 +88,9 @@ module Trello
87
88
  # Return a reference to the board the checklist is on.
88
89
  one :board, path: :checklists, using: :board_id
89
90
 
91
+ # Return a reference to the card the checklist is on.
92
+ one :card, path: :checklists, using: :card_id
93
+
90
94
  # Return a reference to the list the checklist is on.
91
95
  one :list, path: :lists, using: :list_id
92
96
 
@@ -1,5 +1,6 @@
1
+ warn "Use of trello/core_ext/array is deprecated. Use Trello::JsonUtils instead"
1
2
  class Array
2
3
  def jsoned_into(obj)
3
- self.map { |element| element.jsoned_into(obj) }
4
+ obj.from_json(self)
4
5
  end
5
- end
6
+ end
@@ -1,6 +1,6 @@
1
+ warn "Use of trello/core_ext/hash is deprecated. Use Trello::JsonUtils instead"
1
2
  class Hash
2
3
  def jsoned_into(obj)
3
- action = obj.kind_of?(Class) ? :new : :update_fields
4
- obj.send(action, self)
4
+ obj.from_json(self)
5
5
  end
6
- end
6
+ end
@@ -1,31 +1,6 @@
1
+ warn "Use of trello/core_ext/string is deprecated. Use Trello::JsonUtils instead"
1
2
  class String
2
- # Decodes some JSON text in the receiver, and marshals it into a class specified
3
- # in _obj_. If _obj_ is not a class, then we marshall the data into that instance
4
- # via _update_fields_.
5
- #
6
- # For instance:
7
- #
8
- # class Stuff
9
- # attr_reader :a, :b
10
- # def initialize(values)
11
- # @a = values['a']
12
- # @b = values['b']
13
- # end
14
- # end
15
- # thing = '{"a":42,"b":"foo"}'.json_into(Stuff)
16
- # thing.a == 42
17
- # thing.b == "foo"
18
3
  def json_into(obj, encoding = 'UTF-8')
19
- data = JSON.parse(self.force_encoding(encoding))
20
- data.jsoned_into(obj)
21
- rescue JSON::ParserError => json_error
22
- if json_error.message =~ /model not found/
23
- Trello.logger.error "Could not find that record."
24
- raise Trello::Error, "Request could not be found."
25
- elsif json_error.message =~ /A JSON text must at least contain two octets/
26
- else
27
- Trello.logger.error "Unknown error."
28
- raise
29
- end
4
+ obj.from_response(self, encoding)
30
5
  end
31
- end
6
+ end
@@ -2,7 +2,7 @@ module Trello
2
2
  module HasActions
3
3
  # Returns a list of the actions associated with this object.
4
4
  def actions(options = {})
5
- actions = client.get("#{request_prefix}/actions", { filter: :all }.merge(options)).json_into(Action)
5
+ actions = Action.from_response client.get("#{request_prefix}/actions", { filter: :all }.merge(options))
6
6
  MultiAssociation.new(self, actions).proxy
7
7
  end
8
8
  end
@@ -0,0 +1,64 @@
1
+ module Trello
2
+ module JsonUtils
3
+ def self.included(base)
4
+ base.send :include, InstanceMethods
5
+ base.send :extend, ClassMethods
6
+ end
7
+
8
+ module InstanceMethods
9
+ def from_response(*args)
10
+ update_fields parse_json(*args)
11
+ end
12
+
13
+ def parse_json(*args)
14
+ self.class.parse_json(*args)
15
+ end
16
+ end
17
+
18
+ module ClassMethods
19
+ # Public - Decodes some JSON text in the receiver, and marshals it into a class specified
20
+ # in _obj_.
21
+ #
22
+ # For instance:
23
+ #
24
+ # class Stuff
25
+ # attr_reader :a, :b
26
+ # def initialize(values)
27
+ # @a = values['a']
28
+ # @b = values['b']
29
+ # end
30
+ # end
31
+ # thing = Stuff.from_response '{"a":42,"b":"foo"}'
32
+ # thing.a == 42
33
+ # thing.b == "foo"
34
+ #
35
+ def from_response(data, encoding = 'UTF-8')
36
+ from_json(parse_json(data, encoding))
37
+ end
38
+
39
+ def from_json(json)
40
+ case json
41
+ when Array
42
+ json.map { |element| from_json(element) }
43
+ when Hash
44
+ self.new(json)
45
+ else
46
+ json
47
+ end
48
+ end
49
+
50
+ def parse_json(string, encoding = 'UTF-8')
51
+ JSON.parse(string.force_encoding(encoding))
52
+ rescue JSON::ParserError => json_error
53
+ if json_error.message =~ /model not found/
54
+ Trello.logger.error "Could not find that record."
55
+ raise Trello::Error, "Request could not be found."
56
+ elsif json_error.message =~ /A JSON text must at least contain two octets/
57
+ else
58
+ Trello.logger.error "Unknown error."
59
+ raise
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
data/lib/trello/label.rb CHANGED
@@ -21,6 +21,8 @@ module Trello
21
21
  }
22
22
 
23
23
  class << self
24
+ VALID_LABEL_COLOURS = %w{green yellow orange red purple blue sky lime pink black} << ''
25
+
24
26
  # Find a specific card by its id.
25
27
  def find(id, params = {})
26
28
  client.find(:label, id, params)
@@ -37,7 +39,7 @@ module Trello
37
39
 
38
40
  # Label colours
39
41
  def label_colours
40
- %w{green yellow orange red purple blue sky lime pink black}
42
+ VALID_LABEL_COLOURS
41
43
  end
42
44
  end
43
45
 
@@ -78,11 +80,11 @@ module Trello
78
80
  # If we have an id, just update our fields.
79
81
  return update! if id
80
82
 
81
- client.post("/labels", {
83
+ from_response client.post("/labels", {
82
84
  name: name,
83
85
  color: color,
84
86
  idBoard: board_id,
85
- }).json_into(self)
87
+ })
86
88
  end
87
89
 
88
90
  # Update an existing record.
data/lib/trello/list.rb CHANGED
@@ -51,12 +51,12 @@ module Trello
51
51
  def save
52
52
  return update! if id
53
53
 
54
- client.post("/lists", {
54
+ from_response client.post("/lists", {
55
55
  name: name,
56
56
  closed: closed || false,
57
57
  idBoard: board_id,
58
58
  pos: pos
59
- }).json_into(self)
59
+ })
60
60
  end
61
61
 
62
62
  def update!
data/lib/trello/member.rb CHANGED
@@ -98,10 +98,10 @@ module Trello
98
98
  end
99
99
 
100
100
  def update!
101
- client.put(request_prefix, {
101
+ from_response client.put(request_prefix, {
102
102
  fullName: full_name,
103
103
  bio: bio
104
- }).json_into(self)
104
+ })
105
105
  end
106
106
 
107
107
  # :nodoc:
@@ -39,23 +39,23 @@ module Trello
39
39
  one :member_creator, path: :members, via: Member, using: :member_creator_id
40
40
 
41
41
  def board
42
- client.get("/notifications/#{id}/board").json_into(Board)
42
+ Board.from_response client.get("/notifications/#{id}/board")
43
43
  end
44
44
 
45
45
  def list
46
- client.get("/notifications/#{id}/list").json_into(List)
46
+ List.from_response client.get("/notifications/#{id}/list")
47
47
  end
48
48
 
49
49
  def card
50
- client.get("/notifications/#{id}/card").json_into(Card)
50
+ Card.from_response client.get("/notifications/#{id}/card")
51
51
  end
52
52
 
53
53
  def member
54
- client.get("/notifications/#{id}/member").json_into(Member)
54
+ Member.from_response client.get("/notifications/#{id}/member")
55
55
  end
56
56
 
57
57
  def organization
58
- client.get("/notifications/#{id}/organization").json_into(Organization)
58
+ Organization.from_response client.get("/notifications/#{id}/organization")
59
59
  end
60
60
  end
61
61
  end
@@ -14,8 +14,10 @@ module Trello
14
14
  class Organization < BasicData
15
15
  register_attributes :id, :name, :display_name, :description, :url, :invited,
16
16
  :website, :logo_hash, :billable_member_count, :active_billable_member_count,
17
+ :memberships,
17
18
  readonly: [ :id, :name, :display_name, :description, :url, :invited,
18
- :website, :logo_hash, :billable_member_count, :active_billable_member_count ]
19
+ :website, :logo_hash, :billable_member_count, :active_billable_member_count,
20
+ :memberships ]
19
21
  validates_presence_of :id, :name
20
22
 
21
23
  include HasActions
@@ -42,18 +44,19 @@ module Trello
42
44
  attributes[:logo_hash] = fields['logoHash']
43
45
  attributes[:billable_member_count] = fields['billableMemberCount']
44
46
  attributes[:active_billable_member_count] = fields['activeBillableMemberCount']
47
+ attributes[:memberships] = fields['memberships']
45
48
  self
46
49
  end
47
50
 
48
51
  # Returns a list of boards under this organization.
49
52
  def boards
50
- boards = client.get("/organizations/#{id}/boards/all").json_into(Board)
53
+ boards = Board.from_response client.get("/organizations/#{id}/boards/all")
51
54
  MultiAssociation.new(self, boards).proxy
52
55
  end
53
56
 
54
57
  # Returns an array of members associated with the organization.
55
58
  def members(params = {})
56
- members = client.get("/organizations/#{id}/members/all", params).json_into(Member)
59
+ members = Member.from_response client.get("/organizations/#{id}/members/all", params)
57
60
  MultiAssociation.new(self, members).proxy
58
61
  end
59
62
 
@@ -65,11 +65,11 @@ module Trello
65
65
  # If we have an id, just update our fields.
66
66
  return update! if id
67
67
 
68
- client.post("/webhooks", {
68
+ from_response client.post("/webhooks", {
69
69
  description: description,
70
70
  idModel: id_model,
71
71
  callbackURL: callback_url
72
- }).json_into(self)
72
+ })
73
73
  end
74
74
 
75
75
  # Update the webhook.
data/spec/board_spec.rb CHANGED
@@ -74,6 +74,14 @@ module Trello
74
74
  it "gets its url" do
75
75
  expect(board.url).to_not be_nil
76
76
  end
77
+
78
+ it "gets its last_activity_date" do
79
+ expect(board.last_activity_date).to_not be_nil
80
+ end
81
+
82
+ it "gets a Time object for last_activity_date" do
83
+ expect(board.last_activity_date).to be_a(Time)
84
+ end
77
85
  end
78
86
 
79
87
  context "actions" do
@@ -116,11 +124,11 @@ module Trello
116
124
  end
117
125
 
118
126
  it "gets the specific labels for the board" do
119
- client.stub(:get).with("/boards/abcdef123456789123456789/labels", {}).
127
+ allow(client).to receive(:get).with("/boards/abcdef123456789123456789/labels", {:limit => 1000}).
120
128
  and_return label_payload
121
129
  labels = board.labels
122
- labels.count.should eq(4)
123
130
 
131
+ expect(labels.count).to eq(4)
124
132
  expect(labels[2].color).to eq('red')
125
133
  expect(labels[2].id).to eq('abcdef123456789123456789')
126
134
  expect(labels[2].board_id).to eq('abcdef123456789123456789')
@@ -132,6 +140,12 @@ module Trello
132
140
  expect(labels[3].name).to eq('on hold')
133
141
  expect(labels[3].uses).to eq(6)
134
142
  end
143
+
144
+ it "passes the label limit" do
145
+ allow(client).to receive(:get).with("/boards/abcdef123456789123456789/labels", {:limit => 50}).
146
+ and_return label_payload
147
+ labels = board.labels(:limit => 50)
148
+ end
135
149
  end
136
150
 
137
151
  context "find_card" do
@@ -278,7 +292,7 @@ module Trello
278
292
  expect(client).to_not receive(:put)
279
293
  expect {
280
294
  Board.new.save
281
- }.to raise_error
295
+ }.to raise_error(Trello::ConfigurationError)
282
296
  end
283
297
 
284
298
  it "puts all fields except id" do
data/spec/card_spec.rb CHANGED
@@ -99,7 +99,7 @@ module Trello
99
99
  desc: expected_new_desc,
100
100
  }
101
101
 
102
- client.should_receive(:put).once.with("/cards/abcdef123456789123456789", payload)
102
+ expect(client).to receive(:put).once.with("/cards/abcdef123456789123456789", payload)
103
103
 
104
104
  card.desc = expected_new_desc
105
105
  card.save
@@ -367,7 +367,8 @@ module Trello
367
367
  .and_return "not important"
368
368
  end
369
369
  it "can retrieve labels" do
370
- client.stub(:get).with("/cards/abcdef123456789123456789/labels", {}).
370
+ allow(client).to receive(:get).
371
+ with("/cards/abcdef123456789123456789/labels", {}).
371
372
  and_return label_payload
372
373
  labels = card.labels
373
374
  expect(labels.size).to eq(4)
@@ -384,19 +385,19 @@ module Trello
384
385
  end
385
386
 
386
387
  it "can remove a label" do
387
- client.should_receive(:delete).once.with("/cards/abcdef123456789123456789/idLabels/abcdef123456789123456789")
388
+ expect(client).to receive(:delete).once.with("/cards/abcdef123456789123456789/idLabels/abcdef123456789123456789")
388
389
  label = Label.new(label_details.first)
389
390
  card.remove_label(label)
390
391
  end
391
392
 
392
393
  it "can add a label" do
393
- client.should_receive(:post).once.with("/cards/abcdef123456789123456789/idLabels", {:value => "abcdef123456789123456789"})
394
+ expect(client).to receive(:post).once.with("/cards/abcdef123456789123456789/idLabels", {:value => "abcdef123456789123456789"})
394
395
  label = Label.new(label_details.first)
395
396
  card.add_label label
396
397
  end
397
398
 
398
399
  it "throws an error when trying to add a invalid label" do
399
- client.stub(:post).with("/cards/abcdef123456789123456789/idLabels", { value: 'abcdef123456789123456789' }).
400
+ allow(client).to receive(:post).with("/cards/abcdef123456789123456789/idLabels", { value: 'abcdef123456789123456789' }).
400
401
  and_return "not important"
401
402
  label = Label.new(label_details.first)
402
403
  label.name = nil
@@ -405,7 +406,7 @@ module Trello
405
406
  end
406
407
 
407
408
  it "throws an error when trying to remove a invalid label" do
408
- client.stub(:delete).with("/cards/abcdef123456789123456789/idLabels/abcdef123456789123456789").
409
+ allow(client).to receive(:delete).with("/cards/abcdef123456789123456789/idLabels/abcdef123456789123456789").
409
410
  and_return "not important"
410
411
  label = Label.new(label_details.first)
411
412
  label.name = nil
@@ -455,6 +456,10 @@ module Trello
455
456
  expect(first_attachment.date).to eq Time.parse(attachments_details[0]["date"])
456
457
  expect(first_attachment.is_upload).to eq attachments_details[0]["isUpload"]
457
458
  expect(first_attachment.mime_type).to eq attachments_details[0]["mimeType"]
459
+ expect(first_attachment.previews).to eq attachments_details[0]["previews"]
460
+
461
+ second_attachment = card.attachments[1]
462
+ expect(second_attachment.previews).to eq nil
458
463
  end
459
464
 
460
465
  it "can remove an attachment" do
@@ -37,18 +37,21 @@ module Trello
37
37
  payload = {
38
38
  name: 'Test Checklist',
39
39
  desc: '',
40
+ card_id: cards_details.first['id']
40
41
  }
41
42
 
42
- result = JSON.generate(checklists_details.first.merge(payload.merge(idBoard: boards_details.first['id'])))
43
+ attributes = checklists_details.first.merge(payload).except("idBoard")
44
+ result = JSON.generate(attributes)
45
+
43
46
 
44
- expected_payload = {name: "Test Checklist", idBoard: "abcdef123456789123456789"}
47
+ expected_payload = {name: "Test Checklist", idCard: cards_details.first['id']}
45
48
 
46
49
  expect(client)
47
50
  .to receive(:post)
48
51
  .with("/checklists", expected_payload)
49
52
  .and_return result
50
53
 
51
- checklist = Checklist.create(checklists_details.first.merge(payload.merge(board_id: boards_details.first['id'])))
54
+ checklist = Checklist.create(attributes)
52
55
 
53
56
  expect(checklist).to be_a Checklist
54
57
  end
@@ -166,7 +169,7 @@ module Trello
166
169
 
167
170
  context "making a copy" do
168
171
  let(:client) { Trello.client }
169
- let(:copy_options) { { :name => checklist.name, :idBoard => checklist.board_id } }
172
+ let(:copy_options) { { :name => checklist.name, :idCard => checklist.card_id } }
170
173
  let(:copied_checklist) { checklist.copy }
171
174
 
172
175
  before(:each) do
@@ -42,7 +42,7 @@ describe "how to use boards", broken: true do
42
42
  end
43
43
 
44
44
  it "can list all boards" do
45
- Client.get("/members/me/boards/").json_into(Board).should be_an Array
45
+ Board.from_response(Client.get("/members/me/boards/")).should be_an Array
46
46
  end
47
47
  end
48
48
  end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ module Trello
4
+ RSpec.describe JsonUtils do
5
+ include Helpers
6
+
7
+ describe ".from_json" do
8
+ describe "Trello::Card" do
9
+ it "should convert an array of parsed json into cards" do
10
+ cards = Trello::Card.from_json(cards_details)
11
+
12
+ expect(cards.size).to eq(cards_details.size)
13
+
14
+ card = cards.first
15
+ expect(card).to be_a(Trello::Card)
16
+ expect(card.name).to eq(cards_details.first['name'])
17
+ end
18
+
19
+ it "should convert a single parsed json into card" do
20
+ card_details = cards_details.first
21
+ card = Trello::Card.from_json(card_details)
22
+
23
+ expect(card).to be_a(Trello::Card)
24
+ expect(card.name).to eq(cards_details.first['name'])
25
+ end
26
+ end
27
+ end
28
+
29
+ describe ".from_response" do
30
+ def example_class
31
+ @example_class ||= Class.new do
32
+ include Trello::JsonUtils
33
+
34
+ attr_accessor :name, :description
35
+
36
+ def initialize(options = {})
37
+ @name = options['name']
38
+ @description = options['description']
39
+ end
40
+ end
41
+ end
42
+
43
+ it 'converts json into an instance of a class' do
44
+ expect(example_class.from_response('{}')).to be_a example_class
45
+ end
46
+
47
+ it 'supplies the parsed json to the class ctor as a hash' do
48
+ json_text = '{"name" : "Jazz Kang", "description": "Plonker"}'
49
+
50
+ result = example_class.from_response json_text
51
+ expect(result.name).to eq("Jazz Kang")
52
+ expect(result.description).to eq("Plonker")
53
+ end
54
+
55
+ it 'can also handle arrays of instances of a class' do
56
+ json_text = <<-JSON
57
+ [
58
+ {"name" : "Jazz Kang", "description": "Plonker"},
59
+ {"name" : "Phil Murphy", "description": "Shoreditch hipster"}
60
+ ]
61
+ JSON
62
+
63
+ result = example_class.from_response json_text
64
+
65
+ expect(result).to be_an Array
66
+ expect(result.size).to eq 2
67
+ result.each do |parsed|
68
+ expect(parsed).to be_a example_class
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
data/spec/spec_helper.rb CHANGED
@@ -35,6 +35,13 @@ RSpec.configure do |rspec|
35
35
  rspec.before :each do
36
36
  Trello.reset!
37
37
  end
38
+
39
+ rspec.around(:each, :silence_warnings) do |example|
40
+ verbose = $VERBOSE
41
+ $VERBOSE = nil
42
+ example.run
43
+ $VERBOSE = verbose
44
+ end
38
45
  end
39
46
 
40
47
  module Helpers
@@ -57,13 +64,14 @@ module Helpers
57
64
 
58
65
  def boards_details
59
66
  [{
60
- 'id' => 'abcdef123456789123456789',
61
- 'name' => 'Test',
62
- 'desc' => 'This is a test board',
63
- 'closed' => false,
64
- 'starred' => false,
65
- 'idOrganization' => 'abcdef123456789123456789',
66
- 'url' => 'https://trello.com/board/test/abcdef123456789123456789'
67
+ 'id' => 'abcdef123456789123456789',
68
+ 'name' => 'Test',
69
+ 'desc' => 'This is a test board',
70
+ 'closed' => false,
71
+ 'starred' => false,
72
+ 'idOrganization' => 'abcdef123456789123456789',
73
+ 'url' => 'https://trello.com/board/test/abcdef123456789123456789',
74
+ 'dateLastActivity' => '2012-12-08T18:40:24.314Z'
67
75
  }]
68
76
  end
69
77
 
@@ -155,6 +163,7 @@ module Helpers
155
163
  'idMember' => 'abcdef123456789123456781',
156
164
  'isUpload' => false,
157
165
  'date' => '2013-02-28T17:12:28.497Z',
166
+ 'previews' => 'previews'
158
167
  },
159
168
  {
160
169
  'id' => 'abcdef123456789123456781',
data/spec/string_spec.rb CHANGED
@@ -6,6 +6,8 @@ describe String, '#json_into' do
6
6
 
7
7
  def example_class
8
8
  @example_class ||= Class.new do
9
+ include Trello::JsonUtils
10
+
9
11
  attr_accessor :name, :description
10
12
 
11
13
  def initialize(options = {})
data/spec/trello_spec.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'spec_helper'
2
+ require 'launchy'
2
3
 
3
4
  include Trello
4
5
  include Trello::Authorization
@@ -75,6 +76,53 @@ describe Trello do
75
76
  it { expect(Trello.auth_policy).to be_a(AuthPolicy) }
76
77
  it { expect { Trello.client.get(:member) }.to raise_error(Trello::ConfigurationError) }
77
78
  end
79
+ end
80
+
81
+ context 'client authorization helpers' do
82
+ before do
83
+ allow(Launchy).to receive(:open)
84
+ end
85
+
86
+ it { expect(Trello.public_key_url).to eq('https://trello.com/app-key') }
87
+ it { expect(Trello.authorize_url(key: 'foo')).to match(%r{^https://trello.com/1/authorize}) }
88
+
89
+ describe '.open_public_key_url' do
90
+ it 'launches app key endpoint' do
91
+ expect(Launchy).to receive(:open).with('https://trello.com/app-key')
78
92
 
93
+ Trello.open_public_key_url
94
+ end
95
+
96
+ it 'rescues LoadError', :silence_warnings do
97
+ allow(Launchy).to receive(:open).and_raise(LoadError)
98
+
99
+ expect { Trello.open_public_key_url }.to_not raise_error(LoadError)
100
+ end
101
+ end
102
+
103
+ describe '.open_authorization_url' do
104
+ it 'launches authorize endpoint with configured public key' do
105
+ app_key = 'abcd1234'
106
+ allow(Trello.configuration).to receive(:developer_public_key).and_return(app_key)
107
+ authorize_url = "https://trello.com/1/authorize?expiration=never&key=#{app_key}&name=Ruby%20Trello&response_type=token&scope=read%2Cwrite%2Caccount"
108
+ expect(Launchy).to receive(:open).with(authorize_url)
109
+
110
+ Trello.open_authorization_url
111
+ end
112
+
113
+ it 'launches authorize endpoint with given public key' do
114
+ app_key = 'wxyz6789'
115
+ authorize_url = "https://trello.com/1/authorize?expiration=never&key=#{app_key}&name=Ruby%20Trello&response_type=token&scope=read%2Cwrite%2Caccount"
116
+ expect(Launchy).to receive(:open).with(authorize_url)
117
+
118
+ Trello.open_authorization_url(key: 'wxyz6789')
119
+ end
120
+
121
+ it 'raises an error if key not configured' do
122
+ expect(Launchy).to_not receive(:open)
123
+
124
+ expect { Trello.open_authorization_url }.to raise_error(ArgumentError)
125
+ end
126
+ end
79
127
  end
80
128
  end
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.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Tregunna
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-30 00:00:00.000000000 Z
11
+ date: 2015-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -105,6 +105,7 @@ files:
105
105
  - lib/trello/has_actions.rb
106
106
  - lib/trello/item.rb
107
107
  - lib/trello/item_state.rb
108
+ - lib/trello/json_utils.rb
108
109
  - lib/trello/label.rb
109
110
  - lib/trello/label_name.rb
110
111
  - lib/trello/list.rb
@@ -131,6 +132,7 @@ files:
131
132
  - spec/integration/how_to_use_boards_spec.rb
132
133
  - spec/integration/integration_test.rb
133
134
  - spec/item_spec.rb
135
+ - spec/json_utils_spec.rb
134
136
  - spec/label_spec.rb
135
137
  - spec/list_spec.rb
136
138
  - spec/member_spec.rb
@@ -155,12 +157,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
155
157
  requirements:
156
158
  - - '>='
157
159
  - !ruby/object:Gem::Version
158
- version: '0'
160
+ version: 2.0.0
159
161
  required_rubygems_version: !ruby/object:Gem::Requirement
160
162
  requirements:
161
163
  - - '>='
162
164
  - !ruby/object:Gem::Version
163
- version: 1.3.6
165
+ version: '0'
164
166
  requirements: []
165
167
  rubyforge_project: ruby-trello
166
168
  rubygems_version: 2.0.14
@@ -182,6 +184,7 @@ test_files:
182
184
  - spec/integration/how_to_use_boards_spec.rb
183
185
  - spec/integration/integration_test.rb
184
186
  - spec/item_spec.rb
187
+ - spec/json_utils_spec.rb
185
188
  - spec/label_spec.rb
186
189
  - spec/list_spec.rb
187
190
  - spec/member_spec.rb