twitter4r 0.5.3 → 0.6.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.
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require("irb")
4
+ require("irb/completion")
5
+ require("rubygems")
6
+
7
+ begin
8
+ gem('twitter4r', '>0.3.0')
9
+ require("twitter")
10
+ require("twitter/console")
11
+ require("pp")
12
+ rescue Gem::LoadError
13
+ begin
14
+ gem("mbbx6spp-twitter4r", '>=0.3.1')
15
+ require("twitter")
16
+ require("twitter/console")
17
+ require("pp")
18
+ rescue Gem::LoadError
19
+ abort("Error: You must install either twitter4r gem from Rubyforge with version 0.3.1 or greater or the mbbx6spp-twitter4r gem from GitHub's servers with version 0.3.1 or greater (and make sure it is a recent version of the gem).")
20
+ end
21
+ end
22
+
23
+ module Twitter
24
+ class OAuthAccess
25
+ class << self
26
+ @@OPTIONS = { :key => "", :secret => "" }
27
+ def parse_options!
28
+ OptionParser.new do |opt|
29
+ opt.banner = "Usage: t4r-oauth-access [environment] [options]"
30
+ opt.on("--key=[<YOUR KEY>]", 'Use this OAuth consumer key.') { |v| @@OPTIONS[:key] = v }
31
+ opt.on("--secret=[<YOUR SECRET>]", 'Use this OAuth consumer secret.') { |v| @@OPTIONS[:secret] = v }
32
+ opt.parse!(ARGV)
33
+ end
34
+ end
35
+
36
+ def config_file
37
+ result = ENV["T4R_CONFIG"]
38
+ file_name = File.expand_path('twitter.yml')
39
+ result ||= file_name if File.exists?(file_name)
40
+ file_name = File.expand_path('twitter.yml', 'config')
41
+ result ||= file_name if File.exists?(file_name)
42
+ file_name = File.expand_path('~/.twitter.yml')
43
+ result ||= file_name if File.exists?(file_name)
44
+ result
45
+ end
46
+
47
+ def account
48
+ ENV["T4R_ENV"] || ENV["MERB_ENV"] || ENV["RAILS_ENV"]
49
+ end
50
+
51
+ def run()
52
+ # TODO: fill in here
53
+ consumer = OAuth::Consumer.new(@@OPTIONS[:key], @@OPTIONS[:secret],
54
+ :site => "https://twitter.com")
55
+ rtoken = consumer.get_request_token
56
+ puts "1. Visit this URL to grant your application authorization:"
57
+ puts " >> #{rtoken.authorize_url}"
58
+ puts "2. When you have authorized your application to access your account, Twitter.com will display a PIN to you."
59
+ print "3. Enter the PIN here: "
60
+ pin = STDIN.readline.chomp
61
+ atoken = rtoken.get_access_token(:oauth_verifier => pin)
62
+ puts "4. Your access token details are:"
63
+ puts " >> key: #{atoken.token}"
64
+ puts " >> secret: #{atoken.secret}"
65
+ resp = atoken.get("/account/verify_credentials.json")
66
+ data = JSON.parse(resp.body)
67
+ puts
68
+ puts "Your account details are:"
69
+ p data
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ def run_twitter4r_oauth_registration!
76
+ @twitter = nil
77
+ Twitter::OAuthAccess.parse_options!
78
+ config_file = Twitter::OAuthAccess.config_file
79
+ account = Twitter::OAuthAccess.account
80
+
81
+ if config_file && account
82
+ @twitter = Twitter::Client.from_config(config_file, account)
83
+ puts "Used #{config_file} to create client for #{account} account."
84
+ puts "Follow instructions below to grant authorization to your application and determine access token details."
85
+ Twitter::OAuthAccess.run()
86
+ else
87
+ abort("Please make sure #{config_file} exists and contains your Twitter credentials (separated by account/environment) and that you specify the account/environment to use, e.g. if you have a 'test' section in your configuration file that you want to use set/export T4R_ENV=test as an environment variable or RAILS_ENV=test or MERB_ENV=test")
88
+ end
89
+ end
90
+
91
+ run_twitter4r_oauth_registration!
data/bin/t4rsh ADDED
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require("irb")
4
+ require("irb/completion")
5
+ require("rubygems")
6
+
7
+ begin
8
+ gem('twitter4r', '>0.3.0')
9
+ require("twitter")
10
+ require("twitter/console")
11
+ rescue Gem::LoadError
12
+ begin
13
+ gem("mbbx6spp-twitter4r", '>=0.3.1')
14
+ require("twitter")
15
+ require("twitter/console")
16
+ rescue Gem::LoadError
17
+ abort("Error: You must install either twitter4r gem from Rubyforge with version 0.3.1 or greater or the mbbx6spp-twitter4r gem from GitHub's servers with version 0.3.1 or greater (and make sure it is a recent version of the gem).")
18
+ end
19
+ end
20
+
21
+ module Twitter
22
+ class Console
23
+ class << self
24
+ def config_file
25
+ result = ENV["T4R_CONFIG"]
26
+ file_name = File.expand_path('twitter.yml')
27
+ result ||= file_name if File.exists?(file_name)
28
+ file_name = File.expand_path('twitter.yml', 'config')
29
+ result ||= file_name if File.exists?(file_name)
30
+ file_name = File.expand_path('~/.twitter.yml')
31
+ result ||= file_name if File.exists?(file_name)
32
+ result
33
+ end
34
+
35
+ def account
36
+ ENV["T4R_ENV"] || ENV["MERB_ENV"] || ENV["RAILS_ENV"]
37
+ end
38
+
39
+ def run(file)
40
+ IRB.init_config(nil)
41
+ # configuration...
42
+ IRB.conf[:IRB_NAME] = "t4rsh"
43
+ IRB.conf[:VERSION] = Twitter::Version.to_version
44
+ IRB.conf[:USE_READLINE] = true
45
+ IRB.conf[:PROMPT_MODE] = :T4RSH
46
+ IRB.conf[:PROMPT][:T4RSH] = {
47
+ :PROMPT_I => "%N[%3n:%i]> ", # top level prompt
48
+ :PROMPT_C => "%N[%3n:%i]* ", # after conditional like "if"
49
+ :PROMPT_S => "%N[%3n:%i]* ", # during continuing string
50
+ :RETURN => "=> %s\n", # return value
51
+ }
52
+ IRB.start(file)
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ def run_twitter4r_console!
59
+ @twitter = nil
60
+ config_file = Twitter::Console.config_file
61
+ account = Twitter::Console.account
62
+
63
+ if config_file && account
64
+ @twitter = Twitter::Client.from_config(config_file, account)
65
+ puts "Used #{config_file} to create client for #{account} account."
66
+ puts "Access @twitter for instantiated client."
67
+ Twitter::Console.run(__FILE__)
68
+ else
69
+ abort("Please make sure #{config_file} exists and contains your Twitter credentials (separated by account/environment) and that you specify the account/environment to use, e.g. if you have a 'test' section in your configuration file that you want to use set/export T4R_ENV=test as an environment variable or RAILS_ENV=test or MERB_ENV=test")
70
+ end
71
+ end
72
+
73
+ run_twitter4r_console!
@@ -46,7 +46,7 @@ class Twitter::Client
46
46
  end
