pusher 0.11.3 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +0 -2
- data/Gemfile +2 -2
- data/Gemfile.lock +32 -28
- data/README.md +43 -10
- data/lib/pusher.rb +1 -3
- data/lib/pusher/channel.rb +2 -2
- data/lib/pusher/client.rb +40 -39
- data/lib/pusher/request.rb +39 -48
- data/pusher.gemspec +3 -2
- data/spec/channel_spec.rb +6 -6
- data/spec/client_spec.rb +92 -48
- data/spec/spec_helper.rb +1 -1
- metadata +23 -8
- data/lib/pusher/query_encoder.rb +0 -47
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
source
|
2
|
-
gemspec
|
1
|
+
source "https://rubygems.org"
|
2
|
+
gemspec
|
data/Gemfile.lock
CHANGED
@@ -1,48 +1,52 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pusher (0.
|
4
|
+
pusher (0.12.0)
|
5
|
+
httpclient (~> 2.3.0)
|
5
6
|
multi_json (~> 1.0)
|
6
7
|
signature (~> 0.1.6)
|
7
8
|
|
8
9
|
GEM
|
9
|
-
remote:
|
10
|
+
remote: https://rubygems.org/
|
10
11
|
specs:
|
11
|
-
addressable (2.3.
|
12
|
+
addressable (2.3.5)
|
12
13
|
cookiejar (0.3.0)
|
13
|
-
crack (0.
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
crack (0.4.1)
|
15
|
+
safe_yaml (~> 0.9.0)
|
16
|
+
diff-lcs (1.2.4)
|
17
|
+
em-http-request (1.1.0)
|
18
|
+
addressable (>= 2.3.4)
|
17
19
|
cookiejar
|
18
|
-
em-socksify
|
20
|
+
em-socksify (>= 0.3)
|
21
|
+
eventmachine (>= 1.0.3)
|
22
|
+
http_parser.rb (>= 0.6.0.beta.2)
|
23
|
+
em-socksify (0.3.0)
|
19
24
|
eventmachine (>= 1.0.0.beta.4)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
rspec-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
rspec-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
webmock (1.9.0)
|
25
|
+
eventmachine (1.0.3)
|
26
|
+
http_parser.rb (0.6.0.beta.2)
|
27
|
+
httpclient (2.3.4.1)
|
28
|
+
multi_json (1.7.8)
|
29
|
+
rack (1.5.2)
|
30
|
+
rake (10.1.0)
|
31
|
+
rspec (2.14.1)
|
32
|
+
rspec-core (~> 2.14.0)
|
33
|
+
rspec-expectations (~> 2.14.0)
|
34
|
+
rspec-mocks (~> 2.14.0)
|
35
|
+
rspec-core (2.14.4)
|
36
|
+
rspec-expectations (2.14.0)
|
37
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
38
|
+
rspec-mocks (2.14.2)
|
39
|
+
safe_yaml (0.9.4)
|
40
|
+
signature (0.1.7)
|
41
|
+
webmock (1.13.0)
|
38
42
|
addressable (>= 2.2.7)
|
39
|
-
crack (>= 0.
|
43
|
+
crack (>= 0.3.2)
|
40
44
|
|
41
45
|
PLATFORMS
|
42
46
|
ruby
|
43
47
|
|
44
48
|
DEPENDENCIES
|
45
|
-
em-http-request (~> 1.
|
49
|
+
em-http-request (~> 1.1.0)
|
46
50
|
pusher!
|
47
51
|
rack
|
48
52
|
rake
|
data/README.md
CHANGED
@@ -3,7 +3,15 @@ Pusher gem
|
|
3
3
|
|
4
4
|
[![Build Status](https://secure.travis-ci.org/pusher/pusher-gem.png?branch=master)](http://travis-ci.org/pusher/pusher-gem)
|
5
5
|
|
6
|
-
## Configuration
|
6
|
+
## Installation & Configuration
|
7
|
+
|
8
|
+
Add pusher to your Gemfile, and then run `bundle install`
|
9
|
+
|
10
|
+
gem 'pusher'
|
11
|
+
|
12
|
+
or install via gem
|
13
|
+
|
14
|
+
gem install pusher
|
7
15
|
|
8
16
|
After registering at <http://pusher.com> configure your app with the security credentials.
|
9
17
|
|
@@ -25,6 +33,10 @@ By default API requests are made over HTTP. HTTPS can be used by setting
|
|
25
33
|
|
26
34
|
Pusher.encrypted = true
|
27
35
|
|
36
|
+
As of version 0.12, SSL certificates are verified when using the synchronous http client. If you need to disable this behaviour for any reason use:
|
37
|
+
|
38
|
+
Pusher.default_client.sync_http_client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
39
|
+
|
28
40
|
### Instantiating a Pusher client
|
29
41
|
|
30
42
|
Sometimes you may have multiple sets of API keys, or want different configuration in different parts of your application. In these scenarios, a pusher `client` may be configured:
|
@@ -94,24 +106,45 @@ All requests must be signed by using your secret key, which is handled automatic
|
|
94
106
|
# using a client
|
95
107
|
pusher_client.post('url_without_app_id', params)
|
96
108
|
|
97
|
-
Note that you don't need to specify your app_id in the URL, as this is inferred from your credentials.
|
109
|
+
Note that you don't need to specify your app_id in the URL, as this is inferred from your credentials.
|
98
110
|
|
99
111
|
### Asynchronous requests
|
100
112
|
|
101
|
-
|
113
|
+
There are two main reasons for using the `_async` methods:
|
114
|
+
|
115
|
+
* In a web application where the response from Pusher is not used, but you'd like to avoid a blocking call in the request-response cycle
|
116
|
+
* Your application is running in an event loop and you need to avoid blocking the reactor
|
117
|
+
|
118
|
+
Asynchronous calls are supported either by using an event loop (eventmachine, preferred), or via a thread.
|
102
119
|
|
103
|
-
|
120
|
+
The following methods are available (in each case the calling iterface matches the non-async version):
|
104
121
|
|
105
|
-
|
122
|
+
* `Pusher.get_async`
|
123
|
+
* `Pusher.post_async`
|
124
|
+
* `Pusher.trigger_async`
|
106
125
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
126
|
+
It is of course also possible to make calls to pusher via a job queue. This approach is recommended if you're sending a large number of events to pusher.
|
127
|
+
|
128
|
+
#### With eventmachine
|
129
|
+
|
130
|
+
* Add the `em-http-request` gem to your Gemfile (it's not a gem dependency).
|
131
|
+
* Run the eventmachine reactor (either using `EM.run` or by running inside an evented server such as Thin).
|
132
|
+
|
133
|
+
The `_async` methods return an `EM::Deferrable` which you can bind callbacks to:
|
134
|
+
|
135
|
+
Pusher.get_async("/channels").callback { |response|
|
136
|
+
# use reponse[:channels]
|
111
137
|
}.errback { |error|
|
112
|
-
# error is
|
138
|
+
# error is an instance of Pusher::Error
|
113
139
|
}
|
114
140
|
|
141
|
+
A HTTP error or an error response from pusher will cause the errback to be called with an appropriate error object.
|
142
|
+
|
143
|
+
#### Without eventmachine
|
144
|
+
|
145
|
+
If the eventmachine reactor is not running, async requests will be make using threads (managed by the httpclient gem).
|
146
|
+
|
147
|
+
An `HTTPClient::Connection` object is returned immediately which can be [interrogated](http://rubydoc.info/gems/httpclient/HTTPClient/Connection) to discover the status of the request. The usual response checking and processing is not done when the request completes, and frankly this method is most useful when you're not interested in waiting for the response.
|
115
148
|
|
116
149
|
|
117
150
|
## Authenticating subscription requests
|
data/lib/pusher.rb
CHANGED
@@ -28,6 +28,7 @@ module Pusher
|
|
28
28
|
|
29
29
|
def_delegators :default_client, :authentication_token, :url
|
30
30
|
def_delegators :default_client, :encrypted=, :url=
|
31
|
+
def_delegators :default_client, :timeout=, :connect_timeout=, :send_timeout=, :receive_timeout=, :keep_alive_timeout=
|
31
32
|
|
32
33
|
def_delegators :default_client, :get, :get_async, :post, :post_async
|
33
34
|
def_delegators :default_client, :channels, :channel_info, :trigger, :trigger_async
|
@@ -43,8 +44,6 @@ module Pusher
|
|
43
44
|
end
|
44
45
|
end
|
45
46
|
|
46
|
-
private
|
47
|
-
|
48
47
|
def default_client
|
49
48
|
@default_client ||= Pusher::Client.new
|
50
49
|
end
|
@@ -56,7 +55,6 @@ module Pusher
|
|
56
55
|
end
|
57
56
|
|
58
57
|
require 'pusher/channel'
|
59
|
-
require 'pusher/query_encoder'
|
60
58
|
require 'pusher/request'
|
61
59
|
require 'pusher/resource'
|
62
60
|
require 'pusher/webhook'
|
data/lib/pusher/channel.rb
CHANGED
@@ -51,7 +51,7 @@ module Pusher
|
|
51
51
|
# event - see http://pusher.com/docs/publisher_api_guide/publisher_excluding_recipients for more info
|
52
52
|
#
|
53
53
|
# @raise [Pusher::Error] on invalid Pusher response - see the error message for more details
|
54
|
-
# @raise [Pusher::HTTPError] on any error raised inside
|
54
|
+
# @raise [Pusher::HTTPError] on any error raised inside http client - the original error is available in the original_error attribute
|
55
55
|
#
|
56
56
|
def trigger!(event_name, data, socket_id = nil)
|
57
57
|
params = {}
|
@@ -79,7 +79,7 @@ module Pusher
|
|
79
79
|
# @param info [Array] Array of attributes required (as lowercase strings)
|
80
80
|
# @return [Hash] Hash of requested attributes for this channel
|
81
81
|
# @raise [Pusher::Error] on invalid Pusher response - see the error message for more details
|
82
|
-
# @raise [Pusher::HTTPError] on any error raised inside
|
82
|
+
# @raise [Pusher::HTTPError] on any error raised inside http client - the original error is available in the original_error attribute
|
83
83
|
#
|
84
84
|
def info(attributes = [])
|
85
85
|
@client.get("/channels/#{name}", :info => attributes.join(','))
|
data/lib/pusher/client.rb
CHANGED
@@ -2,8 +2,10 @@ require 'signature'
|
|
2
2
|
|
3
3
|
module Pusher
|
4
4
|
class Client
|
5
|
-
attr_accessor :scheme, :host, :port, :app_id, :key, :secret
|
6
|
-
attr_reader :proxy
|
5
|
+
attr_accessor :scheme, :host, :port, :app_id, :key, :secret
|
6
|
+
attr_reader :http_proxy, :proxy
|
7
|
+
attr_writer :connect_timeout, :send_timeout, :receive_timeout,
|
8
|
+
:keep_alive_timeout
|
7
9
|
|
8
10
|
## CONFIGURATION ##
|
9
11
|
|
@@ -16,7 +18,14 @@ module Pusher
|
|
16
18
|
@scheme, @host, @port, @app_id, @key, @secret = options.values_at(
|
17
19
|
:scheme, :host, :port, :app_id, :key, :secret
|
18
20
|
)
|
21
|
+
@http_proxy = nil
|
19
22
|
self.http_proxy = options[:http_proxy] if options[:http_proxy]
|
23
|
+
|
24
|
+
# Default timeouts
|
25
|
+
@connect_timeout = 5
|
26
|
+
@send_timeout = 5
|
27
|
+
@receive_timeout = 5
|
28
|
+
@keep_alive_timeout = 30
|
20
29
|
end
|
21
30
|
|
22
31
|
# @private Returns the authentication token for the client
|
@@ -79,6 +88,12 @@ module Pusher
|
|
79
88
|
@scheme == 'https'
|
80
89
|
end
|
81
90
|
|
91
|
+
# Convenience method to set all timeouts to the same value (in seconds).
|
92
|
+
# For more control, use the individual writers.
|
93
|
+
def timeout=(value)
|
94
|
+
@connect_timeout, @send_timeout, @receive_timeout = value, value, value
|
95
|
+
end
|
96
|
+
|
82
97
|
## INTERACE WITH THE API ##
|
83
98
|
|
84
99
|
def resource(path)
|
@@ -101,7 +116,7 @@ module Pusher
|
|
101
116
|
# @return [Hash] See Pusher API docs
|
102
117
|
#
|
103
118
|
# @raise [Pusher::Error] Unsuccessful response - see the error message
|
104
|
-
# @raise [Pusher::HTTPError] Error raised inside
|
119
|
+
# @raise [Pusher::HTTPError] Error raised inside http client. The original error is wrapped in error.original_error
|
105
120
|
#
|
106
121
|
def get(path, params = {})
|
107
122
|
Resource.new(self, path).get(params)
|
@@ -110,19 +125,14 @@ module Pusher
|
|
110
125
|
# GET arbitrary REST API resource using an asynchronous http client.
|
111
126
|
# All request signing is handled automatically.
|
112
127
|
#
|
113
|
-
#
|
114
|
-
#
|
115
|
-
#
|
116
|
-
# }).callback { |response_hash|
|
117
|
-
# # ...
|
118
|
-
# }.errback { |error|
|
119
|
-
# # error is a instance of Pusher::Error
|
120
|
-
# }
|
128
|
+
# When the eventmachine reactor is running, the em-http-request gem is used;
|
129
|
+
# otherwise an async request is made using httpclient. See README for
|
130
|
+
# details and examples.
|
121
131
|
#
|
122
132
|
# @param path [String] Path excluding /apps/APP_ID
|
123
133
|
# @param params [Hash] API params (see http://pusher.com/docs/rest_api)
|
124
134
|
#
|
125
|
-
# @return
|
135
|
+
# @return Either an EM::DefaultDeferrable or a HTTPClient::Connection
|
126
136
|
#
|
127
137
|
def get_async(path, params = {})
|
128
138
|
Resource.new(self, path).get_async(params)
|
@@ -175,7 +185,7 @@ module Pusher
|
|
175
185
|
# @return [Hash] See Pusher API docs
|
176
186
|
#
|
177
187
|
# @raise [Pusher::Error] Unsuccessful response - see the error message
|
178
|
-
# @raise [Pusher::HTTPError] Error raised inside
|
188
|
+
# @raise [Pusher::HTTPError] Error raised inside http client. The original error is wrapped in error.original_error
|
179
189
|
#
|
180
190
|
def channels(params = {})
|
181
191
|
get('/channels', params)
|
@@ -191,7 +201,7 @@ module Pusher
|
|
191
201
|
# @return [Hash] See Pusher API docs
|
192
202
|
#
|
193
203
|
# @raise [Pusher::Error] Unsuccessful response - see the error message
|
194
|
-
# @raise [Pusher::HTTPError] Error raised inside
|
204
|
+
# @raise [Pusher::HTTPError] Error raised inside http client. The original error is wrapped in error.original_error
|
195
205
|
#
|
196
206
|
def channel_info(channel_name, params = {})
|
197
207
|
get("/channels/#{channel_name}", params)
|
@@ -210,7 +220,7 @@ module Pusher
|
|
210
220
|
# @return [Hash] See Pusher API docs
|
211
221
|
#
|
212
222
|
# @raise [Pusher::Error] Unsuccessful response - see the error message
|
213
|
-
# @raise [Pusher::HTTPError] Error raised inside
|
223
|
+
# @raise [Pusher::HTTPError] Error raised inside http client. The original error is wrapped in error.original_error
|
214
224
|
#
|
215
225
|
def trigger(channels, event_name, data, params = {})
|
216
226
|
post('/events', trigger_params(channels, event_name, data, params))
|
@@ -224,28 +234,16 @@ module Pusher
|
|
224
234
|
end
|
225
235
|
|
226
236
|
# @private Construct a net/http http client
|
227
|
-
def
|
228
|
-
begin
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
237
|
+
def sync_http_client
|
238
|
+
@client ||= begin
|
239
|
+
require 'httpclient'
|
240
|
+
|
241
|
+
HTTPClient.new(@http_proxy).tap do |c|
|
242
|
+
c.connect_timeout = @connect_timeout
|
243
|
+
c.send_timeout = @send_timeout
|
244
|
+
c.receive_timeout = @receive_timeout
|
245
|
+
c.keep_alive_timeout = @keep_alive_timeout
|
233
246
|
end
|
234
|
-
|
235
|
-
http_klass = if (p = @proxy)
|
236
|
-
Net::HTTP.Proxy(p[:host], p[:port], p[:user], p[:password])
|
237
|
-
else
|
238
|
-
Net::HTTP
|
239
|
-
end
|
240
|
-
|
241
|
-
http = http_klass.new(@host, @port)
|
242
|
-
|
243
|
-
if encrypted?
|
244
|
-
http.use_ssl = true
|
245
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
246
|
-
end
|
247
|
-
|
248
|
-
http
|
249
247
|
end
|
250
248
|
end
|
251
249
|
|
@@ -257,9 +255,12 @@ module Pusher
|
|
257
255
|
end
|
258
256
|
require 'em-http' unless defined?(EventMachine::HttpRequest)
|
259
257
|
|
260
|
-
connection_opts = {
|
258
|
+
connection_opts = {
|
259
|
+
:connect_timeout => @connect_timeout,
|
260
|
+
:inactivity_timeout => @receive_timeout,
|
261
|
+
}
|
261
262
|
|
262
|
-
if @proxy
|
263
|
+
if defined?(@proxy)
|
263
264
|
proxy_opts = {
|
264
265
|
:host => @proxy[:host],
|
265
266
|
:port => @proxy[:port]
|
@@ -277,7 +278,7 @@ module Pusher
|
|
277
278
|
private
|
278
279
|
|
279
280
|
def trigger_params(channels, event_name, data, params)
|
280
|
-
channels =
|
281
|
+
channels = Array(channels).map(&:to_s)
|
281
282
|
|
282
283
|
encoded_data = case data
|
283
284
|
when String
|
data/lib/pusher/request.rb
CHANGED
@@ -4,16 +4,16 @@ require 'multi_json'
|
|
4
4
|
|
5
5
|
module Pusher
|
6
6
|
class Request
|
7
|
-
include QueryEncoder
|
8
|
-
|
9
7
|
attr_reader :body, :params
|
10
8
|
|
11
9
|
def initialize(client, verb, uri, params, body = nil)
|
12
10
|
@client, @verb, @uri = client, verb, uri
|
11
|
+
@head = {}
|
13
12
|
|
13
|
+
@body = body
|
14
14
|
if body
|
15
|
-
@body = body
|
16
15
|
params[:body_md5] = Digest::MD5.hexdigest(body)
|
16
|
+
@head['Content-Type'] = 'application/json'
|
17
17
|
end
|
18
18
|
|
19
19
|
request = Signature::Request.new(verb.to_s.upcase, uri.path, params)
|
@@ -22,26 +22,12 @@ module Pusher
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def send_sync
|
25
|
-
http = @client.
|
25
|
+
http = @client.sync_http_client
|
26
26
|
|
27
27
|
begin
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
'Content-Type'=> 'application/json'
|
32
|
-
})
|
33
|
-
when :get
|
34
|
-
response = http.get(encode_query(@uri, @params), {
|
35
|
-
'Content-Type'=> 'application/json'
|
36
|
-
})
|
37
|
-
else
|
38
|
-
raise "Unsuported verb"
|
39
|
-
end
|
40
|
-
rescue Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED,
|
41
|
-
Errno::ETIMEDOUT, Errno::EHOSTUNREACH,
|
42
|
-
Timeout::Error, EOFError,
|
43
|
-
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
|
44
|
-
Net::ProtocolError => e
|
28
|
+
response = http.request(@verb, @uri, @params, @body, @head)
|
29
|
+
rescue HTTPClient::BadResponseError, HTTPClient::TimeoutError,
|
30
|
+
SocketError, Errno::ECONNREFUSED => e
|
45
31
|
error = Pusher::HTTPError.new("#{e.message} (#{e.class})")
|
46
32
|
error.original_error = e
|
47
33
|
raise error
|
@@ -53,36 +39,41 @@ module Pusher
|
|
53
39
|
end
|
54
40
|
|
55
41
|
def send_async
|
56
|
-
|
42
|
+
if defined?(EventMachine) && EventMachine.reactor_running?
|
43
|
+
http_client = @client.em_http_client(@uri)
|
44
|
+
df = EM::DefaultDeferrable.new
|
57
45
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
})
|
70
|
-
else
|
71
|
-
raise "Unsuported verb"
|
72
|
-
end
|
73
|
-
http.callback {
|
74
|
-
begin
|
75
|
-
df.succeed(handle_response(http.response_header.status, http.response.chomp))
|
76
|
-
rescue => e
|
77
|
-
df.fail(e)
|
46
|
+
http = case @verb
|
47
|
+
when :post
|
48
|
+
http_client.post({
|
49
|
+
:query => @params, :body => @body, :head => @head
|
50
|
+
})
|
51
|
+
when :get
|
52
|
+
http_client.get({
|
53
|
+
:query => @params, :head => @head
|
54
|
+
})
|
55
|
+
else
|
56
|
+
raise "Unsuported verb"
|
78
57
|
end
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
58
|
+
http.callback {
|
59
|
+
begin
|
60
|
+
df.succeed(handle_response(http.response_header.status, http.response.chomp))
|
61
|
+
rescue => e
|
62
|
+
df.fail(e)
|
63
|
+
end
|
64
|
+
}
|
65
|
+
http.errback { |e|
|
66
|
+
message = "Network error connecting to pusher (#{http.error})"
|
67
|
+
Pusher.logger.debug(message)
|
68
|
+
df.fail(Error.new(message))
|
69
|
+
}
|
70
|
+
|
71
|
+
return df
|
72
|
+
else
|
73
|
+
http = @client.sync_http_client
|
84
74
|
|
85
|
-
|
75
|
+
return http.request_async(@verb, @uri, @params, @body, @head)
|
76
|
+
end
|
86
77
|
end
|
87
78
|
|
88
79
|
private
|
data/pusher.gemspec
CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "pusher"
|
6
|
-
s.version = "0.
|
6
|
+
s.version = "0.12.0"
|
7
7
|
s.platform = Gem::Platform::RUBY
|
8
8
|
s.authors = ["Pusher"]
|
9
9
|
s.email = ["support@pusher.com"]
|
@@ -13,11 +13,12 @@ Gem::Specification.new do |s|
|
|
13
13
|
|
14
14
|
s.add_dependency "multi_json", "~> 1.0"
|
15
15
|
s.add_dependency 'signature', "~> 0.1.6"
|
16
|
+
s.add_dependency "httpclient", "~> 2.3.0"
|
16
17
|
s.add_dependency "jruby-openssl" if defined?(JRUBY_VERSION)
|
17
18
|
|
18
19
|
s.add_development_dependency "rspec", "~> 2.0"
|
19
20
|
s.add_development_dependency "webmock"
|
20
|
-
s.add_development_dependency "em-http-request", "~> 1.
|
21
|
+
s.add_development_dependency "em-http-request", "~> 1.1.0"
|
21
22
|
s.add_development_dependency "rake"
|
22
23
|
s.add_development_dependency "rack"
|
23
24
|
|
data/spec/channel_spec.rb
CHANGED
@@ -33,18 +33,18 @@ describe Pusher::Channel do
|
|
33
33
|
end
|
34
34
|
|
35
35
|
describe '#trigger' do
|
36
|
-
it "should log failure if error raised in
|
37
|
-
stub_post_to_raise(
|
36
|
+
it "should log failure if error raised in http call" do
|
37
|
+
stub_post_to_raise(HTTPClient::BadResponseError)
|
38
38
|
|
39
|
-
Pusher.logger.should_receive(:error).with("Exception from WebMock (
|
39
|
+
Pusher.logger.should_receive(:error).with("Exception from WebMock (HTTPClient::BadResponseError) (Pusher::HTTPError)")
|
40
40
|
Pusher.logger.should_receive(:debug) #backtrace
|
41
41
|
channel = Pusher::Channel.new(@client.url, 'test_channel', @client)
|
42
42
|
channel.trigger('new_event', 'Some data')
|
43
43
|
end
|
44
44
|
|
45
45
|
it "should log failure if Pusher returns an error response" do
|
46
|
-
stub_post 401
|
47
|
-
Pusher.logger.should_receive(:error).with("
|
46
|
+
stub_post 401, "some signature info"
|
47
|
+
Pusher.logger.should_receive(:error).with("some signature info (Pusher::AuthenticationError)")
|
48
48
|
Pusher.logger.should_receive(:debug) #backtrace
|
49
49
|
channel = Pusher::Channel.new(@client.url, 'test_channel', @client)
|
50
50
|
channel.trigger('new_event', 'Some data')
|
@@ -114,7 +114,7 @@ describe Pusher::Channel do
|
|
114
114
|
end
|
115
115
|
|
116
116
|
it 'should return a hash with signature including custom data and data as json string' do
|
117
|
-
MultiJson.stub
|
117
|
+
MultiJson.stub(:encode).with(@custom_data).and_return 'a json string'
|
118
118
|
|
119
119
|
response = @channel.authenticate('socketid', @custom_data)
|
120
120
|
|
data/spec/client_spec.rb
CHANGED
@@ -102,7 +102,7 @@ describe Pusher do
|
|
102
102
|
|
103
103
|
describe 'logging configuration' do
|
104
104
|
it "can be configured to use any logger" do
|
105
|
-
logger =
|
105
|
+
logger = double("ALogger")
|
106
106
|
logger.should_receive(:debug).with('foo')
|
107
107
|
Pusher.logger = logger
|
108
108
|
Pusher.logger.debug('foo')
|
@@ -314,8 +314,8 @@ describe Pusher do
|
|
314
314
|
}
|
315
315
|
end
|
316
316
|
|
317
|
-
it "should catch all
|
318
|
-
stub_request(verb, @url_regexp).to_raise(
|
317
|
+
it "should catch all http exceptions and raise a Pusher::HTTPError wrapping the original error" do
|
318
|
+
stub_request(verb, @url_regexp).to_raise(HTTPClient::TimeoutError)
|
319
319
|
|
320
320
|
error = nil
|
321
321
|
begin
|
@@ -326,8 +326,8 @@ describe Pusher do
|
|
326
326
|
|
327
327
|
error.class.should == Pusher::HTTPError
|
328
328
|
error.should be_kind_of(Pusher::Error)
|
329
|
-
error.message.should == 'Exception from WebMock (
|
330
|
-
error.original_error.class.should ==
|
329
|
+
error.message.should == 'Exception from WebMock (HTTPClient::TimeoutError)'
|
330
|
+
error.original_error.class.should == HTTPClient::TimeoutError
|
331
331
|
end
|
332
332
|
|
333
333
|
it "should raise Pusher::Error if call returns 400" do
|
@@ -357,61 +357,105 @@ describe Pusher do
|
|
357
357
|
end
|
358
358
|
end
|
359
359
|
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
let(:call_api) { @client.send(method, '/path') }
|
360
|
+
describe "async calling without eventmachine" do
|
361
|
+
[[:get, :get_async], [:post, :post_async]].each do |verb, method|
|
362
|
+
describe "##{method}" do
|
363
|
+
before :each do
|
364
|
+
@url_regexp = %r{api.pusherapp.com}
|
365
|
+
stub_request(verb, @url_regexp).
|
366
|
+
to_return(:status => 200, :body => "{}")
|
367
|
+
end
|
369
368
|
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
369
|
+
let(:call_api) {
|
370
|
+
@client.send(method, '/path').tap { |c|
|
371
|
+
# Allow the async thread (inside httpclient) to run
|
372
|
+
while !c.finished?
|
373
|
+
sleep 0.01
|
374
|
+
end
|
375
375
|
}
|
376
376
|
}
|
377
|
-
end
|
378
377
|
|
379
|
-
|
380
|
-
|
378
|
+
it "should use http by default" do
|
379
|
+
call_api
|
380
|
+
WebMock.should have_requested(verb, %r{http://api.pusherapp.com/apps/20/path})
|
381
|
+
end
|
382
|
+
|
383
|
+
it "should use https if configured" do
|
381
384
|
@client.encrypted = true
|
382
|
-
call_api
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
385
|
+
call_api
|
386
|
+
WebMock.should have_requested(verb, %r{https://api.pusherapp.com})
|
387
|
+
end
|
388
|
+
|
389
|
+
# Note that the raw httpclient connection object is returned and
|
390
|
+
# the response isn't handled (by handle_response) in the normal way.
|
391
|
+
it "should return a httpclient connection object" do
|
392
|
+
connection = call_api
|
393
|
+
connection.finished?.should be_true
|
394
|
+
response = connection.pop
|
395
|
+
response.status.should == 200
|
396
|
+
response.body.read.should == "{}"
|
397
|
+
end
|
387
398
|
end
|
399
|
+
end
|
400
|
+
end
|
388
401
|
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
402
|
+
describe "async calling with eventmachine" do
|
403
|
+
[[:get, :get_async], [:post, :post_async]].each do |verb, method|
|
404
|
+
describe "##{method}" do
|
405
|
+
before :each do
|
406
|
+
@url_regexp = %r{api.pusherapp.com}
|
407
|
+
stub_request(verb, @url_regexp).
|
408
|
+
to_return(:status => 200, :body => "{}")
|
409
|
+
end
|
410
|
+
|
411
|
+
let(:call_api) { @client.send(method, '/path') }
|
412
|
+
|
413
|
+
it "should use http by default" do
|
414
|
+
EM.run {
|
415
|
+
call_api.callback {
|
416
|
+
WebMock.should have_requested(verb, %r{http://api.pusherapp.com/apps/20/path})
|
417
|
+
EM.stop
|
398
418
|
}
|
399
|
-
EM.stop
|
400
419
|
}
|
401
|
-
|
402
|
-
end
|
420
|
+
end
|
403
421
|
|
404
|
-
|
405
|
-
|
406
|
-
|
422
|
+
it "should use https if configured" do
|
423
|
+
EM.run {
|
424
|
+
@client.encrypted = true
|
425
|
+
call_api.callback {
|
426
|
+
WebMock.should have_requested(verb, %r{https://api.pusherapp.com})
|
427
|
+
EM.stop
|
428
|
+
}
|
429
|
+
}
|
430
|
+
end
|
407
431
|
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
432
|
+
it "should format the respose hash with symbols at first level" do
|
433
|
+
EM.run {
|
434
|
+
stub_request(verb, @url_regexp).to_return({
|
435
|
+
:status => 200,
|
436
|
+
:body => MultiJson.encode({'something' => {'a' => 'hash'}})
|
437
|
+
})
|
438
|
+
call_api.callback { |response|
|
439
|
+
response.should == {
|
440
|
+
:something => {'a' => 'hash'}
|
441
|
+
}
|
442
|
+
EM.stop
|
443
|
+
}
|
413
444
|
}
|
414
|
-
|
445
|
+
end
|
446
|
+
|
447
|
+
it "should errback with Pusher::Error on unsuccessful response" do
|
448
|
+
EM.run {
|
449
|
+
stub_request(verb, @url_regexp).to_return({:status => 400})
|
450
|
+
|
451
|
+
call_api.errback { |e|
|
452
|
+
e.class.should == Pusher::Error
|
453
|
+
EM.stop
|
454
|
+
}.callback {
|
455
|
+
fail
|
456
|
+
}
|
457
|
+
}
|
458
|
+
end
|
415
459
|
end
|
416
460
|
end
|
417
461
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pusher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
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: 2013-
|
12
|
+
date: 2013-08-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: multi_json
|
@@ -43,6 +43,22 @@ dependencies:
|
|
43
43
|
- - ~>
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: 0.1.6
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: httpclient
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 2.3.0
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.3.0
|
46
62
|
- !ruby/object:Gem::Dependency
|
47
63
|
name: rspec
|
48
64
|
requirement: !ruby/object:Gem::Requirement
|
@@ -82,7 +98,7 @@ dependencies:
|
|
82
98
|
requirements:
|
83
99
|
- - ~>
|
84
100
|
- !ruby/object:Gem::Version
|
85
|
-
version: 1.
|
101
|
+
version: 1.1.0
|
86
102
|
type: :development
|
87
103
|
prerelease: false
|
88
104
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -90,7 +106,7 @@ dependencies:
|
|
90
106
|
requirements:
|
91
107
|
- - ~>
|
92
108
|
- !ruby/object:Gem::Version
|
93
|
-
version: 1.
|
109
|
+
version: 1.1.0
|
94
110
|
- !ruby/object:Gem::Dependency
|
95
111
|
name: rake
|
96
112
|
requirement: !ruby/object:Gem::Requirement
|
@@ -143,7 +159,6 @@ files:
|
|
143
159
|
- lib/pusher.rb
|
144
160
|
- lib/pusher/channel.rb
|
145
161
|
- lib/pusher/client.rb
|
146
|
-
- lib/pusher/query_encoder.rb
|
147
162
|
- lib/pusher/request.rb
|
148
163
|
- lib/pusher/resource.rb
|
149
164
|
- lib/pusher/webhook.rb
|
@@ -166,7 +181,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
166
181
|
version: '0'
|
167
182
|
segments:
|
168
183
|
- 0
|
169
|
-
hash: -
|
184
|
+
hash: -1060459840263191989
|
170
185
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
171
186
|
none: false
|
172
187
|
requirements:
|
@@ -175,10 +190,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
175
190
|
version: '0'
|
176
191
|
segments:
|
177
192
|
- 0
|
178
|
-
hash: -
|
193
|
+
hash: -1060459840263191989
|
179
194
|
requirements: []
|
180
195
|
rubyforge_project:
|
181
|
-
rubygems_version: 1.8.
|
196
|
+
rubygems_version: 1.8.25
|
182
197
|
signing_key:
|
183
198
|
specification_version: 3
|
184
199
|
summary: Pusher API client
|
data/lib/pusher/query_encoder.rb
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
module Pusher
|
2
|
-
# Query string encoding extracted with thanks from em-http-request
|
3
|
-
module QueryEncoder
|
4
|
-
def encode_query(uri, query)
|
5
|
-
encoded_query = if query.kind_of?(Hash)
|
6
|
-
query.map { |k, v| encode_param(k, v) }.join('&')
|
7
|
-
else
|
8
|
-
query.to_s
|
9
|
-
end
|
10
|
-
|
11
|
-
if uri && !uri.query.to_s.empty?
|
12
|
-
encoded_query = [encoded_query, uri.query].reject {|part| part.empty?}.join("&")
|
13
|
-
end
|
14
|
-
encoded_query.to_s.empty? ? uri.path : "#{uri.path}?#{encoded_query}"
|
15
|
-
end
|
16
|
-
|
17
|
-
# URL encodes query parameters:
|
18
|
-
# single k=v, or a URL encoded array, if v is an array of values
|
19
|
-
def encode_param(k, v)
|
20
|
-
if v.is_a?(Array)
|
21
|
-
v.map { |e| escape(k) + "[]=" + escape(e) }.join("&")
|
22
|
-
else
|
23
|
-
escape(k) + "=" + escape(v)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def escape(s)
|
28
|
-
if defined?(EscapeUtils)
|
29
|
-
EscapeUtils.escape_url(s.to_s)
|
30
|
-
else
|
31
|
-
s.to_s.gsub(/([^a-zA-Z0-9_.-]+)/n) {
|
32
|
-
'%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
|
33
|
-
}
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
if ''.respond_to?(:bytesize)
|
38
|
-
def bytesize(string)
|
39
|
-
string.bytesize
|
40
|
-
end
|
41
|
-
else
|
42
|
-
def bytesize(string)
|
43
|
-
string.size
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|