teejayvanslyke-twitter 0.6.11

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.
Files changed (52) hide show
  1. data/History +206 -0
  2. data/License +20 -0
  3. data/Notes +33 -0
  4. data/README.rdoc +19 -0
  5. data/Rakefile +103 -0
  6. data/VERSION.yml +4 -0
  7. data/examples/connect.rb +30 -0
  8. data/examples/friendship_existance.rb +13 -0
  9. data/examples/helpers/config_store.rb +38 -0
  10. data/examples/httpauth.rb +11 -0
  11. data/examples/ids.rb +13 -0
  12. data/examples/search.rb +15 -0
  13. data/examples/timeline.rb +19 -0
  14. data/examples/unauthorized.rb +16 -0
  15. data/examples/update.rb +11 -0
  16. data/examples/user.rb +5 -0
  17. data/lib/twitter.rb +63 -0
  18. data/lib/twitter/base.rb +216 -0
  19. data/lib/twitter/httpauth.rb +27 -0
  20. data/lib/twitter/oauth.rb +41 -0
  21. data/lib/twitter/request.rb +102 -0
  22. data/lib/twitter/search.rb +106 -0
  23. data/lib/twitter/trends.rb +29 -0
  24. data/test/fixtures/firehose.json +1 -0
  25. data/test/fixtures/follower_ids.json +1 -0
  26. data/test/fixtures/friend_ids.json +1 -0
  27. data/test/fixtures/friends_timeline.json +1 -0
  28. data/test/fixtures/rate_limit_exceeded.json +1 -0
  29. data/test/fixtures/replies.json +1 -0
  30. data/test/fixtures/search.json +1 -0
  31. data/test/fixtures/search_from_jnunemaker.json +1 -0
  32. data/test/fixtures/status.json +1 -0
  33. data/test/fixtures/status_show.json +1 -0
  34. data/test/fixtures/trends_current.json +1 -0
  35. data/test/fixtures/trends_current_exclude.json +1 -0
  36. data/test/fixtures/trends_daily.json +1 -0
  37. data/test/fixtures/trends_daily_date.json +1 -0
  38. data/test/fixtures/trends_daily_exclude.json +1 -0
  39. data/test/fixtures/trends_weekly.json +1 -0
  40. data/test/fixtures/trends_weekly_date.json +1 -0
  41. data/test/fixtures/trends_weekly_exclude.json +1 -0
  42. data/test/fixtures/user.json +1 -0
  43. data/test/fixtures/user_timeline.json +1 -0
  44. data/test/test_helper.rb +36 -0
  45. data/test/twitter/base_test.rb +95 -0
  46. data/test/twitter/httpauth_test.rb +76 -0
  47. data/test/twitter/oauth_test.rb +81 -0
  48. data/test/twitter/request_test.rb +217 -0
  49. data/test/twitter/search_test.rb +144 -0
  50. data/test/twitter/trends_test.rb +95 -0
  51. data/test/twitter_test.rb +38 -0
  52. metadata +202 -0
