ruby-trello 0.4.4.3 → 0.5.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
@@ -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