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.
Files changed (97) hide show
  1. data/History +290 -0
  2. data/License +20 -0
  3. data/Notes +33 -0
  4. data/README.rdoc +19 -0
  5. data/Rakefile +40 -0
  6. data/VERSION.yml +5 -0
  7. data/examples/connect.rb +30 -0
  8. data/examples/friendship_existence.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/lists.rb +11 -0
  13. data/examples/oauth.rb +27 -0
  14. data/examples/search.rb +15 -0
  15. data/examples/timeline.rb +19 -0
  16. data/examples/tumblr.rb +9 -0
  17. data/examples/unauthorized.rb +16 -0
  18. data/examples/update.rb +11 -0
  19. data/examples/user.rb +5 -0
  20. data/lib/twitter/base.rb +390 -0
  21. data/lib/twitter/geo.rb +25 -0
  22. data/lib/twitter/httpauth.rb +53 -0
  23. data/lib/twitter/local_trends.rb +30 -0
  24. data/lib/twitter/oauth.rb +64 -0
  25. data/lib/twitter/request.rb +71 -0
  26. data/lib/twitter/search.rb +163 -0
  27. data/lib/twitter/trends.rb +55 -0
  28. data/lib/twitter.rb +156 -0
  29. data/test/fixtures/blocking.json +1632 -0
  30. data/test/fixtures/firehose.json +1 -0
  31. data/test/fixtures/follower_ids.json +1 -0
  32. data/test/fixtures/followers.json +1 -0
  33. data/test/fixtures/friend_ids.json +1 -0
  34. data/test/fixtures/friends_timeline.json +1 -0
  35. data/test/fixtures/friendship.json +1 -0
  36. data/test/fixtures/friendship_exists.json +1 -0
  37. data/test/fixtures/geo_place.json +1 -0
  38. data/test/fixtures/geo_reverse_geocode.json +1 -0
  39. data/test/fixtures/geo_reverse_geocode_granularity.json +1 -0
  40. data/test/fixtures/geo_reverse_geocode_limit.json +1 -0
  41. data/test/fixtures/geo_search.json +1 -0
  42. data/test/fixtures/geo_search_ip_address.json +1 -0
  43. data/test/fixtures/geo_search_query.json +1 -0
  44. data/test/fixtures/home_timeline.json +1 -0
  45. data/test/fixtures/ids.json +1 -0
  46. data/test/fixtures/list.json +1 -0
  47. data/test/fixtures/list_statuses.json +1 -0
  48. data/test/fixtures/list_statuses_1_1.json +1 -0
  49. data/test/fixtures/list_statuses_2_1.json +1 -0
  50. data/test/fixtures/list_subscriptions.json +1 -0
  51. data/test/fixtures/list_users.json +1 -0
  52. data/test/fixtures/lists.json +1 -0
  53. data/test/fixtures/memberships.json +1 -0
  54. data/test/fixtures/mentions.json +1 -0
  55. data/test/fixtures/not_found.json +1 -0
  56. data/test/fixtures/people_search.json +39 -0
  57. data/test/fixtures/rate_limit_exceeded.json +1 -0
  58. data/test/fixtures/report_spam.json +41 -0
  59. data/test/fixtures/retweet.json +1 -0
  60. data/test/fixtures/retweeted_by_me.json +1 -0
  61. data/test/fixtures/retweeted_to_me.json +1 -0
  62. data/test/fixtures/retweeters_of_tweet.json +166 -0
  63. data/test/fixtures/retweets.json +1 -0
  64. data/test/fixtures/retweets_of_me.json +1 -0
  65. data/test/fixtures/sample-image.png +0 -0
  66. data/test/fixtures/saved_search.json +7 -0
  67. data/test/fixtures/saved_searches.json +16 -0
  68. data/test/fixtures/search.json +1 -0
  69. data/test/fixtures/search_from_jnunemaker.json +1 -0
  70. data/test/fixtures/status.json +1 -0
  71. data/test/fixtures/status_show.json +1 -0
  72. data/test/fixtures/trends_available.json +253 -0
  73. data/test/fixtures/trends_current.json +1 -0
  74. data/test/fixtures/trends_current_exclude.json +1 -0
  75. data/test/fixtures/trends_daily.json +1925 -0
  76. data/test/fixtures/trends_daily_date.json +1 -0
  77. data/test/fixtures/trends_daily_exclude.json +1 -0
  78. data/test/fixtures/trends_location.json +57 -0
  79. data/test/fixtures/trends_weekly.json +1 -0
  80. data/test/fixtures/trends_weekly_date.json +1 -0
  81. data/test/fixtures/trends_weekly_exclude.json +1 -0
  82. data/test/fixtures/unauthorized.json +1 -0
  83. data/test/fixtures/update_profile_background_image.json +1 -0
  84. data/test/fixtures/update_profile_image.json +1 -0
  85. data/test/fixtures/user.json +1 -0
  86. data/test/fixtures/user_timeline.json +710 -0
  87. data/test/fixtures/users.json +1 -0
  88. data/test/test_helper.rb +47 -0
  89. data/test/twitter/base_test.rb +426 -0
  90. data/test/twitter/geo_test.rb +79 -0
  91. data/test/twitter/httpauth_test.rb +86 -0
  92. data/test/twitter/oauth_test.rb +108 -0
  93. data/test/twitter/request_test.rb +217 -0
  94. data/test/twitter/search_test.rb +208 -0
  95. data/test/twitter/trends_test.rb +112 -0
  96. data/test/twitter_test.rb +106 -0
  97. 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")