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
File without changes
@@ -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
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format=nested
3
+ --backtrace
@@ -0,0 +1 @@
1
+ SimpleCov.start
@@ -0,0 +1,8 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - jruby
6
+ - rbx
7
+ - ree
8
+ - ruby-head
@@ -0,0 +1,3 @@
1
+ --markup markdown
2
+ -
3
+ LICENSE.md
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ platforms :jruby do
4
+ gem 'jruby-openssl', '~> 0.7'
5
+ end
6
+
7
+ gemspec
@@ -0,0 +1,6 @@
1
+ guard 'rspec', :version => 2 do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
6
+
@@ -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.
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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