47
47
 
48
48
  private
49
- @@http_header = nil
49
+ @@http_header = nil
50
50
 
51
51
  def rest_consumer
52
52
  unless @rest_consumer
@@ -113,10 +113,11 @@ class Twitter::Client
113
113
 
114
114
  def raise_rest_error(response, uri = nil)
115
115
  map = JSON.parse(response.body)
116
- raise Twitter::RESTError.new(:code => response.code,
117
- :message => response.message,
118
- :error => map["error"],
119
- :uri => uri)
116
+ error = Twitter::RESTError.registry[response.code]
117
+ raise error.new(:code => response.code,
118
+ :message => response.message,
119
+ :error => map["error"],
120
+ :uri => uri)
120
121
  end
121
122
 
122
123
  def handle_rest_response(response, uri = nil)
@@ -55,6 +55,7 @@ module Twitter
55
55
  :oauth_request_token_path,
56
56
  :oauth_access_token_path,
57
57
  :oauth_authorize_path,
58
+ :exception_registry,
58
59
  ]
59
60
 
60
61
  attr_accessor(*@@ATTRIBUTES)
@@ -91,6 +92,7 @@ module Twitter
91
92
  :oauth_request_token_path => '/oauth/request_token',
92
93
  :oauth_access_token_path => '/oauth/access_token',
