ruby-trello 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +6 -1
- data/lib/trello.rb +23 -3
- data/lib/trello/action.rb +1 -0
- data/lib/trello/authorization.rb +77 -0
- data/lib/trello/basic_data.rb +1 -2
- data/lib/trello/board.rb +33 -16
- data/lib/trello/card.rb +24 -3
- data/lib/trello/checklist.rb +1 -0
- data/lib/trello/client.rb +33 -48
- data/lib/trello/item.rb +1 -0
- data/lib/trello/item_state.rb +1 -0
- data/lib/trello/list.rb +19 -0
- data/lib/trello/member.rb +1 -0
- data/lib/trello/net.rb +35 -0
- data/lib/trello/notification.rb +1 -0
- data/lib/trello/organization.rb +1 -0
- data/lib/trello/string.rb +1 -1
- data/spec/action_spec.rb +39 -10
- data/spec/basic_auth_policy_spec.rb +56 -0
- data/spec/board_spec.rb +156 -13
- data/spec/card_spec.rb +58 -23
- data/spec/client_spec.rb +123 -22
- data/spec/integration/how_to_authorize_spec.rb +53 -0
- data/spec/integration/how_to_use_boards_spec.rb +48 -0
- data/spec/integration/integration_test.rb +40 -0
- data/spec/list_spec.rb +15 -9
- data/spec/member_spec.rb +14 -11
- data/spec/oauth_policy_spec.rb +83 -0
- data/spec/spec_helper.rb +27 -11
- data/spec/string_spec.rb +50 -0
- metadata +23 -9
data/README.md
CHANGED
@@ -15,6 +15,11 @@ Seriously, [check it out](http://www.trello.com/).
|
|
15
15
|
|
16
16
|
Full Disclosure: This library is not complete, but it does function enough to be useful.
|
17
17
|
|
18
|
+
## Special thanks
|
19
|
+
|
20
|
+
A special thanks goes out to [Ben Biddington](https://github.com/ben-biddington) who has contributed a significant amount
|
21
|
+
of refactoring and functionality to be deserving of a beer and this special thanks.
|
22
|
+
|
18
23
|
## Contributing
|
19
24
|
|
20
25
|
Pick up an editor, fix a test! (If you want to, of course.) Send a pull
|
@@ -24,4 +29,4 @@ request, and I'll look at it. I only ask a few things:
|
|
24
29
|
2. Adding or refactoring existing features, ensure there are tests.
|
25
30
|
|
26
31
|
Also, don't be afraid to send a pull request if your changes aren't done. Just
|
27
|
-
let me know.
|
32
|
+
let me know.
|
data/lib/trello.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'oauth'
|
2
2
|
require 'json'
|
3
|
+
require 'logger'
|
3
4
|
|
4
5
|
# Ruby wrapper around the Trello[http://trello.com] API
|
5
6
|
#
|
@@ -28,6 +29,7 @@ module Trello
|
|
28
29
|
autoload :BasicData, 'trello/basic_data'
|
29
30
|
autoload :Board, 'trello/board'
|
30
31
|
autoload :Card, 'trello/card'
|
32
|
+
autoload :Checklist, 'trello/checklist'
|
31
33
|
autoload :Client, 'trello/client'
|
32
34
|
autoload :Item, 'trello/item'
|
33
35
|
autoload :ItemState, 'trello/item_state'
|
@@ -35,10 +37,28 @@ module Trello
|
|
35
37
|
autoload :Member, 'trello/member'
|
36
38
|
autoload :Notification, 'trello/notification'
|
37
39
|
autoload :Organization, 'trello/organization'
|
40
|
+
autoload :Request, 'trello/net'
|
41
|
+
autoload :TInternet, 'trello/net'
|
38
42
|
|
39
43
|
# Version of the Trello API that we use by default.
|
40
44
|
API_VERSION = 1
|
41
45
|
|
42
|
-
# Raise this when we
|
43
|
-
class
|
44
|
-
|
46
|
+
# Raise this when we hit a Trello error.
|
47
|
+
class Error < StandardError; end
|
48
|
+
|
49
|
+
def self.logger
|
50
|
+
@@logger || Logger.new(STDOUT)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.logger=(logger)
|
54
|
+
@@logger = logger
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module Trello
|
59
|
+
module Authorization
|
60
|
+
autoload :AuthPolicy, 'trello/authorization'
|
61
|
+
autoload :BasicAuthPolicy, 'trello/authorization'
|
62
|
+
autoload :OAuthPolicy, 'trello/authorization'
|
63
|
+
end
|
64
|
+
end
|
data/lib/trello/action.rb
CHANGED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module Trello
|
4
|
+
module Authorization
|
5
|
+
|
6
|
+
AuthPolicy = Class.new
|
7
|
+
|
8
|
+
class BasicAuthPolicy
|
9
|
+
class << self
|
10
|
+
attr_accessor :developer_public_key, :member_token
|
11
|
+
|
12
|
+
def authorize(request)
|
13
|
+
the_uri = Addressable::URI.parse(request.uri)
|
14
|
+
existing_values = the_uri.query_values.nil? ? {} : the_uri.query_values
|
15
|
+
new_values = { :key => @developer_public_key, :token => @member_token }
|
16
|
+
the_uri.query_values = new_values.merge existing_values
|
17
|
+
|
18
|
+
Request.new request.verb, the_uri, request.headers
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Clock
|
24
|
+
def self.timestamp; Time.now.to_i; end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Nonce
|
28
|
+
def self.next
|
29
|
+
SecureRandom.hex()
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
OAuthCredential = Struct.new "OAuthCredential", :key, :secret
|
34
|
+
|
35
|
+
class OAuthPolicy
|
36
|
+
class << self
|
37
|
+
attr_accessor :consumer_credential, :token
|
38
|
+
|
39
|
+
def authorize(request)
|
40
|
+
fail "The consumer_credential has not been supplied." unless consumer_credential
|
41
|
+
|
42
|
+
request.headers = {"Authorization" => get_auth_header(request.uri, :get)}
|
43
|
+
request
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def get_auth_header(url, verb)
|
49
|
+
require "oauth"
|
50
|
+
|
51
|
+
self.token ||= OAuthCredential.new
|
52
|
+
|
53
|
+
consumer = OAuth::Consumer.new(
|
54
|
+
consumer_credential.key,
|
55
|
+
consumer_credential.secret,
|
56
|
+
{
|
57
|
+
:scheme => :header,
|
58
|
+
:scope => 'read,write,account',
|
59
|
+
:http_method => verb
|
60
|
+
}
|
61
|
+
)
|
62
|
+
|
63
|
+
request = Net::HTTP::Get.new Addressable::URI.parse(url).to_s
|
64
|
+
|
65
|
+
consumer.options[:signature_method] = 'HMAC-SHA1'
|
66
|
+
consumer.options[:nonce] = Nonce.next
|
67
|
+
consumer.options[:timestamp] = Clock.timestamp
|
68
|
+
consumer.options[:uri] = url
|
69
|
+
|
70
|
+
consumer.sign!(request, OAuth::Token.new(token.key, token.secret))
|
71
|
+
|
72
|
+
request['authorization']
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/trello/basic_data.rb
CHANGED
@@ -5,7 +5,6 @@ module Trello
|
|
5
5
|
attr_reader :id
|
6
6
|
|
7
7
|
class << self
|
8
|
-
# Perform a query to retrieve some information at a specific path for a specific id.
|
9
8
|
def find(path, id)
|
10
9
|
Client.get("/#{path}/#{id}").json_into(self)
|
11
10
|
end
|
@@ -29,4 +28,4 @@ module Trello
|
|
29
28
|
id == other.id
|
30
29
|
end
|
31
30
|
end
|
32
|
-
end
|
31
|
+
end
|
data/lib/trello/board.rb
CHANGED
@@ -1,31 +1,44 @@
|
|
1
1
|
module Trello
|
2
|
-
|
2
|
+
|
3
3
|
class Board < BasicData
|
4
|
-
attr_reader :id, :name, :description, :
|
4
|
+
attr_reader :id, :name, :description, :url, :organization_id
|
5
5
|
|
6
6
|
class << self
|
7
|
-
|
7
|
+
|
8
8
|
def find(id)
|
9
9
|
super(:boards, id)
|
10
10
|
end
|
11
|
+
|
12
|
+
def create(attributes)
|
13
|
+
Client.post("/boards/", attributes).json_into Board
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def save!
|
18
|
+
fail "Cannot save new instance." unless self.id
|
19
|
+
|
20
|
+
Client.put("/boards/#{self.id}/", {
|
21
|
+
:name => @name,
|
22
|
+
:description => @description,
|
23
|
+
:closed => @closed,
|
24
|
+
:url => @url,
|
25
|
+
:organisation_id => @organisation_id
|
26
|
+
}).json_into(self)
|
11
27
|
end
|
12
28
|
|
13
|
-
# Update the fields of a board.
|
14
|
-
#
|
15
|
-
# Supply a hash of string keyed data retrieved from the Trello API representing
|
16
|
-
# a board.
|
17
29
|
def update_fields(fields)
|
18
|
-
@id = fields['id']
|
19
|
-
@name = fields['name']
|
20
|
-
@description = fields['desc']
|
21
|
-
@closed = fields['closed']
|
22
|
-
@url = fields['url']
|
23
|
-
@organization_id = fields['idOrganization']
|
30
|
+
@id = fields['id'] if fields['id']
|
31
|
+
@name = fields['name'] if fields['name']
|
32
|
+
@description = fields['desc'] if fields['desc']
|
33
|
+
@closed = fields['closed'] if fields.has_key?('closed')
|
34
|
+
@url = fields['url'] if fields['url']
|
35
|
+
@organization_id = fields['idOrganization'] if fields['idOrganization']
|
36
|
+
|
37
|
+
self
|
24
38
|
end
|
25
39
|
|
26
|
-
# Check if the board is active.
|
27
40
|
def closed?
|
28
|
-
closed
|
41
|
+
@closed
|
29
42
|
end
|
30
43
|
|
31
44
|
# Return a timeline of actions related to this board.
|
@@ -44,6 +57,10 @@ module Trello
|
|
44
57
|
@cards = Client.get("/boards/#{id}/cards").json_into(Card)
|
45
58
|
end
|
46
59
|
|
60
|
+
def has_lists?
|
61
|
+
lists.size > 0
|
62
|
+
end
|
63
|
+
|
47
64
|
# Returns all the lists on this board.
|
48
65
|
#
|
49
66
|
# The options hash may have a filter key which can have its value set as any
|
@@ -61,7 +78,7 @@ module Trello
|
|
61
78
|
# :filter => [ :none, :normal, :owners, :all ] # default :all
|
62
79
|
def members(options = { :filter => :all })
|
63
80
|
return @members if @members
|
64
|
-
@members = Client.get("/boards/#{id}/members").json_into(Member)
|
81
|
+
@members = Client.get("/boards/#{id}/members", options).json_into(Member)
|
65
82
|
end
|
66
83
|
|
67
84
|
# Returns a reference to the organization this board belongs to.
|
data/lib/trello/card.rb
CHANGED
@@ -14,7 +14,7 @@ module Trello
|
|
14
14
|
def create(options)
|
15
15
|
new('name' => options[:name],
|
16
16
|
'idList' => options[:list_id],
|
17
|
-
'desc'
|
17
|
+
'desc' => options[:description]).save!
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -31,6 +31,7 @@ module Trello
|
|
31
31
|
@board_id = fields['idBoard']
|
32
32
|
@member_ids = fields['idMembers']
|
33
33
|
@list_id = fields['idList']
|
34
|
+
self
|
34
35
|
end
|
35
36
|
|
36
37
|
# Returns a list of the actions associated with this card.
|
@@ -69,15 +70,35 @@ module Trello
|
|
69
70
|
end
|
70
71
|
end
|
71
72
|
|
73
|
+
# Change the name of the card
|
74
|
+
def name=(val)
|
75
|
+
Client.put("/card/#{id}/name", :value => val)
|
76
|
+
@name = val
|
77
|
+
end
|
78
|
+
|
79
|
+
# Change the description of the card
|
80
|
+
def description=(val)
|
81
|
+
Client.put("/card/#{id}/desc", :value => val)
|
82
|
+
@description = val
|
83
|
+
end
|
84
|
+
|
85
|
+
# Change the list this card is a part of
|
86
|
+
def list=(other)
|
87
|
+
Client.put("/card/#{id}/idList", :value => other.id)
|
88
|
+
@list_id = other.id
|
89
|
+
other
|
90
|
+
end
|
91
|
+
|
72
92
|
# Saves a record.
|
73
93
|
def save!
|
94
|
+
# If we have an id, just update our fields.
|
74
95
|
return update! if id
|
75
96
|
|
76
97
|
Client.post("/cards", {
|
77
98
|
:name => @name,
|
78
99
|
:desc => @description,
|
79
100
|
:idList => @list_id
|
80
|
-
})
|
101
|
+
}).json_into(self)
|
81
102
|
end
|
82
103
|
|
83
104
|
# Update an existing record.
|
@@ -93,7 +114,7 @@ module Trello
|
|
93
114
|
|
94
115
|
# Add a comment with the supplied text.
|
95
116
|
def add_comment(text)
|
96
|
-
Client.
|
117
|
+
Client.post("/cards/#{id}/actions/comments", :text => text)
|
97
118
|
end
|
98
119
|
end
|
99
120
|
end
|
data/lib/trello/checklist.rb
CHANGED
data/lib/trello/client.rb
CHANGED
@@ -1,66 +1,51 @@
|
|
1
1
|
require 'addressable/uri'
|
2
2
|
|
3
3
|
module Trello
|
4
|
-
# Client is used to handle the OAuth connection to Trello as well as send requests over that authenticated socket.
|
5
4
|
class Client
|
6
|
-
|
7
|
-
class EnterYourSecret < StandardError; end
|
5
|
+
extend Authorization
|
8
6
|
|
9
7
|
class << self
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
# get(path, params)
|
14
|
-
# post(path, params)
|
15
|
-
# put(path, params)
|
16
|
-
# delete(path, params)
|
17
|
-
# query(api_version, path, options)
|
18
|
-
#
|
19
|
-
# Makes a query to a specific path via one of the four HTTP methods, optionally
|
20
|
-
# with a hash specifying parameters to pass to Trello.
|
21
|
-
#
|
22
|
-
# You should use one of _.get_, _.post_, _.put_ or _.delete_ instead of this method.
|
23
|
-
def query(api_version, path, options = { :method => :get, :params => {} })
|
8
|
+
def get(path, params = {})
|
9
|
+
api_version = 1
|
10
|
+
|
24
11
|
uri = Addressable::URI.parse("https://api.trello.com/#{api_version}#{path}")
|
25
|
-
uri.query_values =
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
e.request.each_header do |k,v|
|
31
|
-
headers << [k, v]
|
32
|
-
end
|
33
|
-
|
34
|
-
logger.error("[#{@access_token}] Disposing of access token.\n#{e.inspect}")
|
35
|
-
logger.info("[#{@access_token}] Headers: #{headers.inspect}\nRequest Body: #{e.request.body}")
|
36
|
-
@access_token = nil
|
37
|
-
end
|
12
|
+
uri.query_values = params unless params.empty?
|
13
|
+
|
14
|
+
request = Request.new :get, uri, {}
|
15
|
+
|
16
|
+
response = TInternet.execute AuthPolicy.authorize(request)
|
38
17
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
params = args[1] || {}
|
43
|
-
query(API_VERSION, path, :method => http_method, :params => params).read_body
|
44
|
-
end
|
18
|
+
raise Error, response.body unless response.code.to_i == 200
|
19
|
+
|
20
|
+
response.body
|
45
21
|
end
|
46
22
|
|
47
|
-
|
23
|
+
def post(path, body = {})
|
24
|
+
api_version = 1
|
25
|
+
|
26
|
+
uri = Addressable::URI.parse("https://api.trello.com/#{api_version}#{path}")
|
27
|
+
|
28
|
+
request = Request.new :post, uri, {}, body
|
29
|
+
|
30
|
+
response = TInternet.execute AuthPolicy.authorize(request)
|
48
31
|
|
49
|
-
|
50
|
-
raise EnterYourPublicKey if @public_key.to_s.empty?
|
51
|
-
raise EnterYourSecret if @secret.to_s.empty?
|
32
|
+
raise Error, response.body unless response.code.to_i == 200
|
52
33
|
|
53
|
-
|
54
|
-
:request_token_path => '/1/OAuthGetRequestToken',
|
55
|
-
:authorize_path => '/1/OAuthAuthorizeToken',
|
56
|
-
:access_token_path => '/1/OAuthGetAccessToken',
|
57
|
-
:http_method => :get)
|
34
|
+
response.body
|
58
35
|
end
|
59
36
|
|
60
|
-
def
|
61
|
-
|
37
|
+
def put(path, body = {})
|
38
|
+
api_version = 1
|
39
|
+
|
40
|
+
uri = Addressable::URI.parse("https://api.trello.com/#{api_version}#{path}")
|
41
|
+
|
42
|
+
request = Request.new :put, uri, {}, body
|
43
|
+
|
44
|
+
response = TInternet.execute AuthPolicy.authorize(request)
|
45
|
+
|
46
|
+
raise Error, response.body unless response.code.to_i == 200
|
62
47
|
|
63
|
-
|
48
|
+
response.body
|
64
49
|
end
|
65
50
|
end
|
66
51
|
end
|
data/lib/trello/item.rb
CHANGED
data/lib/trello/item_state.rb
CHANGED
data/lib/trello/list.rb
CHANGED
@@ -8,6 +8,11 @@ module Trello
|
|
8
8
|
def find(id)
|
9
9
|
super(:lists, id)
|
10
10
|
end
|
11
|
+
|
12
|
+
def create(options)
|
13
|
+
new('name' => options[:name],
|
14
|
+
'idBoard' => options[:board_id]).save!
|
15
|
+
end
|
11
16
|
end
|
12
17
|
|
13
18
|
# Updates the fields of a list.
|
@@ -19,6 +24,20 @@ module Trello
|
|
19
24
|
@name = fields['name']
|
20
25
|
@closed = fields['closed']
|
21
26
|
@board_id = fields['idBoard']
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def save!
|
31
|
+
return update! if id
|
32
|
+
|
33
|
+
Client.post("/lists", {
|
34
|
+
:name => @name,
|
35
|
+
:closed => @closed || false,
|
36
|
+
:idBoard => @board_id
|
37
|
+
}).json_into(self)
|
38
|
+
end
|
39
|
+
|
40
|
+
def update!
|
22
41
|
end
|
23
42
|
|
24
43
|
# Check if the list is not active anymore.
|