pusher 0.4.2 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +13 -0
- data/VERSION +1 -1
- data/lib/pusher.rb +0 -4
- data/lib/pusher/channel.rb +80 -32
- data/pusher.gemspec +2 -2
- data/spec/pusher_spec.rb +95 -0
- data/spec/spec_helper.rb +1 -0
- metadata +3 -3
data/README.md
CHANGED
@@ -25,6 +25,19 @@ Errors are logged to `Pusher.logger`. It will by default use `Logger` from stdli
|
|
25
25
|
|
26
26
|
Pusher.logger = Rails.logger
|
27
27
|
|
28
|
+
Asynchronous triggering
|
29
|
+
-----------------------
|
30
|
+
|
31
|
+
To avoid blocking in a typical web application, if you are running inside eventmachine (for example if you use the thin server), you may wish to use the `trigger_async` method which uses the em-http-request gem to make api requests to pusher. It returns a deferrable which you can optionally bind to with success and failure callbacks. This is not a gem dependency, so you will need to install it manually.
|
32
|
+
|
33
|
+
d = Pusher['a_channel'].trigger('an_event', {:some => 'data'}, socket_id)
|
34
|
+
d.callback {
|
35
|
+
# Do something on success
|
36
|
+
}
|
37
|
+
d.errback { |error|
|
38
|
+
# error is a pusher exception
|
39
|
+
}
|
40
|
+
|
28
41
|
Copyright
|
29
42
|
---------
|
30
43
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.3
|
data/lib/pusher.rb
CHANGED
data/lib/pusher/channel.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'crack/core_extensions' # Used for Hash#to_params
|
2
2
|
require 'digest/md5'
|
3
3
|
|
4
|
+
require 'json'
|
5
|
+
require 'uri'
|
6
|
+
|
4
7
|
module Pusher
|
5
8
|
class Channel
|
6
9
|
def initialize(app_id, name)
|
@@ -9,48 +12,51 @@ module Pusher
|
|
9
12
|
:port => Pusher.port,
|
10
13
|
:path => "/apps/#{app_id}/channels/#{name}/events"
|
11
14
|
})
|
12
|
-
@http = Net::HTTP.new(@uri.host, @uri.port)
|
13
15
|
end
|
14
16
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
17
|
+
def trigger_async(event_name, data, socket_id = nil, &block)
|
18
|
+
unless defined?(EventMachine) && EventMachine.reactor_running?
|
19
|
+
raise Error, "In order to use trigger_async you must be running inside an eventmachine loop"
|
20
|
+
end
|
21
|
+
require 'em-http' unless defined?(EventMachine::HttpRequest)
|
22
|
+
|
23
|
+
@http_async ||= EventMachine::HttpRequest.new(@uri)
|
24
|
+
|
25
|
+
query, body = construct_request(event_name, data, socket_id)
|
26
|
+
|
27
|
+
deferrable = EM::DefaultDeferrable.new
|
28
|
+
|
29
|
+
http = @http_async.post({
|
30
|
+
:query => query, :timeout => 2, :body => body
|
31
|
+
})
|
32
|
+
http.callback {
|
25
33
|
begin
|
26
|
-
|
34
|
+
handle_response(http.response_header.status, http.response.chomp)
|
35
|
+
deferrable.succeed
|
27
36
|
rescue => e
|
28
|
-
|
29
|
-
raise e
|
37
|
+
deferrable.fail(e)
|
30
38
|
end
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
39
|
+
}
|
40
|
+
http.errback {
|
41
|
+
Pusher.logger.debug("Network error connecting to pusher: #{http.inspect}")
|
42
|
+
deferrable.fail(Error.new("Network error connecting to pusher"))
|
43
|
+
}
|
44
|
+
|
45
|
+
deferrable
|
46
|
+
end
|
36
47
|
|
37
|
-
|
38
|
-
|
48
|
+
def trigger!(event_name, data, socket_id = nil)
|
49
|
+
require 'net/http' unless defined?(Net::HTTP)
|
39
50
|
|
40
|
-
|
51
|
+
@http_sync ||= Net::HTTP.new(@uri.host, @uri.port)
|
52
|
+
|
53
|
+
query, body = construct_request(event_name, data, socket_id)
|
54
|
+
|
55
|
+
response = @http_sync.post("#{@uri.path}?#{query.to_params}", body, {
|
41
56
|
'Content-Type'=> 'application/json'
|
42
57
|
})
|
43
58
|
|
44
|
-
|
45
|
-
when "202"
|
46
|
-
return true
|
47
|
-
when "401"
|
48
|
-
raise AuthenticationError, response.body.chomp
|
49
|
-
when "404"
|
50
|
-
raise Error, "Resource not found: app_id is probably invalid"
|
51
|
-
else
|
52
|
-
raise Error, "Unknown error in Pusher: #{response.body.chomp}"
|
53
|
-
end
|
59
|
+
handle_response(response.code.to_i, response.body.chomp)
|
54
60
|
end
|
55
61
|
|
56
62
|
def trigger(event_name, data, socket_id = nil)
|
@@ -73,5 +79,47 @@ module Pusher
|
|
73
79
|
Pusher.logger.error("#{e.message} (#{e.class})")
|
74
80
|
Pusher.logger.debug(e.backtrace.join("\n"))
|
75
81
|
end
|
82
|
+
|
83
|
+
def construct_request(event_name, data, socket_id)
|
84
|
+
params = {
|
85
|
+
:name => event_name,
|
86
|
+
}
|
87
|
+
params[:socket_id] = socket_id if socket_id
|
88
|
+
|
89
|
+
body = case data
|
90
|
+
when String
|
91
|
+
data
|
92
|
+
else
|
93
|
+
begin
|
94
|
+
self.class.turn_into_json(data)
|
95
|
+
rescue => e
|
96
|
+
Pusher.logger.error("Could not convert #{data.inspect} into JSON")
|
97
|
+
raise e
|
98
|
+
end
|
99
|
+
end
|
100
|
+
params[:body_md5] = Digest::MD5.hexdigest(body)
|
101
|
+
|
102
|
+
request = Authentication::Request.new('POST', @uri.path, params)
|
103
|
+
auth_hash = request.sign(Pusher.authentication_token)
|
104
|
+
|
105
|
+
query_params = params.merge(auth_hash)
|
106
|
+
|
107
|
+
return query_params, body
|
108
|
+
end
|
109
|
+
|
110
|
+
def handle_response(status_code, body)
|
111
|
+
case status_code
|
112
|
+
when 202
|
113
|
+
return true
|
114
|
+
when 400
|
115
|
+
raise Error, "Bad request: #{body}"
|
116
|
+
when 401
|
117
|
+
raise AuthenticationError, body
|
118
|
+
when 404
|
119
|
+
raise Error, "Resource not found: app_id is probably invalid"
|
120
|
+
else
|
121
|
+
raise Error, "Unknown error in Pusher: #{body}"
|
122
|
+
end
|
123
|
+
end
|
76
124
|
end
|
77
125
|
end
|
data/pusher.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{pusher}
|
8
|
-
s.version = "0.4.
|
8
|
+
s.version = "0.4.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["New Bamboo"]
|
12
|
-
s.date = %q{2010-05-
|
12
|
+
s.date = %q{2010-05-11}
|
13
13
|
s.description = %q{Wrapper for pusherapp.com REST api}
|
14
14
|
s.email = %q{support@pusherapp.com}
|
15
15
|
s.extra_rdoc_files = [
|
data/spec/pusher_spec.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require File.expand_path('../spec_helper', __FILE__)
|
2
2
|
|
3
3
|
require 'webmock/rspec'
|
4
|
+
require 'em-http'
|
5
|
+
require 'em-http/mock'
|
4
6
|
|
5
7
|
describe Pusher do
|
6
8
|
describe 'configuration' do
|
@@ -166,5 +168,98 @@ describe Pusher do
|
|
166
168
|
Pusher['test_channel'].trigger('new_event', 'Some data')
|
167
169
|
end
|
168
170
|
end
|
171
|
+
|
172
|
+
describe "Channgel#trigger_async" do
|
173
|
+
#in order to match URLs when testing http requests
|
174
|
+
#override the method that converts query hash to string
|
175
|
+
#to include a sort so URL is consistent
|
176
|
+
module EventMachine
|
177
|
+
module HttpEncoding
|
178
|
+
def encode_query(path, query, uri_query)
|
179
|
+
encoded_query = if query.kind_of?(Hash)
|
180
|
+
query.sort{|a, b| a.to_s <=> b.to_s}.
|
181
|
+
map { |k, v| encode_param(k, v) }.
|
182
|
+
join('&')
|
183
|
+
else
|
184
|
+
query.to_s
|
185
|
+
end
|
186
|
+
if !uri_query.to_s.empty?
|
187
|
+
encoded_query = [encoded_query, uri_query].reject {|part| part.empty?}.join("&")
|
188
|
+
end
|
189
|
+
return path if encoded_query.to_s.empty?
|
190
|
+
"#{path}?#{encoded_query}"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
before :each do
|
196
|
+
EM::HttpRequest = EM::MockHttpRequest
|
197
|
+
EM::HttpRequest.reset_registry!
|
198
|
+
EM::HttpRequest.reset_counts!
|
199
|
+
EM::HttpRequest.pass_through_requests = false
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should return a deferrable which succeeds in success case" do
|
203
|
+
# Yeah, mocking EM::MockHttpRequest isn't that feature rich :)
|
204
|
+
Time.stub(:now).and_return(123)
|
205
|
+
|
206
|
+
url = 'http://api.pusherapp.com:80/apps/20/channels/test_channel/events?auth_key=12345678900000001&auth_signature=0ffe2a3749f886ca69c3f516a30c7bc9a12d2ebd8bda5b718b90ad58507c8261&auth_timestamp=123&auth_version=1.0&body_md5=5b82f8bf4df2bfb0e66ccaa7306fd024&name=new_event'
|
207
|
+
|
208
|
+
data = <<-RESPONSE.gsub(/^ +/, '')
|
209
|
+
HTTP/1.1 202 Accepted
|
210
|
+
Content-Type: text/html
|
211
|
+
Content-Length: 13
|
212
|
+
Connection: keep-alive
|
213
|
+
Server: thin 1.2.7 codename No Hup
|
214
|
+
|
215
|
+
202 ACCEPTED
|
216
|
+
RESPONSE
|
217
|
+
|
218
|
+
EM::HttpRequest.register(url, :post, data)
|
219
|
+
|
220
|
+
EM.run {
|
221
|
+
d = Pusher['test_channel'].trigger_async('new_event', 'Some data')
|
222
|
+
d.callback {
|
223
|
+
EM::HttpRequest.count(url, :post).should == 1
|
224
|
+
EM.stop
|
225
|
+
}
|
226
|
+
d.errback {
|
227
|
+
fail
|
228
|
+
EM.stop
|
229
|
+
}
|
230
|
+
}
|
231
|
+
end
|
232
|
+
|
233
|
+
it "should return a deferrable which fails (with exception) in fail case" do
|
234
|
+
# Yeah, mocking EM::MockHttpRequest isn't that feature rich :)
|
235
|
+
Time.stub(:now).and_return(123)
|
236
|
+
|
237
|
+
url = 'http://api.pusherapp.com:80/apps/20/channels/test_channel/events?auth_key=12345678900000001&auth_signature=0ffe2a3749f886ca69c3f516a30c7bc9a12d2ebd8bda5b718b90ad58507c8261&auth_timestamp=123&auth_version=1.0&body_md5=5b82f8bf4df2bfb0e66ccaa7306fd024&name=new_event'
|
238
|
+
|
239
|
+
data = <<-RESPONSE.gsub(/^ +/, '')
|
240
|
+
HTTP/1.1 401 Unauthorized
|
241
|
+
Content-Type: text/html
|
242
|
+
Content-Length: 130
|
243
|
+
Connection: keep-alive
|
244
|
+
Server: thin 1.2.7 codename No Hup
|
245
|
+
|
246
|
+
401 UNAUTHORIZED: Timestamp expired: Given timestamp (2010-05-05T11:24:42Z) not within 600s of server time (2010-05-05T11:51:42Z)
|
247
|
+
RESPONSE
|
248
|
+
|
249
|
+
EM::HttpRequest.register(url, :post, data)
|
250
|
+
|
251
|
+
EM.run {
|
252
|
+
d = Pusher['test_channel'].trigger_async('new_event', 'Some data')
|
253
|
+
d.callback {
|
254
|
+
fail
|
255
|
+
}
|
256
|
+
d.errback { |error|
|
257
|
+
EM::HttpRequest.count(url, :post).should == 1
|
258
|
+
error.should be_kind_of(Pusher::AuthenticationError)
|
259
|
+
EM.stop
|
260
|
+
}
|
261
|
+
}
|
262
|
+
end
|
263
|
+
end
|
169
264
|
end
|
170
265
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 4
|
8
|
-
-
|
9
|
-
version: 0.4.
|
8
|
+
- 3
|
9
|
+
version: 0.4.3
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- New Bamboo
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-05-
|
17
|
+
date: 2010-05-11 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|