93
94
  :oauth_authorize_path => '/oauth/authorize',
95
+ :exception_registry => Twitter::RESTError.registry,
94
96
  }
95
97
  @@config = Twitter::Config.new(@@defaults)
96
98
 
data/lib/twitter/core.rb CHANGED
@@ -84,7 +84,7 @@ module Twitter
84
84
  end
85
85
  end # ClassUtilMixin
86
86
 
87
- # Exception subclass raised when there is an error encountered upon
87
+ # Exception API base class raised when there is an error encountered upon
88
88
  # querying or posting to the remote Twitter REST API.
89
89
  #
90
90
  # To consume and query any <tt>RESTError</tt> raised by Twitter4R:
@@ -100,6 +100,18 @@ module Twitter
100
100
  # Resource Not Found
101
101
  # /i_am_crap.json
102
102
  class RESTError < RuntimeError
103
+ class << self
104
+ @@REGISTRY = {}
105
+
106
+ def registry
107
+ @@REGISTRY
108
+ end
109
+
110
+ def register(status_code)
111
+ @@REGISTRY[status_code] = self
112
+ end
113
+ end
114
+
103
115
  include ClassUtilMixin
104
116
  @@ATTRIBUTES = [:code, :message, :uri, :error]
105
117
  attr_accessor :code, :message, :uri, :error
@@ -107,31 +119,126 @@ module Twitter
107
119
  # Returns string in following format:
108
120
  # "HTTP #{@code}: #{@message} at #{@uri}"
109
121
  # For example,
110
- # "HTTP 404: Resource Not Found at /i_am_crap.json"
122
+ # "HTTP 404: Resource Not Found at /i_am_crap.json
123
+ # >This is the error message sent back by the Twitter.com API"
111
124
  def to_s
112
125
  "HTTP #{@code}: #{@message} at #{@uri}"
113
126
  end
114
127
  end # RESTError
128
+
129
+ # Runtime error leaf class raised when Twitter.com API has no new results
130
+ # to return from the last query. HTTP code: 304 (aka Not Modified).
131
+ #
132
+ # To handle specifically you would do the following:
133
+ # begin
134
+ # timeline = twitter.timeline_for(:friends, :since => tweet)
135
+ # rescue NotModifiedError => nme
136
+ # timeline = []
137
+ # end
138
+ class NotModifiedError < RESTError; register('304'); end
115
139
 
116
- # Remote REST API interface representation
117
- #
118
- class RESTInterfaceSpec
119
- include ClassUtilMixin
120
-
121
- end
140
+ # Runtime error leaf class raised when client has reached rate limits.
141
+ # HTTP code: 400 (aka Bad Request).
142
+ #
143
+ # To handle specifically you would do the following:
144
+ # begin
145
+ # timeline = twitter.timeline_for(:friends, :since => tweet)
146
+ # rescue RateLimitError => rlre
147
+ # # do something here...
148
+ # end
149
+ class RateLimitError < RESTError; register('400'); end
122
150
 
123
- # Remote REST API method representation
124
- #
125
- class RESTMethodSpec
126
- include ClassUtilMixin
127
- attr_accessor :uri, :method, :parameters
128
- end
151
+ # Runtime error leaf class raised when user and/or client credentials
152
+ # are missing or invalid.
153
+ # HTTP code: 401 (aka Unauthorized).
154
+ #
155
+ # To handle specifically you would do the following:
156
+ # begin
157
+ # timeline = twitter.timeline_for(:friends, :since => tweet)
158
+ # rescue UnauthorizedError => uae
159
+ # # do something to prompt for valid credentials to user here.
160
+ # end
161
+ class UnauthorizedError < RESTError; register('401'); end
129
162
 
