ruby-trello 0.2.1 → 0.3.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.
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 can't find a record.
43
- class RecordNotFound < StandardError; end
44
- end
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
@@ -19,6 +19,7 @@ module Trello
19
19
  @type = fields['type']
20
20
  @data = fields['data']
21
21
  @member_creator_id = fields['idMemberCreator']
22
+ self
22
23
  end
23
24
 
24
25
  # Returns the board this action occurred on.
@@ -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
@@ -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
- # A board is a container which holds lists. This is where everything lives.
2
+
3
3
  class Board < BasicData
4
- attr_reader :id, :name, :description, :closed, :url, :organization_id
4
+ attr_reader :id, :name, :description, :url, :organization_id
5
5
 
6
6
  class << self
7
- # Locate a board given a specific id.
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' => options[:description]).save!
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.put("/cards/#{id}/actions/comments", :text => text)
117
+ Client.post("/cards/#{id}/actions/comments", :text => text)
97
118
  end
98
119
  end
99
120
  end
@@ -23,6 +23,7 @@ module Trello
23
23
  @check_items = fields['checkItems']
24
24
  @board_id = fields['idBoard']
25
25
  @member_ids = fields['idMembers']
26
+ self
26
27
  end
27
28
 
28
29
  # Check if the checklist is currently active.
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
- class EnterYourPublicKey < StandardError; end
7
- class EnterYourSecret < StandardError; end
5
+ extend Authorization
8
6
 
9
7
  class << self
10
- attr_writer :public_key, :secret
11
-
12
- # call-seq:
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 = options[:params]
26
-
27
- access_token.send(options[:method], uri.to_s)
28
- rescue OAuth::Problem => e
29
- headers = []
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
- %w{get post put delete}.each do |http_method|
40
- send(:define_method, http_method) do |*args|
41
- path = args[0]
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
- protected
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
- def consumer
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
- OAuth::Consumer.new(@public_key, @secret, :site => 'https://trello.com',
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 access_token
61
- return @access_token if @access_token
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
- @access_token = OAuth::AccessToken.new(consumer)
48
+ response.body
64
49
  end
65
50
  end
66
51
  end
data/lib/trello/item.rb CHANGED
@@ -11,6 +11,7 @@ module Trello
11
11
  @id = fields['id']
12
12
  @name = fields['name']
13
13
  @type = fields['type']
14
+ self
14
15
  end
15
16
  end
16
17
  end
@@ -11,6 +11,7 @@ module Trello
11
11
  @id = fields['id']
12
12
  @state = fields['state']
13
13
  @item_id = fields['idItem']
14
+ self
14
15
  end
15
16
 
16
17
  # Return the item this state belongs to.
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.