markpercival-twitter_getter 1.0.6
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/.gitignore +1 -0
- data/README.markdown +24 -0
- data/lib/twitter_getter.rb +123 -0
- data/lib/twitter_getter/promiscuous_struct.rb +35 -0
- data/spec/destroy_spec.rb +11 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/test_account.sample.yaml +4 -0
- data/spec/tweet_spec.rb +48 -0
- data/spec/twitter_getter_spec.rb +49 -0
- metadata +64 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
/spec/test_account.yaml
|
data/README.markdown
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# TwitterGetter - A simple Twitter API gem
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
sudo gem install markpercival-twittergetter --sources http://gems.github.com
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
@tg = TwitterGetter::Base.new('ev', 'password')
|
10
|
+
tweets = @tg.search('failwhale')
|
11
|
+
tweets.each do |t|
|
12
|
+
p "#{t.from_user}: #{t.text}"
|
13
|
+
end
|
14
|
+
|
15
|
+
@tg.follow('aplusk')
|
16
|
+
|
17
|
+
## License
|
18
|
+
|
19
|
+
This code is free to use under the terms of the MIT license.
|
20
|
+
|
21
|
+
## Contact
|
22
|
+
|
23
|
+
Mark Percival <mark@mpercival.com>
|
24
|
+
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'cgi'
|
3
|
+
require 'rest_client'
|
4
|
+
require 'json/pure'
|
5
|
+
require 'twitter_getter/promiscuous_struct'
|
6
|
+
|
7
|
+
module TwitterGetter
|
8
|
+
|
9
|
+
class Tweet
|
10
|
+
include PromiscuousStruct
|
11
|
+
end
|
12
|
+
|
13
|
+
class User
|
14
|
+
include PromiscuousStruct
|
15
|
+
end
|
16
|
+
|
17
|
+
class Base
|
18
|
+
|
19
|
+
attr_accessor :user, :password
|
20
|
+
|
21
|
+
def initialize(user=nil, password=nil)
|
22
|
+
@user = user
|
23
|
+
@password = password
|
24
|
+
end
|
25
|
+
|
26
|
+
def update_status(message, in_reply_to_status_id = nil)
|
27
|
+
raise AuthenicationNeeded if @password.nil?
|
28
|
+
t = JSON.parse(RestClient.post("http://#{@user}:#{@password}@twitter.com/statuses/update.json",
|
29
|
+
:status => message,
|
30
|
+
:in_reply_to_status_id => in_reply_to_status_id))
|
31
|
+
Tweet.new(t)
|
32
|
+
end
|
33
|
+
|
34
|
+
def destroy_status(id)
|
35
|
+
raise AuthenicationNeeded if @password.nil?
|
36
|
+
t = JSON.parse(RestClient.post("http://#{@user}:#{@password}@twitter.com/statuses/destroy/#{id}.json", :id => id))
|
37
|
+
Tweet.new(t)
|
38
|
+
end
|
39
|
+
|
40
|
+
def status(id)
|
41
|
+
t = JSON.parse(RestClient.get("http://twitter.com/statuses/show/#{id}.json"))
|
42
|
+
Tweet.new(t)
|
43
|
+
end
|
44
|
+
|
45
|
+
def friend_ids
|
46
|
+
JSON.parse(RestClient.get("http://twitter.com/friends/ids/#{self.user}.json"))
|
47
|
+
end
|
48
|
+
|
49
|
+
def follower_ids
|
50
|
+
JSON.parse(RestClient.get("http://twitter.com/followers/ids/#{self.user}.json"))
|
51
|
+
end
|
52
|
+
|
53
|
+
def unfriendly_ids
|
54
|
+
self.friend_ids - self.follower_ids
|
55
|
+
end
|
56
|
+
|
57
|
+
def unfollow(id)
|
58
|
+
raise AuthenicationNeeded if @password.nil?
|
59
|
+
begin
|
60
|
+
u = JSON.parse(RestClient.post("http://#{@user}:#{@password}@twitter.com/friendships/destroy/#{id}.json", :id => id))
|
61
|
+
u['status'] ? User.new(u) : nil
|
62
|
+
rescue RestClient::ResourceNotFound => e
|
63
|
+
p "Error - #{e}"
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def follow(id)
|
69
|
+
raise AuthenicationNeeded if @password.nil?
|
70
|
+
begin
|
71
|
+
u = JSON.parse(RestClient.post("http://#{@user}:#{@password}@twitter.com/friendships/create/#{id}.json", :id => id))
|
72
|
+
u['status'] ? User.new(u) : nil
|
73
|
+
rescue RestClient::ResourceNotFound => e
|
74
|
+
p "Error - #{e}"
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def rate_limit_status
|
80
|
+
results = JSON.parse(RestClient.get("http://#{@user}:#{@password}@twitter.com/account/rate_limit_status.json"))
|
81
|
+
end
|
82
|
+
|
83
|
+
def search(term, opts = {})
|
84
|
+
if opts[:max_pages]
|
85
|
+
max_pages = opts[:max_pages]
|
86
|
+
opts.delete(:max_pages)
|
87
|
+
end
|
88
|
+
term = CGI.escape(term)
|
89
|
+
opts = {
|
90
|
+
:since_id => 10,
|
91
|
+
:rpp => 50
|
92
|
+
}.merge(opts)
|
93
|
+
params = hash2params(opts)
|
94
|
+
tweets = []
|
95
|
+
JSON.parse(RestClient.get("http://search.twitter.com/search.json?q=#{term}#{params}"))['results'].each do |t|
|
96
|
+
tweets << Tweet.new(t)
|
97
|
+
end
|
98
|
+
results = tweets.size
|
99
|
+
page = 2
|
100
|
+
while results == 50 && page <= max_pages.to_i
|
101
|
+
next_tweets = JSON.parse(RestClient.get("http://search.twitter.com/search.json?q=#{term}#{params}&page=#{page}"))['results']
|
102
|
+
results = next_tweets.size
|
103
|
+
next_tweets.each do |t|
|
104
|
+
tweets << Tweet.new(t)
|
105
|
+
end
|
106
|
+
page += 1
|
107
|
+
end
|
108
|
+
tweets.reverse
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def hash2params(hash)
|
114
|
+
params = ''
|
115
|
+
hash.each_pair do |k,v|
|
116
|
+
params += "&#{k}=#{CGI.escape(v.to_s)}"
|
117
|
+
end
|
118
|
+
params
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module PromiscuousStruct
|
2
|
+
attr_reader :attributes
|
3
|
+
|
4
|
+
def initialize(h)
|
5
|
+
@attributes = h
|
6
|
+
end
|
7
|
+
|
8
|
+
def get_val(a)
|
9
|
+
val = @attributes[a] || @attributes[a.to_s] || @attributes[a.to_sym]
|
10
|
+
if val.is_a?(Hash)
|
11
|
+
self.class.new(val)
|
12
|
+
else
|
13
|
+
val
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](a)
|
18
|
+
get_val(a)
|
19
|
+
end
|
20
|
+
|
21
|
+
# an existing but depricated method we need to overide
|
22
|
+
def id
|
23
|
+
get_val(:id)
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing(m)
|
27
|
+
if val = get_val(m)
|
28
|
+
return val
|
29
|
+
elsif @attributes.respond_to?(m)
|
30
|
+
return @attributes.send(m)
|
31
|
+
else
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe TwitterGetter, "behaviour" do
|
4
|
+
|
5
|
+
it "should destroy an existing status" do
|
6
|
+
auth = YAML.load(File.open(File.dirname(__FILE__) + '/test_account.yaml'))
|
7
|
+
tg = TwitterGetter::Base.new(auth['user'], auth['password'])
|
8
|
+
p tg.destroy_status(1650005187)
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
|
3
|
+
$:.unshift(File.join(dir, '/../lib/'))
|
4
|
+
require dir + '/../lib/twitter_getter'
|
5
|
+
|
6
|
+
|
7
|
+
def unauthenticate_twitter
|
8
|
+
TwitterGetter::Base.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def authenticated_twitter
|
12
|
+
auth = YAML.load(File.open(File.dirname(__FILE__) + '/test_account.yaml'))
|
13
|
+
TwitterGetter::Base.new(auth['user'], auth['password'])
|
14
|
+
end
|
data/spec/tweet_spec.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe TwitterGetter::Tweet, "behaviour" do
|
4
|
+
|
5
|
+
|
6
|
+
before :all do
|
7
|
+
@t = TwitterGetter::Tweet.new(:itsa => "hash", :andalso => {:complex=>true}, 'andevenallows' => 'string keys')
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should instantiate with a hash and be accessible" do
|
11
|
+
@t.andalso.complex.should == true
|
12
|
+
@t.andalso[:complex].should == true
|
13
|
+
@t[:itsa].should == 'hash'
|
14
|
+
@t['itsa'].should == 'hash'
|
15
|
+
@t.itsa.should == 'hash'
|
16
|
+
@t.size.should == 3
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should force instantiation with a hash" do
|
20
|
+
lambda {TwitterGetter::Tweet.new()}.should raise_error(ArgumentError)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should raise nil on an unknown method call" do
|
24
|
+
@t.wtf.should == nil
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
describe TwitterGetter, "example of what to expect in the return from twitter" do
|
32
|
+
|
33
|
+
before :all do
|
34
|
+
@tg = authenticated_twitter
|
35
|
+
@status_update = @tg.search('twitter').first
|
36
|
+
end
|
37
|
+
|
38
|
+
it "from a status update" do
|
39
|
+
@status_update.from_user.should be_an_instance_of(String)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "from a user follow" do
|
43
|
+
user = @tg.follow(@status_update.from_user)
|
44
|
+
user.screen_name.should == @status_update.from_user
|
45
|
+
user.friends_count.should >= 0
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
describe TwitterGetter, "GeoIP search" do
|
5
|
+
|
6
|
+
|
7
|
+
before :all do
|
8
|
+
@tg = unauthenticate_twitter
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should find all location based tweets" do
|
12
|
+
t = @tg.search('twitter', :geocode => '33.75916942652898,-84.38941955566406,100mi')
|
13
|
+
t.size.should eql(50)
|
14
|
+
t[0].from_user.size.should >= 1
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should allow search multiple pages" do
|
18
|
+
t = @tg.search('twitter', :geocode => '33.75916942652898,-84.38941955566406,100mi', :max_pages => 2)
|
19
|
+
t.size.should eql(100)
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
describe TwitterGetter, "sends a tweet, find it, and destroy it" do
|
26
|
+
|
27
|
+
before :all do
|
28
|
+
@tg = authenticated_twitter
|
29
|
+
tweets = @tg.search('twitter')
|
30
|
+
t = tweets.first
|
31
|
+
@test_message = "RT @#{t.from_user} #{t.text[0, t.from_user.size + 5]}"
|
32
|
+
@test_update = @tg.update_status(@test_message)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should be able to send and then find a status update for the authenticated user" do
|
36
|
+
@test_update.text.should eql(@test_message)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should be able to find the test tweet" do
|
40
|
+
@tg.status(@test_update.id).text.should eql(@test_message)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should also be able to destroy it" do
|
44
|
+
@tg.destroy_status(@test_update.id).text.should eql(@test_message)
|
45
|
+
# Verify it's destroyed
|
46
|
+
lambda {@tg.status(@test_update.id)}.should raise_error(RestClient::Exception)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: markpercival-twitter_getter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.6
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mark Percival
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-09 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: mark@mpercival.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.markdown
|
24
|
+
files:
|
25
|
+
- .gitignore
|
26
|
+
- README.markdown
|
27
|
+
- lib/twitter_getter.rb
|
28
|
+
- lib/twitter_getter/promiscuous_struct.rb
|
29
|
+
- spec/destroy_spec.rb
|
30
|
+
- spec/spec_helper.rb
|
31
|
+
- spec/test_account.sample.yaml
|
32
|
+
- spec/tweet_spec.rb
|
33
|
+
- spec/twitter_getter_spec.rb
|
34
|
+
has_rdoc: true
|
35
|
+
homepage: http://github.com/markpercival/twitter_getter
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options:
|
38
|
+
- --charset=UTF-8
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
version:
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
53
|
+
requirements: []
|
54
|
+
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 1.2.0
|
57
|
+
signing_key:
|
58
|
+
specification_version: 2
|
59
|
+
summary: TwitterGetter - The Twitter API Wrapper
|
60
|
+
test_files:
|
61
|
+
- spec/destroy_spec.rb
|
62
|
+
- spec/spec_helper.rb
|
63
|
+
- spec/tweet_spec.rb
|
64
|
+
- spec/twitter_getter_spec.rb
|