codetocustomer-twitter 0.6.14

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 +215 -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 +165 -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 +111 -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 +105 -0
  48. data/test/twitter/request_test.rb +217 -0
  49. data/test/twitter/search_test.rb +159 -0
  50. data/test/twitter/trends_test.rb +95 -0
  51. data/test/twitter_test.rb +38 -0
  52. metadata +200 -0
@@ -0,0 +1,38 @@
1
+ class ConfigStore
2
+ attr_reader :file
3
+
4
+ def initialize(file)
5
+ @file = file
6
+ end
7
+
8
+ def load
9
+ @config ||= YAML::load(open(file))
10
+ self
11
+ end
12
+
13
+ def [](key)
14
+ load
15
+ @config[key]
16
+ end
17
+
18
+ def []=(key, value)
19
+ @config[key] = value
20
+ end
21
+
22
+ def delete(*keys)
23
+ keys.each { |key| @config.delete(key) }
24
+ save
25
+ self
26
+ end
27
+
28
+ def update(c={})
29
+ @config.merge!(c)
30
+ save
31
+ self
32
+ end
33
+
34
+ def save
35
+ File.open(file, 'w') { |f| f.write(YAML.dump(@config)) }
36
+ self
37
+ end
38
+ end
@@ -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
data/examples/ids.rb ADDED
@@ -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')
data/examples/user.rb ADDED
@@ -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')
data/lib/twitter.rb ADDED
@@ -0,0 +1,63 @@
1
+ require 'forwardable'
2
+ require 'rubygems'
3
+
4
+ gem 'oauth', '>= 0.3.5'
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,165 @@
1
+ module Twitter
2
+ class Base
3
+ extend Forwardable
4
+
5
+ def_delegators :client, :get, :post
6
+
7
+ attr_reader :client
8
+
9
+ def initialize(client)
10
+ @client = client
11
+ end
12
+
13
+ # Options: since_id, max_id, count, page, since
14
+ def friends_timeline(query={})
15
+ perform_get('/statuses/friends_timeline.json', :query => query)
16
+ end
17
+
18
+ # Options: id, user_id, screen_name, since_id, max_id, page, since
19
+ def user_timeline(query={})
20
+ perform_get('/statuses/user_timeline.json', :query => query)
21
+ end
22
+
23
+ def status(id)
24
+ perform_get("/statuses/show/#{id}.json")
25
+ end
26
+
27
+ # Options: in_reply_to_status_id
28
+ def update(status, query={})
29
+ perform_post("/statuses/update.json", :body => {:status => status}.merge(query))
30
+ end
31
+
32
+ # Options: since_id, max_id, since, page
33
+ def replies(query={})
34
+ perform_get('/statuses/replies.json', :query => query)
35
+ end
36
+
37
+ def status_destroy(id)
38
+ perform_post("/statuses/destroy/#{id}.json")
39
+ end
40
+
41
+ # Options: id, user_id, screen_name, page
42
+ def friends(query={})
43
+ perform_get('/statuses/friends.json', :query => query)
44
+ end
45
+
46
+ # Options: id, user_id, screen_name, page
47
+ def followers(query={})
48
+ perform_get('/statuses/followers.json', :query => query)
49
+ end
50
+
51
+ def user(id, query={})
52
+ perform_get("/users/show/#{id}.json", :query => query)
53
+ end
54
+
55
+ # Options: since, since_id, page
56
+ def direct_messages(query={})
57
+ perform_get("/direct_messages.json", :query => query)
58
+ end
59
+
60
+ # Options: since, since_id, page
61
+ def direct_messages_sent(query={})
62
+ perform_get("/direct_messages/sent.json", :query => query)
63
+ end
64
+
65
+ def direct_message_create(user, text)
66
+ perform_post("/direct_messages/new.json", :body => {:user => user, :text => text})
67
+ end
68
+
69
+ def direct_message_destroy(id)
70
+ perform_post("/direct_messages/destroy/#{id}.json")
71
+ end
72
+
73
+ def friendship_create(id, follow=false)
74
+ body = {}
75
+ body.merge!(:follow => follow) if follow
76
+ perform_post("/friendships/create/#{id}.json", :body => body)
77
+ end
78
+
79
+ def friendship_destroy(id)
80
+ perform_post("/friendships/destroy/#{id}.json")
81
+ end
82
+
83
+ def friendship_exists?(a, b)
84
+ perform_get("/friendships/exists.json", :query => {:user_a => a, :user_b => b})
85
+ end
86
+
87
+ # Options: id, user_id, screen_name
88
+ def friend_ids(query={})
89
+ perform_get("/friends/ids.json", :query => query, :mash => false)
90
+ end
91
+
92
+ # Options: id, user_id, screen_name
93
+ def follower_ids(query={})
94
+ perform_get("/followers/ids.json", :query => query, :mash => false)
95
+ end
96
+
97
+ def verify_credentials
98
+ perform_get("/account/verify_credentials.json")
99
+ end
100
+
101
+ # Device must be sms, im or none
102
+ def update_delivery_device(device)
103
+ perform_post('/account/update_delivery_device.json', :body => {:device => device})
104
+ end
105
+
106
+ # One or more of the following must be present:
107
+ # profile_background_color, profile_text_color, profile_link_color,
108
+ # profile_sidebar_fill_color, profile_sidebar_border_color
109
+ def update_profile_colors(colors={})
110
+ perform_post('/account/update_profile_colors.json', :body => colors)
111
+ end
112
+
113
+ def rate_limit_status
114
+ perform_get('/account/rate_limit_status.json')
115
+ end
116
+
117
+ # One or more of the following must be present:
118
+ # name, email, url, location, description
119
+ def update_profile(body={})
120
+ perform_post('/account/update_profile.json', :body => body)
121
+ end
122
+
123
+ # Options: id, page
124
+ def favorites(query={})
125
+ perform_get('/favorites.json', :query => query)
126
+ end
127
+
128
+ def favorite_create(id)
129
+ perform_post("/favorites/create/#{id}.json")
130
+ end
131
+
132
+ def favorite_destroy(id)
133
+ perform_post("/favorites/destroy/#{id}.json")
134
+ end
135
+
136
+ def enable_notifications(id)
137
+ perform_post("/notifications/follow/#{id}.json")
138
+ end
139
+
140
+ def disable_notifications(id)
141
+ perform_post("/notifications/leave/#{id}.json")
142
+ end
143
+
144
+ def block(id)
145
+ perform_post("/blocks/create/#{id}.json")
146
+ end
147
+
148
+ def unblock(id)
149
+ perform_post("/blocks/destroy/#{id}.json")
150
+ end
151
+
152
+ def help
153
+ perform_get('/help/test.json')
154
+ end
155
+
156
+ private
157
+ def perform_get(path, options={})
158
+ Twitter::Request.get(self, path, options)
159
+ end
160
+
161
+ def perform_post(path, options={})
162
+ Twitter::Request.post(self, path, options)
163
+ end
164
+ end
165
+ 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(params = {})
24
+ @request_token ||= consumer.get_request_token(params)
25
+ end
26
+
27
+ def authorize_from_request(rtoken, rsecret, params = {})
28
+ request_token = ::OAuth::RequestToken.new(consumer, rtoken, rsecret)
29
+ access_token = request_token.get_access_token(params)
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
@@ -0,0 +1,102 @@
1
+ module Twitter
2
+ class Request
3
+ extend Forwardable
4
+
5
+ def self.get(client, path, options={})
6
+ new(client, :get, path, options).perform
7
+ end
8
+
9
+ def self.post(client, path, options={})
10
+ new(client, :post, path, options).perform
11
+ end
12
+
13
+ attr_reader :client, :method, :path, :options
14
+
15
+ def_delegators :client, :get, :post
16
+
17
+ def initialize(client, method, path, options={})
18
+ @client, @method, @path, @options = client, method, path, {:mash => true}.merge(options)
19
+ end
20
+
21
+ def uri
22
+ @uri ||= begin
23
+ uri = URI.parse(path)
24
+
25
+ if options[:query] && options[:query] != {}
26
+ uri.query = to_query(options[:query])
27
+ end
28
+
29
+ uri.to_s
30
+ end
31
+ end
32
+
33
+ def perform
34
+ make_friendly(send("perform_#{method}"))
35
+ end
36
+
37
+ private
38
+ def perform_get
39
+ send(:get, uri, options[:headers])
40
+ end
41
+
42
+ def perform_post
43
+ send(:post, uri, options[:body], options[:headers])
44
+ end
45
+
46
+ def make_friendly(response)
47
+ raise_errors(response)
48
+ data = parse(response)
49
+ options[:mash] ? mash(data) : data
50
+ end
51
+
52
+ def raise_errors(response)
53
+ case response.code.to_i
54
+ when 400
55
+ data = parse(response)
56
+ raise RateLimitExceeded.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}"
57
+ when 401
58
+ data = parse(response)
59
+ raise Unauthorized.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}"
60
+ when 403
61
+ data = parse(response)
62
+ raise General.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}"
63
+ when 404
64
+ raise NotFound, "(#{response.code}): #{response.message}"
65
+ when 500
66
+ raise InformTwitter, "Twitter had an internal error. Please let them know in the group. (#{response.code}): #{response.message}"
67
+ when 502..503
68
+ raise Unavailable, "(#{response.code}): #{response.message}"
69
+ end
70
+ end
71
+
72
+ def parse(response)
73
+ Crack::JSON.parse(response.body)
74
+ end
75
+
76
+ def mash(obj)
77
+ if obj.is_a?(Array)
78
+ obj.map { |item| make_mash_with_consistent_hash(item) }
79
+ elsif obj.is_a?(Hash)
80
+ make_mash_with_consistent_hash(obj)
81
+ else
82
+ obj
83
+ end
84
+ end
85
+
86
+ # Lame workaround for the fact that mash doesn't hash correctly
87
+ def make_mash_with_consistent_hash(obj)
88
+ m = Mash.new(obj)
89
+ def m.hash
90
+ inspect.hash
91
+ end
92
+ return m
93
+ end
94
+
95
+ def to_query(options)
96
+ options.inject([]) do |collection, opt|
97
+ collection << "#{opt[0]}=#{opt[1]}"
98
+ collection
99
+ end * '&'
100
+ end
101
+ end
102
+ end