grackle 0.1.10 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,10 @@
1
+ == 0.2.0 (2012-6-10)
2
+ * New auto_append_format attribute (thanks @jcsalterego) for specifying whether or not to automatically add .json, etc to a request URI
3
+ * New response attribute which captures raw info about the response from the most recent request. This permits access to response headers with rate limit information. Thanks to @mandoescamilla.
4
+ * Added Twitter's media upload endpoint. Thanks @jbrennon.
5
+ * Modernized the code a bit with better test running and a new gemspec
6
+ * Now testing in 1.8.6, 1.8.7, 1.9.2 and 1.9.3
7
+
1
8
  == 0.1.10 (2010-6-13)
2
9
  * Changed :v1 (api.twitter.com/1) to be the default API instead of REST. :rest is now deprecated
3
10
  * Fixed issue with DELETE requests not being able to have form encoded body parameters. This was causing the list membership delete method to fail. As a side note, it appears that Twitter is actually violating the HTTP 1.1 spec on this one since RFC 2616 states that a DELETE "requests that the origin server delete the resource identified by the Request-URI" (note no mention of any "enclosed entity").
@@ -1,30 +1,32 @@
1
1
  =grackle
2
2
  by Hayes Davis
3
3
  - http://twitter.com/hayesdavis
4
- - hayes [at] appozite [dot] com
5
- - http://cheaptweet.com
6
- - http://www.appozite.com
4
+ - hayes [at] unionmetrics [dot] com
5
+ - http://unionmetrics.com
7
6
  - http://hayesdavis.net
8
7
 
9
8
  == DESCRIPTION
10
- Grackle is a lightweight Ruby wrapper around the Twitter REST and Search APIs. It's based on my experience using the
11
- Twitter API to build http://cheaptweet.com. The main goal of Grackle is to never require a release when the Twitter
12
- API changes (which it often does) or in the face of a particular Twitter API bug. As such it's somewhat different
13
- from other Twitter API libraries. It doesn't try to hide the Twitter "methods" under an access layer nor does it
14
- introduce concrete classes for the various objects returned by Twitter. Instead, calls to the Grackle client map
15
- directly to Twitter API URLs. The objects returned by API calls are generated as OpenStructs on the fly and make no
16
- assumptions about the presence or absence of any particular attributes. Taking this approach means that changes to
17
- URLs used by Twitter, parameters required by those URLs or return values will not require a new release. It
18
- will potentially require, however, some modifications to your code that uses Grackle.
19
-
20
- Grackle supports both OAuth and HTTP basic authentication.
9
+ Grackle is a lightweight Ruby wrapper around the Twitter REST and Search APIs.
10
+ It's based on my experience using the Twitter API to build http://cheaptweet.com
11
+ and http://tweetreach.com. The main goal of Grackle is to never require a
12
+ release when the Twitter API changes (which it often does) or in the face of a
13
+ particular Twitter API bug. As such it's somewhat different from other Twitter
14
+ API libraries. It doesn't try to hide the Twitter "methods" under an access
15
+ layer nor does it introduce concrete classes for the various objects returned by
16
+ Twitter. Instead, calls to the Grackle client map directly to Twitter API URLs.
17
+ The objects returned by API calls are generated as OpenStructs on the fly and
18
+ make no assumptions about the presence or absence of any particular attributes.
19
+ Taking this approach means that changes to URLs used by Twitter, parameters
20
+ required by those URLs or return values will not require a new release. It will
21
+ potentially require, however, some modifications to your code that uses Grackle.
21
22
 
22
23
  === Support and Announcements
