em-twitter 0.1.0
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/.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,48 @@
|
|
1
|
+
module EventMachine
|
2
|
+
module Twitter
|
3
|
+
class Response
|
4
|
+
|
5
|
+
attr_reader :body
|
6
|
+
|
7
|
+
def initialize(body = '')
|
8
|
+
@body = body
|
9
|
+
end
|
10
|
+
|
11
|
+
def concat(data)
|
12
|
+
return unless data && data.size > 0
|
13
|
+
|
14
|
+
data.strip!
|
15
|
+
|
16
|
+
return if data.empty?
|
17
|
+
|
18
|
+
if json_start?(data) || json_end?(data)
|
19
|
+
@body << data
|
20
|
+
end
|
21
|
+
end
|
22
|
+
alias :<< :concat
|
23
|
+
|
24
|
+
def complete?
|
25
|
+
json_start?(@body) && json_end?(@body)
|
26
|
+
end
|
27
|
+
|
28
|
+
def empty?
|
29
|
+
@body == ''
|
30
|
+
end
|
31
|
+
|
32
|
+
def reset
|
33
|
+
@body = ''
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def json_start?(data)
|
39
|
+
data[0,1] == '{'
|
40
|
+
end
|
41
|
+
|
42
|
+
def json_end?(data)
|
43
|
+
data[data.length-1,1] == '}'
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/em_twitter.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'em-twitter'
|
data/smoke.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'em-twitter'
|
3
|
+
|
4
|
+
EM::run do
|
5
|
+
|
6
|
+
options = {
|
7
|
+
:path => '/1/statuses/filter.json',
|
8
|
+
:params => {
|
9
|
+
:track => 'you,Obama,eli,bachelor,Romney'
|
10
|
+
},
|
11
|
+
:ssl => {
|
12
|
+
:verify_peer => true,
|
13
|
+
:cert_chain_file => '/etc/ssl/certs/cacert.pem'
|
14
|
+
},
|
15
|
+
:oauth => {
|
16
|
+
:consumer_key => 'czveJ6LqlTuVHsbDBuRQ',
|
17
|
+
:consumer_secret => 'wGY6NCptPerGog4lPvAKu450jtUaT3bz7wSQdzDiYaY',
|
18
|
+
:token => '4618-01z5ihmeVYuc343JAYzaejVTuV0VU5CSF7gUWzsB8',
|
19
|
+
:token_secret => 'HVQrPKjfQAyNPkQCCej10dIaLUaFzIIEAkwsI1hQ0'
|
20
|
+
}
|
21
|
+
# , :encoding => 'gzip'
|
22
|
+
}
|
23
|
+
|
24
|
+
client = EM::Twitter::Client.connect(options)
|
25
|
+
|
26
|
+
client.each do |item|
|
27
|
+
puts item
|
28
|
+
end
|
29
|
+
|
30
|
+
client.on_error do |message|
|
31
|
+
puts "oops: error: #{message}"
|
32
|
+
end
|
33
|
+
|
34
|
+
client.on_unauthorized do
|
35
|
+
puts "oops: unauthorized"
|
36
|
+
end
|
37
|
+
|
38
|
+
client.on_forbidden do
|
39
|
+
puts "oops: unauthorized"
|
40
|
+
end
|
41
|
+
|
42
|
+
client.on_not_found do
|
43
|
+
puts "oops: not_found"
|
44
|
+
end
|
45
|
+
|
46
|
+
client.on_not_acceptable do
|
47
|
+
puts "oops: not_acceptable"
|
48
|
+
end
|
49
|
+
|
50
|
+
client.on_too_long do
|
51
|
+
puts "oops: too_long"
|
52
|
+
end
|
53
|
+
|
54
|
+
client.on_range_unacceptable do
|
55
|
+
puts "oops: range_unacceptable"
|
56
|
+
end
|
57
|
+
|
58
|
+
client.on_enhance_your_calm do
|
59
|
+
puts "oops: enhance_your_calm"
|
60
|
+
end
|
61
|
+
|
62
|
+
EM.add_timer(25) do
|
63
|
+
EM.stop
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EM::Twitter::Client do
|
4
|
+
|
5
|
+
describe '.connect' do
|
6
|
+
before do
|
7
|
+
conn = stub('EventMachine::Connection')
|
8
|
+
conn.stub(:start_tls).and_return(nil)
|
9
|
+
EM.stub(:connect).and_return(conn)
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'without a proxy' do
|
13
|
+
it 'connects to the configured host/port' do
|
14
|
+
EventMachine.should_receive(:connect)
|
15
|
+
.with(test_options[:host],
|
16
|
+
test_options[:port],
|
17
|
+
EventMachine::Twitter::Connection,
|
18
|
+
kind_of(EM::Twitter::Client),
|
19
|
+
test_options[:host],
|
20
|
+
test_options[:port])
|
21
|
+
EM::Twitter::Client.connect(default_options)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'when using a proxy' do
|
26
|
+
it 'connects to the proxy server' do
|
27
|
+
EventMachine.should_receive(:connect)
|
28
|
+
.with("my-proxy",
|
29
|
+
8080,
|
30
|
+
EventMachine::Twitter::Connection,
|
31
|
+
kind_of(EM::Twitter::Client),
|
32
|
+
'my-proxy',
|
33
|
+
8080)
|
34
|
+
EM::Twitter::Client.connect(default_options.merge(proxy_options))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should not trigger SSL until connection is established" do
|
39
|
+
connection = stub('EventMachine::Connection')
|
40
|
+
EM.should_receive(:connect).and_return(connection)
|
41
|
+
EM.should_not_receive(:start_tls)
|
42
|
+
client = EM::Twitter::Client.connect(:ssl => { :key => "/path/to/key.pem", :cert => "/path/to/cert.pem" })
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#respond_to?' do
|
47
|
+
it 'delegate to the connection' do
|
48
|
+
EM.run_block do
|
49
|
+
client = EM::Twitter::Client.connect(default_options)
|
50
|
+
client.respond_to?(:immediate_reconnect).should be_true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'EM::Twitter::Connection error handling' do
|
4
|
+
error_callback_invoked('on_unauthorized', 401, 'Unauthorized')
|
5
|
+
error_callback_invoked('on_forbidden', 403, 'Forbidden')
|
6
|
+
error_callback_invoked('on_not_found', 404, 'Not Found')
|
7
|
+
error_callback_invoked('on_not_acceptable', 406, 'Not Acceptable')
|
8
|
+
error_callback_invoked('on_too_long', 413, 'Too Long')
|
9
|
+
error_callback_invoked('on_range_unacceptable', 416, 'Range Unacceptable')
|
10
|
+
error_callback_invoked('on_enhance_your_calm', 420, 'Enhance Your Calm')
|
11
|
+
error_callback_invoked('on_error', 500, 'Internal Server Error', 'An error occurred.')
|
12
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
include EM::Twitter
|
4
|
+
|
5
|
+
describe 'EM::Twitter::Connection reconnections' do
|
6
|
+
|
7
|
+
describe '#reconnect' do
|
8
|
+
before do
|
9
|
+
Mockingbird.setup(test_options) do
|
10
|
+
on_connection(1) do
|
11
|
+
disconnect!
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
after { Mockingbird.teardown }
|
17
|
+
|
18
|
+
it 'calls the on_reconnect callback on reconnects' do
|
19
|
+
called = false
|
20
|
+
|
21
|
+
EM.run do
|
22
|
+
client = Client.connect(default_options)
|
23
|
+
client.on_reconnect { called = true; EM.stop }
|
24
|
+
end
|
25
|
+
|
26
|
+
called.should be_true
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'does not reconnect when auto_reconnect is false' do
|
30
|
+
EM.run do
|
31
|
+
client = Client.connect(default_options.merge(:auto_reconnect => false))
|
32
|
+
client.should_not_receive(:reconnect)
|
33
|
+
client.on_close { EM.stop }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#immediate_reconnect' do
|
39
|
+
before do
|
40
|
+
Mockingbird.setup(test_options) do
|
41
|
+
100.times do
|
42
|
+
send '{"foo":"bar"}'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
after { Mockingbird.teardown }
|
48
|
+
|
49
|
+
it 'reconnects immediately' do
|
50
|
+
called = false
|
51
|
+
|
52
|
+
EM.run_block do
|
53
|
+
client = Client.connect(default_options)
|
54
|
+
client.on_reconnect { called = true; EM.stop }
|
55
|
+
client.immediate_reconnect
|
56
|
+
end
|
57
|
+
|
58
|
+
called.should be_true
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'reconnects the current connection' do
|
62
|
+
EM.run_block do
|
63
|
+
client = Client.connect(default_options)
|
64
|
+
client.connection.should_receive(:reconnect).once
|
65
|
+
client.reconnect
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#on_max_reconnects' do
|
71
|
+
context 'application failure' do
|
72
|
+
before do
|
73
|
+
Mockingbird.setup(test_options) do
|
74
|
+
status 200, "Success"
|
75
|
+
|
76
|
+
on_connection('*') do
|
77
|
+
disconnect!
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
after { Mockingbird.teardown }
|
83
|
+
|
84
|
+
it "should notify after reconnect limit is reached" do
|
85
|
+
timeout, retries = nil, nil
|
86
|
+
|
87
|
+
EM.run do
|
88
|
+
client = Client.new(default_options)
|
89
|
+
client.on_max_reconnects do |t, r|
|
90
|
+
timeout, retries = t, r
|
91
|
+
EM.stop
|
92
|
+
end
|
93
|
+
client.connect
|
94
|
+
|
95
|
+
# do this so that EM doesn't create timers that grind
|
96
|
+
# this test to a halt
|
97
|
+
client.connection.reconnector = Reconnectors::ApplicationFailure.new(:reconnect_count => 320)
|
98
|
+
end
|
99
|
+
|
100
|
+
timeout.should eq(20)
|
101
|
+
retries.should eq(321)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'network failure' do
|
106
|
+
before do
|
107
|
+
Mockingbird.setup(test_options) do
|
108
|
+
status 200, "Success"
|
109
|
+
|
110
|
+
on_connection('*') do
|
111
|
+
disconnect!
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
after { Mockingbird.teardown }
|
117
|
+
|
118
|
+
it "should notify after reconnect limit is reached" do
|
119
|
+
timeout, retries = nil, nil
|
120
|
+
|
121
|
+
EM.run do
|
122
|
+
client = Client.new(default_options)
|
123
|
+
client.on_max_reconnects do |t, r|
|
124
|
+
timeout, retries = t, r
|
125
|
+
EM.stop
|
126
|
+
end
|
127
|
+
client.connect
|
128
|
+
|
129
|
+
# do this so that EM doesn't create timers that grind
|
130
|
+
# this test to a halt
|
131
|
+
client.connection.reconnector = Reconnectors::NetworkFailure.new(:reconnect_count => 320)
|
132
|
+
end
|
133
|
+
|
134
|
+
timeout.should eq(0.5)
|
135
|
+
retries.should eq(321)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EM::Twitter::Connection do
|
4
|
+
|
5
|
+
describe '#post_init' do
|
6
|
+
it 'calls the on_inited callback' do
|
7
|
+
called = false
|
8
|
+
|
9
|
+
EM.run_block do
|
10
|
+
client = EM::Twitter::Client.connect(default_options.merge(:on_inited => lambda { called = true}))
|
11
|
+
end
|
12
|
+
|
13
|
+
called.should be_true
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'sets the inactivity timeout' do
|
17
|
+
EM.should_receive(:set_comm_inactivity_timeout)
|
18
|
+
EM.run_block do
|
19
|
+
client = EM::Twitter::Client.connect(default_options.merge(:timeout => 2))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'streaming' do
|
25
|
+
describe '#receive_data' do
|
26
|
+
before do
|
27
|
+
Mockingbird.setup(test_options) do
|
28
|
+
100.times do
|
29
|
+
send '{"foo":"ba'
|
30
|
+
send 'r"}'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
after { Mockingbird.teardown }
|
36
|
+
|
37
|
+
it 'converts response data into complete buffers' do
|
38
|
+
count = 0
|
39
|
+
|
40
|
+
EM.run do
|
41
|
+
client = EM::Twitter::Client.connect(default_options)
|
42
|
+
client.each do |message|
|
43
|
+
count = count + 1
|
44
|
+
EM.stop if count == 100
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
count.should == 100
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#each' do
|
53
|
+
before do
|
54
|
+
Mockingbird.setup(test_options) do
|
55
|
+
100.times do
|
56
|
+
send '{"foo":"bar"}'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
after { Mockingbird.teardown }
|
62
|
+
|
63
|
+
it 'emits each complete response chunk' do
|
64
|
+
count = 0
|
65
|
+
|
66
|
+
EM.run do
|
67
|
+
client = EM::Twitter::Client.connect(default_options)
|
68
|
+
client.each do |message|
|
69
|
+
count = count + 1
|
70
|
+
EM.stop if count == 100
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
count.should == 100
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "#on_close" do
|
80
|
+
it "sets a callback that is invoked when the connection closes" do
|
81
|
+
called = false
|
82
|
+
|
83
|
+
EM.run_block do
|
84
|
+
client = EM::Twitter::Client.connect(default_options)
|
85
|
+
client.on_close { called = true }
|
86
|
+
end
|
87
|
+
|
88
|
+
called.should be_true
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#stop' do
|
93
|
+
it 'closes the connection' do
|
94
|
+
EM.run_block do
|
95
|
+
client = EM::Twitter::Client.connect(default_options)
|
96
|
+
client.connection.should_receive(:close_connection).once
|
97
|
+
client.stop
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'gracefully closes' do
|
102
|
+
EM.run_block do
|
103
|
+
client = EM::Twitter::Client.connect(default_options)
|
104
|
+
client.stop
|
105
|
+
client.should be_gracefully_closed
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '#update' do
|
111
|
+
before do
|
112
|
+
Mockingbird.setup(test_options) do
|
113
|
+
100.times do
|
114
|
+
send '{"foo":"bar"}'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
after { Mockingbird.teardown }
|
120
|
+
|
121
|
+
it 'updates the options hash' do
|
122
|
+
EM.run_block do
|
123
|
+
client = EM::Twitter::Client.connect(default_options)
|
124
|
+
client.connection.update(:params => { :track => 'rangers' })
|
125
|
+
client.connection.options[:params].should eq({:track => 'rangers'})
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'reconnects after updating' do
|
130
|
+
EM.run_block do
|
131
|
+
client = EM::Twitter::Client.connect(default_options)
|
132
|
+
client.connection.should_receive(:immediate_reconnect).once
|
133
|
+
client.connection.update(:params => { :track => 'rangers' })
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'uses the new options when reconnecting' do
|
138
|
+
EM.run_block do
|
139
|
+
client = EM::Twitter::Client.connect(default_options)
|
140
|
+
client.connection.should_receive(:send_data) do |request|
|
141
|
+
request.to_s.should include('track=rangers')
|
142
|
+
end
|
143
|
+
client.connection.update(:params => { :track => 'rangers' })
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|