twitter-multi 0.9.8.1
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 +40 -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/base.rb +390 -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/lib/twitter.rb +156 -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 +329 -0
@@ -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
|
data/lib/twitter.rb
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
require "oauth"
|
3
|
+
require "hashie"
|
4
|
+
require "httparty"
|
5
|
+
require "multi_json"
|
6
|
+
|
7
|
+
module Twitter
|
8
|
+
include HTTParty
|
9
|
+
API_VERSION = "1".freeze
|
10
|
+
format :json
|
11
|
+
|
12
|
+
class TwitterError < StandardError
|
13
|
+
attr_reader :data
|
14
|
+
|
15
|
+
def initialize(data)
|
16
|
+
@data = data
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class RateLimitExceeded < TwitterError; end
|
22
|
+
class Unauthorized < TwitterError; end
|
23
|
+
class General < TwitterError; end
|
24
|
+
|
25
|
+
class Unavailable < StandardError; end
|
26
|
+
class InformTwitter < StandardError; end
|
27
|
+
class NotFound < StandardError; end
|
28
|
+
|
29
|
+
def self.api_endpoint
|
30
|
+
@api_endpoint ||= "api.twitter.com/#{API_VERSION}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.api_endpoint=(value)
|
34
|
+
@api_endpoint = value
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.firehose(options = {})
|
38
|
+
perform_get("/statuses/public_timeline.json")
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.user(id,options={})
|
42
|
+
perform_get("/users/show/#{id}.json")
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.status(id,options={})
|
46
|
+
perform_get("/statuses/show/#{id}.json")
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.friend_ids(id,options={})
|
50
|
+
perform_get("/friends/ids/#{id}.json")
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.follower_ids(id,options={})
|
54
|
+
perform_get("/followers/ids/#{id}.json")
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.timeline(id, options={})
|
58
|
+
perform_get("/statuses/user_timeline/#{id}.json", :query => options)
|
59
|
+
end
|
60
|
+
|
61
|
+
# :per_page = max number of statues to get at once
|
62
|
+
# :page = which page of tweets you wish to get
|
63
|
+
def self.list_timeline(list_owner_username, slug, query = {})
|
64
|
+
perform_get("/#{list_owner_username}/lists/#{slug}/statuses.json", :query => query)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def self.perform_get(uri, options = {})
|
70
|
+
base_uri self.api_endpoint
|
71
|
+
make_friendly(get(uri, options))
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.make_friendly(response)
|
75
|
+
raise_errors(response)
|
76
|
+
data = parse(response)
|
77
|
+
# Don't mash arrays of integers
|
78
|
+
if data && data.is_a?(Array) && data.first.is_a?(Integer)
|
79
|
+
data
|
80
|
+
else
|
81
|
+
mash(data)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.raise_errors(response)
|
86
|
+
case response.code.to_i
|
87
|
+
when 400
|
88
|
+
data = parse(response)
|
89
|
+
raise RateLimitExceeded.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}"
|
90
|
+
when 401
|
91
|
+
data = parse(response)
|
92
|
+
raise Unauthorized.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}"
|
93
|
+
when 403
|
94
|
+
data = parse(response)
|
95
|
+
raise General.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}"
|
96
|
+
when 404
|
97
|
+
raise NotFound, "(#{response.code}): #{response.message}"
|
98
|
+
when 500
|
99
|
+
raise InformTwitter, "Twitter had an internal error. Please let them know in the group. (#{response.code}): #{response.message}"
|
100
|
+
when 502..503
|
101
|
+
raise Unavailable, "(#{response.code}): #{response.message}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.parse(response)
|
106
|
+
MultiJson.decode(response.body)
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.mash(obj)
|
110
|
+
if obj.is_a?(Array)
|
111
|
+
obj.map{|item| make_mash_with_consistent_hash(item)}
|
112
|
+
elsif obj.is_a?(Hash)
|
113
|
+
make_mash_with_consistent_hash(obj)
|
114
|
+
else
|
115
|
+
obj
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Lame workaround for the fact that mash doesn't hash correctly
|
120
|
+
def self.make_mash_with_consistent_hash(obj)
|
121
|
+
m = Hashie::Mash.new(obj)
|
122
|
+
def m.hash
|
123
|
+
inspect.hash
|
124
|
+
end
|
125
|
+
return m
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
module Hashie
|
131
|
+
class Mash
|
132
|
+
|
133
|
+
# Converts all of the keys to strings, optionally formatting key name
|
134
|
+
def rubyify_keys!
|
135
|
+
keys.each{|k|
|
136
|
+
v = delete(k)
|
137
|
+
new_key = k.to_s.underscore
|
138
|
+
self[new_key] = v
|
139
|
+
v.rubyify_keys! if v.is_a?(Hash)
|
140
|
+
v.each{|p| p.rubyify_keys! if p.is_a?(Hash)} if v.is_a?(Array)
|
141
|
+
}
|
142
|
+
self
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
directory = File.expand_path(File.dirname(__FILE__))
|
149
|
+
|
150
|
+
require File.join(directory, "twitter", "oauth")
|
151
|
+
require File.join(directory, "twitter", "httpauth")
|
152
|
+
require File.join(directory, "twitter", "request")
|
153
|
+
require File.join(directory, "twitter", "base")
|
154
|
+
require File.join(directory, "twitter", "search")
|
155
|
+
require File.join(directory, "twitter", "trends")
|
156
|
+
require File.join(directory, "twitter", "geo")
|