grackle 0.1.9 → 0.1.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,7 @@
1
+ == 0.1.10 (2010-6-13)
2
+ * Changed :v1 (api.twitter.com/1) to be the default API instead of REST. :rest is now deprecated
3
+ * 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").
4
+
1
5
  == 0.1.9 (2010-2-28)
2
6
  * Added support for a boolean option called auto_append_ids that controls whether parameters named "id" are automatically appended to the URL or used as request parameters. It's true by default so there's no change to existing behavior unless you explicitly set it to false.
3
7
  * Updated the README with changes describing how to use custom handlers (thanks akahn!)
@@ -102,27 +102,22 @@ parameter to your method chain called :__method (note the double underscores) to
102
102
  client.direct_messages.destroy! :id=>1, :__method=>:delete #HTTP DELETE
103
103
 
104
104
  ===Toggling APIs
105
- By default, the Grackle::Client sends all requests to the unversioned Twitter REST API. If you want to send requests to
106
- the Twitter Search API, just set Grackle::Client.api to :search. To toggle back, set it to be :rest. All requests made
105
+ DEPRECATION NOTICE: The :rest API key (pointing to twitter.com) has been
106
+ deprecated in favor of the versioned API :v1 (pointing to api.twitter.com/1).
107
+ :v1 is now the default if you do not specify an API. Please do not use :rest
108
+ in your code.
109
+
110
+ By default, the Grackle::Client sends all requests to the versioned Twitter REST API. If you want to send requests to
111
+ the Twitter Search API, just set Grackle::Client.api to :search. To toggle back, set it to be :v1. All requests made
107
112
  after setting this attribute will go to that API.
108
113
 
109
114
  If you want to make a specific request to one API and not change the Client's overall api setting beyond that request, you can use the
110
115
  bracket syntax like so:
111
116
  client[:search].trends.daily? :exclude=>'hashtags'
112
- client[:rest].users.show? :id=>'hayesdavis'
117
+ client[:v1].users.show? :id=>'hayesdavis'
113
118
 
114
119
  Search and REST requests are all built using the same method chaining and termination conventions.
115
120
 
116
- Twitter is introducing API versioning. Grackle now supports the version 1 API using the :v1 API name. When using the :v1
117
- API, resources that were previously separated between the search and REST APIs are available under one unified API. To
118
- use the :v1 API, do the following:
119
- client[:v1].search? :q=>'grackle'
120
- client[:v1].users.show? :id=>'hayesdavis'
121
-
122
- If you want to use the :v1 API for all requests, you may set Grackle::Client.api to :v1 or specify the :api option in the
123
- Grackle::Client constructor like:
124
- client = Grackle::Client.new(:api=>:v1)
125
-
126
121
  ===Parameter handling
127
122
  - All parameters are URL encoded as necessary.
128
123
  - If you use a File object as a parameter it will be POSTed to Twitter in a multipart request.
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{grackle}
5
- s.version = "0.1.9"
5
+ s.version = "0.1.10"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Hayes Davis"]
9
- s.date = %q{2010-2-28}
9
+ s.date = %q{2010-6-13}
10
10
  s.description = %q{Grackle is a lightweight library for the Twitter REST and Search API.}
11
11
  s.email = %q{hayes@appozite.com}
12
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"]
@@ -1,7 +1,7 @@
1
1
  module Grackle
2
2
 
3
3
  # :stopdoc:
4
- VERSION = '0.1.9'
4
+ VERSION = '0.1.10'
5
5
  LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
6
6
  PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
7
7
  # :startdoc:
@@ -86,10 +86,9 @@ 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
- :rest=>'twitter.com',
90
- :search=>'search.twitter.com',
91
- :v1=>'api.twitter.com/1'
89
+ :search=>'search.twitter.com', :v1=>'api.twitter.com/1'
92
90
  }
91
+ TWITTER_API_HOSTS[:rest] = TWITTER_API_HOSTS[:v1]
93
92
 
94
93
  #Basic OAuth information needed to communicate with Twitter
