rorra-twitter 0.9.9
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.
- data/History +290 -0
- data/License +20 -0
- data/Notes +33 -0
- data/README.rdoc +19 -0
- data/Rakefile +36 -0
- data/VERSION.yml +5 -0
- data/examples/connect.rb +30 -0
- data/examples/friendship_existence.rb +13 -0
- data/examples/helpers/config_store.rb +38 -0
- data/examples/httpauth.rb +11 -0
- data/examples/ids.rb +13 -0
- data/examples/lists.rb +11 -0
- data/examples/oauth.rb +27 -0
- data/examples/search.rb +15 -0
- data/examples/timeline.rb +19 -0
- data/examples/tumblr.rb +9 -0
- data/examples/unauthorized.rb +16 -0
- data/examples/update.rb +11 -0
- data/examples/user.rb +5 -0
- data/lib/twitter.rb +156 -0
- data/lib/twitter/base.rb +393 -0
- data/lib/twitter/geo.rb +25 -0
- data/lib/twitter/httpauth.rb +53 -0
- data/lib/twitter/local_trends.rb +30 -0
- data/lib/twitter/oauth.rb +64 -0
- data/lib/twitter/request.rb +71 -0
- data/lib/twitter/search.rb +163 -0
- data/lib/twitter/trends.rb +55 -0
- data/test/fixtures/blocking.json +1632 -0
- data/test/fixtures/firehose.json +1 -0
- data/test/fixtures/follower_ids.json +1 -0
- data/test/fixtures/followers.json +1 -0
- data/test/fixtures/friend_ids.json +1 -0
- data/test/fixtures/friends_timeline.json +1 -0
- data/test/fixtures/friendship.json +1 -0
- data/test/fixtures/friendship_exists.json +1 -0
- data/test/fixtures/geo_place.json +1 -0
- data/test/fixtures/geo_reverse_geocode.json +1 -0
- data/test/fixtures/geo_reverse_geocode_granularity.json +1 -0
- data/test/fixtures/geo_reverse_geocode_limit.json +1 -0
- data/test/fixtures/geo_search.json +1 -0
- data/test/fixtures/geo_search_ip_address.json +1 -0
- data/test/fixtures/geo_search_query.json +1 -0
- data/test/fixtures/home_timeline.json +1 -0
- data/test/fixtures/ids.json +1 -0
- data/test/fixtures/list.json +1 -0
- data/test/fixtures/list_statuses.json +1 -0
- data/test/fixtures/list_statuses_1_1.json +1 -0
- data/test/fixtures/list_statuses_2_1.json +1 -0
- data/test/fixtures/list_subscriptions.json +1 -0
- data/test/fixtures/list_users.json +1 -0
- data/test/fixtures/lists.json +1 -0
- data/test/fixtures/memberships.json +1 -0
- data/test/fixtures/mentions.json +1 -0
- data/test/fixtures/not_found.json +1 -0
- data/test/fixtures/people_search.json +39 -0
- data/test/fixtures/rate_limit_exceeded.json +1 -0
- data/test/fixtures/report_spam.json +41 -0
- data/test/fixtures/retweet.json +1 -0
- data/test/fixtures/retweeted_by_me.json +1 -0
- data/test/fixtures/retweeted_to_me.json +1 -0
- data/test/fixtures/retweeters_of_tweet.json +166 -0
- data/test/fixtures/retweets.json +1 -0
- data/test/fixtures/retweets_of_me.json +1 -0
- data/test/fixtures/sample-image.png +0 -0
- data/test/fixtures/saved_search.json +7 -0
- data/test/fixtures/saved_searches.json +16 -0
- data/test/fixtures/search.json +1 -0
- data/test/fixtures/search_from_jnunemaker.json +1 -0
- data/test/fixtures/status.json +1 -0
- data/test/fixtures/status_show.json +1 -0
- data/test/fixtures/trends_available.json +253 -0
- data/test/fixtures/trends_current.json +1 -0
- data/test/fixtures/trends_current_exclude.json +1 -0
- data/test/fixtures/trends_daily.json +1925 -0
- data/test/fixtures/trends_daily_date.json +1 -0
- data/test/fixtures/trends_daily_exclude.json +1 -0
- data/test/fixtures/trends_location.json +57 -0
- data/test/fixtures/trends_weekly.json +1 -0
- data/test/fixtures/trends_weekly_date.json +1 -0
- data/test/fixtures/trends_weekly_exclude.json +1 -0
- data/test/fixtures/unauthorized.json +1 -0
- data/test/fixtures/update_profile_background_image.json +1 -0
- data/test/fixtures/update_profile_image.json +1 -0
- data/test/fixtures/user.json +1 -0
- data/test/fixtures/user_timeline.json +710 -0
- data/test/fixtures/users.json +1 -0
- data/test/test_helper.rb +47 -0
- data/test/twitter/base_test.rb +426 -0
- data/test/twitter/geo_test.rb +79 -0
- data/test/twitter/httpauth_test.rb +86 -0
- data/test/twitter/oauth_test.rb +108 -0
- data/test/twitter/request_test.rb +217 -0
- data/test/twitter/search_test.rb +208 -0
- data/test/twitter/trends_test.rb +112 -0
- data/test/twitter_test.rb +106 -0
- metadata +328 -0
data/lib/twitter/geo.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Twitter
|
2
|
+
class Geo
|
3
|
+
include HTTParty
|
4
|
+
base_uri "api.twitter.com/#{API_VERSION}/geo"
|
5
|
+
format :json
|
6
|
+
|
7
|
+
def self.place(place_id, query={})
|
8
|
+
Twitter.mash(get("/id/#{place_id}.json", :query => query))
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.search(query={})
|
12
|
+
mashup(get("/search.json", :query => query))
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.reverse_geocode(query={})
|
16
|
+
mashup(get("/reverse_geocode.json", :query => query))
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def self.mashup(response)
|
22
|
+
response["result"].values.flatten.map{|t| Twitter.mash(t)}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Twitter
|
2
|
+
class HTTPAuth
|
3
|
+
include HTTParty
|
4
|
+
|
5
|
+
format :plain
|
6
|
+
|
7
|
+
attr_reader :username, :password, :options
|
8
|
+
|
9
|
+
def initialize(username, password, options={})
|
10
|
+
|
11
|
+
@username, @password = username, password
|
12
|
+
@options = {:ssl => false}.merge(options)
|
13
|
+
options[:api_endpoint] ||= "api.twitter.com"
|
14
|
+
|
15
|
+
if options[:api_version] == false
|
16
|
+
version_path = ''
|
17
|
+
else
|
18
|
+
options[:api_version] ||= API_VERSION
|
19
|
+
version_path = "/#{options[:api_version]}"
|
20
|
+
end
|
21
|
+
|
22
|
+
self.class.base_uri "http#{'s' if options[:ssl]}://#{options[:api_endpoint]}#{version_path}"
|
23
|
+
self.class.default_timeout options[:timeout] if options[:timeout]
|
24
|
+
end
|
25
|
+
|
26
|
+
def get(uri, headers={})
|
27
|
+
|
28
|
+
self.class.get(uri, :headers => headers, :basic_auth => basic_auth)
|
29
|
+
end
|
30
|
+
|
31
|
+
def post(uri, body={}, headers={})
|
32
|
+
|
33
|
+
self.class.post(uri, :body => body, :headers => headers, :basic_auth => basic_auth)
|
34
|
+
end
|
35
|
+
|
36
|
+
def put(uri, body={}, headers={})
|
37
|
+
|
38
|
+
self.class.put(uri, :body => body, :headers => headers, :basic_auth => basic_auth)
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete(uri, body={}, headers={})
|
42
|
+
|
43
|
+
self.class.delete(uri, :body => body, :headers => headers, :basic_auth => basic_auth)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def basic_auth
|
49
|
+
@basic_auth ||= {:username => @username, :password => @password}
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Twitter
|
2
|
+
class LocalTrends
|
3
|
+
include HTTParty
|
4
|
+
base_uri "api.twitter.com/#{API_VERSION}/trends"
|
5
|
+
format :json
|
6
|
+
|
7
|
+
def self.available(query={})
|
8
|
+
before_test(query)
|
9
|
+
query.delete(:api_endpoint)
|
10
|
+
get("/available.json", :query => query).map{|location| Twitter.mash(location)}
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.for_location(woeid,options = {})
|
14
|
+
before_test(options)
|
15
|
+
get("/#{woeid}.json").map{|location| Twitter.mash(location)}
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def self.before_test(options)
|
21
|
+
configure_base_uri(options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.configure_base_uri(options)
|
25
|
+
new_base_url = options[:api_endpoint]
|
26
|
+
base_uri "#{new_base_url}/#{API_VERSION}/trends" if new_base_url
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Twitter
|
2
|
+
class OAuth
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def_delegators :access_token, :get, :post, :put, :delete
|
6
|
+
|
7
|
+
attr_reader :ctoken, :csecret, :consumer_options, :api_endpoint, :signing_endpoint
|
8
|
+
|
9
|
+
# Options
|
10
|
+
# :sign_in => true to just sign in with twitter instead of doing oauth authorization
|
11
|
+
# (http://apiwiki.twitter.com/Sign-in-with-Twitter)
|
12
|
+
def initialize(ctoken, csecret, options={})
|
13
|
+
@ctoken, @csecret, @consumer_options = ctoken, csecret, {}
|
14
|
+
@api_endpoint = options[:api_endpoint] || 'http://api.twitter.com'
|
15
|
+
@signing_endpoint = options[:signing_endpoint] || 'http://api.twitter.com'
|
16
|
+
if options[:sign_in]
|
17
|
+
@consumer_options[:authorize_path] = '/oauth/authenticate'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def consumer
|
22
|
+
@consumer ||= ::OAuth::Consumer.new(@ctoken, @csecret, {:site => api_endpoint}.merge(consumer_options))
|
23
|
+
end
|
24
|
+
|
25
|
+
def signing_consumer
|
26
|
+
@signing_consumer ||= ::OAuth::Consumer.new(@ctoken, @csecret, {:site => signing_endpoint, :request_endpoint => api_endpoint }.merge(consumer_options))
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_callback_url(url)
|
30
|
+
clear_request_token
|
31
|
+
request_token(:oauth_callback => url)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Note: If using oauth with a web app, be sure to provide :oauth_callback.
|
35
|
+
# Options:
|
36
|
+
# :oauth_callback => String, url that twitter should redirect to
|
37
|
+
def request_token(options={})
|
38
|
+
@request_token ||= signing_consumer.get_request_token(options)
|
39
|
+
end
|
40
|
+
|
41
|
+
# For web apps use params[:oauth_verifier], for desktop apps,
|
42
|
+
# use the verifier is the pin that twitter gives users.
|
43
|
+
def authorize_from_request(rtoken, rsecret, verifier_or_pin)
|
44
|
+
request_token = ::OAuth::RequestToken.new(signing_consumer, rtoken, rsecret)
|
45
|
+
access_token = request_token.get_access_token(:oauth_verifier => verifier_or_pin)
|
46
|
+
@atoken, @asecret = access_token.token, access_token.secret
|
47
|
+
end
|
48
|
+
|
49
|
+
def access_token
|
50
|
+
@access_token ||= ::OAuth::AccessToken.new(signing_consumer, @atoken, @asecret)
|
51
|
+
end
|
52
|
+
|
53
|
+
def authorize_from_access(atoken, asecret)
|
54
|
+
@atoken, @asecret = atoken, asecret
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def clear_request_token
|
60
|
+
@request_token = nil
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,71 @@
|
|
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
|
+
def self.put(client, path, options={})
|
14
|
+
new(client, :put, path, options).perform
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.delete(client, path, options={})
|
18
|
+
new(client, :delete, path, options).perform
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :client, :method, :path, :options
|
22
|
+
|
23
|
+
def_delegators :client, :get, :post, :put, :delete
|
24
|
+
|
25
|
+
def initialize(client, method, path, options={})
|
26
|
+
@client, @method, @path, @options = client, method, path, options
|
27
|
+
end
|
28
|
+
|
29
|
+
def uri
|
30
|
+
@uri ||= begin
|
31
|
+
uri = URI.parse(path)
|
32
|
+
|
33
|
+
if options[:query] && options[:query] != {}
|
34
|
+
uri.query = to_query(options[:query])
|
35
|
+
end
|
36
|
+
|
37
|
+
uri.to_s
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def perform
|
42
|
+
Twitter.make_friendly(send("perform_#{method}"))
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def perform_get
|
48
|
+
get(uri, options[:headers])
|
49
|
+
end
|
50
|
+
|
51
|
+
def perform_post
|
52
|
+
post(uri, options[:body], options[:headers])
|
53
|
+
end
|
54
|
+
|
55
|
+
def perform_put
|
56
|
+
put(uri, options[:body], options[:headers])
|
57
|
+
end
|
58
|
+
|
59
|
+
def perform_delete
|
60
|
+
delete(uri, options[:headers])
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_query(options)
|
64
|
+
options.inject([]) do |collection, opt|
|
65
|
+
collection << "#{opt[0]}=#{opt[1]}"
|
66
|
+
collection
|
67
|
+
end * '&'
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'pp'
|
2
|
+
module Twitter
|
3
|
+
class Search
|
4
|
+
include HTTParty
|
5
|
+
include Enumerable
|
6
|
+
base_uri "search.twitter.com/search"
|
7
|
+
format :json
|
8
|
+
|
9
|
+
attr_reader :result, :query
|
10
|
+
|
11
|
+
def initialize(q=nil, options={})
|
12
|
+
@options = options
|
13
|
+
clear
|
14
|
+
containing(q) if q && q.strip != ""
|
15
|
+
endpoint_url = options[:api_endpoint]
|
16
|
+
endpoint_url = "#{endpoint_url}/search" if endpoint_url && !endpoint_url.include?("/search")
|
17
|
+
self.class.base_uri(endpoint_url) if endpoint_url
|
18
|
+
end
|
19
|
+
|
20
|
+
def user_agent
|
21
|
+
@options[:user_agent] || "Ruby Twitter Gem"
|
22
|
+
end
|
23
|
+
|
24
|
+
def from(user, exclude=false)
|
25
|
+
@query[:q] << "#{exclude ? "-" : ""}from:#{user}"
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def to(user, exclude=false)
|
30
|
+
@query[:q] << "#{exclude ? "-" : ""}to:#{user}"
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def referencing(user, exclude=false)
|
35
|
+
@query[:q] << "#{exclude ? "-" : ""}@#{user}"
|
36
|
+
self
|
37
|
+
end
|
38
|
+
alias :references :referencing
|
39
|
+
alias :ref :referencing
|
40
|
+
|
41
|
+
def containing(word, exclude=false)
|
42
|
+
@query[:q] << "#{exclude ? "-" : ""}#{word}"
|
43
|
+
self
|
44
|
+
end
|
45
|
+
alias :contains :containing
|
46
|
+
|
47
|
+
# adds filtering based on hash tag ie: #twitter
|
48
|
+
def hashed(tag, exclude=false)
|
49
|
+
@query[:q] << "#{exclude ? "-" : ""}\##{tag}"
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
# Search for a phrase instead of a group of words
|
54
|
+
def phrase(phrase)
|
55
|
+
@query[:phrase] = phrase
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
# lang must be ISO 639-1 code ie: en, fr, de, ja, etc.
|
60
|
+
#
|
61
|
+
# when I tried en it limited my results a lot and took
|
62
|
+
# out several tweets that were english so i'd avoid
|
63
|
+
# this unless you really want it
|
64
|
+
def lang(lang)
|
65
|
+
@query[:lang] = lang
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
# popular|recent
|
70
|
+
def result_type(result_type)
|
71
|
+
@query[:result_type] = result_type
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
# Limits the number of results per page
|
76
|
+
def per_page(num)
|
77
|
+
@query[:rpp] = num
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
# Which page of results to fetch
|
82
|
+
def page(num)
|
83
|
+
@query[:page] = num
|
84
|
+
self
|
85
|
+
end
|
86
|
+
|
87
|
+
# Only searches tweets since a given id.
|
88
|
+
# Recommended to use this when possible.
|
89
|
+
def since(since_id)
|
90
|
+
@query[:since_id] = since_id
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
94
|
+
# From the advanced search form, not documented in the API
|
95
|
+
# Format YYYY-MM-DD
|
96
|
+
def since_date(since_date)
|
97
|
+
@query[:since] = since_date
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
# From the advanced search form, not documented in the API
|
102
|
+
# Format YYYY-MM-DD
|
103
|
+
def until_date(until_date)
|
104
|
+
@query[:until] = until_date
|
105
|
+
self
|
106
|
+
end
|
107
|
+
|
108
|
+
# Ranges like 25km and 50mi work.
|
109
|
+
def geocode(lat, long, range)
|
110
|
+
@query[:geocode] = [lat, long, range].join(",")
|
111
|
+
self
|
112
|
+
end
|
113
|
+
|
114
|
+
def max(id)
|
115
|
+
@query[:max_id] = id
|
116
|
+
self
|
117
|
+
end
|
118
|
+
|
119
|
+
# Clears all the query filters to make a new search
|
120
|
+
def clear
|
121
|
+
@fetch = nil
|
122
|
+
@query = {}
|
123
|
+
@query[:q] = []
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
127
|
+
def fetch(force=false)
|
128
|
+
if @fetch.nil? || force
|
129
|
+
query = @query.dup
|
130
|
+
query[:q] = query[:q].join(" ")
|
131
|
+
perform_get(query)
|
132
|
+
end
|
133
|
+
|
134
|
+
@fetch
|
135
|
+
end
|
136
|
+
|
137
|
+
def each
|
138
|
+
results = fetch()['results']
|
139
|
+
return if results.nil?
|
140
|
+
results.each {|r| yield r}
|
141
|
+
end
|
142
|
+
|
143
|
+
def next_page?
|
144
|
+
!!fetch()["next_page"]
|
145
|
+
end
|
146
|
+
|
147
|
+
def fetch_next_page
|
148
|
+
if next_page?
|
149
|
+
s = Search.new(nil, :user_agent => user_agent)
|
150
|
+
s.perform_get(fetch()["next_page"][1..-1])
|
151
|
+
s
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
protected
|
156
|
+
|
157
|
+
def perform_get(query)
|
158
|
+
response = self.class.get("#{self.class.base_uri}.json", :query => query, :format => :json, :headers => {"User-Agent" => user_agent})
|
159
|
+
@fetch = Twitter.mash(response)
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "local_trends")
|
2
|
+
|
3
|
+
module Twitter
|
4
|
+
class Trends
|
5
|
+
include HTTParty
|
6
|
+
format :json
|
7
|
+
|
8
|
+
def self.api_endpoint
|
9
|
+
@api_endpoint ||= "search.twitter.com/trends"
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.api_endpoint=(value)
|
13
|
+
@api_endpoint = value
|
14
|
+
end
|
15
|
+
|
16
|
+
# :exclude => 'hashtags' to exclude hashtags
|
17
|
+
def self.current(options={})
|
18
|
+
get("/current.json", :query => options)
|
19
|
+
end
|
20
|
+
|
21
|
+
# :exclude => 'hashtags' to exclude hashtags
|
22
|
+
# :date => yyyy-mm-dd for specific date
|
23
|
+
def self.daily(options={})
|
24
|
+
get("/daily.json", :query => options)
|
25
|
+
end
|
26
|
+
|
27
|
+
# :exclude => 'hashtags' to exclude hashtags
|
28
|
+
# :date => yyyy-mm-dd for specific date
|
29
|
+
def self.weekly(options={})
|
30
|
+
get("/weekly.json", :query => options)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.available(query={})
|
34
|
+
#checking for api_endpoint in local_trends
|
35
|
+
LocalTrends.available(query)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.for_location(woeid,options={})
|
39
|
+
#checking for api_endpoint in local_trends
|
40
|
+
LocalTrends.for_location(woeid,options)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def self.get(*args)
|
46
|
+
base_uri api_endpoint
|
47
|
+
mashup(super)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.mashup(response)
|
51
|
+
response["trends"].values.flatten.map{|t| Twitter.mash(t)}
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|