em-twitter 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemtest +0 -0
- data/.gitignore +40 -0
- data/.rspec +3 -0
- data/.simplecov +1 -0
- data/.travis.yml +8 -0
- data/.yardopts +3 -0
- data/Gemfile +7 -0
- data/Guardfile +6 -0
- data/LICENSE.md +20 -0
- data/README.md +117 -0
- data/Rakefile +15 -0
- data/em-twitter.gemspec +31 -0
- data/examples/stream.rb +61 -0
- data/lib/em-twitter.rb +35 -0
- data/lib/em-twitter/client.rb +111 -0
- data/lib/em-twitter/connection.rb +273 -0
- data/lib/em-twitter/decoders/base_decoder.rb +11 -0
- data/lib/em-twitter/decoders/gzip_decoder.rb +14 -0
- data/lib/em-twitter/proxy.rb +25 -0
- data/lib/em-twitter/reconnectors/application_failure.rb +50 -0
- data/lib/em-twitter/reconnectors/network_failure.rb +51 -0
- data/lib/em-twitter/request.rb +126 -0
- data/lib/em-twitter/response.rb +48 -0
- data/lib/em-twitter/version.rb +5 -0
- data/lib/em_twitter.rb +1 -0
- data/smoke.rb +66 -0
- data/spec/em-twitter/client_spec.rb +55 -0
- data/spec/em-twitter/connection_error_handling_spec.rb +12 -0
- data/spec/em-twitter/connection_reconnect_spec.rb +139 -0
- data/spec/em-twitter/connection_spec.rb +148 -0
- data/spec/em-twitter/decoders/base_decoder_spec.rb +15 -0
- data/spec/em-twitter/decoders/gzip_decoder_spec.rb +20 -0
- data/spec/em-twitter/proxy_spec.rb +23 -0
- data/spec/em-twitter/reconnectors/application_failure_spec.rb +74 -0
- data/spec/em-twitter/reconnectors/network_failure_spec.rb +80 -0
- data/spec/em-twitter/request_spec.rb +77 -0
- data/spec/em-twitter/response_spec.rb +89 -0
- data/spec/em_twitter_spec.rb +19 -0
- data/spec/spec_helper.rb +68 -0
- metadata +207 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EM::Twitter::BaseDecoder do
|
4
|
+
|
5
|
+
describe '#decode' do
|
6
|
+
before do
|
7
|
+
@decoder = EM::Twitter::BaseDecoder.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'passes through the response data' do
|
11
|
+
@decoder.decode('abc').should eq('abc')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EM::Twitter::GzipDecoder do
|
4
|
+
|
5
|
+
describe '#decode' do
|
6
|
+
before do
|
7
|
+
@decoder = EM::Twitter::GzipDecoder.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'decodes the response data' do
|
11
|
+
output = StringIO.new
|
12
|
+
gz = Zlib::GzipWriter.new(output)
|
13
|
+
gz.write('abc')
|
14
|
+
gz.close
|
15
|
+
|
16
|
+
@decoder.decode(output.string).should eq('abc')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EM::Twitter::Proxy do
|
4
|
+
describe '.new' do
|
5
|
+
it 'interprets a proxy configuration' do
|
6
|
+
proxy = EM::Twitter::Proxy.new(proxy_options[:proxy])
|
7
|
+
proxy.user.should eq('username')
|
8
|
+
proxy.password.should eq('password')
|
9
|
+
proxy.uri.should eq('http://my-proxy:8080')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#header' do
|
14
|
+
it 'returns false when no proxy credentials are passed' do
|
15
|
+
EM::Twitter::Proxy.new.header.should be_false
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'generates a header when passed credentials' do
|
19
|
+
proxy = EM::Twitter::Proxy.new(proxy_options[:proxy])
|
20
|
+
proxy.header.should be
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
include EM::Twitter::Reconnectors
|
4
|
+
|
5
|
+
describe EM::Twitter::Reconnectors::ApplicationFailure do
|
6
|
+
describe 'initialization' do
|
7
|
+
it 'initializes the reconnect_timeout' do
|
8
|
+
reconn = ApplicationFailure.new
|
9
|
+
reconn.reconnect_timeout.should eq(ApplicationFailure::START)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'accepts an options hash' do
|
13
|
+
reconn = ApplicationFailure.new(:reconnect_count => 25)
|
14
|
+
reconn.reconnect_count.should eq(25)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'reconnect_timeout' do
|
19
|
+
it 'returns the reconnect_timeout' do
|
20
|
+
reconn = ApplicationFailure.new
|
21
|
+
reconn.reconnect_timeout = 12
|
22
|
+
reconn.reconnect_timeout.should eq(12)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#increment' do
|
27
|
+
it 'increments the reconnect_count' do
|
28
|
+
reconn = ApplicationFailure.new
|
29
|
+
reconn.increment
|
30
|
+
reconn.reconnect_count.should eq(1)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'increments the reconnect_timeout' do
|
34
|
+
reconn = ApplicationFailure.new
|
35
|
+
reconn.increment
|
36
|
+
reconn.reconnect_timeout.should eq(20)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'accepts a block and yields the current timeout' do
|
40
|
+
recon_timeout = 0
|
41
|
+
|
42
|
+
reconn = ApplicationFailure.new
|
43
|
+
reconn.increment do |timeout|
|
44
|
+
recon_timeout = timeout
|
45
|
+
end
|
46
|
+
|
47
|
+
recon_timeout.should eq(20)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'raises an ReconnectLimitError after exceeding max reconnects' do
|
51
|
+
lambda {
|
52
|
+
reconn = ApplicationFailure.new(:reconnect_count => 11)
|
53
|
+
reconn.increment
|
54
|
+
}.should raise_error(EventMachine::Twitter::ReconnectLimitError)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'raises an ReconnectLimitError after exceeding the reconnect time limit' do
|
58
|
+
lambda {
|
59
|
+
reconn = ApplicationFailure.new(:reconnect_timeout => 321)
|
60
|
+
reconn.increment
|
61
|
+
}.should raise_error(EventMachine::Twitter::ReconnectLimitError)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#reset' do
|
66
|
+
it 'resets the reconnect_count' do
|
67
|
+
reconn = ApplicationFailure.new(:reconnect_count => 25)
|
68
|
+
reconn.reconnect_count.should eq(25)
|
69
|
+
|
70
|
+
reconn.reset
|
71
|
+
reconn.reconnect_count.should be_zero
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
include EM::Twitter::Reconnectors
|
4
|
+
|
5
|
+
describe EM::Twitter::Reconnectors::NetworkFailure do
|
6
|
+
describe 'initialization' do
|
7
|
+
it 'initializes the reconnect_timeout' do
|
8
|
+
reconn = NetworkFailure.new
|
9
|
+
reconn.reconnect_timeout.should eq(NetworkFailure::START)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'accepts an options hash' do
|
13
|
+
reconn = NetworkFailure.new(:reconnect_count => 25)
|
14
|
+
reconn.reconnect_count.should eq(25)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'reconnect_timeout' do
|
19
|
+
it 'returns the reconnect_timeout' do
|
20
|
+
reconn = NetworkFailure.new
|
21
|
+
reconn.reconnect_timeout = 12
|
22
|
+
reconn.reconnect_timeout.should eq(12)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'returns the maximum timeout when greater than the max' do
|
26
|
+
reconn = NetworkFailure.new
|
27
|
+
reconn.reconnect_timeout = NetworkFailure::MAX + 2
|
28
|
+
reconn.reconnect_timeout.should eq(NetworkFailure::MAX)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#increment' do
|
33
|
+
it 'increments the reconnect_count' do
|
34
|
+
reconn = NetworkFailure.new
|
35
|
+
reconn.increment
|
36
|
+
reconn.reconnect_count.should eq(1)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'increments the reconnect_timeout' do
|
40
|
+
reconn = NetworkFailure.new
|
41
|
+
reconn.increment
|
42
|
+
reconn.reconnect_timeout.should eq(0.5)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'accepts a block and yields the current timeout' do
|
46
|
+
recon_timeout = 0
|
47
|
+
|
48
|
+
reconn = NetworkFailure.new
|
49
|
+
reconn.increment do |timeout|
|
50
|
+
recon_timeout = timeout
|
51
|
+
end
|
52
|
+
|
53
|
+
recon_timeout.should eq(0.5)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'raises an ReconnectLimitError after exceeding max reconnects' do
|
57
|
+
lambda {
|
58
|
+
reconn = NetworkFailure.new(:reconnect_count => 11)
|
59
|
+
reconn.increment
|
60
|
+
}.should raise_error(EventMachine::Twitter::ReconnectLimitError)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'raises an ReconnectLimitError after exceeding the reconnect time limit' do
|
64
|
+
lambda {
|
65
|
+
reconn = NetworkFailure.new(:reconnect_timeout => 321)
|
66
|
+
reconn.increment
|
67
|
+
}.should raise_error(EventMachine::Twitter::ReconnectLimitError)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#reset' do
|
72
|
+
it 'resets the reconnect_count' do
|
73
|
+
reconn = NetworkFailure.new(:reconnect_count => 25)
|
74
|
+
reconn.reconnect_count.should eq(25)
|
75
|
+
|
76
|
+
reconn.reset
|
77
|
+
reconn.reconnect_count.should be_zero
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EM::Twitter::Request do
|
4
|
+
describe '.new' do
|
5
|
+
it 'assigns a proxy if one is set' do
|
6
|
+
req = EM::Twitter::Request.new(proxy_options)
|
7
|
+
req.proxy.should be
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'overrides defaults' do
|
11
|
+
req = EM::Twitter::Request.new(default_options)
|
12
|
+
req.options[:path].should eq('/1/statuses/filter.json')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#proxy?' do
|
17
|
+
it 'defaults to false' do
|
18
|
+
req = EM::Twitter::Request.new
|
19
|
+
req.proxy?.should be_false
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'returns true when a proxy is set' do
|
23
|
+
req = EM::Twitter::Request.new(proxy_options)
|
24
|
+
req.proxy?.should be_true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#to_s' do
|
29
|
+
context 'without a proxy' do
|
30
|
+
before do
|
31
|
+
@request = EM::Twitter::Request.new(default_options)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'requests the defined path' do
|
35
|
+
@request.to_s.should include('/1/statuses/filter.json')
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'includes an OAuth header' do
|
39
|
+
@request.to_s.should include('Authorization: OAuth')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when using a proxy' do
|
44
|
+
before do
|
45
|
+
@request = EM::Twitter::Request.new(default_options.merge(proxy_options))
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'requests the full uri' do
|
49
|
+
@request.to_s.should include("POST http://#{test_options[:host]}:#{test_options[:port]}/1/statuses/filter.json")
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'includes a Proxy header' do
|
53
|
+
@request.to_s.should include('Proxy-Authorization: Basic ')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'adds a POST body' do
|
58
|
+
@request = EM::Twitter::Request.new(default_options)
|
59
|
+
@request.to_s.should include('track=nfl')
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'adds query parameters' do
|
63
|
+
@request = EM::Twitter::Request.new(default_options.merge(:method => :get))
|
64
|
+
@request.to_s.should include('/1/statuses/filter.json?track=nfl')
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'allows defining a custom user-agent' do
|
68
|
+
@request = EM::Twitter::Request.new(default_options.merge(:user_agent => 'EM::Twitter Test Suite'))
|
69
|
+
@request.to_s.should include('User-Agent: EM::Twitter Test Suite')
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'adds custom headers' do
|
73
|
+
@request = EM::Twitter::Request.new(default_options.merge(:headers => { 'foo' => 'bar'}))
|
74
|
+
@request.to_s.should include('foo: bar')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EM::Twitter::Response do
|
4
|
+
|
5
|
+
describe '.new' do
|
6
|
+
it 'initializes an empty body' do
|
7
|
+
EM::Twitter::Response.new.body.should eq('')
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'initializes with a body parameter' do
|
11
|
+
EM::Twitter::Response.new('ohai').body.should eq('ohai')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#concat' do
|
16
|
+
it 'sets the body when empty' do
|
17
|
+
response = EM::Twitter::Response.new
|
18
|
+
response.concat('{ "status" : true }')
|
19
|
+
response.body.should eq('{ "status" : true }')
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'appends to an existing body' do
|
23
|
+
response = EM::Twitter::Response.new('{ "status" : true')
|
24
|
+
response.concat(', "enabled" : false }')
|
25
|
+
response.body.should eq('{ "status" : true, "enabled" : false }')
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'only appends when passed json' do
|
29
|
+
str = '{ "status" : true'
|
30
|
+
response = EM::Twitter::Response.new(str)
|
31
|
+
response.concat('ohai')
|
32
|
+
response.body.should eq(str)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'passively fails on nil' do
|
36
|
+
response = EM::Twitter::Response.new
|
37
|
+
lambda {
|
38
|
+
response.concat(nil)
|
39
|
+
}.should_not raise_error
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'passively fails on empty strings' do
|
43
|
+
response = EM::Twitter::Response.new('ohai')
|
44
|
+
response.concat('')
|
45
|
+
response.body.should eq('ohai')
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'passively fails on blank strings' do
|
49
|
+
response = EM::Twitter::Response.new('ohai')
|
50
|
+
response.concat(' ')
|
51
|
+
response.body.should eq('ohai')
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'is aliased as <<' do
|
55
|
+
response = EM::Twitter::Response.new
|
56
|
+
response << '{ "status" : true }'
|
57
|
+
response.body.should eq('{ "status" : true }')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '#complete?' do
|
62
|
+
it 'returns false when an incomplete body' do
|
63
|
+
EM::Twitter::Response.new('{ "status" : true').complete?.should be_false
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'returns false when an complete body' do
|
67
|
+
EM::Twitter::Response.new('{ "status" : true }').complete?.should be_true
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#empty?' do
|
72
|
+
it 'returns true when an empty body' do
|
73
|
+
EM::Twitter::Response.new.should be_empty
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'returns false when a body is present' do
|
77
|
+
EM::Twitter::Response.new('{ "status" : true }').should_not be_empty
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#reset' do
|
82
|
+
it 'resets the body to an empty string' do
|
83
|
+
response = EM::Twitter::Response.new('{ "status" : true }')
|
84
|
+
response.body.length.should be > 0
|
85
|
+
response.reset
|
86
|
+
response.body.should eq('')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EM::Twitter do
|
4
|
+
|
5
|
+
describe '.logger' do
|
6
|
+
it 'returns a Logger by default' do
|
7
|
+
EM::Twitter.logger.should be_kind_of(Logger)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '.logger=' do
|
12
|
+
it 'assigns a custom logger' do
|
13
|
+
FakeLogger = Class.new
|
14
|
+
EM::Twitter.logger = FakeLogger.new
|
15
|
+
EM::Twitter.logger.should be_kind_of(FakeLogger)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'simplecov'
|
3
|
+
|
4
|
+
require 'em-twitter'
|
5
|
+
require 'rspec'
|
6
|
+
require 'mockingbird'
|
7
|
+
|
8
|
+
def test_options
|
9
|
+
{ :host => '127.0.0.1', :port => 9551, :quiet => true }
|
10
|
+
end
|
11
|
+
|
12
|
+
def default_options
|
13
|
+
EM::Twitter::DEFAULT_CONNECTION_OPTIONS.merge({
|
14
|
+
:path => '/1/statuses/filter.json',
|
15
|
+
:params => {
|
16
|
+
:track => 'nfl'
|
17
|
+
},
|
18
|
+
:oauth => {
|
19
|
+
:consumer_key => 'cVcIw5zoLFE2a4BdDsmmA',
|
20
|
+
:consumer_secret => 'yYgVgvTT9uCFAi2IuscbYTCqwJZ1sdQxzISvLhNWUA',
|
21
|
+
:token => '4618-H3gU7mjDQ7MtFkAwHhCqD91Cp4RqDTp1AKwGzpHGL3I',
|
22
|
+
:token_secret => 'xmc9kFgOXpMdQ590Tho2gV7fE71v5OmBrX8qPGh7Y'
|
23
|
+
},
|
24
|
+
:ssl => false
|
25
|
+
}).merge(test_options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def proxy_options
|
29
|
+
{ :proxy => { :uri => 'http://my-proxy:8080', :user => 'username', :password => 'password'} }
|
30
|
+
end
|
31
|
+
|
32
|
+
def error_callback_invoked(callback, code, desc, msg = nil)
|
33
|
+
describe "##{callback}" do
|
34
|
+
before do
|
35
|
+
Mockingbird.setup(test_options) do
|
36
|
+
status code, desc
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
after { Mockingbird.teardown }
|
41
|
+
|
42
|
+
it "it invokes the callback on a #{code}" do
|
43
|
+
called = false
|
44
|
+
response_code = nil
|
45
|
+
|
46
|
+
if msg
|
47
|
+
block = lambda do |code|
|
48
|
+
response_code = code
|
49
|
+
called = true
|
50
|
+
EM.stop
|
51
|
+
end
|
52
|
+
else
|
53
|
+
block = lambda do
|
54
|
+
called = true
|
55
|
+
EM.stop
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
EM.run do
|
60
|
+
client = EM::Twitter::Client.connect(default_options)
|
61
|
+
client.send(:"#{callback}", &block)
|
62
|
+
end
|
63
|
+
|
64
|
+
response_code.should eq("Unhandled status code: #{code}.") if response_code
|
65
|
+
called.should be_true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|