130
- # Remote REST API method parameter representation
131
- #
132
- class RESTParameterSpec
133
- include ClassUtilMixin
134
- attr_accessor :name, :type, :required
135
- def required?; @required; end
136
- end
163
+ # Runtime error leaf class raised when update limit reached.
164
+ # HTTP code: 403 (aka Forbidden).
165
+ #
166
+ # To handle specifically you would do the following:
167
+ # begin
168
+ # timeline = twitter.timeline_for(:friends, :since => tweet)
169
+ # rescue ForbiddenError => fe
170
+ # # do something to notify user that update limit has been reached
171
+ # end
172
+ class ForbiddenError < RESTError; register('403'); end
173
+
174
+ # Runtime error leaf class raised when a resource requested was not found.
175
+ # HTTP code: 404 (aka Not Found).
176
+ #
177
+ # To handle specifically you would do the following:
178
+ # begin
179
+ # timeline = twitter.timeline_for(:friends, :since => tweet)
180
+ # rescue NotFoundError => nfe
181
+ # # do something to notify user that resource was not found.
182
+ # end
183
+ class NotFoundError < RESTError; register('404'); end
184
+
185
+ # Runtime error leaf class raised when the format specified in the request
186
+ # is not understood by the Twitter.com API.
187
+ # HTTP code: 406 (aka Not Acceptable).
188
+ #
189
+ # To handle specifically you would do the following:
190
+ # begin
191
+ # timeline = twitter.timeline_for(:friends, :since => tweet)
192
+ # rescue NotAcceptableError => nae
193
+ # #
194
+ # end
195
+ class NotAcceptableError < RESTError; register('406'); end
196
+
197
+ # Runtime error leaf class raised when search rate limit reached.
198
+ # HTTP code: 420.
199
+ #
200
+ # To handle specifically you would do the following:
201
+ # begin
202
+ # timeline = twitter.timeline_for(:friends, :since => tweet)
203
+ # rescue SearchRateLimitError => nme
204
+ # #
205
+ # end
206
+ class SearchRateLimitError < RESTError; register('420'); end
207
+
208
+ # Runtime error leaf class raised when Twitter.com API is borked for
209
+ # an unknown reason.
210
+ # HTTP code: 500 (aka Internal Server Error).
211
+ #
212
+ # To handle specifically you would do the following:
213
+ # begin
214
+ # timeline = twitter.timeline_for(:friends, :since => tweet)
215
+ # rescue InternalServerError => ise
216
+ # # do something to notify user that an unknown internal server error
217
+ # # has arisen.
218
+ # end
219
+ class InternalServerError < RESTError; register('500'); end
220
+
221
+ # Runtime error leaf class raised when Twitter.com servers are being
222
+ # upgraded.
223
+ # HTTP code: 502 (aka Bad Gateway).
224
+ #
225
+ # To handle specifically you would do the following:
226
+ # begin
227
+ # timeline = twitter.timeline_for(:friends, :since => tweet)
228
+ # rescue BadGatewayError => bge
229
+ # #
230
+ # end
231
+ class BadGatewayError < RESTError; register('502'); end
232
+
233
+ # Runtime error leaf class raised when Twitter.com servers are unable
234
+ # to respond to the current load.
235
+ # HTTP code: 502 (aka Service Unavailable).
236
+ #
237
+ # To handle specifically you would do the following:
238
+ # begin
239
+ # timeline = twitter.timeline_for(:friends, :since => tweet)
240
+ # rescue ServiceUnavailableError => sue
241
+ # #
242
+ # end
243
+ class ServiceUnavailableError < RESTError; register('503'); end
137
244
  end
