pusher-client 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +15 -0
- data/Gemfile +6 -1
- data/README.rdoc +13 -0
- data/examples/hello_pusher_ssl.rb +20 -0
- data/examples/subscribe_private.rb +21 -0
- data/lib/pusher-client/socket.rb +17 -12
- data/lib/pusher-client/websocket.rb +32 -24
- data/pusher-client.gemspec +19 -50
- metadata +23 -73
- data/Gemfile.lock +0 -41
data/.gitignore
ADDED
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -11,6 +11,11 @@ may be sent on. You can't just bind a global event without subscribing to any ch
|
|
11
11
|
|
12
12
|
== Installation
|
13
13
|
gem install pusher-client
|
14
|
+
|
15
|
+
If you're using Ruby 1.8.7, then you'll also need the openssl-nonblock gem:
|
16
|
+
|
17
|
+
gem install openssl-nonblock
|
18
|
+
|
14
19
|
== Single-Threaded Usage
|
15
20
|
The application will pause at socket.connect and handle events from Pusher as they happen.
|
16
21
|
|
@@ -71,6 +76,14 @@ and you can continue to subscribe/unsubscribe to channels and bind new events.
|
|
71
76
|
For further documentation, read the source & test suite. Some features of the JavaScript client
|
72
77
|
are not yet implemented.
|
73
78
|
|
79
|
+
== Native extension
|
80
|
+
Pusher depends on the websocket[https://github.com/imanel/websocket-ruby] gem
|
81
|
+
which is a pure Ruby implementation of websockets.
|
82
|
+
|
83
|
+
However it can optionally use a native C or Java implementation for a 25% speed
|
84
|
+
increase by including the websocket-native[https://github.com/imanel/websocket-ruby-native]
|
85
|
+
gem in your Gemfile.
|
86
|
+
|
74
87
|
== Contributing to pusher-client
|
75
88
|
|
76
89
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Usage: $ PUSHER_KEY=YOURKEY ruby examples/hello_pusher.rb
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require './lib/pusher-client.rb'
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
APP_KEY = ENV['PUSHER_KEY'] # || "YOUR_APPLICATION_KEY"
|
8
|
+
|
9
|
+
PusherClient.logger = Logger.new(STDOUT)
|
10
|
+
socket = PusherClient::Socket.new(APP_KEY, { :encrypted => true } )
|
11
|
+
|
12
|
+
# Subscribe to a channel
|
13
|
+
socket.subscribe('hellopusher')
|
14
|
+
|
15
|
+
# Bind to a channel event
|
16
|
+
socket['hellopusher'].bind('hello') do |data|
|
17
|
+
pp data
|
18
|
+
end
|
19
|
+
|
20
|
+
socket.connect
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Usage: $ PUSHER_KEY=YOURKEY ruby examples/hello_pusher.rb
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require './lib/pusher-client.rb'
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
APP_KEY = ENV['PUSHER_KEY'] # || "YOUR_APPLICATION_KEY"
|
8
|
+
APP_SECRET = ENV['PUSHER_SECRET'] # || "YOUR_APPLICATION_SECRET"
|
9
|
+
|
10
|
+
PusherClient.logger = Logger.new(STDOUT)
|
11
|
+
socket = PusherClient::Socket.new(APP_KEY, { :encrypted => true, :secret => APP_SECRET } )
|
12
|
+
|
13
|
+
# Subscribe to a channel
|
14
|
+
socket.subscribe('private-hellopusher')
|
15
|
+
|
16
|
+
# Bind to a channel event
|
17
|
+
socket['hellopusher'].bind('hello') do |data|
|
18
|
+
pp data
|
19
|
+
end
|
20
|
+
|
21
|
+
socket.connect
|
data/lib/pusher-client/socket.rb
CHANGED
@@ -5,9 +5,8 @@ require 'digest/md5'
|
|
5
5
|
module PusherClient
|
6
6
|
class Socket
|
7
7
|
|
8
|
-
# Mimick the JavaScript client
|
9
8
|
CLIENT_ID = 'pusher-ruby-client'
|
10
|
-
VERSION = '0.
|
9
|
+
VERSION = '0.3.0'
|
11
10
|
PROTOCOL = '5'
|
12
11
|
|
13
12
|
attr_accessor :encrypted, :secure
|
@@ -41,6 +40,12 @@ module PusherClient
|
|
41
40
|
bind('pusher:error') do |data|
|
42
41
|
PusherClient.logger.fatal("Pusher : error : #{data.inspect}")
|
43
42
|
end
|
43
|
+
|
44
|
+
# Keep this in case we're using a websocket protocol that doesn't
|
45
|
+
# implement ping/pong
|
46
|
+
bind('pusher:ping') do
|
47
|
+
send_event('pusher:pong', nil)
|
48
|
+
end
|
44
49
|
end
|
45
50
|
|
46
51
|
def connect(async = false)
|
@@ -52,23 +57,23 @@ module PusherClient
|
|
52
57
|
PusherClient.logger.debug("Pusher : connecting : #{url}")
|
53
58
|
|
54
59
|
@connection_thread = Thread.new {
|
55
|
-
options
|
56
|
-
@connection =
|
60
|
+
options = {:ssl => @encrypted || @secure}
|
61
|
+
@connection = PusherWebSocket.new(url, options)
|
57
62
|
PusherClient.logger.debug "Websocket connected"
|
63
|
+
|
58
64
|
loop do
|
59
|
-
msg
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
send_local_event(event_name, event_data, channel_name)
|
65
|
+
msg = @connection.receive[0]
|
66
|
+
next if msg.nil?
|
67
|
+
params = parser(msg)
|
68
|
+
next if params['socket_id'] && params['socket_id'] == self.socket_id
|
69
|
+
|
70
|
+
send_local_event params['event'], params['data'], params['channel']
|
66
71
|
end
|
67
72
|
}
|
68
73
|
|
69
74
|
@connection_thread.run
|
70
75
|
@connection_thread.join unless async
|
71
|
-
|
76
|
+
self
|
72
77
|
end
|
73
78
|
|
74
79
|
def disconnect
|
@@ -1,19 +1,22 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'socket'
|
3
|
-
require '
|
3
|
+
require 'websocket'
|
4
4
|
require 'openssl'
|
5
|
+
require 'openssl/nonblock' if RUBY_VERSION == '1.8.7'
|
5
6
|
|
6
7
|
module PusherClient
|
7
|
-
class
|
8
|
+
class PusherWebSocket
|
9
|
+
WAIT_EXCEPTIONS = [Errno::EAGAIN, Errno::EWOULDBLOCK]
|
10
|
+
WAIT_EXCEPTIONS << IO::WaitReadable if defined?(IO::WaitReadable)
|
11
|
+
|
12
|
+
attr_accessor :socket
|
8
13
|
|
9
14
|
def initialize(url, params = {})
|
10
|
-
@hs ||=
|
11
|
-
@frame ||=
|
12
|
-
|
13
|
-
@socket = TCPSocket.new(@hs.url.host, @hs.url.port || 80)
|
15
|
+
@hs ||= WebSocket::Handshake::Client.new(:url => url, :version => params[:version])
|
16
|
+
@frame ||= WebSocket::Frame::Incoming::Server.new(:version => @hs.version)
|
17
|
+
@socket = TCPSocket.new(@hs.host, @hs.port || 80)
|
14
18
|
|
15
19
|
if params[:ssl] == true
|
16
|
-
|
17
20
|
ctx = OpenSSL::SSL::SSLContext.new
|
18
21
|
ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
19
22
|
# http://curl.haxx.se/ca/cacert.pem
|
@@ -24,7 +27,6 @@ module PusherClient
|
|
24
27
|
ssl_sock.connect
|
25
28
|
|
26
29
|
@socket = ssl_sock
|
27
|
-
|
28
30
|
end
|
29
31
|
|
30
32
|
@socket.write(@hs.to_s)
|
@@ -34,11 +36,10 @@ module PusherClient
|
|
34
36
|
data = @socket.getc
|
35
37
|
next if data.nil?
|
36
38
|
|
37
|
-
|
39
|
+
@hs << data
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
if @hs.done?
|
41
|
+
if @hs.finished?
|
42
|
+
raise Exception unless @hs.valid?
|
42
43
|
@handshaked = true
|
43
44
|
break
|
44
45
|
end
|
@@ -49,10 +50,10 @@ module PusherClient
|
|
49
50
|
File.join(File.dirname(File.expand_path(__FILE__)), '../../certs/cacert.pem')
|
50
51
|
end
|
51
52
|
|
52
|
-
def send(data)
|
53
|
+
def send(data, type = :text)
|
53
54
|
raise "no handshake!" unless @handshaked
|
54
55
|
|
55
|
-
data =
|
56
|
+
data = WebSocket::Frame::Outgoing::Server.new(:version => @hs.version, :data => data, :type => type).to_s
|
56
57
|
@socket.write data
|
57
58
|
@socket.flush
|
58
59
|
end
|
@@ -60,25 +61,32 @@ module PusherClient
|
|
60
61
|
def receive
|
61
62
|
raise "no handshake!" unless @handshaked
|
62
63
|
|
63
|
-
|
64
|
-
|
64
|
+
begin
|
65
|
+
data = @socket.read_nonblock(1024)
|
66
|
+
rescue *WAIT_EXCEPTIONS
|
67
|
+
IO.select([@socket])
|
68
|
+
retry
|
69
|
+
end
|
70
|
+
@frame << data
|
65
71
|
|
66
72
|
messages = []
|
67
73
|
while message = @frame.next
|
68
|
-
|
74
|
+
if message.type === :ping
|
75
|
+
send(message.data, :pong)
|
76
|
+
return messages
|
77
|
+
end
|
78
|
+
messages << message.to_s
|
69
79
|
end
|
70
80
|
messages
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
@socket
|
81
|
+
rescue IOError, Errno::EBADF => error
|
82
|
+
PusherClient.logger.debug error.message
|
83
|
+
[]
|
75
84
|
end
|
76
85
|
|
77
86
|
def close
|
78
87
|
@socket.close
|
88
|
+
rescue IOError => error
|
89
|
+
PusherClient.logger.debug error.message
|
79
90
|
end
|
80
|
-
|
81
91
|
end
|
82
92
|
end
|
83
|
-
|
84
|
-
|
data/pusher-client.gemspec
CHANGED
@@ -1,56 +1,25 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
2
|
Gem::Specification.new do |s|
|
4
|
-
s.name
|
5
|
-
s.version
|
3
|
+
s.name = 'pusher-client'
|
4
|
+
s.version = "0.3.0"
|
5
|
+
s.authors = ["Logan Koester", "Phil Leggetter"]
|
6
|
+
s.email = ['support@pusher.com']
|
7
|
+
s.homepage = 'http://github.com/pusher/pusher-ruby-client'
|
8
|
+
s.summary = 'Client for consuming WebSockets from http://pusher.com'
|
9
|
+
s.description = 'Client for consuming WebSockets from http://pusher.com'
|
6
10
|
|
7
|
-
s.
|
8
|
-
s.
|
9
|
-
s.
|
10
|
-
|
11
|
-
|
12
|
-
s.extra_rdoc_files =
|
13
|
-
|
14
|
-
|
15
|
-
]
|
16
|
-
s.files = [
|
17
|
-
".document",
|
18
|
-
"Gemfile",
|
19
|
-
"Gemfile.lock",
|
20
|
-
"LICENSE.txt",
|
21
|
-
"README.rdoc",
|
22
|
-
"Rakefile",
|
23
|
-
"examples/hello_pusher.rb",
|
24
|
-
"examples/hello_pusher_async.rb",
|
25
|
-
"lib/pusher-client.rb",
|
26
|
-
"lib/pusher-client/channel.rb",
|
27
|
-
"lib/pusher-client/channels.rb",
|
28
|
-
"lib/pusher-client/socket.rb",
|
29
|
-
"lib/pusher-client/websocket.rb",
|
30
|
-
"certs/cacert.pem",
|
31
|
-
"pusher-client.gemspec",
|
32
|
-
"test/pusherclient_test.rb",
|
33
|
-
"test/test.watchr",
|
34
|
-
"test/teststrap.rb"
|
35
|
-
]
|
36
|
-
s.homepage = %q{http://github.com/pusher/pusher-ruby-client}
|
37
|
-
s.licenses = ["MIT"]
|
38
|
-
s.require_paths = ["lib"]
|
39
|
-
s.rubygems_version = %q{1.3.7}
|
40
|
-
s.summary = %q{Ruby client for consuming WebSockets from http://pusher.com}
|
41
|
-
s.test_files = [
|
42
|
-
"examples/hello_pusher.rb",
|
43
|
-
"examples/hello_pusher_async.rb",
|
44
|
-
"test/pusherclient_test.rb",
|
45
|
-
"test/teststrap.rb"
|
46
|
-
]
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
13
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f|
|
14
|
+
File.basename(f)
|
15
|
+
}
|
16
|
+
s.extra_rdoc_files = %w(LICENSE.txt README.rdoc)
|
17
|
+
s.require_paths = ['lib']
|
18
|
+
s.licenses = ['MIT']
|
47
19
|
|
48
|
-
s.add_runtime_dependency
|
49
|
-
s.add_runtime_dependency
|
50
|
-
s.add_runtime_dependency(%q<addressable>, ["~> 2.3.1"])
|
51
|
-
s.add_development_dependency(%q<bacon>, [">= 0"])
|
52
|
-
s.add_development_dependency(%q<rake>, [">= 0"])
|
53
|
-
s.add_development_dependency(%q<ruby-debug19>, [">= 0"])
|
20
|
+
s.add_runtime_dependency 'websocket', '~> 1.0.0'
|
21
|
+
s.add_runtime_dependency 'ruby-hmac', '~> 0.4.0'
|
54
22
|
|
23
|
+
s.add_development_dependency 'bacon', '>= 0'
|
24
|
+
s.add_development_dependency 'rake', '>= 0'
|
55
25
|
end
|
56
|
-
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pusher-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,59 +10,33 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2013-04-05 00:00:00.000000000Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
|
-
name:
|
17
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
+
name: websocket
|
17
|
+
requirement: &70212587581360 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
|
-
- -
|
20
|
+
- - ~>
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 0.
|
22
|
+
version: 1.0.0
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements:
|
26
|
-
none: false
|
27
|
-
requirements:
|
28
|
-
- - '='
|
29
|
-
- !ruby/object:Gem::Version
|
30
|
-
version: 0.1.5
|
25
|
+
version_requirements: *70212587581360
|
31
26
|
- !ruby/object:Gem::Dependency
|
32
27
|
name: ruby-hmac
|
33
|
-
requirement: !ruby/object:Gem::Requirement
|
34
|
-
none: false
|
35
|
-
requirements:
|
36
|
-
- - ~>
|
37
|
-
- !ruby/object:Gem::Version
|
38
|
-
version: 0.4.0
|
39
|
-
type: :runtime
|
40
|
-
prerelease: false
|
41
|
-
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirement: &70212587580880 !ruby/object:Gem::Requirement
|
42
29
|
none: false
|
43
30
|
requirements:
|
44
31
|
- - ~>
|
45
32
|
- !ruby/object:Gem::Version
|
46
33
|
version: 0.4.0
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: addressable
|
49
|
-
requirement: !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
|
-
requirements:
|
52
|
-
- - ~>
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: 2.3.1
|
55
34
|
type: :runtime
|
56
35
|
prerelease: false
|
57
|
-
version_requirements:
|
58
|
-
none: false
|
59
|
-
requirements:
|
60
|
-
- - ~>
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
version: 2.3.1
|
36
|
+
version_requirements: *70212587580880
|
63
37
|
- !ruby/object:Gem::Dependency
|
64
38
|
name: bacon
|
65
|
-
requirement: !ruby/object:Gem::Requirement
|
39
|
+
requirement: &70212587580420 !ruby/object:Gem::Requirement
|
66
40
|
none: false
|
67
41
|
requirements:
|
68
42
|
- - ! '>='
|
@@ -70,31 +44,10 @@ dependencies:
|
|
70
44
|
version: '0'
|
71
45
|
type: :development
|
72
46
|
prerelease: false
|
73
|
-
version_requirements:
|
74
|
-
none: false
|
75
|
-
requirements:
|
76
|
-
- - ! '>='
|
77
|
-
- !ruby/object:Gem::Version
|
78
|
-
version: '0'
|
47
|
+
version_requirements: *70212587580420
|
79
48
|
- !ruby/object:Gem::Dependency
|
80
49
|
name: rake
|
81
|
-
requirement: !ruby/object:Gem::Requirement
|
82
|
-
none: false
|
83
|
-
requirements:
|
84
|
-
- - ! '>='
|
85
|
-
- !ruby/object:Gem::Version
|
86
|
-
version: '0'
|
87
|
-
type: :development
|
88
|
-
prerelease: false
|
89
|
-
version_requirements: !ruby/object:Gem::Requirement
|
90
|
-
none: false
|
91
|
-
requirements:
|
92
|
-
- - ! '>='
|
93
|
-
- !ruby/object:Gem::Version
|
94
|
-
version: '0'
|
95
|
-
- !ruby/object:Gem::Dependency
|
96
|
-
name: ruby-debug19
|
97
|
-
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirement: &70212587579960 !ruby/object:Gem::Requirement
|
98
51
|
none: false
|
99
52
|
requirements:
|
100
53
|
- - ! '>='
|
@@ -102,14 +55,10 @@ dependencies:
|
|
102
55
|
version: '0'
|
103
56
|
type: :development
|
104
57
|
prerelease: false
|
105
|
-
version_requirements:
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
111
|
-
description: Ruby client for consuming WebSockets from http://pusher.com
|
112
|
-
email: support@pusher.com
|
58
|
+
version_requirements: *70212587579960
|
59
|
+
description: Client for consuming WebSockets from http://pusher.com
|
60
|
+
email:
|
61
|
+
- support@pusher.com
|
113
62
|
executables: []
|
114
63
|
extensions: []
|
115
64
|
extra_rdoc_files:
|
@@ -117,19 +66,21 @@ extra_rdoc_files:
|
|
117
66
|
- README.rdoc
|
118
67
|
files:
|
119
68
|
- .document
|
69
|
+
- .gitignore
|
120
70
|
- Gemfile
|
121
|
-
- Gemfile.lock
|
122
71
|
- LICENSE.txt
|
123
72
|
- README.rdoc
|
124
73
|
- Rakefile
|
74
|
+
- certs/cacert.pem
|
125
75
|
- examples/hello_pusher.rb
|
126
76
|
- examples/hello_pusher_async.rb
|
77
|
+
- examples/hello_pusher_ssl.rb
|
78
|
+
- examples/subscribe_private.rb
|
127
79
|
- lib/pusher-client.rb
|
128
80
|
- lib/pusher-client/channel.rb
|
129
81
|
- lib/pusher-client/channels.rb
|
130
82
|
- lib/pusher-client/socket.rb
|
131
83
|
- lib/pusher-client/websocket.rb
|
132
|
-
- certs/cacert.pem
|
133
84
|
- pusher-client.gemspec
|
134
85
|
- test/pusherclient_test.rb
|
135
86
|
- test/test.watchr
|
@@ -155,12 +106,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
155
106
|
version: '0'
|
156
107
|
requirements: []
|
157
108
|
rubyforge_project:
|
158
|
-
rubygems_version: 1.8.
|
109
|
+
rubygems_version: 1.8.10
|
159
110
|
signing_key:
|
160
111
|
specification_version: 3
|
161
|
-
summary:
|
112
|
+
summary: Client for consuming WebSockets from http://pusher.com
|
162
113
|
test_files:
|
163
|
-
- examples/hello_pusher.rb
|
164
|
-
- examples/hello_pusher_async.rb
|
165
114
|
- test/pusherclient_test.rb
|
115
|
+
- test/test.watchr
|
166
116
|
- test/teststrap.rb
|
data/Gemfile.lock
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
pusher-client (0.2.2)
|
5
|
-
addressable (~> 2.3.1)
|
6
|
-
libwebsocket (= 0.1.5)
|
7
|
-
ruby-hmac (~> 0.4.0)
|
8
|
-
|
9
|
-
GEM
|
10
|
-
remote: http://rubygems.org/
|
11
|
-
specs:
|
12
|
-
addressable (2.3.2)
|
13
|
-
archive-tar-minitar (0.5.2)
|
14
|
-
bacon (1.2.0)
|
15
|
-
columnize (0.3.6)
|
16
|
-
libwebsocket (0.1.5)
|
17
|
-
addressable
|
18
|
-
linecache19 (0.5.12)
|
19
|
-
ruby_core_source (>= 0.1.4)
|
20
|
-
rake (10.0.3)
|
21
|
-
ruby-debug-base19 (0.11.25)
|
22
|
-
columnize (>= 0.3.1)
|
23
|
-
linecache19 (>= 0.5.11)
|
24
|
-
ruby_core_source (>= 0.1.4)
|
25
|
-
ruby-debug19 (0.11.6)
|
26
|
-
columnize (>= 0.3.1)
|
27
|
-
linecache19 (>= 0.5.11)
|
28
|
-
ruby-debug-base19 (>= 0.11.19)
|
29
|
-
ruby-hmac (0.4.0)
|
30
|
-
ruby_core_source (0.1.5)
|
31
|
-
archive-tar-minitar (>= 0.5.2)
|
32
|
-
|
33
|
-
PLATFORMS
|
34
|
-
java
|
35
|
-
ruby
|
36
|
-
|
37
|
-
DEPENDENCIES
|
38
|
-
bacon
|
39
|
-
pusher-client!
|
40
|
-
rake
|
41
|
-
ruby-debug19
|