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
data/.gemtest
ADDED
File without changes
|
data/.gitignore
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
*.sw[a-p]
|
4
|
+
*.tmproj
|
5
|
+
*.tmproject
|
6
|
+
*.un~
|
7
|
+
*~
|
8
|
+
.DS_Store
|
9
|
+
.Spotlight-V100
|
10
|
+
.Trashes
|
11
|
+
._*
|
12
|
+
.bundle
|
13
|
+
.config
|
14
|
+
.directory
|
15
|
+
.elc
|
16
|
+
.emacs.desktop
|
17
|
+
.emacs.desktop.lock
|
18
|
+
.redcar
|
19
|
+
.yardoc
|
20
|
+
Desktop.ini
|
21
|
+
Gemfile.lock
|
22
|
+
Icon?
|
23
|
+
InstalledFiles
|
24
|
+
Session.vim
|
25
|
+
Thumbs.db
|
26
|
+
\#*\#
|
27
|
+
_yardoc
|
28
|
+
auto-save-list
|
29
|
+
coverage
|
30
|
+
doc
|
31
|
+
lib/bundler/man
|
32
|
+
pkg
|
33
|
+
pkg/*
|
34
|
+
rdoc
|
35
|
+
spec/reports
|
36
|
+
test/tmp
|
37
|
+
test/version_tmp
|
38
|
+
tmp
|
39
|
+
tmtags
|
40
|
+
tramp
|
data/.rspec
ADDED
data/.simplecov
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
SimpleCov.start
|
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Steve Agalloco
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
# EM-Twitter
|
2
|
+
|
3
|
+
EM-Twitter is an EventMachine-based ruby client for the Twitter Streaming API.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
require 'em-twitter'
|
9
|
+
|
10
|
+
options = {
|
11
|
+
:path => '/1/statuses/filter.json',
|
12
|
+
:params => { :track => 'yankees' },
|
13
|
+
:oauth => {
|
14
|
+
:consumer_key => ENV['CONSUMER_KEY'],
|
15
|
+
:consumer_secret => ENV['CONSUMER_SECRET'],
|
16
|
+
:token => ENV['OAUTH_TOKEN'],
|
17
|
+
:token_secret => ENV['OAUTH_TOKEN_SECRET']
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
EM.run do
|
22
|
+
client = EM::Twitter::Client.connect(options)
|
23
|
+
|
24
|
+
client.each do |result|
|
25
|
+
puts result
|
26
|
+
end
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
## SSL
|
31
|
+
|
32
|
+
SSL is used by default (EventMachine defaults to verify_peer => false), and can be configured:
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
options = {
|
36
|
+
:ssl => {
|
37
|
+
:private_key_file => "path/to/key.pem",
|
38
|
+
:cert_chain_file => "path/to/cert.pem",
|
39
|
+
:verify_peer => true
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
client = EM::Twitter.Client.connect(options)
|
44
|
+
```
|
45
|
+
|
46
|
+
## Proxy Support
|
47
|
+
|
48
|
+
EM-Twitter includes proxy support via a configuration option:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
options = {
|
52
|
+
:proxy => {
|
53
|
+
:username => 'myusername',
|
54
|
+
:passowrd => 'mypassword',
|
55
|
+
:uri => 'http://my-proxy:8080'
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
client = EM::Twitter.Client.connect(options)
|
60
|
+
```
|
61
|
+
|
62
|
+
## Error Handling
|
63
|
+
|
64
|
+
EM-Twitter supports the following callbacks for handling errors:
|
65
|
+
|
66
|
+
* on_unauthorized
|
67
|
+
* on_forbidden
|
68
|
+
* on_not_found
|
69
|
+
* on_not_acceptable
|
70
|
+
* on_too_long
|
71
|
+
* on_range_unacceptable
|
72
|
+
* on_enhance_your_calm (aliased as on_rate_limited)
|
73
|
+
|
74
|
+
Errors callbacks are invoked on a Client like so:
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
client = EM::Twitter.Client.connect(options)
|
78
|
+
client.on_forbidden do
|
79
|
+
puts 'oops'
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
## Reconnections
|
84
|
+
|
85
|
+
EM-Twitter has two callbacks for reconnection handling:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
client = EM::Twitter.Client.connect(options)
|
89
|
+
client.on_reconnect do |timeout, count|
|
90
|
+
# called each time the client reconnects
|
91
|
+
end
|
92
|
+
|
93
|
+
client.on_max_reconnects do |timeout, count|
|
94
|
+
# called when the client has exceeded either:
|
95
|
+
# 1. the maximum number of reconnect attempts
|
96
|
+
# 2. the maximum timeout limit for reconnections
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
100
|
+
## Todo
|
101
|
+
|
102
|
+
* Gzip encoding support (see [issue #1](https://github.com/spagalloco/em-twitter/issues/1) for more information)
|
103
|
+
* JSON Parser (see [issue #2](https://github.com/spagalloco/em-twitter/issues/2) for more information)
|
104
|
+
|
105
|
+
## Inspiration
|
106
|
+
|
107
|
+
EM-Twitter is heavily inspired by Vladimir Kolesnikov's [twitter-stream](https://github.com/voloko/twitter-stream). I learned an incredible amount from studying his code and much of the reconnection handling in EM-Twitter is derived/borrowed from his code as are . Eloy Durán's [ssalleyware](https://github.com/alloy/ssalleyware) was very helpful in adding SSL Certificate verification as was David Graham's [vines](https://github.com/negativecode/vines).
|
108
|
+
|
109
|
+
Testing with EM can be a challenge, but was made incredibly easy through the use of Hayes Davis' awesome [mockingbird](https://github.com/hayesdavis/mockingbird) gem.
|
110
|
+
|
111
|
+
## Contributing
|
112
|
+
|
113
|
+
Pull requests welcome: fork, make a topic branch, commit (squash when possible) *with tests* and I'll happily consider.
|
114
|
+
|
115
|
+
## Copyright
|
116
|
+
|
117
|
+
Copyright (c) 2012 Steve Agalloco. See [LICENSE](https://github.com/spagalloco/em-twitter/blob/master/LICENSE.md) for detail
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
5
|
+
|
6
|
+
task :default => :spec
|
7
|
+
task :test => :spec
|
8
|
+
|
9
|
+
require 'yard'
|
10
|
+
namespace :doc do
|
11
|
+
YARD::Rake::YardocTask.new do |task|
|
12
|
+
task.files = ['LICENSE.md', 'lib/**/*.rb']
|
13
|
+
task.options = ['--markup', 'markdown']
|
14
|
+
end
|
15
|
+
end
|
data/em-twitter.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.expand_path('../lib/em-twitter/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = 'em-twitter'
|
6
|
+
gem.version = EventMachine::Twitter::VERSION
|
7
|
+
gem.homepage = 'https://github.com/spagalloco/em-twitter'
|
8
|
+
|
9
|
+
gem.author = "Steve Agalloco"
|
10
|
+
gem.email = 'steve.agalloco@gmail.com'
|
11
|
+
gem.description = %q{Twitter Streaming API client for EventMachine}
|
12
|
+
gem.summary = %q{Twitter Streaming API client for EventMachine}
|
13
|
+
|
14
|
+
gem.add_dependency "eventmachine", ">= 1.0.0.beta.4"
|
15
|
+
gem.add_dependency "http_parser.rb", "~> 0.5"
|
16
|
+
gem.add_dependency "simple_oauth", "~> 0.1"
|
17
|
+
|
18
|
+
gem.add_development_dependency 'rake'
|
19
|
+
gem.add_development_dependency 'rdiscount'
|
20
|
+
gem.add_development_dependency 'rspec'
|
21
|
+
gem.add_development_dependency 'simplecov'
|
22
|
+
gem.add_development_dependency 'yard'
|
23
|
+
gem.add_development_dependency 'mockingbird', "~> 0.1.1"
|
24
|
+
gem.add_development_dependency 'guard-rspec'
|
25
|
+
|
26
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
|
27
|
+
gem.files = `git ls-files`.split("\n")
|
28
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
29
|
+
|
30
|
+
gem.require_paths = ['lib']
|
31
|
+
end
|
data/examples/stream.rb
ADDED
@@ -0,0 +1,61 @@
|
|
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 => 'yankees'
|
10
|
+
},
|
11
|
+
:oauth => {
|
12
|
+
:consumer_key => ENV['CONSUMER_KEY'],
|
13
|
+
:consumer_secret => ENV['CONSUMER_SECRET'],
|
14
|
+
:token => ENV['OAUTH_TOKEN'],
|
15
|
+
:token_secret => ENV['OAUTH_TOKEN_SECRET']
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
client = EM::Twitter::Client.connect(options)
|
20
|
+
|
21
|
+
client.each do |result|
|
22
|
+
puts result
|
23
|
+
end
|
24
|
+
|
25
|
+
client.on_error do |message|
|
26
|
+
puts "oops: error: #{message}"
|
27
|
+
end
|
28
|
+
|
29
|
+
client.on_unauthorized do
|
30
|
+
puts "oops: unauthorized"
|
31
|
+
end
|
32
|
+
|
33
|
+
client.on_forbidden do
|
34
|
+
puts "oops: unauthorized"
|
35
|
+
end
|
36
|
+
|
37
|
+
client.on_not_found do
|
38
|
+
puts "oops: not_found"
|
39
|
+
end
|
40
|
+
|
41
|
+
client.on_not_acceptable do
|
42
|
+
puts "oops: not_acceptable"
|
43
|
+
end
|
44
|
+
|
45
|
+
client.on_too_long do
|
46
|
+
puts "oops: too_long"
|
47
|
+
end
|
48
|
+
|
49
|
+
client.on_range_unacceptable do
|
50
|
+
puts "oops: range_unacceptable"
|
51
|
+
end
|
52
|
+
|
53
|
+
client.on_enhance_your_calm do
|
54
|
+
puts "oops: enhance_your_calm"
|
55
|
+
end
|
56
|
+
|
57
|
+
EM.add_timer(10) do
|
58
|
+
EM.stop
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
data/lib/em-twitter.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'em-twitter/client'
|
2
|
+
require 'em-twitter/version'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module EventMachine
|
6
|
+
module Twitter
|
7
|
+
DEFAULT_CONNECTION_OPTIONS = {
|
8
|
+
:host => 'stream.twitter.com',
|
9
|
+
:port => 443,
|
10
|
+
:method => 'POST',
|
11
|
+
:content_type => "application/x-www-form-urlencoded",
|
12
|
+
:path => '/',
|
13
|
+
:params => {},
|
14
|
+
:headers => {},
|
15
|
+
:user_agent => "EM::Twitter Ruby Gem #{EM::Twitter::VERSION}",
|
16
|
+
:proxy => nil,
|
17
|
+
:ssl => {},
|
18
|
+
:timeout => 0,
|
19
|
+
:auth => {},
|
20
|
+
:reconnect_options => {},
|
21
|
+
:encoding => nil,
|
22
|
+
:auto_reconnect => true
|
23
|
+
}
|
24
|
+
|
25
|
+
class ReconnectLimitError < StandardError; end
|
26
|
+
|
27
|
+
def self.logger
|
28
|
+
@logger ||= Logger.new(STDOUT)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.logger=(new_logger)
|
32
|
+
@logger = new_logger
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'em-twitter/connection'
|
2
|
+
|
3
|
+
module EventMachine
|
4
|
+
module Twitter
|
5
|
+
class Client
|
6
|
+
|
7
|
+
CALLBACKS = [
|
8
|
+
:each_item_callback,
|
9
|
+
:error_callback,
|
10
|
+
:unauthorized_callback,
|
11
|
+
:forbidden_callback,
|
12
|
+
:not_found_callback,
|
13
|
+
:not_acceptable_callback,
|
14
|
+
:too_long_callback,
|
15
|
+
:range_unacceptable_callback,
|
16
|
+
:enhance_your_calm_callback,
|
17
|
+
:reconnect_callback,
|
18
|
+
:max_reconnects_callback,
|
19
|
+
:close_callback
|
20
|
+
].freeze unless defined?(CALLBACKS)
|
21
|
+
|
22
|
+
attr_accessor :connection, :options, :host, :port
|
23
|
+
attr_accessor *CALLBACKS
|
24
|
+
|
25
|
+
# A convenience method for creating and connecting.
|
26
|
+
def self.connect(options = {})
|
27
|
+
new(options).tap do |client|
|
28
|
+
client.connect
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(options = {})
|
33
|
+
@options = DEFAULT_CONNECTION_OPTIONS.merge(options)
|
34
|
+
|
35
|
+
@host = @options[:host]
|
36
|
+
@port = @options[:port]
|
37
|
+
|
38
|
+
if @options[:proxy] && @options[:proxy][:uri]
|
39
|
+
proxy_uri = URI.parse(@options[:proxy][:uri])
|
40
|
+
@host = proxy_uri.host
|
41
|
+
@port = proxy_uri.port
|
42
|
+
end
|
43
|
+
@connection = nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def connect
|
47
|
+
@connection = EM.connect(@host, @port, Connection, self, @host, @port)
|
48
|
+
end
|
49
|
+
|
50
|
+
def each(&block)
|
51
|
+
@each_item_callback = block
|
52
|
+
end
|
53
|
+
|
54
|
+
def on_error(&block)
|
55
|
+
@error_callback = block
|
56
|
+
end
|
57
|
+
|
58
|
+
def on_unauthorized(&block)
|
59
|
+
@unauthorized_callback = block
|
60
|
+
end
|
61
|
+
|
62
|
+
def on_forbidden(&block)
|
63
|
+
@forbidden_callback = block
|
64
|
+
end
|
65
|
+
|
66
|
+
def on_not_found(&block)
|
67
|
+
@not_found_callback = block
|
68
|
+
end
|
69
|
+
|
70
|
+
def on_not_acceptable(&block)
|
71
|
+
@not_acceptable_callback = block
|
72
|
+
end
|
73
|
+
|
74
|
+
def on_too_long(&block)
|
75
|
+
@too_long_callback = block
|
76
|
+
end
|
77
|
+
|
78
|
+
def on_range_unacceptable(&block)
|
79
|
+
@range_unacceptable_callback = block
|
80
|
+
end
|
81
|
+
|
82
|
+
def on_enhance_your_calm(&block)
|
83
|
+
@enhance_your_calm_callback = block
|
84
|
+
end
|
85
|
+
alias :on_rate_limited :on_enhance_your_calm
|
86
|
+
|
87
|
+
def on_reconnect(&block)
|
88
|
+
@reconnect_callback = block
|
89
|
+
end
|
90
|
+
|
91
|
+
def on_max_reconnects(&block)
|
92
|
+
@max_reconnects_callback = block
|
93
|
+
end
|
94
|
+
|
95
|
+
def on_close(&block)
|
96
|
+
@close_callback = block
|
97
|
+
end
|
98
|
+
|
99
|
+
# Delegate to EM::Twitter::Connection
|
100
|
+
def method_missing(method, *args, &block)
|
101
|
+
return super unless @connection.respond_to?(method)
|
102
|
+
@connection.send(method, *args, &block)
|
103
|
+
end
|
104
|
+
|
105
|
+
def respond_to?(method, include_private=false)
|
106
|
+
@connection.respond_to?(method, include_private) || super(method, include_private)
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|