data/lib/twitter/model.rb CHANGED
@@ -225,9 +225,9 @@ module Twitter
225
225
  # Represents a status posted to <tt>Twitter</tt> by a <tt>Twitter</tt> user.
226
226
  class Status
227
227
  include ModelMixin
228
- @@ATTRIBUTES = [:id, :text, :source, :truncated, :created_at, :user, :from_user, :to_user,
229
- :favorited, :in_reply_to_status_id, :in_reply_to_user_id,
230
- :in_reply_to_screen_name, :geo]
228
+ @@ATTRIBUTES = [:id, :id_str, :text, :source, :truncated, :created_at, :user,
229
+ :from_user, :to_user, :favorited, :in_reply_to_status_id,
230
+ :in_reply_to_user_id, :in_reply_to_screen_name, :geo]
231
231
  attr_accessor(*@@ATTRIBUTES)
232
232
 
233
233
  class << self
@@ -3,8 +3,8 @@
3
3
 
4
4
  module Twitter::Version #:nodoc:
5
5
  MAJOR = 0
6
- MINOR = 5
7
- REVISION = 3
6
+ MINOR = 6
7
+ REVISION = 0
8
8
  class << self
9
9
  # Returns X.Y.Z formatted version string
10
10
  def to_version
@@ -27,15 +27,28 @@ describe "Twitter::ClassUtilMixin mixed-in class" do
27
27
  end
28
28
  end
29
29
 
30
- describe "Twitter::RESTError#to_s" do
31
- before(:each) do
32
- @hash = { :code => 200, :message => 'OK', :uri => 'http://test.host/bla' }
33
- @error = Twitter::RESTError.new(@hash)
34
- @expected_message = "HTTP #{@hash[:code]}: #{@hash[:message]} at #{@hash[:uri]}"
30
+ describe Twitter::RESTError do
31
+ describe "#to_s" do
32
+ before(:each) do
33
+ @hash = { :code => 200, :message => 'OK', :uri => 'http://test.host/bla' }
34
+ @error = Twitter::RESTError.new(@hash)
35
+ @expected_message = "HTTP #{@hash[:code]}: #{@hash[:message]} at #{@hash[:uri]}"
36
+ end
37
+
38
+ it "should return @expected_message" do
39
+ @error.to_s.should eql(@expected_message)
40
+ end
35
41
  end
36
-
37
- it "should return @expected_message" do
38
- @error.to_s.should eql(@expected_message)
42
+
43
+ describe ".register" do
44
+ before(:each) do
45
+ @status_code = '999'
46
+ class MyCustomError < Twitter::RESTError; register('999'); end
47
+ end
48
+
49
+ it "should register a new RESTError subclass with a status code" do
50
+ described_class.registry[@status_code].should eql(MyCustomError)
51
+ end
39
52
  end
40
53
  end
41
54
 
@@ -125,3 +138,67 @@ describe "Twitter::ClassUtilMixin#require_block" do
125
138
  @test_subject = nil
126
139
  end
127
140
  end
