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

Potentially problematic release.


This version of twitter-stream might be problematic. Click here for more details.

@@ -0,0 +1,30 @@
1
+ # twitter-stream
2
+
3
+ Simple Ruby client library for [twitter streaming API](http://apiwiki.twitter.com/Streaming-API-Documentation).
4
+ Uses [EventMachine](http://rubyeventmachine.com/) for connection handling. Adheres to twitter's [reconnection guidline](http://apiwiki.twitter.com/Streaming-API-Documentation#Connecting).
5
+
6
+ JSON format only.
7
+
8
+ ## Usage
9
+
10
+ EventMachine::run {
11
+ stream = Twitter::JSONStream.connect(
12
+ :path => '/1/statuses/filter.json?track=football',
13
+ :auth => 'LOGIN:PASSWORD',
14
+ )
15
+
16
+ stream.each_item do |item|
17
+ # do someting with unparsed JSON item
18
+ end
19
+
20
+ stream.on_error do |message|
21
+ # log or something
22
+ end
23
+ }
24
+
25
+
26
+ ## Examples
27
+
28
+ Open examples/reader.rb. Replace LOGIN:PASSWORD with your real twitter login and password. And
29
+ ruby examples/reader.rb
30
+
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ gem 'rspec'
3
+ require 'spec/rake/spectask'
4
+
5
+ desc "Run all specs"
6
+ Spec::Rake::SpecTask.new('spec') do |t|
7
+ t.spec_files = FileList['spec/**/*.rb']
8
+ t.spec_opts = %w(-fs --color)
9
+ end
10
+
11
+ begin
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gemspec|
14
+ gemspec.name = "twitter-stream"
15
+ gemspec.summary = "Twitter realtime API client"
16
+ gemspec.description = "Simple Ruby client library for twitter streaming API. Uses EventMachine for connection handling. Adheres to twitter's reconnection guidline. JSON format only."
17
+ gemspec.email = "voloko@gmail.com"
18
+ gemspec.homepage = "http://github.com/voloko/twitter-stream"
19
+ gemspec.authors = ["Vladimir Kolesnikov"]
20
+ end
21
+ rescue LoadError
22
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
23
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,40 @@
1
+ require 'rubygems'
2
+ lib_path = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ $LOAD_PATH.unshift lib_path unless $LOAD_PATH.include?(lib_path)
4
+
5
+ require 'twitter/json_stream'
6
+
7
+ EventMachine::run {
8
+ stream = Twitter::JSONStream.connect(
9
+ :path => '/1/statuses/filter.json',
10
+ :auth => 'LOGIN:PASSWORD',
11
+ :method => 'POST',
12
+ :content => 'track=basketball,football,baseball,footy,soccer'
13
+ )
14
+
15
+ stream.each_item do |item|
16
+ $stdout.print "item: #{item}\n"
17
+ $stdout.flush
18
+ end
19
+
20
+ stream.on_error do |message|
21
+ $stdout.print "error: #{message}\n"
22
+ $stdout.flush
23
+ end
24
+
25
+ stream.on_reconnect do |timeout, retries|
26
+ $stdout.print "reconnecting in: #{timeout} seconds\n"
27
+ $stdout.flush
28
+ end
29
+
30
+ stream.on_max_reconnects do |timeout, retries|
31
+ $stdout.print "Failed after #{retries} failed reconnects\n"
32
+ $stdout.flush
33
+ end
34
+
35
+ trap('TERM') {
36
+ stream.stop
37
+ EventMachine.stop if EventMachine.reactor_running?
38
+ }
39
+ }
40
+ puts "The event loop has ended"
@@ -0,0 +1,14 @@
1
+ HTTP/1.1 200 OK
2
+ Content-Type: application/json
3
+ Transfer-Encoding: chunked
4
+ Server: Jetty(6.1.17)
5
+
6
+ {"text":"Just wanted the world to know what our theologically deep pastor @bprentice has for a desktop background. http://yfrog.com/1rl4ij","favorited":false,"in_reply_to_user_id":null,"in_reply_to_screen_name":null,"source":"<a href=\"http://www.atebits.com/\" rel=\"nofollow\">Tweetie</a>","truncated":false,"created_at":"Thu Oct 08 19:34:09 +0000 2009","geo":null,"user":{"geo_enabled":false,"profile_text_color":"5b5252","location":"Stillwater, Oklahoma","statuses_count":122,"followers_count":70,"profile_link_color":"220099","description":"Taking an unchanging Savior to a changing world! Eagle Heights in Stillwater, Oklahoma.","following":null,"friends_count":136,"profile_sidebar_fill_color":"f37a20","url":"http://www.eagleheights.com","profile_image_url":"http://a3.twimg.com/profile_images/249941843/online_logo_normal.jpg","verified":false,"notifications":null,"favourites_count":0,"profile_sidebar_border_color":"c5f109","protected":false,"screen_name":"eagleheights","profile_background_tile":false,"profile_background_image_url":"http://a1.twimg.com/profile_background_images/5935314/EHBC_LOGO.jpg","created_at":"Tue Mar 17 14:52:04 +0000 2009","name":"Eagle Heights","time_zone":"Central Time (US & Canada)","id":24892440,"utc_offset":-21600,"profile_background_color":"3d3d3d"},"id":4714982187,"in_reply_to_status_id":null}
7
+
8
+ {"text":"I finally took a good pic of our resident BobCat @ the JakeCruise Ranch: http://yfrog.com/769vij","favorited":false,"in_reply_to_user_id":null,"in_reply_to_screen_name":null,"source":"<a href=\"http://www.atebits.com/\" rel=\"nofollow\">Tweetie</a>","truncated":false,"created_at":"Thu Oct 08 19:34:12 +0000 2009","geo":null,"user":{"geo_enabled":false,"profile_text_color":"141318","location":"West Hollywood, CA","statuses_count":265,"followers_count":80,"profile_link_color":"DA4425","description":"Gay Programmer in SoCal","following":null,"friends_count":37,"profile_sidebar_fill_color":"5DD2F4","url":"http://www.kelvo.com/","profile_image_url":"http://a1.twimg.com/profile_images/197950488/kelvis_green_current_normal.jpeg","verified":false,"notifications":null,"favourites_count":1,"profile_sidebar_border_color":"1F6926","protected":false,"screen_name":"KelvisWeHo","profile_background_tile":false,"profile_background_image_url":"http://a1.twimg.com/profile_background_images/9898116/thaneeya3.jpg","created_at":"Fri Apr 17 18:44:57 +0000 2009","name":"Kelvis Del Rio","time_zone":"Pacific Time (US & Canada)","id":32517583,"utc_offset":-28800,"profile_background_color":"cccccc"},"id":4714983168,"in_reply_to_status_id":null}
9
+
10
+ {"text":"Thursdays are long. But at least we're allowed a cheat sheet for stats. http://yfrog.com/9gjc7j","favorited":false,"in_reply_to_user_id":null,"in_reply_to_screen_name":null,"source":"<a href=\"http://twitterrific.com\" rel=\"nofollow\">Twitterrific</a>","truncated":false,"created_at":"Thu Oct 08 19:34:21 +0000 2009","geo":null,"user":{"geo_enabled":false,"profile_text_color":"663B12","location":"Calgary ","statuses_count":68,"followers_count":10,"profile_link_color":"1F98C7","description":"pure. love. art. music. ","following":null,"friends_count":22,"profile_sidebar_fill_color":"DAECF4","url":null,"profile_image_url":"http://a3.twimg.com/profile_images/391882135/Photo_550_normal.jpg","verified":false,"notifications":null,"favourites_count":5,"profile_sidebar_border_color":"C6E2EE","protected":false,"screen_name":"KarinRudmik","profile_background_tile":false,"profile_background_image_url":"http://a3.twimg.com/profile_background_images/33520483/shimmering__by_AnBystrowska.jpg","created_at":"Wed Jul 29 02:37:13 +0000 2009","name":"Karin Rudmik","time_zone":"Mountain Time (US & Canada)","id":61091098,"utc_offset":-25200,"profile_background_color":"C6E2EE"},"id":4714986148,"in_reply_to_status_id":null}
11
+
12
+
13
+
14
+ {"text":"acabando de almorzar, les comparto mi ultima creacion: http://yfrog.com/5mi94j pollo a la maracuya, con verduras sofritas ^^","favorited":false,"in_reply_to_user_id":null,"in_reply_to_screen_name":null,"source":"<a href=\"http://www.seesmic.com/\" rel=\"nofollow\">Seesmic</a>","truncated":false,"created_at":"Thu Oct 08 19:34:28 +0000 2009","geo":null,"user":{"geo_enabled":false,"profile_text_color":"3C3940","location":"Cartagena de Indias","statuses_count":1016,"followers_count":190,"profile_link_color":"0099B9","description":"Cartagenero extremadamente consentido, flojo y amante del anime, el manga, la cocina y los mmorpgs. pdt: y de los postres por supuesto!!!","following":null,"friends_count":253,"profile_sidebar_fill_color":"95E8EC","url":"http://www.flickr.com/photos/lobitokun/","profile_image_url":"http://a1.twimg.com/profile_images/451679242/shippo_normal.jpg","verified":false,"notifications":null,"favourites_count":9,"profile_sidebar_border_color":"5ED4DC","protected":false,"screen_name":"lobitokun","profile_background_tile":false,"profile_background_image_url":"http://a3.twimg.com/profile_background_images/24664583/hideki1.jpg","created_at":"Fri Jul 17 15:32:14 +0000 2009","name":"emiro gomez beltran","time_zone":"Bogota","id":57672295,"utc_offset":-18000,"profile_background_color":"0099B9"},"id":4714988998,"in_reply_to_status_id":null}
@@ -0,0 +1,201 @@
1
+ require 'eventmachine'
2
+ require 'em/buftok'
3
+
4
+ module Twitter
5
+ class JSONStream < EventMachine::Connection
6
+ MAX_LINE_LENGTH = 16*1024
7
+
8
+ # network failure reconnections
9
+ NF_RECONNECT_START = 0.25
10
+ NF_RECONNECT_ADD = 0.25
11
+ NF_RECONNECT_MAX = 16
12
+
13
+ # app failure reconnections
14
+ AF_RECONNECT_START = 10
15
+ AF_RECONNECT_MUL = 2
16
+
17
+ RECONNECT_MAX = 320
18
+ RETRIES_MAX = 10
19
+
20
+ DEFAULT_OPTIONS = {
21
+ :method => 'GET',
22
+ :path => '/',
23
+ :content_type => "application/x-www-form-urlencoded",
24
+ :content => '',
25
+ :path => '/1/statuses/filter.json',
26
+ :host => 'stream.twitter.com',
27
+ :port => 80,
28
+ :auth => 'test:test',
29
+ :user_agent => 'TwitterStream',
30
+ }
31
+
32
+ attr_accessor :code
33
+ attr_accessor :headers
34
+ attr_accessor :nf_last_reconnect
35
+ attr_accessor :af_last_reconnect
36
+ attr_accessor :reconnect_retries
37
+
38
+ def self.connect options = {}
39
+ options = DEFAULT_OPTIONS.merge(options)
40
+ EventMachine.connect options[:host], options[:port], self, options
41
+ end
42
+
43
+ def initialize options = {}
44
+ @options = DEFAULT_OPTIONS.merge(options) # merge in case initialize called directly
45
+ @gracefully_closed = false
46
+ @nf_last_reconnect = nil
47
+ @af_last_reconnect = nil
48
+ @reconnect_retries = 0
49
+ end
50
+
51
+ def each_item &block
52
+ @each_item_callback = block
53
+ end
54
+
55
+ def on_error &block
56
+ @error_callback = block
57
+ end
58
+
59
+ def on_reconnect &block
60
+ @reconnect_callback = block
61
+ end
62
+
63
+ def on_max_reconnects &block
64
+ @max_reconnects_callback = block
65
+ end
66
+
67
+ def stop
68
+ @gracefully_closed = true
69
+ close_connection
70
+ end
71
+
72
+ def unbind
73
+ receive_line(@buffer.flush) unless @buffer.empty?
74
+ schedule_reconnect unless @gracefully_closed
75
+ end
76
+
77
+ def receive_data data
78
+ begin
79
+ @buffer.extract(data).each do |line|
80
+ receive_line(line)
81
+ end
82
+ rescue Exception => e
83
+ receive_error(e.message)
84
+ close_connection
85
+ return
86
+ end
87
+ end
88
+
89
+ def post_init
90
+ reset_state
91
+ send_request
92
+ end
93
+
94
+ protected
95
+ def schedule_reconnect
96
+ timeout = reconnect_timeout
97
+ @reconnect_retries += 1
98
+ if timeout <= RECONNECT_MAX && @reconnect_retries <= RETRIES_MAX
99
+ reconnect_after(timeout)
100
+ else
101
+ @max_reconnects_callback.call(timeout, @reconnect_retries) if @max_reconnects_callback
102
+ end
103
+ end
104
+
105
+ def reconnect_after timeout
106
+ @reconnect_callback.call(timeout, @reconnect_retries) if @reconnect_callback
107
+ EventMachine.add_timer(timeout) do
108
+ reconnect @options[:host], @options[:port]
109
+ end
110
+ end
111
+
112
+ def reconnect_timeout
113
+ if (@code == 0) # network failure
114
+ if @nf_last_reconnect
115
+ @nf_last_reconnect += NF_RECONNECT_ADD
116
+ else
117
+ @nf_last_reconnect = NF_RECONNECT_START
118
+ end
119
+ [@nf_last_reconnect,NF_RECONNECT_MAX].min
120
+ else
121
+ if @af_last_reconnect
122
+ @af_last_reconnect *= AF_RECONNECT_MUL
123
+ else
124
+ @af_last_reconnect = AF_RECONNECT_START
125
+ end
126
+ @af_last_reconnect
127
+ end
128
+ end
129
+
130
+ def reset_state
131
+ @code = 0
132
+ @headers = []
133
+ @state = :init
134
+ @buffer = BufferedTokenizer.new("\r", MAX_LINE_LENGTH)
135
+ end
136
+
137
+ def send_request
138
+ data = []
139
+ data << "#{@options[:method]} #{@options[:path]} HTTP/1.1"
140
+ data << "Host: #{@options[:host]}"
141
+ data << "User-agent: #{@options[:user_agent]}" if @options[:user_agent]
142
+ data << "Authorization: Basic " + [@options[:auth]].pack('m').delete("\r\n")
143
+ if @options[:method] == 'POST'
144
+ data << "Content-type: #{@options[:content_type]}"
145
+ data << "Content-length: #{@options[:content].length}"
146
+ end
147
+ data << "\r\n"
148
+ send_data data.join("\r\n") + @options[:content]
149
+ end
150
+
151
+ def receive_line ln
152
+ case @state
153
+ when :init
154
+ parse_response_line ln
155
+ when :headers
156
+ parse_header_line ln
157
+ when :stream
158
+ parse_stream_line ln
159
+ end
160
+ end
161
+
162
+ def receive_error e
163
+ @error_callback.call(e) if @error_callback
164
+ end
165
+
166
+ def parse_stream_line ln
167
+ ln.strip!
168
+ unless ln.empty?
169
+ if ln[0,1] == '{'
170
+ @each_item_callback.call(ln) if @each_item_callback
171
+ end
172
+ end
173
+ end
174
+
175
+ def parse_header_line ln
176
+ ln.strip!
177
+ if ln.empty?
178
+ reset_timeouts
179
+ @state = :stream
180
+ else
181
+ headers << ln
182
+ end
183
+ end
184
+
185
+ def parse_response_line ln
186
+ if ln =~ /\AHTTP\/1\.[01] ([\d]{3})/
187
+ @code = $1.to_i
188
+ @state = :headers
189
+ else
190
+ receive_error('invalid response')
191
+ close_connection
192
+ end
193
+ end
194
+
195
+ def reset_timeouts
196
+ @nf_last_reconnect = @af_last_reconnect = nil
197
+ @reconnect_retries = 0
198
+ end
199
+
200
+ end
201
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ lib_path = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ $LOAD_PATH.unshift lib_path unless $LOAD_PATH.include?(lib_path)
4
+
5
+ gem 'rspec'
6
+ require 'spec'
7
+ require 'spec/mocks'
8
+
9
+ def fixture_path(path)
10
+ File.join(File.dirname(__FILE__), '..', 'fixtures', path)
11
+ end
12
+
13
+ def read_fixture(path)
14
+ File.read(fixture_path(path))
15
+ end
@@ -0,0 +1,189 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+ require 'twitter/json_stream'
3
+
4
+ include Twitter
5
+
6
+ describe JSONStream do
7
+
8
+ context "on create" do
9
+
10
+ it "should return stream" do
11
+ EM.should_receive(:connect).and_return('TEST INSTANCE')
12
+ stream = JSONStream.connect {}
13
+ stream.should == 'TEST INSTANCE'
14
+ end
15
+
16
+ it "should define default properties" do
17
+ EM.should_receive(:connect).with do |host, port, handler, opts|
18
+ host.should == 'stream.twitter.com'
19
+ port.should == 80
20
+ opts[:path].should == '/1/statuses/filter.json'
21
+ opts[:method].should == 'GET'
22
+ end
23
+ stream = JSONStream.connect {}
24
+ end
25
+
26
+ end
27
+
28
+
29
+ Host = "127.0.0.1"
30
+ Port = 9550
31
+
32
+ class JSONServer < EM::Connection
33
+ attr_accessor :data
34
+ def receive_data data
35
+ $recieved_data = data
36
+ send_data $data_to_send
37
+ EventMachine.next_tick {
38
+ close_connection if $close_connection
39
+ }
40
+ end
41
+ end
42
+
43
+ context "on valid stream" do
44
+ before :each do
45
+ $data_to_send = read_fixture('twitter/basic_http.txt')
46
+ $recieved_data = ''
47
+ $close_connection = false
48
+ end
49
+
50
+ it "should parse headers" do
51
+ EM.run {
52
+ EM.start_server Host, Port, JSONServer
53
+ @stream = JSONStream.connect :host => Host, :port => Port
54
+ EM.add_timer(0.5){ EM.stop }
55
+ }
56
+ @stream.code.should == 200
57
+ @stream.headers[0].downcase.should include 'content-type'
58
+ end
59
+
60
+ it "should parse headers even after connection close" do
61
+ EM.run {
62
+ $close_connection = true
63
+ EM.start_server Host, Port, JSONServer
64
+ @stream = JSONStream.connect :host => Host, :port => Port
65
+ EM.add_timer(0.5){ EM.stop }
66
+ }
67
+ @stream.code.should == 200
68
+ @stream.headers[0].downcase.should include 'content-type'
69
+ end
70
+
71
+ it "should extract records" do
72
+ EM.run {
73
+ EM.start_server Host, Port, JSONServer
74
+ @stream = JSONStream.connect :host => Host, :port => Port, :user_agent => 'TEST_USER_AGENT'
75
+ EM.add_timer(0.5){ EM.stop }
76
+ }
77
+ $recieved_data.upcase.should include('USER-AGENT: TEST_USER_AGENT')
78
+ end
79
+
80
+ it "should send correct user agent" do
81
+ EM.run {
82
+ $close_connection = true
83
+ EM.start_server Host, Port, JSONServer
84
+ @stream = JSONStream.connect :host => Host, :port => Port
85
+ EM.add_timer(0.5){ EM.stop }
86
+ }
87
+ end
88
+ end
89
+
90
+ context "on network failure" do
91
+ before :each do
92
+ $data_to_send = ''
93
+ $close_connection = true
94
+ end
95
+
96
+ it "should reconnect on network failure" do
97
+ EM.run {
98
+ EM.start_server Host, Port, JSONServer
99
+ @stream = JSONStream.connect :host => Host, :port => Port
100
+ @stream.should_receive(:reconnect)
101
+ EM.add_timer(0.5){ EM.stop }
102
+ }
103
+ end
104
+
105
+ it "should reconnect with 0.25 at base" do
106
+ EM.run {
107
+ EM.start_server Host, Port, JSONServer
108
+ @stream = JSONStream.connect :host => Host, :port => Port
109
+ @stream.should_receive(:reconnect_after).with(0.25)
110
+ EM.add_timer(0.5){ EM.stop }
111
+ }
112
+ end
113
+
114
+ it "should reconnect with linear timeout" do
115
+ EM.run {
116
+ EM.start_server Host, Port, JSONServer
117
+ @stream = JSONStream.connect :host => Host, :port => Port
118
+ @stream.nf_last_reconnect = 1
119
+ @stream.should_receive(:reconnect_after).with(1.25)
120
+ EM.add_timer(0.5){ EM.stop }
121
+ }
122
+ end
123
+
124
+ it "should stop reconnecting after 100 times" do
125
+ EM.run {
126
+ EM.start_server Host, Port, JSONServer
127
+ @stream = JSONStream.connect :host => Host, :port => Port
128
+ @stream.reconnect_retries = 100
129
+ @stream.should_not_receive(:reconnect_after)
130
+ EM.add_timer(0.5){ EM.stop }
131
+ }
132
+ end
133
+
134
+ it "should notify after reconnect limit is reached" do
135
+ timeout, retries = nil, nil
136
+ EM.run {
137
+ EM.start_server Host, Port, JSONServer
138
+ @stream = JSONStream.connect :host => Host, :port => Port
139
+ @stream.on_max_reconnects do |t, r|
140
+ timeout, retries = t, r
141
+ end
142
+ @stream.reconnect_retries = 100
143
+ EM.add_timer(0.5){ EM.stop }
144
+ }
145
+ timeout.should == 0.25
146
+ retries.should == 101
147
+ end
148
+ end
149
+
150
+ context "on application failure" do
151
+ before :each do
152
+ $data_to_send = 'HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm="Firehose"\r\n\r\n1'
153
+ $close_connection = true
154
+ end
155
+
156
+ it "should reconnect on application failure 10 at base" do
157
+ EM.run {
158
+ EM.start_server Host, Port, JSONServer
159
+ @stream = JSONStream.connect :host => Host, :port => Port
160
+ @stream.should_receive(:reconnect_after).with(10)
161
+ EM.add_timer(0.5){ EM.stop }
162
+ }
163
+ end
164
+
165
+ it "should reconnect with exponential timeout" do
166
+ EM.run {
167
+ EM.start_server Host, Port, JSONServer
168
+ @stream = JSONStream.connect :host => Host, :port => Port
169
+ @stream.af_last_reconnect = 160
170
+ @stream.should_receive(:reconnect_after).with(320)
171
+ EM.add_timer(0.5){ EM.stop }
172
+ }
173
+ end
174
+
175
+ it "should not try to reconnect after limit is reached" do
176
+ EM.run {
177
+ EM.start_server Host, Port, JSONServer
178
+ @stream = JSONStream.connect :host => Host, :port => Port
179
+ @stream.af_last_reconnect = 320
180
+ @stream.should_not_receive(:reconnect_after)
181
+ EM.add_timer(0.5){ EM.stop }
182
+ }
183
+ end
184
+ end
185
+
186
+
187
+
188
+ end
189
+
@@ -0,0 +1,46 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{twitter-stream}
5
+ s.version = "0.1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Vladimir Kolesnikov"]
9
+ s.date = %q{2009-10-11}
10
+ s.description = %q{Simple Ruby client library for twitter streaming API. Uses EventMachine for connection handling. Adheres to twitter's reconnection guidline. JSON format only.}
11
+ s.email = %q{voloko@gmail.com}
12
+ s.extra_rdoc_files = [
13
+ "README.markdown"
14
+ ]
15
+ s.files = [
16
+ "README.markdown",
17
+ "Rakefile",
18
+ "VERSION",
19
+ "examples/reader.rb",
20
+ "fixtures/twitter/basic_http.txt",
21
+ "lib/twitter/json_stream.rb",
22
+ "spec/spec_helper.rb",
23
+ "spec/twitter/json_stream.rb",
24
+ "twitter-stream.gemspec"
25
+ ]
26
+ s.homepage = %q{http://github.com/voloko/twitter-stream}
27
+ s.rdoc_options = ["--charset=UTF-8"]
28
+ s.require_paths = ["lib"]
29
+ s.rubygems_version = %q{1.3.4}
30
+ s.summary = %q{Twitter realtime API client}
31
+ s.test_files = [
32
+ "spec/spec_helper.rb",
33
+ "spec/twitter/json_stream.rb",
34
+ "examples/reader.rb"
35
+ ]
36
+
37
+ if s.respond_to? :specification_version then
38
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
39
+ s.specification_version = 3
40
+
41
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
42
+ else
43
+ end
44
+ else
45
+ end
46
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: twitter-stream
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Vladimir Kolesnikov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-11 00:00:00 +04:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Simple Ruby client library for twitter streaming API. Uses EventMachine for connection handling. Adheres to twitter's reconnection guidline. JSON format only.
17
+ email: voloko@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.markdown
24
+ files:
25
+ - README.markdown
26
+ - Rakefile
27
+ - VERSION
28
+ - examples/reader.rb
29
+ - fixtures/twitter/basic_http.txt
30
+ - lib/twitter/json_stream.rb
31
+ - spec/spec_helper.rb
32
+ - spec/twitter/json_stream.rb
33
+ - twitter-stream.gemspec
34
+ has_rdoc: true
35
+ homepage: http://github.com/voloko/twitter-stream
36
+ licenses: []
37
+
38
+ post_install_message:
39
+ rdoc_options:
40
+ - --charset=UTF-8
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ requirements: []
56
+
57
+ rubyforge_project:
58
+ rubygems_version: 1.3.4
59
+ signing_key:
60
+ specification_version: 3
61
+ summary: Twitter realtime API client
62
+ test_files:
63
+ - spec/spec_helper.rb
64
+ - spec/twitter/json_stream.rb
65
+ - examples/reader.rb