@@ -0,0 +1,11 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
2
+ require File.join(File.dirname(__FILE__), 'helpers', 'config_store')
3
+ require 'pp'
4
+
5
+ config = ConfigStore.new("#{ENV['HOME']}/.twitter")
6
+
7
+ httpauth = Twitter::HTTPAuth.new(config['email'], config['password'])
8
+ base = Twitter::Base.new(httpauth)
9
+
10
+ pp base.user_timeline
11
+ pp base.verify_credentials
@@ -0,0 +1,13 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
2
+ require File.join(File.dirname(__FILE__), 'helpers', 'config_store')
3
+ require 'pp'
4
+
5
+ config = ConfigStore.new("#{ENV['HOME']}/.twitter")
6
+
7
+ oauth = Twitter::OAuth.new(config['token'], config['secret'])
8
+ oauth.authorize_from_access(config['atoken'], config['asecret'])
9
+
10
+ client = Twitter::Base.new(oauth)
11
+
12
+ puts client.friend_ids
13
+ puts client.follower_ids
@@ -0,0 +1,15 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
2
+ require 'pp'
3
+
4
+ search = Twitter::Search.new.from('jnunemaker')
5
+
6
+ puts '*'*50, 'First Run', '*'*50
7
+ search.each { |result| pp result }
8
+
9
+ puts '*'*50, 'Second Run', '*'*50
10
+ search.each { |result| pp result }
11
+
12
+ puts '*'*50, 'Parameter Check', '*'*50
13
+ pp Twitter::Search.new('#austineats').fetch().results.first
14
+ pp Twitter::Search.new('#austineats').page(2).fetch().results.first
15
+ pp Twitter::Search.new('#austineats').since(1412737343).fetch().results.first
@@ -0,0 +1,19 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
2
+ require File.join(File.dirname(__FILE__), 'helpers', 'config_store')
3
+ require 'pp'
4
+
5
+ config = ConfigStore.new("#{ENV['HOME']}/.twitter")
6
+
7
+ oauth = Twitter::OAuth.new(config['token'], config['secret'])
8
+ oauth.authorize_from_access(config['atoken'], config['asecret'])
9
+
10
+ client = Twitter::Base.new(oauth)
11
+
12
+ pp client.friends_timeline
13
+ puts '*'*50
14
+
15
+ pp client.user_timeline
16
+ puts '*'*50
17
+
18
+ pp client.replies
19
+ puts '*'*50
@@ -0,0 +1,16 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
2
+ require 'pp'
3
+
4
+ puts 'User', '*'*50
5
+ pp Twitter.user('jnunemaker')
6
+ pp Twitter.user('snitch_test')
7
+
8
+ puts 'Status', '*'*50
9
+ pp Twitter.status(1533815199)
10
+
11
+ puts 'Friend Ids', '*'*50
12
+ pp Twitter.friend_ids('jnunemaker')
13
+
14
+ puts 'Follower Ids', '*'*50
15
+ pp Twitter.follower_ids('jnunemaker')
16
+
@@ -0,0 +1,11 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
2
+ require File.join(File.dirname(__FILE__), 'helpers', 'config_store')
3
+ require 'pp'
4
+
5
+ config = ConfigStore.new("#{ENV['HOME']}/.twitter")
6
+
7
+ oauth = Twitter::OAuth.new(config['token'], config['secret'])
8
+ oauth.authorize_from_access(config['atoken'], config['asecret'])
9
+
10
+ client = Twitter::Base.new(oauth)
11
+ pp client.update('This is an update from the twitter gem')
@@ -0,0 +1,5 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
2
+ require 'pp'
3
+
4
+ pp Twitter.user('jnunemaker')
5
+ pp Twitter.user('snitch_test')
@@ -0,0 +1,63 @@
1
+ require 'forwardable'
2
+ require 'rubygems'
3
+
4
+ gem 'oauth', '0.3.4'
5
+ require 'oauth'
6
+
7
+ gem 'mash', '0.0.3'
8
+ require 'mash'
9
+
10
+ gem 'httparty', '0.4.3'
11
+ require 'httparty'
12
+
13
+ module Twitter
14
+ class TwitterError < StandardError
15
+ attr_reader :data
16
+
17
+ def initialize(data)
18
+ @data = data
19
+ super
20
+ end
21
+ end
22
+
23
+ class RateLimitExceeded < TwitterError; end
24
+ class Unauthorized < TwitterError; end
25
+ class General < TwitterError; end
26
+
27
+ class Unavailable < StandardError; end
28
+ class InformTwitter < StandardError; end
29
+ class NotFound < StandardError; end
30
+
31
+
32
+ def self.firehose
33
+ response = HTTParty.get('http://twitter.com/statuses/public_timeline.json', :format => :json)
34
+ response.map { |tweet| Mash.new(tweet) }
35
+ end
36
+
37
+ def self.user(id)
38
+ response = HTTParty.get("http://twitter.com/users/show/#{id}.json", :format => :json)
39
+ Mash.new(response)
40
+ end
41
+
42
+ def self.status(id)
43
+ response = HTTParty.get("http://twitter.com/statuses/show/#{id}.json", :format => :json)
44
+ Mash.new(response)
45
+ end
46
+
47
+ def self.friend_ids(id)
48
+ HTTParty.get("http://twitter.com/friends/ids/#{id}.json", :format => :json)
49
+ end
50
+
51
+ def self.follower_ids(id)
52
+ HTTParty.get("http://twitter.com/followers/ids/#{id}.json", :format => :json)
53
+ end
54
+ end
55
+
56
+ directory = File.expand_path(File.dirname(__FILE__))
57
+
58
+ require File.join(directory, 'twitter', 'oauth')
59
+ require File.join(directory, 'twitter', 'httpauth')
60
+ require File.join(directory, 'twitter', 'request')
61
+ require File.join(directory, 'twitter', 'base')
62
+ require File.join(directory, 'twitter', 'search')
63
+ require File.join(directory, 'twitter', 'trends')
@@ -0,0 +1,216 @@
1
+ require 'open-uri'
2
+
3
+ module Twitter
4
+ class Base
5
+ extend Forwardable
6
+
7
+ def_delegators :client, :get, :post
8
+
9
+ attr_reader :client
10
+
11
+ def initialize(client)
12
+ @client = client
13
+ end
14
+
15
+ # Options: since_id, max_id, count, page, since
16
+ def friends_timeline(query={})
17
+ perform_get('/statuses/friends_timeline.json', :query => query)
18
+ end
19
+
20
+ # Options: id, user_id, screen_name, since_id, max_id, page, since
21
+ def user_timeline(query={})
22
+ perform_get('/statuses/user_timeline.json', :query => query)
23
+ end
24
+
25
+ def status(id)
26
+ perform_get("/statuses/show/#{id}.json")
27
+ end
28
+
29
+ # Options: in_reply_to_status_id
30
+ def update(status, query={})
31
+ perform_post("/statuses/update.json", :body => {:status => status}.merge(query))
32
+ end
33
+
34
+ # Options: since_id, max_id, since, page
35
+ def replies(query={})
36
+ perform_get('/statuses/replies.json', :query => query)
37
+ end
38
+
39
+ def status_destroy(id)
40
+ perform_post("/statuses/destroy/#{id}.json")
41
+ end
42
+
43
+ # Options: id, user_id, screen_name, page
44
+ def friends(query={})
45
+ perform_get('/statuses/friends.json', :query => query)
46
+ end
47
+
48
+ # Options: id, user_id, screen_name, page
49
+ def followers(query={})
50
+ perform_get('/statuses/followers.json', :query => query)
51
+ end
52
+
53
+ def user(id, query={})
54
+ perform_get("/users/show/#{id}.json", :query => query)
55
+ end
56
+
57
+ # Options: since, since_id, page
58
+ def direct_messages(query={})
59
+ perform_get("/direct_messages.json", :query => query)
60
+ end
61
+
62
+ # Options: since, since_id, page
63
+ def direct_messages_sent(query={})
64
+ perform_get("/direct_messages/sent.json", :query => query)
65
+ end
66
+
67
+ def direct_message_create(user, text)
68
+ perform_post("/direct_messages/new.json", :body => {:user => user, :text => text})
69
+ end
70
+
71
+ def direct_message_destroy(id)
72
+ perform_post("/direct_messages/destroy/#{id}.json")
73
+ end
74
+
75
+ def friendship_create(id, follow=false)
76
+ body = {}
77
+ body.merge!(:follow => follow) if follow
78
+ perform_post("/friendships/create/#{id}.json", :body => body)
79
+ end
80
+
81
+ def friendship_destroy(id)
82
+ perform_post("/friendships/destroy/#{id}.json")
83
+ end
84
+
85
+ def friendship_exists?(a, b)
86
+ perform_get("/friendships/exists.json", :query => {:user_a => a, :user_b => b})
87
+ end
88
+
89
+ # Options: id, user_id, screen_name
90
+ def friend_ids(query={})
91
+ perform_get("/friends/ids.json", :query => query, :mash => false)
92
+ end
93
+
94
+ # Options: id, user_id, screen_name
95
+ def follower_ids(query={})
96
+ perform_get("/followers/ids.json", :query => query, :mash => false)
97
+ end
98
+
99
+ def verify_credentials
100
+ perform_get("/account/verify_credentials.json")
101
+ end
102
+
103
+ # Device must be sms, im or none
104
+ def update_delivery_device(device)
105
+ perform_post('/account/update_delivery_device.json', :body => {:device => device})
106
+ end
107
+
108
+ # One or more of the following must be present:
109
+ # profile_background_color, profile_text_color, profile_link_color,
110
+ # profile_sidebar_fill_color, profile_sidebar_border_color
111
+ def update_profile_colors(colors={})
112
+ perform_post('/account/update_profile_colors.json', :body => colors)
113
+ end
114
+
115
+ def update_profile_background_image(path_or_url_to_image)
116
+ file = open(path_or_url_to_image)
117
+ url = URI.parse('http://twitter.com/account/update_profile_background_image.json')
118
+ Net::HTTP.new(url.host, url.port).start do |http|
119
+ req = Net::HTTP::Post.new(url.request_uri)
120
+ add_multipart_data(req, :image => file)
121
+ add_oauth(req)
122
+ res = http.request(req)
123
+ end
124
+ end
125
+
126
+ def rate_limit_status
127
+ perform_get('/account/rate_limit_status.json')
128
+ end
129
+
130
+ # One or more of the following must be present:
131
+ # name, email, url, location, description
132
+ def update_profile(body={})
133
+ perform_post('/account/update_profile.json', :body => body)
134
+ end
135
+
136
+ # Options: id, page
137
+ def favorites(query={})
138
+ perform_get('/favorites.json', :query => query)
139
+ end
140
+
141
+ def favorite_create(id)
142
+ perform_post("/favorites/create/#{id}.json")
143
+ end
144
+
145
+ def favorite_destroy(id)
146
+ perform_post("/favorites/destroy/#{id}.json")
147
+ end
148
+
149
+ def enable_notifications(id)
150
+ perform_post("/notifications/follow/#{id}.json")
151
+ end
152
+
153
+ def disable_notifications(id)
154
+ perform_post("/notifications/leave/#{id}.json")
155
+ end
156
+
157
+ def block(id)
158
+ perform_post("/blocks/create/#{id}.json")
159
+ end
160
+
161
+ def unblock(id)
162
+ perform_post("/blocks/destroy/#{id}.json")
163
+ end
164
+
165
+ def help
166
+ perform_get('/help/test.json')
167
+ end
168
+
169
+ private
170
+ def perform_get(path, options={})
171
+ Twitter::Request.get(self, path, options)
172
+ end
173
+
174
+ def perform_post(path, options={})
175
+ Twitter::Request.post(self, path, options)
176
+ end
177
+
178
+ # Quick and dirty method for determining mime type of uploaded file
179
+ def mime_type(file)
180
+ case
181
+ when file =~ /\.jpg/ then 'image/jpg'
182
+ when file =~ /\.gif$/ then 'image/gif'
183
+ when file =~ /\.png$/ then 'image/png'
184
+ else 'application/octet-stream'
185
+ end
186
+ end
187
+
188
+ CRLF = "\r\n"
189
+ # Encodes the request as multipart
190
+ def add_multipart_data(req,params)
191
+ boundary = Time.now.to_i.to_s(16)
192
+ req["Content-Type"] = "multipart/form-data; boundary=#{boundary}"
193
+ body = ""
194
+ params.each do |key,value|
195
+ esc_key = CGI.escape(key.to_s)
196
+ body << "--#{boundary}#{CRLF}"
197
+ if value.respond_to?(:read)
198
+ body << "Content-Disposition: form-data; name=\"#{esc_key}\"; filename=\"#{File.basename(value.path)}\"#{CRLF}"
199
+ body << "Content-Type: #{mime_type(value.path)}#{CRLF*2}"
200
+ body << value.read
201
+ else
202
+ body << "Content-Disposition: form-data; name=\"#{esc_key}\"#{CRLF*2}#{value}"
203
+ end
204
+ body << CRLF
205
+ end
206
+ body << "--#{boundary}--#{CRLF*2}"
207
+ req.body = body
208
+ req["Content-Length"] = req.body.size
209
+ end
210
+
211
+ #Uses the OAuth gem to add the signed Authorization header
212
+ def add_oauth(req)
213
+ self.client.consumer.sign!(req, self.client.access_token)
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,27 @@
1
+ module Twitter
2
+ class HTTPAuth
3
+ include HTTParty
4
+ format :plain
5
+
6
+ attr_reader :username, :password, :options
7
+
8
+ def initialize(username, password, options={})
9
+ @username, @password = username, password
10
+ @options = {:ssl => false}.merge(options)
11
+ self.class.base_uri "http#{'s' if options[:ssl]}://twitter.com"
12
+ end
13
+
14
+ def get(uri, headers={})
15
+ self.class.get(uri, :headers => headers, :basic_auth => basic_auth)
16
+ end
17
+
18
+ def post(uri, body={}, headers={})
19
+ self.class.post(uri, :body => body, :headers => headers, :basic_auth => basic_auth)
20
+ end
21
+
22
+ private
23
+ def basic_auth
24
+ @basic_auth ||= {:username => @username, :password => @password}
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,41 @@
1
+ module Twitter
2
+ class OAuth
3
+ extend Forwardable
4
+ def_delegators :access_token, :get, :post
5
+
6
+ attr_reader :ctoken, :csecret, :consumer_options
7
+
8
+ # Options
9
+ # :sign_in => true to just sign in with twitter instead of doing oauth authorization
10
+ # (http://apiwiki.twitter.com/Sign-in-with-Twitter)
11
+ def initialize(ctoken, csecret, options={})
12
+ @ctoken, @csecret, @consumer_options = ctoken, csecret, {}
13
+
14
+ if options[:sign_in]
15
+ @consumer_options[:authorize_path] = '/oauth/authenticate'
16
+ end
17
+ end
18
+
19
+ def consumer
20
+ @consumer ||= ::OAuth::Consumer.new(@ctoken, @csecret, {:site => 'http://twitter.com'}.merge(consumer_options))
21
+ end
22
+
23
+ def request_token
24
+ @request_token ||= consumer.get_request_token
25
+ end
26
+
27
+ def authorize_from_request(rtoken, rsecret)
28
+ request_token = ::OAuth::RequestToken.new(consumer, rtoken, rsecret)
29
+ access_token = request_token.get_access_token
30
+ @atoken, @asecret = access_token.token, access_token.secret
31
+ end
32
+
33
+ def access_token
34
+ @access_token ||= ::OAuth::AccessToken.new(consumer, @atoken, @asecret)
35
+ end
36
+
37
+ def authorize_from_access(atoken, asecret)
38
+ @atoken, @asecret = atoken, asecret
39
+ end
40
+ end
41
+ end