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