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.
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