em-twitter 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.gemtest +0 -0
  2. data/.gitignore +40 -0
  3. data/.rspec +3 -0
  4. data/.simplecov +1 -0
  5. data/.travis.yml +8 -0
  6. data/.yardopts +3 -0
  7. data/Gemfile +7 -0
  8. data/Guardfile +6 -0
  9. data/LICENSE.md +20 -0
  10. data/README.md +117 -0
  11. data/Rakefile +15 -0
  12. data/em-twitter.gemspec +31 -0
  13. data/examples/stream.rb +61 -0
  14. data/lib/em-twitter.rb +35 -0
  15. data/lib/em-twitter/client.rb +111 -0
  16. data/lib/em-twitter/connection.rb +273 -0
  17. data/lib/em-twitter/decoders/base_decoder.rb +11 -0
  18. data/lib/em-twitter/decoders/gzip_decoder.rb +14 -0
  19. data/lib/em-twitter/proxy.rb +25 -0
  20. data/lib/em-twitter/reconnectors/application_failure.rb +50 -0
  21. data/lib/em-twitter/reconnectors/network_failure.rb +51 -0
  22. data/lib/em-twitter/request.rb +126 -0
  23. data/lib/em-twitter/response.rb +48 -0
  24. data/lib/em-twitter/version.rb +5 -0
  25. data/lib/em_twitter.rb +1 -0
  26. data/smoke.rb +66 -0
  27. data/spec/em-twitter/client_spec.rb +55 -0
  28. data/spec/em-twitter/connection_error_handling_spec.rb +12 -0
  29. data/spec/em-twitter/connection_reconnect_spec.rb +139 -0
  30. data/spec/em-twitter/connection_spec.rb +148 -0
  31. data/spec/em-twitter/decoders/base_decoder_spec.rb +15 -0
  32. data/spec/em-twitter/decoders/gzip_decoder_spec.rb +20 -0
  33. data/spec/em-twitter/proxy_spec.rb +23 -0
  34. data/spec/em-twitter/reconnectors/application_failure_spec.rb +74 -0
  35. data/spec/em-twitter/reconnectors/network_failure_spec.rb +80 -0
  36. data/spec/em-twitter/request_spec.rb +77 -0
  37. data/spec/em-twitter/response_spec.rb +89 -0
  38. data/spec/em_twitter_spec.rb +19 -0
  39. data/spec/spec_helper.rb +68 -0
  40. 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
@@ -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