firehose 0.0.8 → 0.0.9
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/Procfile +1 -1
- data/README.md +17 -21
- data/lib/assets/javascripts/firehose/client.js.coffee +41 -4
- data/lib/assets/javascripts/firehose/long_poll.js.coffee +17 -12
- data/lib/assets/javascripts/firehose/transport.js.coffee +11 -17
- data/lib/assets/javascripts/firehose/web_socket.js.coffee +14 -10
- data/lib/firehose/cli.rb +1 -3
- data/lib/firehose/rack.rb +33 -2
- data/lib/firehose/version.rb +2 -2
- metadata +24 -25
- data/lib/firehose/thin.rb +0 -7
data/Procfile
CHANGED
@@ -1 +1 @@
|
|
1
|
-
firehose: firehose
|
1
|
+
firehose: firehose server
|
data/README.md
CHANGED
@@ -31,7 +31,7 @@ $ gem install firehose
|
|
31
31
|
Now fire up the server.
|
32
32
|
|
33
33
|
```ruby
|
34
|
-
$ firehose
|
34
|
+
$ firehose server
|
35
35
|
>> Thin web server (v1.3.1 codename Triple Espresso)
|
36
36
|
>> Maximum connections set to 1024
|
37
37
|
>> Listening on 127.0.0.1:7478, CTRL+C to stop
|
@@ -72,27 +72,23 @@ Firehose doesn't just stop at curl; it has a full-featured JavaScript client tha
|
|
72
72
|
Still have the server running? Copy and paste the code below into Firebug or the WebKit console.
|
73
73
|
|
74
74
|
```javascript
|
75
|
-
new Firehose.Client(
|
76
|
-
|
77
|
-
websocket: 'ws://localhost:7478/hello',
|
78
|
-
longpoll: 'http://localhost:7478/hello'
|
79
|
-
})
|
80
|
-
.params({
|
81
|
-
cid: '024023948234'
|
82
|
-
})
|
83
|
-
.options({
|
84
|
-
timeout: 5000
|
85
|
-
})
|
86
|
-
.message(function(msg){
|
75
|
+
new Firehose.Client({
|
76
|
+
message: function(msg){
|
87
77
|
console.log(msg);
|
88
|
-
}
|
89
|
-
|
90
|
-
console.log('
|
91
|
-
}
|
92
|
-
|
93
|
-
console.log('
|
94
|
-
}
|
95
|
-
|
78
|
+
},
|
79
|
+
connected: function(){
|
80
|
+
console.log("Great Scotts!! We're connected!");
|
81
|
+
},
|
82
|
+
disconnected: function(){
|
83
|
+
console.log("Well shucks, we're not connected anymore");
|
84
|
+
},
|
85
|
+
error: function(){
|
86
|
+
console.log("Well then, something went horribly wrong.");
|
87
|
+
},
|
88
|
+
// Note that we do NOT specify a protocol here because we don't
|
89
|
+
// know that yet.
|
90
|
+
uri: '//localhost:7478/hello'
|
91
|
+
}).connect();
|
96
92
|
```
|
97
93
|
|
98
94
|
Then publish another message.
|
@@ -1,8 +1,45 @@
|
|
1
1
|
class Firehose.Client
|
2
|
-
|
3
|
-
|
2
|
+
# Transports that are available to Firehose.
|
3
|
+
@transports: ['WebSocket', 'LongPoll']
|
4
4
|
|
5
|
+
# Generate a random client_id.
|
6
|
+
@nextClientId: ->
|
7
|
+
Math.floor((Math.random()*99999999999)+1)
|
8
|
+
|
9
|
+
constructor: (config={}) ->
|
10
|
+
# The clientId is used by the server to remember messages between requests. In a production environment,
|
11
|
+
# this should probably be some combination of "user_id-rand". Why the rand? Because a user may have multiple
|
12
|
+
# tabs open to the application, and each tab needs a different channel on the server.
|
13
|
+
config.clientId ||= Firehose.Client.nextClientId()
|
14
|
+
# List of transport stragies we have to use.
|
15
|
+
config.transports ||= Firehose.Client.transports
|
16
|
+
# Empty handler for messages.
|
17
|
+
config.message ||= ->
|
18
|
+
# Empty handler for error handling.
|
19
|
+
config.error ||= ->
|
20
|
+
# Empty handler for when we establish a connection.
|
21
|
+
config.connected ||= ->
|
22
|
+
# Empty handler for when we're disconnected.
|
23
|
+
config.disconnected ||= ->
|
24
|
+
# Additional options.
|
25
|
+
config.options ||= {}
|
26
|
+
# URL that we'll connect to.
|
27
|
+
config.uri ||= undefined
|
28
|
+
# Params that we'll tack on to the URL. We include a clientId in here for kicks.
|
29
|
+
config.params ||= { cid: config.clientId }
|
30
|
+
# Do stuff before we send the message into config.message. The sensible
|
31
|
+
# default on the webs is to parse JSON.
|
32
|
+
config.parse ||= (body) ->
|
33
|
+
$.parseJSON(body)
|
34
|
+
|
35
|
+
# Hang on to these config for when we connect.
|
36
|
+
@config = config
|
37
|
+
# Make sure we return ourself out of the constructor so we can chain.
|
38
|
+
this
|
39
|
+
|
40
|
+
connect: =>
|
5
41
|
# Figure out what transport is supported and return it.
|
6
|
-
|
42
|
+
# TODO if the initial connection fails, try the next transport mmmkay?
|
43
|
+
for transport in @config.transports
|
7
44
|
if transport = Firehose[transport]
|
8
|
-
return new transport(
|
45
|
+
return new transport(@config).connect() if transport.supported()
|
@@ -5,22 +5,28 @@ class Firehose.LongPoll extends Firehose.Transport
|
|
5
5
|
|
6
6
|
@supported: =>
|
7
7
|
# IE 8+, FF 3.5+, Chrome 4+, Safari 4+, Opera 12+, iOS 3.2+, Android 2.1+
|
8
|
-
$.support.cors || LongPoll.ieSupported()
|
8
|
+
$.support.cors || Firehose.LongPoll.ieSupported()
|
9
9
|
|
10
10
|
constructor: (args) ->
|
11
11
|
super args
|
12
12
|
|
13
|
+
# Configrations specifically for web sockets
|
14
|
+
@config.longPoll ||= {}
|
15
|
+
# Protocol schema we should use for talking to WS server.
|
16
|
+
@config.longPoll.url ||= "http:#{@config.uri}"
|
17
|
+
|
13
18
|
# We use the lag time to make the client live longer than the server.
|
14
19
|
@_lagTime = 5000
|
15
|
-
@_timeout = @options.timeout + @_lagTime
|
16
|
-
@_dataType = "json"
|
20
|
+
@_timeout = @config.options.timeout + @_lagTime
|
17
21
|
@_offlineTimer
|
18
22
|
@_okInterval = 0
|
19
23
|
|
20
24
|
@registerIETransport()
|
21
25
|
|
22
26
|
registerIETransport: =>
|
23
|
-
if LongPoll.ieSupported()
|
27
|
+
if Firehose.LongPoll.ieSupported()
|
28
|
+
# TODO - Ask Steel what this is for. Looks like some kind of polygot fill, but I want
|
29
|
+
# to take the 'json' transport out and do that myself.
|
24
30
|
$.ajaxTransport 'json', (options, orignalOptions, jqXhr) ->
|
25
31
|
xdr = null
|
26
32
|
send: (_, callback) ->
|
@@ -44,15 +50,14 @@ class Firehose.LongPoll extends Firehose.Transport
|
|
44
50
|
$.support.cors = true;
|
45
51
|
|
46
52
|
connect: (delay = 0) =>
|
47
|
-
@
|
53
|
+
@config.connected()
|
48
54
|
super(delay)
|
49
55
|
|
50
56
|
_request: =>
|
51
|
-
$.ajax @url
|
57
|
+
$.ajax @config.longPoll.url,
|
52
58
|
crossDomain: true
|
53
59
|
cache: false
|
54
|
-
|
55
|
-
data: @params
|
60
|
+
data: @config.params
|
56
61
|
timeout: @_timeout
|
57
62
|
success: @_success
|
58
63
|
error: @_error
|
@@ -67,13 +72,13 @@ class Firehose.LongPoll extends Firehose.Transport
|
|
67
72
|
# in thise case
|
68
73
|
@connect(@_okInterval)
|
69
74
|
else
|
70
|
-
@
|
75
|
+
@config.message(@config.parse(data))
|
71
76
|
@connect(@_okInterval)
|
72
77
|
|
73
78
|
# We need this custom handler to have the connection status
|
74
79
|
# properly displayed
|
75
80
|
_error: (jqXhr, status, error) =>
|
76
81
|
clearTimeout(@_offlineTimer)
|
77
|
-
@
|
78
|
-
@_offlineTimer = setTimeout(@
|
79
|
-
@connect(@
|
82
|
+
@config.disconnected()
|
83
|
+
@_offlineTimer = setTimeout(@config.connected, @_retryDelay + @_lagTime)
|
84
|
+
@connect(@_retryDelay)
|
@@ -1,21 +1,15 @@
|
|
1
1
|
class Firehose.Transport
|
2
|
-
# Class method to determine whether transport is supported by the current browser
|
2
|
+
# Class method to determine whether transport is supported by the current browser. Note that while
|
3
|
+
# the transport may be supported by the browser, its possible that the network connection won't
|
4
|
+
# succeed. That should be accounted for during the initial connecting to the server.
|
3
5
|
@supported: =>
|
4
6
|
false
|
5
7
|
|
6
|
-
constructor: (
|
7
|
-
@
|
8
|
+
constructor: (config={}) ->
|
9
|
+
@config = config
|
10
|
+
@_retryDelay = 5000
|
8
11
|
|
9
|
-
#
|
10
|
-
connected: (@onConnected) -> this
|
11
|
-
disconnected: (@onDisconnected) -> this
|
12
|
-
message: (@onMessage) -> this
|
13
|
-
error: (@onError) -> this
|
14
|
-
url: (@url) -> this
|
15
|
-
params: (@params) -> this
|
16
|
-
options: (@options) -> this
|
17
|
-
|
18
|
-
# Lets rock'n'roll
|
12
|
+
# Lets rock'n'roll! Connect to the server.
|
19
13
|
connect: (delay = 0) =>
|
20
14
|
setTimeout =>
|
21
15
|
@_request()
|
@@ -28,13 +22,13 @@ class Firehose.Transport
|
|
28
22
|
# Default error handler
|
29
23
|
_error: (event) =>
|
30
24
|
# Lets try to connect again with delay
|
31
|
-
@
|
32
|
-
@connect(@
|
25
|
+
@config.disconnected()
|
26
|
+
@connect(@_retryDelay)
|
33
27
|
|
34
28
|
# Default connection established handler
|
35
29
|
_open: (event) =>
|
36
|
-
@
|
30
|
+
@config.connected()
|
37
31
|
|
38
32
|
# Default connection closed handler
|
39
33
|
_close: (event) =>
|
40
|
-
@
|
34
|
+
@config.disconnected()
|
@@ -10,24 +10,28 @@ class Firehose.WebSocket extends Firehose.Transport
|
|
10
10
|
constructor: (args) ->
|
11
11
|
super args
|
12
12
|
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
# Configrations specifically for web sockets
|
14
|
+
@config.webSocket ||= {}
|
15
|
+
# Protocol schema we should use for talking to WS server.
|
16
|
+
@config.webSocket.url ||= "ws:#{@config.uri}?#{$.param(@config.params)}"
|
17
|
+
# Path of the swf WebSocket that we use in non-WS flash browsers.
|
18
|
+
@config.webSocket.swf_path ||= "/flash/firehose/WebSocketMain.swf"
|
19
|
+
|
20
|
+
# Set flash socket path for the WS SWF polyfill.
|
21
|
+
WebSocket.__swfLocation = @config.webSocket.swf_path
|
22
|
+
|
23
|
+
# Mozilla decided to have their own implementation of Web Sockets so detect for that.
|
17
24
|
window.WebSocket = window.MozWebSocket if window["MozWebSocket"] and window.MozWebSocket
|
18
25
|
|
19
26
|
_request: =>
|
20
|
-
@socket = new window.WebSocket(@url
|
27
|
+
@socket = new window.WebSocket(@config.webSocket.url)
|
21
28
|
@socket.onopen = @_open
|
22
29
|
@socket.onclose = @_close
|
23
30
|
@socket.onerror = @_error
|
24
31
|
@socket.onmessage = @_message
|
25
32
|
|
26
33
|
_message: (event) =>
|
27
|
-
|
28
|
-
@onMessage($.parseJSON(event.data))
|
29
|
-
catch e # If JSON parsing doesn't work, send the rest of it on through
|
30
|
-
@onMessage(event.data)
|
34
|
+
@config.message(@config.parse(event.data))
|
31
35
|
|
32
36
|
_close: (event) =>
|
33
37
|
if !event || (event and !event.wasClean)
|
@@ -44,5 +48,5 @@ class Firehose.WebSocket extends Firehose.Transport
|
|
44
48
|
@socket.onmessage = null
|
45
49
|
@socket.close()
|
46
50
|
delete(@socket)
|
47
|
-
|
51
|
+
|
48
52
|
super
|
data/lib/firehose/cli.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'thor'
|
2
|
+
require 'thin'
|
2
3
|
|
3
4
|
module Firehose
|
4
5
|
class CLI < Thor
|
@@ -6,12 +7,9 @@ module Firehose
|
|
6
7
|
method_option :port, :type => :numeric, :default => 7474, :required => true, :aliases => '-p'
|
7
8
|
method_option :host, :type => :string, :default => '0.0.0.0', :required => true, :aliases => '-h'
|
8
9
|
def server
|
9
|
-
require 'thin'
|
10
|
-
|
11
10
|
server = Thin::Server.new(options[:host], options[:port]) do
|
12
11
|
run Firehose::Rack::App.new
|
13
12
|
end
|
14
|
-
|
15
13
|
server.start!
|
16
14
|
end
|
17
15
|
end
|
data/lib/firehose/rack.rb
CHANGED
@@ -10,19 +10,50 @@ module Firehose
|
|
10
10
|
cid = req.params['cid']
|
11
11
|
path = req.path
|
12
12
|
method = req.request_method
|
13
|
+
timeout = 30
|
14
|
+
cors_headers = {
|
15
|
+
'Access-Control-Allow-Origin' => env['HTTP_ORIGIN'],
|
16
|
+
'Access-Control-Allow-Methods' => 'GET',
|
17
|
+
'Access-Control-Max-Age' => '1728000'
|
18
|
+
}
|
13
19
|
|
14
20
|
case method
|
15
21
|
# GET is how clients subscribe to the queue. When a messages comes in, we flush out a response,
|
16
22
|
# close down the requeust, and the client then reconnects.
|
17
23
|
when 'GET'
|
24
|
+
p [:subscribed, cid, path]
|
25
|
+
|
18
26
|
EM.next_tick do
|
27
|
+
# Setup a subscription with a client id. We haven't subscribed yet here.
|
19
28
|
subscription = Firehose::Subscription.new(cid)
|
29
|
+
|
30
|
+
# Setup a timeout timer to tell clients that time out that everything is OK
|
31
|
+
# and they should come back for more
|
32
|
+
timer = EM.add_timer(timeout) do
|
33
|
+
# We send a 204 OK to tell the client to reconnect.
|
34
|
+
env['async.callback'].call [204, cors_headers, []]
|
35
|
+
p [:timeout]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Ok, now subscribe to the subscription.
|
20
39
|
subscription.subscribe path do |payload|
|
21
40
|
subscription.unsubscribe
|
22
|
-
|
41
|
+
subscription = nil # Set this to nil so that our heart beat timer doesn't try to double unsub.
|
42
|
+
EM.cancel_timer timer # Turn off the heart beat so we don't execute any of that business.
|
43
|
+
env['async.callback'].call [200, cors_headers, [payload]]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Unsubscribe from the subscription if its still open and something bad happened
|
47
|
+
# or the heart beat triggered before we could finish.
|
48
|
+
env['async.close'].callback do
|
49
|
+
if subscription
|
50
|
+
subscription.unsubscribe
|
51
|
+
p [:close_unsubscription]
|
52
|
+
end
|
23
53
|
end
|
24
54
|
end
|
25
55
|
|
56
|
+
# Tell the web server that this will be an async response.
|
26
57
|
Firehose::Rack::AsyncResponse
|
27
58
|
|
28
59
|
# PUT is how we throw messages on to the fan-out queue.
|
@@ -33,7 +64,7 @@ module Firehose
|
|
33
64
|
|
34
65
|
[202, {}, []]
|
35
66
|
else
|
36
|
-
[501, {}, ["#{method} not supported."]]
|
67
|
+
[501, {'Content-Type' => 'text/plain'}, ["#{method} not supported."]]
|
37
68
|
end
|
38
69
|
end
|
39
70
|
end
|
data/lib/firehose/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: firehose
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-04-
|
13
|
+
date: 2012-04-27 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: eventmachine
|
17
|
-
requirement: &
|
17
|
+
requirement: &70248633218840 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: 1.0.0.beta
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *70248633218840
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: amqp
|
28
|
-
requirement: &
|
28
|
+
requirement: &70248633201380 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ! '>='
|
@@ -33,10 +33,10 @@ dependencies:
|
|
33
33
|
version: 0.9.4
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *70248633201380
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: thin
|
39
|
-
requirement: &
|
39
|
+
requirement: &70248633200480 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
42
|
- - ! '>='
|
@@ -44,10 +44,10 @@ dependencies:
|
|
44
44
|
version: '0'
|
45
45
|
type: :runtime
|
46
46
|
prerelease: false
|
47
|
-
version_requirements: *
|
47
|
+
version_requirements: *70248633200480
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: thor
|
50
|
-
requirement: &
|
50
|
+
requirement: &70248633199280 !ruby/object:Gem::Requirement
|
51
51
|
none: false
|
52
52
|
requirements:
|
53
53
|
- - ! '>='
|
@@ -55,10 +55,10 @@ dependencies:
|
|
55
55
|
version: '0'
|
56
56
|
type: :runtime
|
57
57
|
prerelease: false
|
58
|
-
version_requirements: *
|
58
|
+
version_requirements: *70248633199280
|
59
59
|
- !ruby/object:Gem::Dependency
|
60
60
|
name: websocket-rack
|
61
|
-
requirement: &
|
61
|
+
requirement: &70248633198520 !ruby/object:Gem::Requirement
|
62
62
|
none: false
|
63
63
|
requirements:
|
64
64
|
- - ! '>='
|
@@ -66,10 +66,10 @@ dependencies:
|
|
66
66
|
version: '0'
|
67
67
|
type: :runtime
|
68
68
|
prerelease: false
|
69
|
-
version_requirements: *
|
69
|
+
version_requirements: *70248633198520
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: rspec
|
72
|
-
requirement: &
|
72
|
+
requirement: &70248633197580 !ruby/object:Gem::Requirement
|
73
73
|
none: false
|
74
74
|
requirements:
|
75
75
|
- - ! '>='
|
@@ -77,10 +77,10 @@ dependencies:
|
|
77
77
|
version: '0'
|
78
78
|
type: :development
|
79
79
|
prerelease: false
|
80
|
-
version_requirements: *
|
80
|
+
version_requirements: *70248633197580
|
81
81
|
- !ruby/object:Gem::Dependency
|
82
82
|
name: rack-test
|
83
|
-
requirement: &
|
83
|
+
requirement: &70248633196960 !ruby/object:Gem::Requirement
|
84
84
|
none: false
|
85
85
|
requirements:
|
86
86
|
- - ! '>='
|
@@ -88,10 +88,10 @@ dependencies:
|
|
88
88
|
version: '0'
|
89
89
|
type: :development
|
90
90
|
prerelease: false
|
91
|
-
version_requirements: *
|
91
|
+
version_requirements: *70248633196960
|
92
92
|
- !ruby/object:Gem::Dependency
|
93
93
|
name: guard-rspec
|
94
|
-
requirement: &
|
94
|
+
requirement: &70248633195800 !ruby/object:Gem::Requirement
|
95
95
|
none: false
|
96
96
|
requirements:
|
97
97
|
- - ! '>='
|
@@ -99,10 +99,10 @@ dependencies:
|
|
99
99
|
version: '0'
|
100
100
|
type: :development
|
101
101
|
prerelease: false
|
102
|
-
version_requirements: *
|
102
|
+
version_requirements: *70248633195800
|
103
103
|
- !ruby/object:Gem::Dependency
|
104
104
|
name: guard-bundler
|
105
|
-
requirement: &
|
105
|
+
requirement: &70248633195200 !ruby/object:Gem::Requirement
|
106
106
|
none: false
|
107
107
|
requirements:
|
108
108
|
- - ! '>='
|
@@ -110,10 +110,10 @@ dependencies:
|
|
110
110
|
version: '0'
|
111
111
|
type: :development
|
112
112
|
prerelease: false
|
113
|
-
version_requirements: *
|
113
|
+
version_requirements: *70248633195200
|
114
114
|
- !ruby/object:Gem::Dependency
|
115
115
|
name: em-http-request
|
116
|
-
requirement: &
|
116
|
+
requirement: &70248633178620 !ruby/object:Gem::Requirement
|
117
117
|
none: false
|
118
118
|
requirements:
|
119
119
|
- - ~>
|
@@ -121,10 +121,10 @@ dependencies:
|
|
121
121
|
version: 0.3.0
|
122
122
|
type: :development
|
123
123
|
prerelease: false
|
124
|
-
version_requirements: *
|
124
|
+
version_requirements: *70248633178620
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: guard-coffeescript
|
127
|
-
requirement: &
|
127
|
+
requirement: &70248633177760 !ruby/object:Gem::Requirement
|
128
128
|
none: false
|
129
129
|
requirements:
|
130
130
|
- - ! '>='
|
@@ -132,7 +132,7 @@ dependencies:
|
|
132
132
|
version: '0'
|
133
133
|
type: :development
|
134
134
|
prerelease: false
|
135
|
-
version_requirements: *
|
135
|
+
version_requirements: *70248633177760
|
136
136
|
description: Firehose is a realtime web application toolkit for building realtime
|
137
137
|
Ruby web applications.
|
138
138
|
email:
|
@@ -166,7 +166,6 @@ files:
|
|
166
166
|
- lib/firehose/publisher.rb
|
167
167
|
- lib/firehose/rack.rb
|
168
168
|
- lib/firehose/subscription.rb
|
169
|
-
- lib/firehose/thin.rb
|
170
169
|
- lib/firehose/version.rb
|
171
170
|
- spec/integrations/amqp_resources_spec.rb
|
172
171
|
- spec/integrations/thin_spec.rb
|