ruby-trello 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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