pusher 0.4.2 → 0.4.3
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.
- 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
|