nervion 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,4 +1,6 @@
1
- TODOs
2
1
  .DS_Store
3
- examples
2
+ .rbx
3
+ .yardoc
4
4
  coverage
5
+ doc
6
+ examples
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ matrix:
3
+ allow_failures:
4
+ - rvm: ruby-head
5
+ rvm:
6
+ - rbx-19mode
7
+ - 1.9.2
8
+ - 1.9.3
9
+ - ruby-head
data/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # version 0.0.2
2
+
3
+ - Support for firehose endpoint
4
+ - Validate client setup before start streaming
5
+
6
+ # version 0.0.1
7
+
8
+ - Support for sample and filter endpoints
9
+ - OAuth authentication
data/Gemfile.lock CHANGED
@@ -22,6 +22,7 @@ GEM
22
22
  http_parser.rb (0.5.3)
23
23
  json (1.7.3)
24
24
  multi_json (1.3.6)
25
+ rake (0.9.2.2)
25
26
  rspec (2.9.0)
26
27
  rspec-core (~> 2.9.0)
27
28
  rspec-expectations (~> 2.9.0)
@@ -35,6 +36,7 @@ GEM
35
36
  simplecov-html (~> 0.5.3)
36
37
  simplecov-html (0.5.3)
37
38
  yajl-ruby (1.1.0)
39
+ yard (0.8.2.1)
38
40
 
39
41
  PLATFORMS
40
42
  ruby
@@ -42,5 +44,7 @@ PLATFORMS
42
44
  DEPENDENCIES
43
45
  cucumber
44
46
  nervion!
47
+ rake
45
48
  rspec
46
49
  simplecov
50
+ yard
data/README.md CHANGED
@@ -1,24 +1,16 @@
1
- # Nervion
1
+ # Nervion [![Build Status](https://secure.travis-ci.org/jacegu/nervion.png?branch=master)][travis] [![Dependency Status](https://gemnasium.com/jacegu/nervion.png?travis)][gemnasium]
2
2
 