23
- The preferred forum for questions and discussions is the Google group at http://groups.google.com/group/gracklerb.
24
- You can email me directly or @reply me on Twitter, but the group is better since the questions and responses
25
- will be available to everyone. I'll also make announcements there. There are some examples on the wiki at
26
- http://wiki.github.com/hayesdavis/grackle. If you prefer your information in 140 characters, follow
27
- @gracklerb[http://twitter.com/gracklerb].
24
+ The preferred forum for questions and discussions is the Google group at
25
+ http://groups.google.com/group/gracklerb. You can email me directly or @reply me
26
+ on Twitter, but the group is better since the questions and responses will be
27
+ available to everyone. I'll also make announcements there. There are some
28
+ examples on the wiki at http://wiki.github.com/hayesdavis/grackle. If you prefer
29
+ your information in 140 characters, follow @gracklerb[http://twitter.com/gracklerb].
28
30
 
29
31
  ==USING GRACKLE
30
32
 
@@ -32,9 +34,6 @@ Before you do anything else, you'll need to
32
34
  require 'grackle'
33
35
 
34
36
  ===Creating a Grackle::Client
35
- ====Using Basic Auth
36
- client = Grackle::Client.new(:auth=>{:type=>:basic,:username=>'your_user',:password=>'yourpass'})
37
-
38
37
  ====Using OAuth
39
38
  client = Grackle::Client.new(:auth=>{
40
39
  :type=>:oauth,
@@ -42,11 +41,18 @@ Before you do anything else, you'll need to
42
41
  :token=>'ACCESSTOKENACQUIREDONUSERSBEHALF', :token_secret=>'SUPERSECRETACCESSTOKENSECRET'
43
42
  })
44
43
 
44
+ OAuth can be a bit complicated. See the wiki[http://wiki.github.com/hayesdavis/grackle/grackle-and-oauth]
45
+ for more information on acquiring the keys, tokens and secrets needed to
46
+ successfully authenticate with OAuth.
47
+
45
48
  ====Using No Auth
46
49
  client = Grackle::Client.new
47
50
 
48
- See Grackle::Client for more information about valid arguments to the constructor. It's quite configurable. Among other things,
49
- you can turn on ssl and specify custom headers. The calls below are pretty much as simple as it gets.
51
+ ====Using Basic Auth (DEPRECATED)
52
+ As of August 31st, 2010, Twitter has deprecated basic authentication in favor of OAuth. Please refer to the section on OAuth authentication.
53
+ client = Grackle::Client.new(:auth=>{:type=>:basic,:username=>'your_user',:password=>'yourpass'})
54
+
55
+ See Grackle::Client for more information about valid arguments to the constructor. It's quite configurable. Among other things, you can turn on ssl and specify custom headers. The calls below are pretty much as simple as it gets.
50
56
 
51
57
  ===Grackle Method Syntax
52
58
  Grackle uses a method syntax that corresponds to the Twitter API URLs with a few twists. Where you would have a slash in
@@ -64,7 +70,7 @@ format (usually JSON, but see Formats section below):
64
70
  client.users.show? :screen_name=>'some_user' #http://twitter.com/users/show.json?screen_name=some_user
65
71
 
66
72
  You can force XML format by doing:
67
- client.users.show.xml? :screen_name=>'some_user' #http://twitter.com/users/show.xml?scren_name=some_user
73
+ client.users.show.xml? :screen_name=>'some_user' #http://twitter.com/users/show.xml?screen_name=some_user
68
74
 
69
75
  You can force JSON:
70
76
  client.users.show.json? :screen_name=>'some_user' #http://twitter.com/users/show.json?screen_name=some_user
@@ -73,7 +79,7 @@ Or, since Twitter also allows certain ids/screen_names to be part of their URLs,
73
79
  client.users.show.some_user? #http://twitter.com/users/show/some_user.json
74
80
 
75
81
  If you use an explicit format, you can leave off the "?" like so:
76
- client.users.show.xml :screen_name=>'some_user' #http://twitter.com/users/show.xml?scren_name=some_user
82
+ client.users.show.xml :screen_name=>'some_user' #http://twitter.com/users/show.xml?screen_name=some_user
77
83
 
78
84
  ===POSTing data
79
85
  To use Twitter API methods that require an HTTP POST, you need to end your method chain with a bang (!)
@@ -127,6 +133,23 @@ Search and REST requests are all built using the same method chaining and termin
127
133
  Regardless of the format used, Grackle returns an OpenStruct (actually a Grackle::TwitterStruct) of data. The attributes
128
134
  available on these structs correspond to the data returned by Twitter.
129
135
 
136
+ ===Rate Limits and the Response
137
+ The Grackle client has a response method which is populated with information
138
+ about the most recent response received as a result of a request. This response
139
+ contains the requested URI, the HTTP status code, a subset of the response
140
+ headers and the raw response body. The response headers are the most useful
141
+ because they can be used to retrieve the rate limit information that Twitter
142
+ includes in a response.
143
+
144
+ user = client.users.show.hayesdavis?
145
+ client.response.headers["X-Ratelimit-Remaining"]
146
+
147
+ Not all headers returned by Twitter are included by default. You can control
148
+ which headers are accessible with the response_headers attribute on the Client.
149
+ By default, the important rate limit headers are returned. Please note that all
150
+ header values will be Strings so you'll need to convert rate limits, etc to ints
151
+ in your code.
152
+
130
153
  ===Dealing with Errors
131
154
  If the request to Twitter does not return a status code of 200, then a TwitterError is thrown. This contains the HTTP method used,
132
155
  the full request URI, the response status, the response body in text and a response object build by parsing the formatted error
@@ -164,26 +187,17 @@ You'll need the following gems to use all features of Grackle:
164
187
  - mime-types
165
188
 
166
189
  === Ruby Version Support
167
- Grackle works just fine on Ruby 1.8.x. It is also known to work on 1.9.1 with
168
- the exception of OAuth. The OAuth gem used by Grackle has not been updated fully
169
- to support 1.9. Please see this thread[http://groups.google.com/group/oauth-ruby/browse_thread/thread/d0851a907878cd22]
170
- for more information.
190
+ Grackle is tested on 1.8.6, 1.8.7, 1.9.2 and 1.9.3.
171
191
 
172
- Once the OAuth gem has been updated, Grackle will work fully on 1.9. If you
173
- aren't using OAuth it should be fine for use on 1.9 as is.
192
+ If you are using 1.8.6, please be aware that certain newer versions of the json
193
+ and oauth gems are not compatible with that Ruby version. You can use older
194
+ version and grackle will work fine. On 1.8.6, I recommend using oauth 0.4.4 and
195
+ json < 1.7.
174
196
 
175
197
  == INSTALL
176
- The grackle gem is now hosted at http://gemcutter.org. If you've already setup gemcutter
177
- in your sources, you can do the following:
178
- sudo gem install grackle
179
-
180
- If you haven't yet setup gemcutter in your sources, go to http://gemcutter.org and follow the instructions there.
181
- They will likely tell you to do the following:
182
- sudo gem install gemcutter
183
- sudo gem tumble
198
+ Grackle is available on rubygems.org. Just do the usual:
184
199
 
185
- Once you've done that you can do:
186
- sudo gem install grackle
200
+ gem install grackle
187
201
 
188
202
  == LICENSE
189
203
 
@@ -1,20 +1,3 @@
1
- module Grackle
2
-
3
- # :stopdoc:
4
- VERSION = '0.1.10'
5
- LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
6
- PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
7
- # :startdoc:
8
-
9
- # Returns the version string for the library.
10
- def self.version
11
- VERSION
12
- end
13
-
14
- end # module Grackle
15
-
16
- $:.unshift File.dirname(__FILE__)
17
-
18
1
  require 'ostruct'
19
2
  require 'open-uri'
20
3
  require 'net/http'
@@ -25,6 +8,7 @@ require 'oauth'
25
8
  require 'oauth/client'
26
9
  require 'mime/types'
27
10
 
11
+ require 'grackle/version'
28
12
  require 'grackle/utils'
29
13
  require 'grackle/transport'
30
14
  require 'grackle/handlers'
@@ -86,10 +86,18 @@ module Grackle
86
86
  # refer to it wherever Grackle::Client uses an API symbol. You may wish
87
87
  # to do this when Twitter introduces API versions greater than 1.
88
88
  TWITTER_API_HOSTS = {
89
- :search=>'search.twitter.com', :v1=>'api.twitter.com/1'
89
+ :search=>'search.twitter.com', :v1=>'api.twitter.com/1',
90
+ :upload=>'upload.twitter.com/1'
90
91
  }
91
92
  TWITTER_API_HOSTS[:rest] = TWITTER_API_HOSTS[:v1]
92
-
93
+
94
+ # Contains the response headers from twitter
95
+ DEFAULT_RESPONSE_HEADERS =[
96
+ 'x-ratelimit-limit',
97
+ 'x-ratelimit-remaining',
98
+ 'x-ratelimit-reset'
99
+ ]
100
+
93
101
  #Basic OAuth information needed to communicate with Twitter
94
102
  TWITTER_OAUTH_SPEC = {
95
103
  :request_token_path=>'/oauth/request_token',
@@ -98,24 +106,28 @@ module Grackle
98
106
  }
99
107
 
100
108
  attr_accessor :auth, :handlers, :default_format, :headers, :ssl, :api,
101
- :transport, :request, :api_hosts, :timeout, :auto_append_ids
109
+ :transport, :request, :api_hosts, :timeout, :auto_append_ids,
110
+ :auto_append_format, :response_headers, :response
102
111
 
103
112
  # Arguments (all are optional):
104
- # - :username - twitter username to authenticate with (deprecated in favor of :auth arg)
105
- # - :password - twitter password to authenticate with (deprecated in favor of :auth arg)
106
- # - :handlers - Hash of formats to Handler instances (e.g. {:json=>CustomJSONHandler.new})
107
- # - :default_format - Symbol of format to use when no format is specified in an API call (e.g. :json, :xml)
108
- # - :headers - Hash of string keys and values for headers to pass in the HTTP request to twitter
109
- # - :ssl - true or false to turn SSL on or off. Default is off (i.e. http://)
110
- # - :api - one of :rest, :search or :v1. :v1 is the default and :rest is now deprecated
111
- # - :auth - Hash of authentication type and credentials. Must have :type key with value one of :basic or :oauth
112
- # - :type=>:basic - Include :username and :password keys
113
- # - :type=>:oauth - Include :consumer_key, :consumer_secret, :token and :token_secret keys
113
+ # - :username - Twitter username to authenticate with (deprecated in favor of :auth arg)
114
+ # - :password - Twitter password to authenticate with (deprecated in favor of :auth arg)
115
+ # - :handlers - Hash of formats to Handler instances (e.g. {:json=>CustomJSONHandler.new})
116
+ # - :default_format - Symbol of format to use when no format is specified in an API call (e.g. :json, :xml)
117
+ # - :headers - Hash of string keys and values for headers to pass in the HTTP request to twitter
118
+ # - :ssl - true or false to turn SSL on or off. Default is off (i.e. http://)
119
+ # - :api - one of :rest, :search or :v1. :v1 is the default and :rest is now deprecated
120
+ # - :auth - Hash of authentication type and credentials. Must have :type key with value one of :basic or :oauth
121
+ # - :type=>:basic - Include :username and :password keys
122
+ # - :type=>:oauth - Include :consumer_key, :consumer_secret, :token and :token_secret keys
123
+ # - :auto_append_format - true or false to include format in URI (e.g. /test.json). Default is true
124
+ # - :response_headers - array of headers to return from the response
114
125
  def initialize(options={})
115
126
  self.transport = Transport.new
116
127
  self.handlers = {:json=>Handlers::JSONHandler.new,:xml=>Handlers::XMLHandler.new,:unknown=>Handlers::StringHandler.new}
117
128
  self.handlers.merge!(options[:handlers]||{})
118
129
  self.default_format = options[:default_format] || :json
130
+ self.auto_append_format = options[:auto_append_format] == false ? false : true
119
131
  self.headers = {"User-Agent"=>"Grackle/#{Grackle::VERSION}"}.merge!(options[:headers]||{})
120
132
  self.ssl = options[:ssl] == true
121
133
  self.api = options[:api] || :v1
@@ -123,6 +135,7 @@ module Grackle
123
135
  self.timeout = options[:timeout] || 60
124
136
  self.auto_append_ids = options[:auto_append_ids] == false ? false : true
125
137
  self.auth = {}
138
+ self.response_headers = options[:response_headers] || DEFAULT_RESPONSE_HEADERS
126
139
  if options.has_key?(:username) || options.has_key?(:password)
127
140
  #Use basic auth if :username and :password args are passed in
128
141
  self.auth.merge!({:type=>:basic,:username=>options[:username],:password=>options[:password]})
@@ -218,7 +231,9 @@ module Grackle
218
231
  id = request.params.delete(:id)
219
232
  request << "/#{id}" if id
220
233
  end
221
- request << ".#{format}"
234
+ if auto_append_format
235
+ request << ".#{format}"
236
+ end
222
237
  res = send_request
223
238
  process_response(format,res)
224
239
  ensure
@@ -230,10 +245,11 @@ module Grackle
230
245
  http_method = (
231
246
  request.params.delete(:__method) or request.method or :get
232
247
  )
233
- transport.request(
248
+ @response = transport.request(
234
249
  http_method, request.url,
235
250
  :auth=>auth,:headers=>headers,
236
- :params=>request.params,:timeout => timeout
251
+ :params=>request.params,:timeout=>timeout,
252
+ :response_headers=>response_headers
237
253
  )
238
254
  rescue => e
239
255
  puts e
@@ -288,4 +304,4 @@ module Grackle
288
304
  end
289
305
  end
290
306
  end
291
- end
307
+ end
@@ -1,12 +1,50 @@
1
1
  module Grackle
2
-
2
+
3
+ class Headers #:nodoc:
4
+ include Enumerable
5
+
6
+ def initialize
7
+ @data = {}
8
+ end
9
+
10
+ def [](name)
11
+ res = @data[name.downcase.to_sym]
12
+ res ? res.join(",") : nil
13
+ end
14
+
15
+ def []=(name,value)
16
+ @data[name.downcase.to_sym] = [value]
17
+ end
18
+
19
+ def add(name,value)
20
+ res = (@data[name.downcase.to_sym] ||= [])
21
+ res << value
22
+ end
23
+
24
+ def add_all(name,values)
25
+ res = (@data[name.downcase.to_sym] ||= [])
26
+ res.push(*values)
27
+ end
28
+
29
+ def each
30
+ @data.each do |name,value|
31
+ yield(name.to_s,value.join(","))
32
+ end
33
+ end
34
+
35
+ def size
36
+ @data.size
37
+ end
38
+ end
39
+
3
40
  class Response #:nodoc:
4
- attr_accessor :method, :request_uri, :status, :body
41
+ attr_accessor :method, :request_uri, :status, :body, :headers
5
42
 
6
- def initialize(method,request_uri,status,body)
43
+ def initialize(method,request_uri,status,body,headers)
7
44
  self.method = method
8
45
  self.request_uri = request_uri
9
46
  self.status = status
47
+ self.headers = headers
10
48
  self.body = body
11
49
  end
12
50
  end
@@ -32,6 +70,7 @@ module Grackle
32
70
  # - :headers - a hash of headers to send with the request
33
71
  # - :auth - a hash of authentication parameters for either basic or oauth
34
72
  # - :timeout - timeout for the http request in seconds
73
+ # - :response_headers - a list of headers to return with the response
35
74
  def request(method, string_url, options={})
36
75
  params = stringify_params(options[:params])
37
76
  if method == :get && params
@@ -73,8 +112,9 @@ module Grackle
73
112
  redirect_limit = options[:redirect_limit] || DEFAULT_REDIRECT_LIMIT
74
113
  if res.code.to_s =~ /^3\d\d$/ && redirect_limit > 0 && res['location']
75
114
  execute_request(method,URI.parse(res['location']),options.merge(:redirect_limit=>redirect_limit-1))
76
- else
77
- Response.new(method,url.to_s,res.code.to_i,res.body)
115
+ else
116
+ headers = filter_headers(options[:response_headers],res)
117
+ Response.new(method,url.to_s,res.code.to_i,res.body,headers)
78
118
  end
79
119
  end
80
120
  end
@@ -199,6 +239,14 @@ module Grackle
199
239
  end
200
240
  end
201
241
 
242
+ def filter_headers(headers, res)
243
+ filtered = Headers.new
244
+ headers.each do |h|
245
+ filtered.add(h, res[h])
246
+ end
247
+ filtered
248
+ end
249
+
202
250
  def http_class
203
251
  if proxy
204
252
  if proxy.kind_of?(Proc)
@@ -245,4 +293,4 @@ EOS
245
293
  req.request_body_permitted? || req.kind_of?(Net::HTTP::Delete)
246
294
  end
247
295
  end
248
- end
296
+ end
@@ -0,0 +1,9 @@
1
+ module Grackle
2
+
3
+ VERSION = "0.2.0"
4
+
5
+ def self.version
6
+ VERSION
7
+ end
8
+
9
+ end
@@ -1,6 +1,6 @@
1
1
  require File.dirname(__FILE__) + '/test_helper'
2
2
 
3
- class TestClient < Test::Unit::TestCase
3
+ class ClientTest < Test::Unit::TestCase
4
4
 
5
5
  #Used for mocking HTTP requests
6
6
  class Net::HTTP
@@ -197,7 +197,44 @@ class TestClient < Test::Unit::TestCase
197
197
  assert_equal('TestAgent/1.0',Net::HTTP.request['User-Agent'],"Custom User-Agent header should have been set")
198
198
  assert_equal('Header Value',Net::HTTP.request['X-Test-Header'],"Custom X-Test-Header header should have been set")
199
199
  end
200
-
200
+
201
+ def test_default_response_headers
202
+ client = new_client(200, '[{"id":1,"text":"test 1"}]')
203
+
204
+ # Load up some other headers in the response
205
+ Grackle::Client::DEFAULT_RESPONSE_HEADERS.each_with_index do |header,i|
206
+ Net::HTTP.response[header] = "value#{i}"
207
+ end
208
+
209
+ client.statuses.public_timeline?
210
+ headers = client.response.headers
211
+ assert(!headers.nil?)
212
+ assert_equal(Grackle::Client::DEFAULT_RESPONSE_HEADERS.size, headers.size)
213
+
214
+ Grackle::Client::DEFAULT_RESPONSE_HEADERS.each_with_index do |h,i|
215
+ assert_equal("value#{i}",headers[h])
216
+ end
217
+ end
218
+
219
+ def test_custom_response_headers
220
+ response_headers = ['X-Your-Face-Header']
221
+ client = new_client(200, '[{"id":1,"text":"test 1"}]', :response_headers=>response_headers)
222
+
223
+ # Load up some other headers in the response
224
+ Net::HTTP.response["X-Your-Face-Header"] = "asdf"
225
+ Net::HTTP.response["X-Something-Else"] = "foo"
226
+
227
+ assert_equal(response_headers,client.response_headers,"Response headers should override defaults")
228
+
229
+ client.statuses.public_timeline?
230
+ headers = client.response.headers
231
+ assert(!headers.nil?)
232
+ assert_equal(response_headers.size, headers.size)
233
+
234
+ assert_equal("asdf",headers["X-Your-Face-Header"])
235
+ assert(headers["X-Something-Else"].nil?)
236
+ end
237
+
201
238
  def test_custom_handlers
202
239
  client = new_client(200,'[{"id":1,"text":"test 1"}]',:handlers=>{:json=>TestHandler.new(42)})
203
240
  value = client.statuses.public_timeline.json?
@@ -209,7 +246,7 @@ class TestClient < Test::Unit::TestCase
209
246
  client.some.url.that.does.not.exist
210
247
  assert_equal('/some/url/that/does/not/exist',client.send(:request).path,"An unexecuted path should be built up")
211
248
  client.clear
212
- assert_equal('',client.send(:request).path,"The path shoudl be cleared")
249
+ assert_equal('',client.send(:request).path,"The path should be cleared")
213
250
  end
214
251
 
215
252
  def test_file_param_triggers_multipart_encoding
@@ -311,7 +348,22 @@ class TestClient < Test::Unit::TestCase
311
348
  assert_equal(12345,client.transport.options[:params][:id], "Id should be treated as a parameter")
312
349
  assert_equal("id=#{12345}",Net::HTTP.request.path.split(/\?/)[1],"id should be part of the query string")
313
350
  end
314
-
351
+
352
+ def test_auto_append_format_is_honored
353
+ client = new_client(200,'{"id":12345,"screen_name":"test_user"}')
354
+ client.users.show.hayesdavis?
355
+ assert_equal('/1/users/show/hayesdavis.json',client.transport.url.path,"Format should be appended by default")
356
+ client.auto_append_format = false
357
+ client.users.show.hayesdavis?
358
+ assert_equal('/1/users/show/hayesdavis',client.transport.url.path,"Format should not be appended to the URI")
359
+ end
360
+
361
+ def test_auto_append_format_can_be_set_in_constructor
362
+ client = new_client(200,'{"id":12345,"screen_name":"test_user"}',:auto_append_format=>false)
363
+ client.users.show.hayesdavis?
364
+ assert_equal('/1/users/show/hayesdavis',client.transport.url.path,"Format should not be appended to the URI")
365
+ end
366
+
315
367
  def test_default_api
316
368
  client = Grackle::Client.new
317
369
  assert_equal(:v1,client.api,":v1 should be default api")
@@ -351,9 +403,15 @@ class TestClient < Test::Unit::TestCase
351
403
  assert_equal('http',client.transport.url.scheme,"Expected scheme to be http")
352
404
  assert_equal('api.twitter.com',client.transport.url.host,"Expected request to be against twitter.com")
353
405
  assert_equal('/1/statuses/update.json',client.transport.url.path)
354
- assert_match(/status=test%20status/,Net::HTTP.request.body,"Parameters should be form encoded")
406
+
407
+ if RUBY_VERSION >= "1.9.3"
408
+ # 1.9.3 encodes a space with a + instead of %20
409
+ assert_match(/status=test\+status/,Net::HTTP.request.body,"Parameters should be form encoded")
410
+ else
411
+ assert_match(/status=test%20status/,Net::HTTP.request.body,"Parameters should be form encoded")
412
+ end
355
413
  assert_equal(12345,value.id)
356
414
  yield(client) if block_given?
357
415
  end
358
416
 
359
- end
417
+ end
@@ -1,6 +1,6 @@
1
1
  require File.dirname(__FILE__) + '/test_helper'
2
2
 
3
- class TestHandlers < Test::Unit::TestCase
3
+ class HandlersTest < Test::Unit::TestCase
4
4
 
5
5
  def test_string_handler_echoes
6
6
  sh = Grackle::Handlers::StringHandler.new
@@ -1,3 +1,5 @@
1
- require 'test/unit'
2
1
  require 'rubygems'
3
- require File.dirname(__FILE__) + '/../lib/grackle'
2
+ require 'test/unit'
3
+
4
+ $: << File.join(File.dirname(__FILE__),"..","lib")
5
+ require 'grackle'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grackle
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
9
- - 10
10
- version: 0.1.10
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Hayes Davis
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-06-13 00:00:00 -05:00
18
+ date: 2012-06-10 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -60,36 +60,34 @@ dependencies:
60
60
  version: "0"
61
61
  type: :runtime
62
62
  version_requirements: *id003
63
- description: Grackle is a lightweight library for the Twitter REST and Search API.
64
- email: hayes@appozite.com
63
+ description: " Grackle is a library for the Twitter REST and Search API designed to not\n require a new release in the face Twitter API changes or errors.\n"
64
+ email: hayes@unionmetrics.com
65
65
  executables: []
66
66
 
67
67
  extensions: []
68
68
 
69
69
  extra_rdoc_files:
70
70
  - README.rdoc
71
- files:
72
71
  - CHANGELOG.rdoc
72
+ files:
73
73
  - README.rdoc
74
- - grackle.gemspec
75
- - lib/grackle.rb
74
+ - CHANGELOG.rdoc
76
75
  - lib/grackle/client.rb
77
76
  - lib/grackle/handlers.rb
78
77
  - lib/grackle/transport.rb
79
78
  - lib/grackle/utils.rb
80
- - test/test_grackle.rb
79
+ - lib/grackle/version.rb
80
+ - lib/grackle.rb
81
+ - test/client_test.rb
82
+ - test/handlers_test.rb
81
83
  - test/test_helper.rb
82
- - test/test_client.rb
83
- - test/test_handlers.rb
84
84
  has_rdoc: true
85
85
  homepage: http://github.com/hayesdavis/grackle
86
86
  licenses: []
87
87
 
88
88
  post_install_message:
89
- rdoc_options:
90
- - --inline-source
91
- - --charset=UTF-8
92
- - --main=README.rdoc
89
+ rdoc_options: []
90
+
93
91
  require_paths:
94
92
  - lib
95
93
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -112,10 +110,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
110
  version: "0"
113
111
  requirements: []
114
112
 
115
- rubyforge_project: grackle
113
+ rubyforge_project:
116
114
  rubygems_version: 1.3.7
117
115
  signing_key:
118
- specification_version: 2
119
- summary: Grackle is a library for the Twitter REST and Search API designed to not require a new release in the face Twitter API changes or errors. It supports both basic and OAuth authentication mechanisms.
116
+ specification_version: 3
117
+ summary: Grackle is a lightweight library for the Twitter REST and Search API.
120
118
  test_files: []
121
119
 
@@ -1,40 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
-
3
- Gem::Specification.new do |s|
4
- s.name = %q{grackle}
5
- s.version = "0.1.10"
6
-
7
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
- s.authors = ["Hayes Davis"]
9
- s.date = %q{2010-6-13}
10
- s.description = %q{Grackle is a lightweight library for the Twitter REST and Search API.}
11
- s.email = %q{hayes@appozite.com}
12
- s.files = ["CHANGELOG.rdoc", "README.rdoc", "grackle.gemspec", "lib/grackle.rb", "lib/grackle/client.rb", "lib/grackle/handlers.rb", "lib/grackle/transport.rb", "lib/grackle/utils.rb", "test/test_grackle.rb", "test/test_helper.rb", "test/test_client.rb", "test/test_handlers.rb"]
13
- s.has_rdoc = true
14
- s.homepage = %q{http://github.com/hayesdavis/grackle}
15
- s.rdoc_options = ["--inline-source", "--charset=UTF-8","--main=README.rdoc"]
16
- s.extra_rdoc_files = ['README.rdoc']
17
- s.require_paths = ["lib"]
18
- s.rubyforge_project = %q{grackle}
19
- s.rubygems_version = %q{1.3.1}
20
- s.summary = %q{Grackle is a library for the Twitter REST and Search API designed to not require a new release in the face Twitter API changes or errors. It supports both basic and OAuth authentication mechanisms.}
21
-
22
- if s.respond_to? :specification_version then
23
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
- s.specification_version = 2
25
-
26
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
- s.add_runtime_dependency(%q<json>, [">= 0"])
28
- s.add_dependency(%q<mime-types>, [">= 0"])
29
- s.add_dependency(%q<oauth>, [">= 0"])
30
- else
31
- s.add_dependency(%q<json>, [">= 0"])
32
- s.add_dependency(%q<mime-types>, [">= 0"])
33
- s.add_dependency(%q<oauth>, [">= 0"])
34
- end
35
- else
36
- s.add_dependency(%q<json>, [">= 0"])
37
- s.add_dependency(%q<mime-types>, [">= 0"])
38
- s.add_dependency(%q<oauth>, [">= 0"])
39
- end
40
- end
@@ -1,4 +0,0 @@
1
- Dir.glob("#{File.dirname(__FILE__)}/*.rb").each do |file|
2
- require file
3
- end
4
-