141
+
142
+ shared_examples_for "REST error returned" do
143
+ before(:each) do
144
+ @twitter = client_context
145
+ @connection = mas_net_http(mas_net_http_response(error_response_code))
146
+ end
147
+
148
+ it "should raise relevant RuntimeError subclass" do
149
+ lambda {
150
+ @twitter.account_info
151
+ }.should raise_error(described_class)
152
+ end
153
+ end
154
+
155
+ describe Twitter::NotModifiedError do
156
+ def error_response_code; :not_modified; end
157
+ it_should_behave_like "REST error returned"
158
+ end
159
+
160
+ describe Twitter::RateLimitError do
161
+ def error_response_code; :bad_request; end
162
+ it_should_behave_like "REST error returned"
163
+ end
164
+
165
+ describe Twitter::UnauthorizedError do
166
+ def error_response_code; :not_authorized; end
167
+ it_should_behave_like "REST error returned"
168
+ end
169
+
170
+ describe Twitter::ForbiddenError do
171
+ def error_response_code; :forbidden; end
172
+ it_should_behave_like "REST error returned"
173
+ end
174
+
175
+ describe Twitter::NotFoundError do
176
+ def error_response_code; :file_not_found; end
177
+ it_should_behave_like "REST error returned"
178
+ end
179
+
180
+ describe Twitter::NotAcceptableError do
181
+ def error_response_code; :not_acceptable; end
182
+ it_should_behave_like "REST error returned"
183
+ end
184
+
185
+ describe Twitter::SearchRateLimitError do
186
+ def error_response_code; :search_rate_limit; end
187
+ it_should_behave_like "REST error returned"
188
+ end
189
+
190
+ describe Twitter::InternalServerError do
191
+ def error_response_code; :server_error; end
192
+ it_should_behave_like "REST error returned"
193
+ end
194
+
195
+ describe Twitter::BadGatewayError do
196
+ def error_response_code; :bad_gateway; end
197
+ it_should_behave_like "REST error returned"
198
+ end
199
+
200
+ describe Twitter::ServiceUnavailableError do
201
+ def error_response_code; :service_unavailable; end
202
+ it_should_behave_like "REST error returned"
203
+ end
204
+
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twitter4r
3
3
  version: !ruby/object:Gem::Version
4
- hash: 13
5
4
  prerelease: false
6
5
  segments:
7
6
  - 0
8
- - 5
9
- - 3
10
- version: 0.5.3
7
+ - 6
8
+ - 0
9
+ version: 0.6.0
11
10
  platform: ruby
12
11
  authors:
13
12
  - Susan Potter
@@ -15,45 +14,44 @@ autorequire: twitter
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2010-09-21 00:00:00 -05:00
17
+ date: 2010-11-03 00:00:00 -05:00
19
18
  default_executable:
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
22
- name: oauth
21
+ name: json
23
22
  prerelease: false
24
23
  requirement: &id001 !ruby/object:Gem::Requirement
25
24
  none: false
26
25
  requirements:
27
26
  - - ">="
28
27
  - !ruby/object:Gem::Version
29
- hash: 13
30
28
  segments:
31
- - 0
32
- - 4
33
29
  - 1
34
- version: 0.4.1
30
+ - 1
31
+ - 1
32
+ version: 1.1.1
35
33
  type: :runtime
36
34
  version_requirements: *id001
37
35
  - !ruby/object:Gem::Dependency
38
- name: json
36
+ name: oauth
39
37
  prerelease: false
40
38
  requirement: &id002 !ruby/object:Gem::Requirement
41
39
  none: false
42
40
  requirements:
43
41
  - - ">="
44
42
  - !ruby/object:Gem::Version
45
- hash: 17
46
43
  segments:
44
+ - 0
45
+ - 4
47
46
  - 1
48
- - 1
49
- - 1
50
- version: 1.1.1
47
+ version: 0.4.1
51
48
  type: :runtime
52
49
  version_requirements: *id002
53
50
  description:
54
51
  email: twitter4r-users@googlegroups.com
55
- executables: []
56
-
52
+ executables:
53
+ - t4rsh
54
+ - t4r-oauth-access
57
55
  extensions: []
58
56
 
59
57
  extra_rdoc_files:
@@ -108,6 +106,8 @@ files:
108
106
  - spec/twitter/meta_spec.rb
109
107
  - spec/twitter/config_spec.rb
110
108
  - spec/twitter/console_spec.rb
109
+ - bin/t4rsh
110
+ - bin/t4r-oauth-access
111
111
  - README
112
112
  - CHANGES
113
113
  - TODO
@@ -126,7 +126,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
126
126
  requirements:
127
127
  - - ">="
128
128
  - !ruby/object:Gem::Version
129
- hash: 59
130
129
  segments:
131
130
  - 1
132
131
  - 8
@@ -137,7 +136,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
137
136
  requirements:
138
137
  - - ">="
139
138
  - !ruby/object:Gem::Version
140
- hash: 3
141
139
  segments:
142
140
  - 0
143
141
  version: "0"