3
- **A minimalistic Twitter Stream API Ruby client**.
3
+ **A minimalistic Ruby client for the
4
+ [Public Streams](https://dev.twitter.com/docs/streaming-apis/streams/public)
5
+ of Twitter Streaming API**.
4
6
 
5
-
6
- ## Motivation
7
-
8
- In our current project we had the need to consume the stream provided by twitter.
9
- Although there are a few gems available we had to suffer the pain of poor
10
- documentation and error swallowing, which made us lose a lot of time.
11
-
12
- At that point I decided to build one on my own, and that's why you are reading
13
- this.
7
+ [travis]: http://travis-ci.org/jacegu/nervion
8
+ [gemnasium]: https://gemnasium.com/jacegu/nervion
14
9
 
15
10
 
16
11
 
17
12
  ## Installation
18
13
 
19
- **WARNING: This project hasn't been released as a Gem yet**. This is here for
20
- future reference.
21
-
22
14
  Add this line to your application's Gemfile:
23
15
 
24
16
  gem 'nervion'
@@ -33,36 +25,29 @@ Or install it yourself as:
33
25
 
34
26
 
35
27
 
36
- ## Usage
28
+ ## Overview
37
29
 
38
30
  Nervion mimics the endpoints provided by the
39
- [Twitter Stream API](https://dev.twitter.com/docs/streaming-apis).
40
- Currently it supports the
41
- [Public Streams](https://dev.twitter.com/docs/streaming-apis/streams/public).
42
- In the future we will add support for the
43
- [User Streams](https://dev.twitter.com/docs/streaming-apis/streams/user)
44
- and the
45
- [Site Streams](Use://dev.twitter.com/docs/streaming-apis/streams/site).
46
-
47
- Specifically the two calls that are that are available to the broad audience:
31
+ [Twitter Stream API](https://dev.twitter.com/docs/streaming-apis)
32
+ through the following methods:
48
33
 
49
34
  - [`follow`](https://dev.twitter.com/docs/api/1/post/statuses/filter)
50
35
  - [`sample`](https://dev.twitter.com/docs/api/1/get/statuses/sample)
51
-
52
- [`firehose`](https://dev.twitter.com/docs/api/1/get/statuses/firehose)
53
- is not supported yet since requires a special access level.
54
-
55
- Checkout the docs of both endpoints to know what tweets you can query the
56
- Streaming API for.
57
-
58
- You can specify any of the parameters supported by the endpoints by passing them
36
+ - [`firehose`](https://dev.twitter.com/docs/api/1/get/statuses/firehose)
37
+ *notice that the firehose support hasn't been tested against the actual API
38
+ since it requires a level of access I don't have. If you were able to verify
39
+ that it works, please, let me know*
40
+
41
+ Checkout the docs of the endpoints to know what tweets you can query the
42
+ Streaming API for and what parameters you have to provide to do so. You can
43
+ specify any of the parameters supported by the endpoints by passing them
59
44
  as named parameters to the provided methods:
60
45
 
61
46
  ```ruby
62
- require 'nervion'
63
-
64
- Nervion.filter(delimited: 1953, track: 'ruby', stall_warnings: true) do |parsed_status|
65
- #do something with the parsed status
47
+ # This is tracking every tweet that includes the string "madrid" OR any tweet
48
+ # that is geo-located in Madrid.
49
+ Nervion.filter(track: 'madrid', locations: '40.364,-3.760,40.365,-3.609') do |message|
50
+ # do something with the message
66
51
  end
67
52
  ```
68
53
 
@@ -72,7 +57,7 @@ Twitter.
72
57
 
73
58
 
74
59
 
75
- ###Authentication
60
+ ## Authentication
76
61
 
77
62
  Since Twitter plans to remove support for basic auth eventually, **Nervion only
78
63
  supports OAuth authentication**.
@@ -81,15 +66,16 @@ You can provide the tokens and secrets in a configuration flavour:
81
66
 
82
67
  ```ruby
83
68
  Nervion.configure do |config|
84
- config.consumer_key = the_consumer_key
85
- config.consumer_secret = the_consumer_secret
86
- config.access_token = the_access_token
87
- config.access_token_secret = the_access_token_secret
69
+ config.consumer_key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'
70
+ config.consumer_secret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'
71
+ config.access_token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'
72
+ config.access_token_secret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'
88
73
  end
89
74
  ```
90
75
 
91
76
 
92
- ###Parsing JSON
77
+
78
+ ## JSON Parsing
93
79
 
94
80
  **Nervion will parse the JSON returned by twitter for you**. It uses
95
81
  [Yajl](https://github.com/brianmario/yajl-ruby) as JSON parser for its out of
@@ -100,37 +86,42 @@ to use symbols to fetch data in the callbacks.
100
86
 
101
87
 
102
88
 
103
- ###Callbacks
89
+ ## Callbacks
104
90
 
105
- Nowdays Nervion only has one callback that acts upon the received statuses. It
106
- **will support callbacks on specific types of tweets and errors** in future
107
- versions.
91
+ Nervion provides three callbacks:
108
92
 
109
- The callbacks will receive only one parameter: the hash with the symbolized keys
110
- resultant of the JSON parsing. You get to choose what to do with the hash:
111
- [mash](https://github.com/intridea/hashie) it before working with it or even
112
- wrap it in some object that specializes on querying the information that is
113
- relevant to you.
114
-
115
- To know what keys to expect you should browse the
116
- [*Platform Objects Documentation*](https://dev.twitter.com/docs/platform-objects/tweets).
93
+ - **Message callback**: called when message is received and parsed
94
+ - **HTTP error callback**: called when Twitter responds with a status above 200
95
+ - **Network error callback**: called when the connection to the stream is lost
117
96
 
118
97
 
119
- ####Status Callback
98
+ ### Message Callback
120
99
 
121
- You can setup a callback that **acts on all the received statuses** by simply
100
+ You must setup a callback that **acts on all the received messages** by simply
122
101
  passing in a block to the API call you are making:
123
102
 
124
103
  ```ruby
125
- Nervion.sample { |status| puts status[:text] if status.has_key? :text }
104
+ Nervion.sample { |message| puts message[:text] if message.has_key? :text }
126
105
  ```
127
106
 
128
- Be aware that **the callback will be called with any type of timeline update**
129
- (or even with warnings if the `stall_warnings` parameter is set to `true`. Keep
130
- this in mind when querying the hash.
107
+ Be aware that
108
+ **[every message type](https://dev.twitter.com/docs/streaming-apis/messages)
109
+ will trigger this callback**. Keep this in mind when querying the hash.
110
+
111
+ The callback receives only one parameter: the hash with the symbolized keys
112
+ resultant of the JSON parsing. You get to choose what to do with the hash:
113
+ [mash](https://github.com/intridea/hashie) it before working with it or even
114
+ wrap it in some object that specializes on querying the information that is
115
+ relevant to you.
116
+
117
+ To know what keys to expect you should browse the
118
+ [*Platform Objects Documentation*](https://dev.twitter.com/docs/platform-objects/tweets)
119
+ and know the different
120
+ [message types](https://dev.twitter.com/docs/streaming-apis/messages)
121
+ .
131
122
 
132
123
 
133
- #### HTTP Error Callback
124
+ ### HTTP Error Callback
134
125
 
135
126
  This callback will be executed when the Streaming API sends a response with a
136
127
  status code above 200. After the callback has been executed a retry will be
@@ -142,17 +133,18 @@ You can setup the callback like this:
142
133
 
143
134
  ```ruby
144
135
  Nervion.on_http_error do |status, body|
145
- puts "Response status was: #{status}"
146
- puts "Response body was: #{body}"
136
+ puts "the status of the response was: #{status}"
137
+ puts "the body of the response body was: #{body}"
147
138
  end
148
139
  ```
149
140
 
150
- If no callback is set, Nervion's default behaviour will be to output the an
151
- error message to `STDERR` that contains both the status and the body of Twitter
152
- Streaming API's response.
141
+ Given that most of the HTTP errors are due to client configuration, if no
142
+ callback is set, Nervion's default behaviour will be to output an error message
143
+ to `STDERR` that contains both the status and the body of Twitter Streaming
144
+ API's response.
153
145
 
154
146
 
155
- #### Network Error callback
147
+ ### Network Error callback
156
148
 
157
149
  This callback will be executed when the connection with the Twitter Stream API
158
150
  is unexpectedly closed.
@@ -163,10 +155,26 @@ Nervion.on_network_error do
163
155
  end
164
156
  ```
165
157
 
166
- **Nervion will do nothing by default when network errors occurr** because it is
158
+ Nervion will do nothing by default when network errors occur because it is
167
159
  unlikely that they are provoked by the client itself.
168
160
 
169
161
 
162
+ ### Callback chaining
163
+
164
+ Callback setup can be chained like this:
165
+
166
+ ```ruby
167
+ Nervion.on_network_error do
168
+ #do something about the error
169
+ end.on_http_error do |status, body|
170
+ #do something about the error
171
+ end.sample do |status|
172
+ #do something with the status
173
+ end
174
+ ```
175
+
176
+
177
+
170
178
  ## EventMachine Integration
171
179
 
172
180
  Nervion runs on the top of EventMachine.
@@ -174,6 +182,8 @@ Nervion runs on the top of EventMachine.
174
182
  In the near future this `README` will provide a guideline to take advantage of
175
183
  the benefits that EventMachine can provide when used correctly.
176
184
 
185
+
186
+
177
187
  ## Roadmap
178
188
 
179
189
  There are some features that are needed and that will be developed before the first
@@ -184,14 +194,12 @@ release of the gem:
184
194
  - <del>Adhere to the
185
195
  [Twitter Connection guidelines](https://dev.twitter.com/docs/streaming-api/concepts#connecting)</del>
186
196
  *done!*
187
- - Take advantage of EventMachine deferrables on callbacks
188
- - Rewrite and improve the DSL provided to setup Nervion
197
+ - <del>Improve the DSL provided to setup Nervion to validate the client
198
+ setup</del> *done!*
189
199
 
190
- Once those basic features are provided there are a few more that will be very
191
- interesting to have:
200
+ Future features will be:
192
201
 
193
202
  - Use a gzip compressed stream
194
- - Add callbacks to act on specific types of tweets: i.e. `on_retweet`,
195
- `on_deleted_status`
196
-
197
- If people start using the client more features will be added.
203
+ - Be able to configure the client to skip parsing and yield bare Strings with
204
+ the JSON of the streamed messages. The objective is to improve performance by
205
+ parsing in other process.
data/Rakefile CHANGED
@@ -1,10 +1,11 @@
1
- #!/usr/bin/env rake
2
-
3
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
4
2
  require 'rspec/core/rake_task'
5
3
  require 'cucumber/rake/task'
4
+ require 'yard'
6
5
 
7
6
  task :default => [:test]
7
+
8
+ desc 'Run all the features and specs'
8
9
  task :test => [:rspec, :cucumber]
9
10
 
10
11
  RSpec::Core::RakeTask.new(:rspec) do |t|
@@ -15,7 +16,10 @@ Cucumber::Rake::Task.new(:cucumber) do |t|
15
16
  t.cucumber_opts = '--format progress'
16
17
  end
17
18
 
18
- task :cov do
19
+ YARD::Rake::YardocTask.new
20
+
21
+ desc 'Generate test coverage report'
22
+ task :coverage do
19
23
  ENV['COVERAGE'] = 'true'
20
24
  Rake::Task[:rspec].execute
21
25
  Rake::Task[:cucumber].execute
@@ -1,7 +1,8 @@
1
1
  Feature: Callbacks
2
2
 
3
3
  Background:
4
- Given Nervion is connected to Twitter Streaming API
4
+ Given Nervion has been configured
5
+ And Nervion is connected to Twitter Streaming API
5
6
 
6
7
  Scenario: Calling the status callback
7
8
  When a status update is sent by Twitter
@@ -0,0 +1,17 @@
1
+ Feature: Client setup validation
2
+
3
+ Scenario: Missing authentication
4
+ Given I haven't configured Nervion
5
+ When I try to start streaming
6
+ Then I get an error pointing me to the readme file
7
+
8
+ Scenario Outline: Calling filter without a message callback
9
+ Given Nervion has been configured
10
+ When I try to start streaming the <endpoint_name> endpoint
11
+ Then I get an error pointing me to the readme file
12
+
13
+ Examples:
14
+ | endpoint_name |
15
+ | filter |
16
+ | firehose |
17
+ | sample |
@@ -0,0 +1,26 @@
1
+ AUTHENTICATION_README_URL = 'https://github.com/jacegu/nervion'
2
+
3
+ def capture_errors_in
4
+ begin
5
+ yield
6
+ rescue Exception => error
7
+ @error = error
8
+ end
9
+ end
10
+
11
+ Given /^I haven't configured Nervion$/ do
12
+ Nervion::Configuration.instance_variable_set '@configured', nil
13
+ end
14
+
15
+ When /^I try to start streaming$/ do
16
+ capture_errors_in { Nervion.sample { |status| puts status } }
17
+ end
18
+
19
+ When /^I try to start streaming the (.*?) endpoint$/ do |endpoint_name|
20
+ params = { stall_warnings: true }
21
+ capture_errors_in { Nervion.send(endpoint_name.to_sym, params) }
22
+ end
23
+
24
+ Then /^I get an error pointing me to the readme file$/ do
25
+ @error.message.should match /#{AUTHENTICATION_README_URL}/
26
+ end
@@ -1,3 +1,4 @@
1
+ KNOWN_STATUS_COUNT = 100
1
2
  TEST_HOST = '0.0.0.0'
2
3
  TEST_PORT = '9000'
3
4
 
@@ -12,7 +13,16 @@ def test_client_with(server_version)
12
13
  EM.run do
13
14
  EM.start_server(TEST_HOST, TEST_PORT, server_version)
14
15
  EM.add_timer(0) { Nervion.sample { |status| @statuses << status } }
15
- EM.add_timer(0.1) { EM.stop }
16
+ EM.add_timer(0.3) { EM.stop }
17
+ end
18
+ end
19
+
20
+ Given /^Nervion has been configured$/ do
21
+ Nervion.configure do |config|
22
+ config.consumer_key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'
23
+ config.consumer_secret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'
24
+ config.access_token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'
25
+ config.access_token_secret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'
16
26
  end
17
27
  end
18
28
 
@@ -42,7 +52,7 @@ When /^a network error occurs$/ do
42
52
  end
43
53
 
44
54
  Then /^Nervion calls the status callback with it$/ do
45
- @statuses.count.should eq 100
55
+ @statuses.count.should eq KNOWN_STATUS_COUNT
46
56
  end
47
57
 
48
58
  Then /^Nervion calls the HTTP error callback$/ do
data/lib/nervion.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  $: << File.join(File.dirname(__FILE__), '..')
2
+ $: << File.join(File.dirname(__FILE__), '..', 'lib')
2
3
 
3
4
  require 'nervion/facade'
4
5
  require 'nervion/version'
@@ -1,5 +1,3 @@
1
- require 'nervion/configuration'
2
- require 'nervion/request'
3
1
  require 'nervion/stream'
4
2
  require 'nervion/stream_handler'
5
3
 
@@ -11,12 +9,12 @@ module Nervion
11
9
  end
12
10
 
13
11
  def stream(request, callbacks)
14
- @stream_handler = StreamHandler.new(callbacks)
15
- EM.run { EM.connect @host, @port, Stream, request, @stream_handler }
12
+ handler = StreamHandler.new(callbacks)
13
+ EM.run { @stream = EM.connect @host, @port, Stream, request, handler }
16
14
  end
17
15
 
18
16
  def stop
19
- @stream_handler.close_stream
17
+ @stream.close
20
18
  EM.stop
21
19
  end
22
20
  end
@@ -1,12 +1,19 @@
1
1
  module Nervion
2
2
 
3
+ # Allows to configure Nervion.
4
+ #
5
+ # @yieldparam [Configuration] config the configuration object.
3
6
  def self.configure
7
+ Configuration.configured!
4
8
  yield Configuration
5
9
  end
6
10
 
7
11
  class Configuration
8
12
  UNCONFIGURED_SETTING = ''
9
13
 
14
+ # Configures the consumer key
15
+ #
16
+ # @param [String] consumer_key the consumer key
10
17
  def self.consumer_key=(consumer_key)
11
18
  @consumer_key = consumer_key
12
19
  end
@@ -15,6 +22,9 @@ module Nervion
15
22
  @consumer_key || UNCONFIGURED_SETTING
16
23
  end
17
24
 
25
+ # Configures the consumer secret
26
+ #
27
+ # @param [String] consumer_secret the consumer secret
18
28
  def self.consumer_secret=(consumer_secret)
19
29
  @consumer_secret = consumer_secret
20
30
  end
@@ -23,6 +33,9 @@ module Nervion
23
33
  @consumer_secret || UNCONFIGURED_SETTING
24
34
  end
25
35
 
36
+ # Configures the access token
37
+ #
38
+ # @param [String] access_token the access token
26
39
  def self.access_token=(access_token)
27
40
  @access_token = access_token
28
41
  end
@@ -31,6 +44,9 @@ module Nervion
31
44
  @access_token || UNCONFIGURED_SETTING
32
45
  end
33
46
 
47
+ # Configures the access token secret
48
+ #
49
+ # @param [String] access_token_secret the access token secret
34
50
  def self.access_token_secret=(access_token_secret)
35
51
  @access_token_secret = access_token_secret
36
52
  end
@@ -46,5 +62,13 @@ module Nervion
46
62
  def self.fetch(setting)
47
63
  send setting.to_sym
48
64
  end
65
+
66
+ def self.configured?
67
+ @configured
68
+ end
69
+
70
+ def self.configured!
71
+ @configured = true
72
+ end
49
73
  end
50
74
  end
@@ -4,31 +4,91 @@ require 'nervion/request'
4
4
  require 'nervion/configuration'
5
5
 
6
6
  module Nervion
7
- STREAM_API_HOST = 'stream.twitter.com'
8
- STREAM_API_PORT = 443
9
- SAMPLE_ENDPOINT = "https://#{STREAM_API_HOST}/1/statuses/sample.json"
10
- FILTER_ENDPOINT = "https://#{STREAM_API_HOST}/1/statuses/filter.json"
11
-
7
+ # Sets up the callback to be called upon HTTP errors (when the response from
8
+ # Twitter's Streaming API has a status above 200).
9
+ #
10
+ # @param [Proc] callback the callback
11
+ # @return [self] to allow callback setup chaining
12
12
  def self.on_http_error(&callback)
13
13
  callback_table[:http_error] = callback
14
14
  self
15
15
  end
16
16
 
17
+ # Sets up the callback to be called upon network errors or unexpected
18
+ # disconnection.
19
+ #
20
+ # @param [Proc] callback the callback
21
+ # @return [self] to allow callback setup chaining
17
22
  def self.on_network_error(&callback)
18
23
  callback_table[:network_error] = callback
19
24
  self
20
25
  end
21
26
 
27
+ # Sets up the message callback and starts streaming the sample endpoint.
28
+ #
29
+ # @see https://dev.twitter.com/docs/api/1/get/statuses/sample
30
+ # @see https://dev.twitter.com/docs/streaming-apis/parameters#delimited
31
+ # @see https://dev.twitter.com/docs/streaming-apis/parameters#stall_warnings
32
+ #
33
+ # @param [hash] params the parameters submitted to the sample endpoint
34
+ # @option params [Boolean] :delimited specifies whether messages should be
35
+ # length-delimited.
36
+ # @option params [Boolean] :stall_warnings specifies whether stall warnings
37
+ # should be delivered.
38
+ # @param [Proc] callback the callback
22
39
  def self.sample(params = {}, &callback)
23
- callback_table[:status] = callback
24
- new_client.tap { |c| c.stream sample_endpoint(params), callback_table }
40
+ stream sample_endpoint(params), callback
25
41
  end
26
42
 
43
+ # Sets up the message callback and starts streaming the filter endpoint.
44
+ #
45
+ # @note At least one predicate parameter (follow, locations, or track) must be
46
+ # specified.
47
+ #
48
+ # @see https://dev.twitter.com/docs/api/1/get/statuses/filter
49
+ # @see https://dev.twitter.com/docs/streaming-apis/parameters#follow
50
+ # @see https://dev.twitter.com/docs/streaming-apis/parameters#track
51
+ # @see https://dev.twitter.com/docs/streaming-apis/parameters#locations
52
+ # @see https://dev.twitter.com/docs/streaming-apis/parameters#delimited
53
+ # @see https://dev.twitter.com/docs/streaming-apis/parameters#stall_warnings
54
+ #
55
+ # @param [hash] params the parameters submitted to the sample endpoint
56
+ # @option params [String] :follow a comma separated list of user IDs,
57
+ # indicating the users to return statuses for in the stream.
58
+ # @option params [String] :track keywords to track. Phrases of keywords are
59
+ # specified by a comma-separated list.
60
+ # @option params [String] :locations Specifies a set of bounding boxes to track.
61
+ # @option params [Boolean] :delimited specifies whether messages should be
62
+ # length-delimited.
63
+ # @option params [Boolean] :stall_warnings specifies whether stall warnings
64
+ # should be delivered.
65
+ # @param [Proc] callback the callback
27
66
  def self.filter(params, &callback)
28
- callback_table[:status] = callback
29
- new_client.tap { |c| c.stream filter_endpoint(params), callback_table }
67
+ stream filter_endpoint(params), callback
68
+ end
69
+
70
+ # Sets up the message callback and starts streaming the firehose endpoint.
71
+ #
72
+ # @note This endpoint requires a special access level.
73
+ # @since 0.0.2
74
+ #
75
+ # @see https://dev.twitter.com/docs/api/1/get/statuses/firehose
76
+ # @see https://dev.twitter.com/docs/streaming-apis/parameters#count
77
+ # @see https://dev.twitter.com/docs/streaming-apis/parameters#delimited
78
+ # @see https://dev.twitter.com/docs/streaming-apis/parameters#stall_warnings
79
+ #
80
+ # @param [hash] params the parameters submitted to the sample endpoint
81
+ # @option params [Integer] :count the number of messages to backfill.
82
+ # @option params [Boolean] :delimited specifies whether messages should be
83
+ # length-delimited.
84
+ # @option params [Boolean] :stall_warnings specifies whether stall warnings
85
+ # should be delivered.
86
+ # @param [Proc] callback the callback
87
+ def self.firehose(params = {}, &callback)
88
+ stream firehose_endpoint(params), callback
30
89
  end
31
90
 
91
+ # Stops streaming
32
92
  def self.stop
33
93
  raise 'Nervion is not running' if @client.nil?
34
94
  @client.stop
@@ -40,15 +100,44 @@ module Nervion
40
100
  @callback_table ||= CallbackTable.new
41
101
  end
42
102
 
103
+ def self.stream(endpoint, callback)
104
+ raise_not_configured_error unless Configuration.configured?
105
+ raise_no_message_callback_error if callback.nil?
106
+ callback_table[:message] = callback
107
+ new_client.tap { |c| c.stream endpoint, callback_table }
108
+ end
109
+
43
110
  def self.new_client
44
111
  @client = Client.new(STREAM_API_HOST, STREAM_API_PORT)
45
112
  end
46
113
 
47
114
  def self.sample_endpoint(params)
48
- get SAMPLE_ENDPOINT, params, Configuration
115
+ get SAMPLE_ENDPOINT, params, Configuration
49
116
  end
50
117
 
51
118
  def self.filter_endpoint(params)
52
119
  post FILTER_ENDPOINT, params, Configuration
53
120
  end
121
+
122
+ def self.firehose_endpoint(params)
123
+ get FIREHOSE_ENDPOINT, params, Configuration
124
+ end
125
+
126
+ def self.raise_not_configured_error
127
+ raise "You need to setup the authentication information for Nervion to work. Please, check out #{AUTHENTICATION_README_URL}"
128
+ end
129
+
130
+ def self.raise_no_message_callback_error
131
+ raise "You have to setup a message callback. Please, check out #{MSG_CALLBACK_README_URL}"
132
+ end
133
+
134
+ STREAM_API_HOST = 'stream.twitter.com'
135
+ STREAM_API_PORT = 443
136
+ SAMPLE_ENDPOINT = "https://#{STREAM_API_HOST}/1/statuses/sample.json"
137
+ FILTER_ENDPOINT = "https://#{STREAM_API_HOST}/1/statuses/filter.json"
138
+ FIREHOSE_ENDPOINT = "https://#{STREAM_API_HOST}/1/statuses/firehose.json"
139
+
140
+ AUTHENTICATION_README_URL = 'https://github.com/jacegu/nervion#authentication'
141
+ MSG_CALLBACK_README_URL = 'https://github.com/jacegu/nervion#message-callback'
142
+
54
143
  end
@@ -1,14 +1,7 @@
1
1
  require_relative 'oauth_header'
2
+ require_relative 'percent_encoder'
2
3
 
3
4
  module Nervion
4
- def self.get(uri, params = {}, oauth_params)
5
- Get.new uri, params, oauth_params
6
- end
7
-
8
- def self.post(uri, params = {}, oauth_params)
9
- Post.new uri, params, oauth_params
10
- end
11
-
12
5
  module Request
13
6
  attr_reader :params, :oauth_params
14
7
 
@@ -73,7 +66,6 @@ module Nervion
73
66
 
74
67
  class Post
75
68
  include Request
76
- include PercentEncoder
77
69
 
78
70
  def to_s
79
71
  "#{request_line}\r\n#{headers.join("\r\n")}\r\n\r\n#{body}\r\n"
@@ -96,4 +88,15 @@ module Nervion
96
88
  percent_encode params
97
89
  end
98
90
  end
91
+
92
+ private
93
+
94
+ def self.get(uri, params = {}, oauth_params)
95
+ Get.new uri, params, oauth_params
96
+ end
97
+
98
+ def self.post(uri, params = {}, oauth_params)
99
+ Post.new uri, params, oauth_params
100
+ end
101
+
99
102
  end
@@ -1,5 +1,5 @@
1
1
  require 'eventmachine'
2
- require 'nervion/http_parser'
2
+ require 'nervion/stream_parser'
3
3
  require 'nervion/reconnection_scheduler'
4
4
 
5
5
  module Nervion
@@ -33,29 +33,37 @@ module Nervion
33
33
  end
34
34
 
35
35
  def unbind
36
- handle_closed_stream unless @handler.stream_close_requested?
36
+ handle_closed_stream unless close_requested?
37
37
  end
38
38
 
39
39
  def http_error_occurred?
40
40
  not http_error.nil?
41
41
  end
42
42
 
43
+ def close_requested?
44
+ @close_stream ||= false
45
+ end
46
+
47
+ def close
48
+ @close_stream = true
49
+ end
50
+
43
51
  private
44
52
 
45
53
  def handle_closed_stream
46
54
  if http_error_occurred?
47
- handle_http_error_and_reopen_stream
55
+ handle_http_error_and_retry
48
56
  else
49
- handle_network_error_and_reopen_stream
57
+ handle_network_error_and_retry
50
58
  end
51
59
  end
52
60
 
53
- def handle_http_error_and_reopen_stream
61
+ def handle_http_error_and_retry
54
62
  @handler.handle_http_error http_error
55
63
  @scheduler.reconnect_after_http_error_in self
56
64
  end
57
65
 
58
- def handle_network_error_and_reopen_stream
66
+ def handle_network_error_and_retry
59
67
  @handler.handle_network_error
60
68
  @scheduler.reconnect_after_network_error_in self
61
69
  end
@@ -1,42 +1,24 @@
1
- require 'yajl'
2
- require 'nervion/http_parser'
1
+ require 'nervion/stream_parser'
3
2
 
4
3
  module Nervion
5
4
  class StreamHandler
6
-
7
- def initialize(callbacks)
8
- @callbacks = callbacks
9
- @http_parser = HttpParser.new(setup_json_parser)
5
+ def initialize(callbacks, stream_parser = StreamParser.new)
6
+ @callbacks, @stream_parser = callbacks, stream_parser
7
+ @stream_parser.on_json_parsed = @callbacks[:message]
10
8
  end
11
9
 
12
10
  def <<(data)
13
- @http_parser << data
11
+ @stream_parser << data
14
12
  end
15
13
 
16
14
  def handle_http_error(error)
17
- @http_parser.reset!
15
+ @stream_parser.reset!
18
16
  @callbacks[:http_error].call(error.status, error.body)
19
17
  end
20
18
 
21
19
  def handle_network_error
22
- @http_parser.reset!
20
+ @stream_parser.reset!
23
21
  @callbacks[:network_error].call
24
22
  end
25
-
26
- def stream_close_requested?
27
- @close_stream ||= false
28
- end
29
-
30
- def close_stream
31
- @close_stream = true
32
- end
33
-
34
- private
35
-
36
- def setup_json_parser
37
- Yajl::Parser.new(symbolize_keys: true).tap do |json_parser|
38
- json_parser.on_parse_complete = @callbacks[:status]
39
- end
40
- end
41
23
  end
42
24
  end
@@ -1,12 +1,18 @@
1
+ require 'yajl'
1
2
  require 'http/parser'
2
3
 
3
4
  module Nervion
4
- class HttpParser
5
+ class StreamParser
5
6
  attr_reader :json_parser, :http_parser
6
7
 
7
- def initialize(json_parser)
8
- @json_parser = json_parser
9
- @http_parser = setup_http_parser
8
+ def initialize(parsers = {})
9
+ @http_parser = parsers[:http_parser] || Http::Parser.new
10
+ @json_parser = parsers[:json_parser] || Yajl::Parser.new(symbolize_keys: true)
11
+ @http_parser.on_body = method(:process)
12
+ end
13
+
14
+ def on_json_parsed=(callback)
15
+ @json_parser.on_parse_complete = callback
10
16
  end
11
17
 
12
18
  def <<(http_stream)
@@ -19,10 +25,6 @@ module Nervion
19
25
 
20
26
  private
21
27
 
22
- def setup_http_parser
23
- Http::Parser.new.tap { |parser| parser.on_body = method(:process) }
24
- end
25
-
26
28
  def process(chunk)
27
29
  if request_successful?
28
30
  parse_json_from chunk
@@ -1,3 +1,3 @@
1
1
  module Nervion
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/nervion.gemspec CHANGED
@@ -18,7 +18,9 @@ Gem::Specification.new do |gem|
18
18
  gem.add_runtime_dependency 'eventmachine', '~> 1.0.0.beta.4'
19
19
  gem.add_runtime_dependency 'http_parser.rb', '~> 0.5.3'
20
20
  gem.add_runtime_dependency 'yajl-ruby', '~> 1.1.0'
21
- gem.add_development_dependency 'rspec'
22
21
  gem.add_development_dependency 'cucumber'
22
+ gem.add_development_dependency 'rake'
23
+ gem.add_development_dependency 'rspec'
23
24
  gem.add_development_dependency 'simplecov'
25
+ gem.add_development_dependency 'yard'
24
26
  end
@@ -5,6 +5,7 @@ describe Nervion::Client do
5
5
  subject { described_class.new('http://twitter.com', 443) }
6
6
  let(:request) { stub :request }
7
7
  let(:callbacks) { stub :callbacks }
8
+ let(:stream) { mock :stream }
8
9
  let(:stream_handler) { stub :stream_handler }
9
10
 
10
11
  before(:all) do
@@ -43,7 +44,8 @@ describe Nervion::Client do
43
44
  end
44
45
 
45
46
  it 'stops streaming' do
46
- stream_handler.should_receive(:close_stream).ordered
47
+ EM.stub(:connect).and_return(stream)
48
+ stream.should_receive(:close).ordered
47
49
  EM.should_receive(:stop).ordered
48
50
  subject.stream(request, callbacks)
49
51
  subject.stop
@@ -2,11 +2,25 @@ require 'nervion/configuration'
2
2
 
3
3
  describe Nervion::Configuration do
4
4
 
5
+ before do
6
+ %w{
7
+ consumer_key consumer_secret access_token access_token_secret configured
8
+ }.each do |variable|
9
+ described_class.instance_variable_set "@#{variable}", nil
10
+ end
11
+ end
12
+
5
13
  it 'allows configuration from the top level' do
6
14
  Nervion::Configuration.should_receive(:access_key=).with 'access_key'
7
15
  Nervion.configure { |config| config.access_key = 'access_key' }
8
16
  end
9
17
 
18
+ it 'knows if Nervion has been configured' do
19
+ Nervion::Configuration.should_not be_configured
20
+ Nervion.configure { |config| }
21
+ Nervion::Configuration.should be_configured
22
+ end
23
+
10
24
  context 'when it has not been configured' do
11
25
  it 'has an empty string as consumer_key' do
12
26
  described_class.consumer_key.should eq ''
@@ -1,8 +1,10 @@
1
1
  require 'nervion/facade'
2
2
 
3
+ class Callable; def call; end; end;
4
+
3
5
  describe "Facade that exposes Nervion's API" do
4
6
  let(:callback_table) { mock(:callback_table).as_null_object }
5
- let(:status_callback) { lambda { :status_callback } }
7
+ let(:message_callback) { lambda { :message_callback } }
6
8
  let(:http_callback) { lambda { :http_error_callback } }
7
9
  let(:network_callback) { lambda { :network_error_callback } }
8
10
 
@@ -40,43 +42,65 @@ describe "Facade that exposes Nervion's API" do
40
42
  let(:request) { stub :request }
41
43
 
42
44
  before do
45
+ Nervion.configure { |config| }
43
46
  Nervion::Client.stub(:new).
44
47
  with(Nervion::STREAM_API_HOST, Nervion::STREAM_API_PORT).
45
48
  and_return(client)
46
49
  end
47
50
 
48
- context 'sample endpoint' do
49
- it 'sets up the status callback' do
50
- callback_table.should_receive(:[]=).with(:status, status_callback)
51
- Nervion.sample(&status_callback)
51
+ shared_examples_for 'an endpoint' do
52
+ it 'sets up the message callback' do
53
+ callback_table.should_receive(:[]=).with(:message, message_callback)
54
+ Nervion.send(method_name, params, &message_callback)
52
55
  end
53
56
 
54
57
  it 'starts the streaming to the sample endpoint' do
55
- Nervion.stub(:get).with(Nervion::SAMPLE_ENDPOINT, params, config).
58
+ Nervion.stub(http_method).with(endpoint, params, config).
56
59
  and_return(request)
57
60
  client.should_receive(:stream).with(request, callback_table)
58
- Nervion.sample(params, &status_callback)
61
+ Nervion.send(method_name, params, &message_callback)
59
62
  end
60
- end
61
63
 
62
- context 'filter endpoint' do
63
- it 'sets up the status callback' do
64
- callback_table.should_receive(:[]=).with(:status, status_callback)
65
- Nervion.filter(params, &status_callback)
64
+ it 'raises an error if Nervion was not configured' do
65
+ expect do
66
+ Nervion::Configuration.instance_variable_set(:@configured, nil)
67
+ Nervion.send(method_name, params, &message_callback)
68
+ end.to raise_error
66
69
  end
67
70
 
68
- it 'starts the streaming to the filter endpoint' do
69
- Nervion.stub(:post).with(Nervion::FILTER_ENDPOINT, params, config).
70
- and_return(request)
71
- client.should_receive(:stream).with(request, callback_table)
72
- Nervion.filter(params, &status_callback)
71
+ it 'raises an error if no message callback was provided' do
72
+ expect { Nervion.send(method_name, params) }.to raise_error
73
73
  end
74
74
  end
75
75
 
76
+ context 'sample endpoint' do
77
+ let(:http_method) { :get }
78
+ let(:endpoint) { Nervion::SAMPLE_ENDPOINT }
79
+ let(:method_name) { :sample }
80
+
81
+ it_behaves_like 'an endpoint'
82
+ end
83
+
84
+ context 'filter endpoint' do
85
+ let(:http_method) { :post }
86
+ let(:endpoint) { Nervion::FILTER_ENDPOINT }
87
+ let(:method_name) { :filter }
88
+
89
+ it_behaves_like 'an endpoint'
90
+ end
91
+
92
+ context 'firehose endpoint' do
93
+ let(:http_method) { :get }
94
+ let(:endpoint) { Nervion::FIREHOSE_ENDPOINT }
95
+ let(:method_name) { :firehose }
96
+
97
+ it_behaves_like 'an endpoint'
98
+ end
99
+
76
100
  context 'stoping' do
77
101
  it 'can stop the streaming' do
78
102
  client.should_receive(:stop)
79
- Nervion.sample(->{})
103
+ Nervion.sample{}
80
104
  Nervion.stop
81
105
  end
82
106
 
@@ -59,7 +59,6 @@ describe Nervion::Request do
59
59
  subject.path.should eq '/endpoint?p1=param%20value&p2=%24%26'
60
60
  end
61
61
 
62
-
63
62
  it 'has an string representation' do
64
63
  Nervion::OAuthHeader.stub(:for).with(subject).and_return 'OAuth xxx'
65
64
  subject.to_s.should eq EXPECTED_GET_REQUEST
@@ -3,27 +3,25 @@ require 'nervion/stream_handler'
3
3
  describe Nervion::StreamHandler do
4
4
  subject { described_class.new callbacks }
5
5
  let(:http_parser) { mock(:http_parser).as_null_object }
6
- let(:json_parser) { mock(:json_parser).as_null_object }
7
6
 
8
7
  let(:callbacks) do
9
8
  {
10
- status: status_callback,
9
+ message: message_callback,
11
10
  http_error: http_error_callback,
12
11
  network_error: network_error_callback
13
12
  }
14
13
  end
15
14
 
16
- let(:status_callback) { mock :status_callback }
15
+ let(:message_callback) { mock :message_callback }
17
16
  let(:http_error_callback) { mock(:http_error_callback).as_null_object }
18
17
  let(:network_error_callback) { mock(:network_error_callback).as_null_object }
19
18
 
20
19
  before do
21
- Yajl::Parser.stub(:new).with(symbolize_keys: true).and_return(json_parser)
22
- Nervion::HttpParser.stub(:new).with(json_parser).and_return(http_parser)
20
+ Nervion::StreamParser.stub(:new).and_return(http_parser)
23
21
  end
24
22
 
25
- it 'sets up the status received callback' do
26
- json_parser.should_receive(:on_parse_complete=).with(status_callback)
23
+ it 'sets up the message received callback' do
24
+ http_parser.should_receive(:on_json_parsed=).with(message_callback)
27
25
  described_class.new callbacks
28
26
  end
29
27
 
@@ -33,12 +31,6 @@ describe Nervion::StreamHandler do
33
31
  subject << data
34
32
  end
35
33
 
36
- it 'can be told to close the stream' do
37
- subject.stream_close_requested?.should be_false
38
- subject.close_stream
39
- subject.stream_close_requested?.should be_true
40
- end
41
-
42
34
  context 'handling HTTP errors' do
43
35
  let(:http_error) { stub(:http_error, status: 401, body: 'Unauthorized') }
44
36
 
@@ -0,0 +1,44 @@
1
+ require 'nervion/stream_parser'
2
+ require 'fixtures/responses'
3
+
4
+ describe Nervion::StreamParser do
5
+ subject { described_class.new(json_parser: json_parser) }
6
+ let(:json_parser) { stub(:json_parser).as_null_object }
7
+
8
+ it 'takes a JSON parser' do
9
+ subject.json_parser.should be json_parser
10
+ end
11
+
12
+ it 'uses Http::Parser as the default HTTP parser' do
13
+ http_parser = stub(:http_parser).as_null_object
14
+ Http::Parser.stub(:new).and_return(http_parser)
15
+ described_class.new.http_parser.should be http_parser
16
+ end
17
+
18
+ it 'uses Yajl with symbolized keys as the default JSON parser' do
19
+ yajl_parser = stub(:yajl_parser)
20
+ Yajl::Parser.stub(:new).with(symbolize_keys: true).and_return(yajl_parser)
21
+ described_class.new.json_parser.should be yajl_parser
22
+ end
23
+
24
+ it 'can be set up with a JSON parse complete callback' do
25
+ callback = lambda {}
26
+ json_parser.should_receive(:on_parse_complete=).with callback
27
+ subject.on_json_parsed = callback
28
+ end
29
+
30
+ it 'can be reset' do
31
+ subject << RESPONSE_200
32
+ subject.reset!
33
+ expect { subject << RESPONSE_200 }.not_to raise_error Http::Parser::Error
34
+ end
35
+
36
+ it 'parses response body if the response status is 200' do
37
+ json_parser.should_receive(:<<).with(BODY_200)
38
+ subject << RESPONSE_200
39
+ end
40
+
41
+ it 'raises an error if the response status is above 200' do
42
+ expect { subject << RESPONSE_401 }.to raise_error Nervion::HttpError
43
+ end
44
+ end
@@ -46,10 +46,15 @@ describe Nervion::Stream do
46
46
  subject.retry
47
47
  end
48
48
 
49
+ it 'can be closed' do
50
+ subject.close_requested?.should be_false
51
+ subject.close
52
+ subject.close_requested?.should be_true
53
+ end
54
+
49
55
  context 'on unbound connections' do
50
56
  context 'due to HTTP errors' do
51
57
  before do
52
- handler.stub(:stream_close_requested?).and_return(false)
53
58
  subject.stub(:http_error).and_return(http_error)
54
59
  end
55
60
 
@@ -67,8 +72,6 @@ describe Nervion::Stream do
67
72
  end
68
73
 
69
74
  context 'due to network errors' do
70
- before { handler.stub(:stream_close_requested?).and_return(false) }
71
-
72
75
  it 'notifies the error' do
73
76
  handler.should_receive(:handle_network_error)
74
77
  scheduler.stub(:reconnect_after_network_error_in)
@@ -84,11 +87,11 @@ describe Nervion::Stream do
84
87
 
85
88
  context 'due to a request to close the stream' do
86
89
  it 'lets the stream to be closed' do
87
- handler.stub(:stream_close_requested?).and_return(true)
88
90
  handler.should_not_receive(:handle_http_error)
89
91
  handler.should_not_receive(:handle_network_error)
90
92
  scheduler.should_not_receive(:reconnect_after_http_error_in)
91
93
  scheduler.should_not_receive(:reconnect_after_network_error_in)
94
+ subject.close
92
95
  subject.unbind
93
96
  end
94
97
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nervion
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-19 00:00:00.000000000 Z
12
+ date: 2012-06-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: eventmachine
@@ -60,7 +60,7 @@ dependencies:
60
60
  - !ruby/object:Gem::Version
61
61
  version: 1.1.0
62
62
  - !ruby/object:Gem::Dependency
63
- name: rspec
63
+ name: cucumber
64
64
  requirement: !ruby/object:Gem::Requirement
65
65
  none: false
66
66
  requirements:
@@ -76,7 +76,23 @@ dependencies:
76
76
  - !ruby/object:Gem::Version
77
77
  version: '0'
78
78
  - !ruby/object:Gem::Dependency
79
- name: cucumber
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rspec
80
96
  requirement: !ruby/object:Gem::Requirement
81
97
  none: false
82
98
  requirements:
@@ -107,6 +123,22 @@ dependencies:
107
123
  - - ! '>='
108
124
  - !ruby/object:Gem::Version
109
125
  version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: yard
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
110
142
  description: A minimalistic Twitter Stream API Ruby client
111
143
  email:
112
144
  - j4cegu@gmail.com
@@ -117,13 +149,17 @@ files:
117
149
  - .gitignore
118
150
  - .rspec
119
151
  - .rvmrc
152
+ - .travis.yml
153
+ - CHANGELOG.md
120
154
  - Gemfile
121
155
  - Gemfile.lock
122
156
  - LICENSE
123
157
  - README.md
124
158
  - Rakefile
159
+ - features/callbacks.feature
160
+ - features/client_validation.feature
161
+ - features/step_definitions/client_validation_steps.rb
125
162
  - features/step_definitions/streaming_steps.rb
126
- - features/streaming.feature
127
163
  - features/support/env.rb
128
164
  - features/support/streaming_api_double.rb
129
165
  - fixtures/responses.rb
@@ -133,7 +169,6 @@ files:
133
169
  - lib/nervion/client.rb
134
170
  - lib/nervion/configuration.rb
135
171
  - lib/nervion/facade.rb
136
- - lib/nervion/http_parser.rb
137
172
  - lib/nervion/oauth_header.rb
138
173
  - lib/nervion/oauth_signature.rb
139
174
  - lib/nervion/percent_encoder.rb
@@ -141,19 +176,20 @@ files:
141
176
  - lib/nervion/request.rb
142
177
  - lib/nervion/stream.rb
143
178
  - lib/nervion/stream_handler.rb
179
+ - lib/nervion/stream_parser.rb
144
180
  - lib/nervion/version.rb
145
181
  - nervion.gemspec
146
182
  - spec/nervion/callback_table_spec.rb
147
183
  - spec/nervion/client_spec.rb
148
184
  - spec/nervion/configuration_spec.rb
149
185
  - spec/nervion/facade_spec.rb
150
- - spec/nervion/http_parser_spec.rb
151
186
  - spec/nervion/oauth_header_spec.rb
152
187
  - spec/nervion/oauth_signature_spec.rb
153
188
  - spec/nervion/percent_encoder_spec.rb
154
189
  - spec/nervion/reconnection_scheduler_spec.rb
155
190
  - spec/nervion/request_spec.rb
156
191
  - spec/nervion/stream_handler_spec.rb
192
+ - spec/nervion/stream_parser_spec.rb
157
193
  - spec/nervion/stream_spec.rb
158
194
  - spec/spec_helper.rb
159
195
  homepage: https://github.com/jacegu/nervion
@@ -181,20 +217,23 @@ signing_key:
181
217
  specification_version: 3
182
218
  summary: ''
183
219
  test_files:
220
+ - features/callbacks.feature
221
+ - features/client_validation.feature
222
+ - features/step_definitions/client_validation_steps.rb
184
223
  - features/step_definitions/streaming_steps.rb
185
- - features/streaming.feature
186
224
  - features/support/env.rb
187
225
  - features/support/streaming_api_double.rb
188
226
  - spec/nervion/callback_table_spec.rb
189
227
  - spec/nervion/client_spec.rb
190
228
  - spec/nervion/configuration_spec.rb
191
229
  - spec/nervion/facade_spec.rb
192
- - spec/nervion/http_parser_spec.rb
193
230
  - spec/nervion/oauth_header_spec.rb
194
231
  - spec/nervion/oauth_signature_spec.rb
195
232
  - spec/nervion/percent_encoder_spec.rb
196
233
  - spec/nervion/reconnection_scheduler_spec.rb
197
234
  - spec/nervion/request_spec.rb
198
235
  - spec/nervion/stream_handler_spec.rb
236
+ - spec/nervion/stream_parser_spec.rb
199
237
  - spec/nervion/stream_spec.rb
200
238
  - spec/spec_helper.rb
239
+ has_rdoc:
@@ -1,26 +0,0 @@
1
- require 'nervion/http_parser'
2
- require 'fixtures/responses'
3
-
4
- describe Nervion::HttpParser do
5
- subject { described_class.new(json_parser) }
6
- let(:json_parser) { stub(:json_parser).as_null_object }
7
-
8
- it 'takes a JSON parser' do
9
- subject.json_parser.should be json_parser
10
- end
11
-
12
- it 'can be reset' do
13
- subject << RESPONSE_200
14
- subject.reset!
15
- expect { subject << RESPONSE_200 }.not_to raise_error Http::Parser::Error
16
- end
17
-
18
- it 'parses response body if the response status is 200' do
19
- json_parser.should_receive(:<<).with(BODY_200)
20
- subject << RESPONSE_200
21
- end
22
-
23
- it 'raises an error if the response status is above 200' do
24
- expect { subject << RESPONSE_401 }.to raise_error Nervion::HttpError
25
- end
26
- end