ruby-trello 0.4.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -16,6 +16,86 @@ Seriously, [check it out](http://www.trello.com/).
16
16
  Full Disclosure: This library is mostly complete, if you do find anything missing or not functioning as you expect it
17
17
  to, please [let us know](https://trello.com/card/spot-a-bug-report-it/4f092b2ee23cb6fe6d1aaabd/17).
18
18
 
19
+ ## Configuration
20
+
21
+ Basic authorization
22
+
23
+ ```ruby
24
+ Trello.configure do |config|
25
+ config.developer_public_key = TRELLO_DEVELOPER_PUBLIC_KEY
26
+ config.member_token = TRELLO_MEMBER_TOKEN
27
+ end
28
+ ```
29
+
30
+ 2-legged OAuth authorization
31
+
32
+ ```ruby
33
+ Trello.configure do |config|
34
+ config.consumer_key = TRELLO_CONSUMER_KEY
35
+ config.consumer_secret = TRELLO_CONSUMER_SECRET
36
+ config.oauth_token = TRELLO_OAUTH_TOKEN
37
+ config.oauth_token_secret = TRELLO_OAUTH_TOKEN_SECRET
38
+ end
39
+ ```
40
+
41
+ 3-legged OAuth authorization
42
+
43
+ ```ruby
44
+ Trello.configure do |config|
45
+ config.consumer_key = TRELLO_CONSUMER_KEY
46
+ config.consumer_secret = TRELLO_CONSUMER_SECRET
47
+ config.return_url = "http://your.site.com/path/to/receive/post"
48
+ config.callback = lambda { |request_token| DB.save(request_token.key, request_token.secret) }
49
+ end
50
+ ```
51
+
52
+ All the calls this library make to Trello require authentication using these keys. Be sure to protect them.
53
+
54
+ So lets say you want to get information about the user *bobtester*. We can do something like this:
55
+
56
+ ```ruby
57
+ bob = Trello::Member.find("bobtester")
58
+ # Print out his name
59
+ puts bob.full_name # "Bob Tester"
60
+ # Print his bio
61
+ puts bob.bio # A wonderfully delightful test user
62
+ # How about a list of his boards?
63
+ bob.boards
64
+ ```
65
+
66
+ #### Multiple Users
67
+
68
+ Applications that make requests on behalf of multiple Trello users have an alternative to global configuration. For each user's access token/secret pair, instantiate a `Trello::Client`:
69
+
70
+ ```ruby
71
+ @client_bob = Trello::Client.new(
72
+ :consumer_key => YOUR_CONSUMER_KEY,
73
+ :consumer_secret => YOUR_CONSUMER_SECRET,
74
+ :oauth_token => "Bob's access token",
75
+ :oauth_token_secret => "Bob's access secret"
76
+ )
77
+
78
+ @client_alice = Trello::Client.new(
79
+ :consumer_key => YOUR_CONSUMER_KEY,
80
+ :consumer_secret => YOUR_CONSUMER_SECRET,
81
+ :oauth_token => "Alice's access token",
82
+ :oauth_token_secret => "Alice's access secret"
83
+ )
84
+ ```
85
+
86
+ You can now make threadsafe requests as the authenticated user:
87
+
88
+ ```ruby
89
+ Thread.new do
90
+ @client_bob.find(:members, "bobtester")
91
+ @client_bob.find(:boards, "bobs_board_id")
92
+ end
93
+ Thread.new do
94
+ @client_alice.find(:members, "alicetester")
95
+ @client_alice.find(:boards, "alices_board_id")
96
+ end
97
+ ```
98
+
19
99
  ## Special thanks
20
100
 
21
101
  A special thanks goes out to [Ben Biddington](https://github.com/ben-biddington) who has contributed a significant amount
@@ -27,11 +107,4 @@ Several ways you can contribute. Documentation, code, tests, feature requests, b
27
107
 
28
108
  We develop ruby-trello using [Trello itself](https://trello.com/board/ruby-trello/4f092b2ee23cb6fe6d1aaabd).
29
109
 
30
- Pick up an editor, fix a test! (If you want to, of course.) Send a pull
31
- request, and I'll look at it. I only ask a few things:
32
-
33
- 1. Feature branches please!
34
- 2. Adding or refactoring existing features, ensure there are tests.
35
-
36
- Also, don't be afraid to send a pull request if your changes aren't done. Just
37
- let me know.
110
+ Please see the `CONTRIBUTING.md` file for more information.
data/lib/trello.rb CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  require 'oauth'
2
3
  require 'json'
3
4
  require 'logger'
@@ -7,20 +8,18 @@ require 'active_model'
7
8
  #
8
9
  # First, set up your key information. You can get this information by {clicking here}[https://trello.com/1/appKey/generate].
9
10
  #
10
- # include Trello
11
- # include Trello::Authorization
12
- #
13
- # Trello::Authorization.const_set :AuthPolicy, OAuthPolicy
14
- #
15
- # OAuthPolicy.consumer_credential = OAuthCredential.new 'PUBLIC_KEY', 'SECRET'
16
- #
17
11
  # You can get the key by going to this url in your browser:
18
- # https://trello.com/1/authorize?key=PUBLIC_KEY_FROM_ABOVE&name=MyApp&response_type=token&scope=read,write,account&expiration=never
12
+ # https://trello.com/1/authorize?key=TRELLO_CONSUMER_KEY_FROM_ABOVE&name=MyApp&response_type=token&scope=read,write,account&expiration=never
19
13
  # Only request the permissions you need; i.e., scope=read if you only need read, or scope=write if you only need write. Comma separate scopes you need.
20
14
  # If you want your token to expire after 30 days, drop the &expiration=never. Then run the following code, where KEY denotes the key returned from the
21
15
  # url above:
22
16
  #
23
- # OAuthPolicy.token = OAuthCredential.new 'KEY', nil
17
+ # Trello.configure do |config|
18
+ # config.consumer_key = TRELLO_CONSUMER_KEY
19
+ # config.consumer_secret = TRELLO_CONSUMER_SECRET
20
+ # config.oauth_token = TRELLO_OAUTH_TOKEN
21
+ # config.oauth_token_secret = TRELLO_OAUTH_TOKEN_SECRET
22
+ # end
24
23
  #
25
24
  # All the calls this library make to Trello require authentication using these keys. Be sure to protect them.
26
25
  #
@@ -47,6 +46,7 @@ module Trello
47
46
  autoload :Card, 'trello/card'
48
47
  autoload :Checklist, 'trello/checklist'
49
48
  autoload :Client, 'trello/client'
49
+ autoload :Configuration, 'trello/configuration'
50
50
  autoload :HasActions, 'trello/has_actions'
51
51
  autoload :Item, 'trello/item'
52
52
  autoload :CheckItemState, 'trello/item_state'
@@ -81,4 +81,20 @@ module Trello
81
81
  def self.logger=(logger)
82
82
  @logger = logger
83
83
  end
84
+
85
+ def self.client
86
+ @client ||= Client.new
87
+ end
88
+
89
+ def self.configure(&block)
90
+ reset!
91
+ client.configure(&block)
92
+ end
93
+
94
+ def self.reset!
95
+ @client = nil
96
+ end
97
+
98
+ def self.auth_policy; client.auth_policy; end
99
+ def self.configuration; client.configuration; end
84
100
  end
data/lib/trello/action.rb CHANGED
@@ -8,7 +8,7 @@ module Trello
8
8
  class << self
9
9
  # Locate a specific action and return a new Action object.
10
10
  def find(id)
11
- super(:actions, id)
11
+ client.find(:action, id)
12
12
  end
13
13
  end
14
14
 
@@ -27,20 +27,20 @@ module Trello
27
27
 
28
28
  # Returns the board this action occurred on.
29
29
  def board
30
- Client.get("/actions/#{id}/board").json_into(Board)
30
+ client.get("/actions/#{id}/board").json_into(Board)
31
31
  end
32
32
 
33
33
  # Returns the card the action occurred on.
34
34
  def card
35
- Client.get("/actions/#{id}/card").json_into(Card)
35
+ client.get("/actions/#{id}/card").json_into(Card)
36
36
  end
37
37
 
38
38
  # Returns the list the action occurred on.
39
39
  def list
40
- Client.get("/actions/#{id}/list").json_into(List)
40
+ client.get("/actions/#{id}/list").json_into(List)
41
41
  end
42
42
 
43
43
  # Returns the member who created the action.
44
- one :member_creator, :via => Member, :using => :member_creator_id
44
+ one :member_creator, :via => Member, :path => :members, :using => :member_creator_id
45
45
  end
46
46
  end
@@ -2,12 +2,12 @@ require 'active_support/core_ext'
2
2
 
3
3
  module Trello
4
4
  class AssociationProxy
5
+ extend Forwardable
5
6
  alias :proxy_extend :extend
6
7
 
7
8
  instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|^respond_to|proxy_/ }
8
9
 
9
- delegate :owner, :target, :to => :@association
10
- delegate :count, :to => :@association
10
+ def_delegators :@association, :owner, :target, :count
11
11
 
12
12
  def initialize(association)
13
13
  @association = association
@@ -39,4 +39,4 @@ module Trello
39
39
  proxy_assocation.concat(records) && self
40
40
  end
41
41
  end
42
- end
42
+ end
@@ -4,21 +4,34 @@ require "oauth"
4
4
  module Trello
5
5
  module Authorization
6
6
 
7
- AuthPolicy = Class.new
7
+ AuthPolicy = Class.new do
8
+ def initialize(attrs = {}); end
9
+ end
8
10
 
9
11
  class BasicAuthPolicy
10
12
  class << self
11
13
  attr_accessor :developer_public_key, :member_token
12
14
 
13
15
  def authorize(request)
14
- the_uri = Addressable::URI.parse(request.uri)
15
- existing_values = the_uri.query_values.nil? ? {} : the_uri.query_values
16
- new_values = { :key => @developer_public_key, :token => @member_token }
17
- the_uri.query_values = new_values.merge existing_values
18
-
19
- Request.new request.verb, the_uri, request.headers, request.body
16
+ new.authorize(request)
20
17
  end
21
18
  end
19
+
20
+ attr_accessor :developer_public_key, :member_token
21
+
22
+ def initialize(attrs = {})
23
+ @developer_public_key = attrs[:developer_public_key] || self.class.developer_public_key
24
+ @member_token = attrs[:member_token] || self.class.member_token
25
+ end
26
+
27
+ def authorize(request)
28
+ the_uri = Addressable::URI.parse(request.uri)
29
+ existing_values = the_uri.query_values.nil? ? {} : the_uri.query_values
30
+ new_values = { :key => @developer_public_key, :token => @member_token }
31
+ the_uri.query_values = new_values.merge existing_values
32
+
33
+ Request.new request.verb, the_uri, request.headers, request.body
34
+ end
22
35
  end
23
36
 
24
37
  class Clock
@@ -57,60 +70,103 @@ module Trello
57
70
  attr_accessor :consumer_credential, :token, :return_url, :callback
58
71
 
59
72
  def authorize(request)
60
- unless consumer_credential
61
- Trello.logger.error "The consumer_credential has not been supplied."
62
- fail "The consumer_credential has not been supplied."
63
- end
64
-
65
- if token
66
- request.headers = {"Authorization" => get_auth_header(request.uri, :get)}
67
- request
68
- else
69
- consumer(:return_url => return_url, :callback_method => :postMessage)
70
- request_token = consumer.get_request_token
71
- callback.call request_token
72
- return nil
73
- end
73
+ new.authorize(request)
74
74
  end
75
+ end
75
76
 
76
- private
77
-
78
- def consumer_params(params = {})
79
- {
80
- :scheme => :header,
81
- :scope => 'read,write,account',
82
- :http_method => :get,
83
- :request_token_path => "https://trello.com/1/OAuthGetRequestToken",
84
- :authorize_path => "https://trello.com/1/OAuthAuthorizeToken",
85
- :access_token_path => "https://trello.com/1/OAuthGetAccessToken"
86
- }.merge!(params)
77
+ attr_accessor :attributes
78
+ attr_accessor :consumer_credential, :token, :return_url, :callback
79
+
80
+ def initialize(attrs = {})
81
+ @consumer_key = attrs[:consumer_key]
82
+ @consumer_secret = attrs[:consumer_secret]
83
+ @oauth_token = attrs[:oauth_token]
84
+ @oauth_token_secret = attrs[:oauth_token_secret]
85
+ @return_url = attrs[:return_url] || self.class.return_url
86
+ @callback = attrs[:callback] || self.class.callback
87
+ end
88
+
89
+ def authorize(request)
90
+ unless consumer_credential
91
+ Trello.logger.error "The consumer_credential has not been supplied."
92
+ fail "The consumer_credential has not been supplied."
87
93
  end
88
94
 
89
- def consumer(options = {})
90
- @consumer ||= OAuth::Consumer.new(
91
- consumer_credential.key,
92
- consumer_credential.secret,
93
- consumer_params(options)
94
- )
95
+ if token
96
+ request.headers = {"Authorization" => get_auth_header(request.uri, :get)}
97
+ request
98
+ else
99
+ consumer(:return_url => return_url, :callback_method => :postMessage)
100
+ request_token = consumer.get_request_token
101
+ callback.call request_token
102
+ return nil
95
103
  end
104
+ end
96
105
 
97
- def get_auth_header(url, verb, options = {})
98
- self.token ||= OAuththCredential.new
106
+ def consumer_credential
107
+ @consumer_credential ||= build_consumer_credential
108
+ end
99
109
 
100
- request = Net::HTTP::Get.new Addressable::URI.parse(url).to_s
110
+ def token
111
+ @token ||= build_token
112
+ end
113
+
114
+ def consumer_key; consumer_credential.key; end
115
+ def consumer_secret; consumer_credential.secret; end
116
+ def oauth_token; token.key; end
117
+ def oauth_token_secret; token.secret; end
101
118
 
102
- consumer.options[:signature_method] = 'HMAC-SHA1'
103
- consumer.options[:nonce] = Nonce.next
104
- consumer.options[:timestamp] = Clock.timestamp
105
- consumer.options[:uri] = url
106
- consumer.key = consumer_credential.key
107
- consumer.secret = consumer_credential.secret
119
+ private
108
120
 
109
- consumer.sign!(request, OAuth::Token.new(token.key, token.secret))
121
+ def build_consumer_credential
122
+ if @consumer_key && @consumer_secret
123
+ OAuthCredential.new @consumer_key, @consumer_secret
124
+ else
125
+ self.class.consumer_credential
126
+ end
127
+ end
110
128
 
111
- request['authorization']
129
+ def build_token
130
+ if @oauth_token
131
+ OAuthCredential.new @oauth_token, @oauth_token_secret
132
+ else
133
+ self.class.token
112
134
  end
113
135
  end
136
+
137
+ def consumer_params(params = {})
138
+ {
139
+ :scheme => :header,
140
+ :scope => 'read,write,account',
141
+ :http_method => :get,
142
+ :request_token_path => "https://trello.com/1/OAuthGetRequestToken",
143
+ :authorize_path => "https://trello.com/1/OAuthAuthorizeToken",
144
+ :access_token_path => "https://trello.com/1/OAuthGetAccessToken"
145
+ }.merge!(params)
146
+ end
147
+
148
+ def consumer(options = {})
149
+ @consumer ||= OAuth::Consumer.new(
150
+ consumer_credential.key,
151
+ consumer_credential.secret,
152
+ consumer_params(options)
153
+ )
154
+ end
155
+
156
+ def get_auth_header(url, verb, options = {})
157
+ request = Net::HTTP::Get.new Addressable::URI.parse(url).to_s
158
+
159
+ consumer.options[:signature_method] = 'HMAC-SHA1'
160
+ consumer.options[:nonce] = Nonce.next
161
+ consumer.options[:timestamp] = Clock.timestamp
162
+ consumer.options[:uri] = url
163
+ consumer.key = consumer_credential.key
164
+ consumer.secret = consumer_credential.secret
165
+
166
+ consumer.sign!(request, OAuth::Token.new(token.key, token.secret))
167
+
168
+ request['authorization']
169
+ end
114
170
  end
115
171
  end
116
172
  end
@@ -7,8 +7,36 @@ module Trello
7
7
  include ActiveModel::Serializers::JSON
8
8
 
9
9
  class << self
10
- def find(path, id)
11
- Client.get("/#{path}/#{id}").json_into(self)
10
+ def path_name
11
+ name.split("::").last.underscore
12
+ end
13
+
14
+ def find(id)
15
+ client.find(path_name, id)
16
+ end
17
+
18
+ def create(options)
19
+ client.create(path_name, options)
20
+ end
21
+
22
+ def save(options)
23
+ new(options).tap do |basic_data|
24
+ yield basic_data if block_given?
25
+ end.save
26
+ end
27
+
28
+ def parse(response)
29
+ response.json_into(self).tap do |basic_data|
30
+ yield basic_data if block_given?
31
+ end
32
+ end
33
+
34
+ def parse_many(response)
35
+ response.json_into(self).map do |data|
36
+ data.tap do |d|
37
+ yield d if block_given?
38
+ end
39
+ end
12
40
  end
13
41
  end
14
42
 
@@ -43,7 +71,13 @@ module Trello
43
71
  options = opts.dup
44
72
  klass = options.delete(:via) || Trello.const_get(name.to_s.camelize)
45
73
  ident = options.delete(:using) || :id
46
- klass.find(self.send(ident))
74
+ path = options.delete(:path)
75
+
76
+ if path
77
+ client.find(path, self.send(ident))
78
+ else
79
+ klass.find(self.send(ident))
80
+ end
47
81
  end
48
82
  end
49
83
  end
@@ -55,14 +89,20 @@ module Trello
55
89
  resource = options.delete(:in) || self.class.to_s.split("::").last.downcase.pluralize
56
90
  klass = options.delete(:via) || Trello.const_get(name.to_s.singularize.camelize)
57
91
  params = options.merge(args[0] || {})
58
- resources = Client.get("/#{resource}/#{id}/#{name}", params).json_into(klass)
92
+ resources = client.find_many(klass, "/#{resource}/#{id}/#{name}", params)
59
93
  MultiAssociation.new(self, resources).proxy
60
94
  end
61
95
  end
62
96
  end
63
97
 
98
+ def self.client
99
+ Trello.client
100
+ end
101
+
64
102
  register_attributes :id, :readonly => [ :id ]
65
103
 
104
+ attr_writer :client
105
+
66
106
  def initialize(fields = {})
67
107
  update_fields(fields)
68
108
  end
@@ -80,5 +120,9 @@ module Trello
80
120
  def ==(other)
81
121
  id == other.id
82
122
  end
123
+
124
+ def client
125
+ @client ||= self.class.client
126
+ end
83
127
  end
84
128
  end