95
94
  TWITTER_OAUTH_SPEC = {
@@ -108,7 +107,7 @@ module Grackle
108
107
  # - :default_format - Symbol of format to use when no format is specified in an API call (e.g. :json, :xml)
109
108
  # - :headers - Hash of string keys and values for headers to pass in the HTTP request to twitter
110
109
  # - :ssl - true or false to turn SSL on or off. Default is off (i.e. http://)
111
- # - :api - one of :rest, :search or :v1. :rest is the default
110
+ # - :api - one of :rest, :search or :v1. :v1 is the default and :rest is now deprecated
112
111
  # - :auth - Hash of authentication type and credentials. Must have :type key with value one of :basic or :oauth
113
112
  # - :type=>:basic - Include :username and :password keys
114
113
  # - :type=>:oauth - Include :consumer_key, :consumer_secret, :token and :token_secret keys
@@ -119,7 +118,7 @@ module Grackle
119
118
  self.default_format = options[:default_format] || :json
120
119
  self.headers = {"User-Agent"=>"Grackle/#{Grackle::VERSION}"}.merge!(options[:headers]||{})
121
120
  self.ssl = options[:ssl] == true
122
- self.api = options[:api] || :rest
121
+ self.api = options[:api] || :v1
123
122
  self.api_hosts = TWITTER_API_HOSTS.clone
124
123
  self.timeout = options[:timeout] || 60
125
124
  self.auto_append_ids = options[:auto_append_ids] == false ? false : true
@@ -126,7 +126,7 @@ module Grackle
126
126
  end
127
127
 
128
128
  def add_form_data(req,params)
129
- if req.request_body_permitted? && params
129
+ if request_body_permitted?(req) && params
130
130
  req.set_form_data(params)
131
131
  end
132
132
  end
@@ -236,5 +236,13 @@ EOS
236
236
  end
237
237
  end
238
238
  end
239
+
240
+ # Methods like Twitter's DELETE list membership expect that the user id
241
+ # will be form encoded like a POST request in the body. Net::HTTP seems
242
+ # to think that DELETEs can't have body parameters so we have to work
243
+ # around that.
244
+ def request_body_permitted?(req)
245
+ req.request_body_permitted? || req.kind_of?(Net::HTTP::Delete)
246
+ end
239
247
  end
240
248
  end
@@ -118,8 +118,8 @@ class TestClient < Test::Unit::TestCase
118
118
  assert_equal(:get,client.transport.method)
119
119
  assert_equal('http',client.transport.url.scheme)
120
120
  assert(!Net::HTTP.last_instance.use_ssl?,'Net::HTTP instance should not be set to use SSL')
121
- assert_equal('twitter.com',client.transport.url.host)
122
- assert_equal('/users/show.json',client.transport.url.path)
121
+ assert_equal('api.twitter.com',client.transport.url.host)
122
+ assert_equal('/1/users/show.json',client.transport.url.path)
123
123
  assert_equal('test_user',client.transport.options[:params][:screen_name])
124
124
  assert_equal('screen_name=test_user',Net::HTTP.request.path.split(/\?/)[1])
125
125
  assert_equal(12345,value.id)
@@ -174,7 +174,7 @@ class TestClient < Test::Unit::TestCase
174
174
  assert_equal('search.twitter.com',client.transport.url.host)
175
175
 
176
176
  client[:rest].users.show.some_user?
177
- assert_equal('twitter.com',client.transport.url.host)
177
+ assert_equal('api.twitter.com',client.transport.url.host)
178
178
 
179
179
  client.api = :search
180
180
  client.trends?
@@ -207,7 +207,7 @@ class TestClient < Test::Unit::TestCase
207
207
  def test_clear
208
208
  client = new_client(200,'[{"id":1,"text":"test 1"}]')
209
209
  client.some.url.that.does.not.exist
210
- assert_equal('/some/url/that/does/not/exist',client.send(:request).path,"An unexecuted path should be build up")
210
+ assert_equal('/some/url/that/does/not/exist',client.send(:request).path,"An unexecuted path should be built up")
211
211
  client.clear
212
212
  assert_equal('',client.send(:request).path,"The path shoudl be cleared")
213
213
  end
@@ -222,14 +222,14 @@ class TestClient < Test::Unit::TestCase
222
222
  client = new_client(200,'[{"id":1,"text":"test 1"}]')
223
223
  time = Time.now-60*60
224
224
  client.statuses.public_timeline? :since=>time
225
- assert_equal("/statuses/public_timeline.json?since=#{CGI::escape(time.httpdate)}",Net::HTTP.request.path)
225
+ assert_equal("/1/statuses/public_timeline.json?since=#{CGI::escape(time.httpdate)}",Net::HTTP.request.path)
226
226
  end
227
227
 
228
228
  def test_simple_http_method_block
229
229
  client = new_client(200,'[{"id":1,"text":"test 1"}]')
230
230
  client.delete { direct_messages.destroy :id=>1, :other=>'value' }
231
231
  assert_equal(:delete,client.transport.method, "delete block should use delete method")
232
- assert_equal("/direct_messages/destroy/1.json",Net::HTTP.request.path)
232
+ assert_equal("/1/direct_messages/destroy/1.json",Net::HTTP.request.path)
233
233
  assert_equal('value',client.transport.options[:params][:other])
234
234
 
235
235
  client = new_client(200,'{"id":54321,"screen_name":"test_user"}')
@@ -237,8 +237,8 @@ class TestClient < Test::Unit::TestCase
237
237
  assert_equal(:get,client.transport.method)
238
238
  assert_equal('http',client.transport.url.scheme)
239
239
  assert(!Net::HTTP.last_instance.use_ssl?,'Net::HTTP instance should not be set to use SSL')
240
- assert_equal('twitter.com',client.transport.url.host)
241
- assert_equal('/users/show.json',client.transport.url.path)
240
+ assert_equal('api.twitter.com',client.transport.url.host)
241
+ assert_equal('/1/users/show.json',client.transport.url.path)
242
242
  assert_equal('test_user',client.transport.options[:params][:screen_name])
243
243
  assert_equal('screen_name=test_user',Net::HTTP.request.path.split(/\?/)[1])
244
244
  assert_equal(54321,value.id)
@@ -270,8 +270,8 @@ class TestClient < Test::Unit::TestCase
270
270
  assert_equal(:get,client.transport.method)
271
271
  assert_equal('http',client.transport.url.scheme)
272
272
  assert(!Net::HTTP.last_instance.use_ssl?,'Net::HTTP instance should not be set to use SSL')
273
- assert_equal('twitter.com',client.transport.url.host)
274
- assert_equal('/users/show/12345.json',client.transport.url.path)
273
+ assert_equal('api.twitter.com',client.transport.url.host)
274
+ assert_equal('/1/users/show/12345.json',client.transport.url.path)
275
275
  assert_equal(12345,value.id)
276
276
  end
277
277
 
@@ -296,10 +296,10 @@ class TestClient < Test::Unit::TestCase
296
296
  def test_auto_append_ids_is_honored
297
297
  client = new_client(200,'{"id":12345,"screen_name":"test_user"}')
298
298
  client.users.show.json? :id=>12345
299
- assert_equal('/users/show/12345.json',client.transport.url.path,"Id should be appended by default")
299
+ assert_equal('/1/users/show/12345.json',client.transport.url.path,"Id should be appended by default")
300
300
  client.auto_append_ids = false
301
301
  client.users.show.json? :id=>12345
302
- assert_equal('/users/show.json',client.transport.url.path,"Id should not be appended")
302
+ assert_equal('/1/users/show.json',client.transport.url.path,"Id should not be appended")
303
303
  assert_equal(12345,client.transport.options[:params][:id], "Id should be treated as a parameter")
304
304
  assert_equal("id=#{12345}",Net::HTTP.request.path.split(/\?/)[1],"id should be part of the query string")
305
305
  end
@@ -307,11 +307,29 @@ class TestClient < Test::Unit::TestCase
307
307
  def test_auto_append_ids_can_be_set_in_constructor
308
308
  client = new_client(200,'{"id":12345,"screen_name":"test_user"}',:auto_append_ids=>false)
309
309
  client.users.show.json? :id=>12345
310
- assert_equal('/users/show.json',client.transport.url.path,"Id should not be appended")
310
+ assert_equal('/1/users/show.json',client.transport.url.path,"Id should not be appended")
311
311
  assert_equal(12345,client.transport.options[:params][:id], "Id should be treated as a parameter")
312
312
  assert_equal("id=#{12345}",Net::HTTP.request.path.split(/\?/)[1],"id should be part of the query string")
313
313
  end
314
314
 
315
+ def test_default_api
316
+ client = Grackle::Client.new
317
+ assert_equal(:v1,client.api,":v1 should be default api")
318
+ end
319
+
320
+ # Methods like Twitter's DELETE list membership expect that the user id will
321
+ # be form encoded like a POST request in the body. Net::HTTP seems to think
322
+ # that DELETEs can't have body parameters so we have to work around that.
323
+ def test_delete_can_send_body_parameters
324
+ client = new_client(200,'{"id":12345,"name":"Test List","members":0}')
325
+ client.delete { some_user.some_list.members? :user_id=>12345 }
326
+ assert_equal(:delete,client.transport.method,"Expected delete request")
327
+ assert_equal('http',client.transport.url.scheme,"Expected scheme to be http")
328
+ assert_equal('api.twitter.com',client.transport.url.host,"Expected request to be against twitter.com")
329
+ assert_equal('/1/some_user/some_list/members.json',client.transport.url.path)
330
+ assert_match(/user_id=12345/,Net::HTTP.request.body,"Parameters should be form encoded")
331
+ end
332
+
315
333
  private
316
334
  def with_http_responder(responder)
317
335
  Net::HTTP.responder = responder
@@ -331,8 +349,8 @@ class TestClient < Test::Unit::TestCase
331
349
  value = client.statuses.update! :status=>'test status'
332
350
  assert_equal(:post,client.transport.method,"Expected post request")
333
351
  assert_equal('http',client.transport.url.scheme,"Expected scheme to be http")
334
- assert_equal('twitter.com',client.transport.url.host,"Expected request to be against twitter.com")
335
- assert_equal('/statuses/update.json',client.transport.url.path)
352
+ assert_equal('api.twitter.com',client.transport.url.host,"Expected request to be against twitter.com")
353
+ assert_equal('/1/statuses/update.json',client.transport.url.path)
336
354
  assert_match(/status=test%20status/,Net::HTTP.request.body,"Parameters should be form encoded")
337
355
  assert_equal(12345,value.id)
338
356
  yield(client) if block_given?
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grackle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ hash: 15
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 10
10
+ version: 0.1.10
5
11
  platform: ruby
6
12
  authors:
7
13
  - Hayes Davis
@@ -9,39 +15,51 @@ autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2010-02-28 00:00:00 -06:00
18
+ date: 2010-06-13 00:00:00 -05:00
13
19
  default_executable:
14
20
  dependencies:
15
21
  - !ruby/object:Gem::Dependency
16
22
  name: json
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
20
26
  requirements:
21
27
  - - ">="
22
28
  - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
23
32
  version: "0"
24
- version:
33
+ type: :runtime
34
+ version_requirements: *id001
25
35
  - !ruby/object:Gem::Dependency
26
36
  name: mime-types
27
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
30
40
  requirements:
31
41
  - - ">="
32
42
  - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
33
46
  version: "0"
34
- version:
47
+ type: :runtime
48
+ version_requirements: *id002
35
49
  - !ruby/object:Gem::Dependency
36
50
  name: oauth
37
- type: :runtime
38
- version_requirement:
39
- version_requirements: !ruby/object:Gem::Requirement
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
40
54
  requirements:
41
55
  - - ">="
42
56
  - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
43
60
  version: "0"
44
- version:
61
+ type: :runtime
62
+ version_requirements: *id003
45
63
  description: Grackle is a lightweight library for the Twitter REST and Search API.
46
64
  email: hayes@appozite.com
47
65
  executables: []
@@ -75,21 +93,27 @@ rdoc_options:
75
93
  require_paths:
76
94
  - lib
77
95
  required_ruby_version: !ruby/object:Gem::Requirement
96
+ none: false
78
97
  requirements:
79
98
  - - ">="
80
99
  - !ruby/object:Gem::Version
100
+ hash: 3
101
+ segments:
102
+ - 0
81
103
  version: "0"
82
- version:
83
104
  required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
84
106
  requirements:
85
107
  - - ">="
86
108
  - !ruby/object:Gem::Version
109
+ hash: 3
110
+ segments:
111
+ - 0
87
112
  version: "0"
88
- version:
89
113
  requirements: []
90
114
 
91
115
  rubyforge_project: grackle
92
- rubygems_version: 1.3.5
116
+ rubygems_version: 1.3.7
93
117
  signing_key:
94
118
  specification_version: 2
95
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.