startback-websocket 0.14.0 → 0.14.1
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.
- checksums.yaml +4 -4
- data/Gemfile +3 -2
- data/README.md +64 -9
- data/lib/startback/ext/context.rb +6 -0
- data/lib/startback/ext.rb +1 -2
- data/lib/startback/websocket/app.rb +82 -0
- data/lib/startback/websocket/hub/app.rb +28 -0
- data/lib/startback/websocket/hub/builder.rb +55 -0
- data/lib/startback/websocket/hub/errors.rb +9 -0
- data/lib/startback/websocket/hub/message.rb +29 -0
- data/lib/startback/websocket/hub/middleware/command_handler.rb +34 -0
- data/lib/startback/websocket/hub/middleware/room_handler.rb +30 -0
- data/lib/startback/websocket/hub/middleware.rb +12 -0
- data/lib/startback/websocket/hub/participant.rb +16 -0
- data/lib/startback/websocket/hub/room.rb +46 -0
- data/lib/startback/websocket/hub.rb +15 -0
- data/lib/startback/websocket.rb +8 -0
- data/spec/spec_helper.rb +21 -32
- data/spec/unit/hub/test_builder.rb +141 -0
- data/spec/unit/hub/test_room.rb +27 -0
- data/spec/unit/test_app.rb +35 -0
- data/tasks/test.rake +0 -1
- metadata +20 -91
- data/lib/startback/audit/prometheus.rb +0 -87
- data/lib/startback/audit/shared.rb +0 -17
- data/lib/startback/audit/trailer.rb +0 -129
- data/lib/startback/audit.rb +0 -3
- data/lib/startback/caching/entity_cache.rb +0 -157
- data/lib/startback/caching/no_store.rb +0 -28
- data/lib/startback/caching/store.rb +0 -34
- data/lib/startback/context/h_factory.rb +0 -43
- data/lib/startback/context/middleware.rb +0 -53
- data/lib/startback/context.rb +0 -122
- data/lib/startback/errors.rb +0 -197
- data/lib/startback/event/agent.rb +0 -84
- data/lib/startback/event/bus/bunny/async.rb +0 -162
- data/lib/startback/event/bus/bunny.rb +0 -1
- data/lib/startback/event/bus/memory/async.rb +0 -45
- data/lib/startback/event/bus/memory/sync.rb +0 -35
- data/lib/startback/event/bus/memory.rb +0 -2
- data/lib/startback/event/bus.rb +0 -100
- data/lib/startback/event/engine.rb +0 -94
- data/lib/startback/event/ext/context.rb +0 -5
- data/lib/startback/event/ext/operation.rb +0 -13
- data/lib/startback/event.rb +0 -47
- data/lib/startback/ext/date_time.rb +0 -9
- data/lib/startback/ext/time.rb +0 -9
- data/lib/startback/model.rb +0 -6
- data/lib/startback/operation/error_operation.rb +0 -19
- data/lib/startback/operation/multi_operation.rb +0 -28
- data/lib/startback/operation.rb +0 -78
- data/lib/startback/services.rb +0 -11
- data/lib/startback/support/data_object.rb +0 -71
- data/lib/startback/support/env.rb +0 -41
- data/lib/startback/support/fake_logger.rb +0 -18
- data/lib/startback/support/hooks.rb +0 -48
- data/lib/startback/support/log_formatter.rb +0 -34
- data/lib/startback/support/logger.rb +0 -34
- data/lib/startback/support/operation_runner.rb +0 -150
- data/lib/startback/support/robustness.rb +0 -157
- data/lib/startback/support/transaction_manager.rb +0 -25
- data/lib/startback/support/transaction_policy.rb +0 -33
- data/lib/startback/support/world.rb +0 -54
- data/lib/startback/support.rb +0 -26
- data/lib/startback/version.rb +0 -8
- data/lib/startback/web/api.rb +0 -99
- data/lib/startback/web/auto_caching.rb +0 -85
- data/lib/startback/web/catch_all.rb +0 -52
- data/lib/startback/web/cors_headers.rb +0 -80
- data/lib/startback/web/health_check.rb +0 -49
- data/lib/startback/web/magic_assets/ng_html_transformer.rb +0 -80
- data/lib/startback/web/magic_assets/rake_tasks.rb +0 -64
- data/lib/startback/web/magic_assets.rb +0 -98
- data/lib/startback/web/middleware.rb +0 -13
- data/lib/startback/web/prometheus.rb +0 -16
- data/lib/startback/web/shield.rb +0 -58
- data/lib/startback.rb +0 -43
- data/spec/unit/audit/test_prometheus.rb +0 -72
- data/spec/unit/audit/test_trailer.rb +0 -105
- data/spec/unit/caching/test_entity_cache.rb +0 -136
- data/spec/unit/context/test_abstraction_factory.rb +0 -64
- data/spec/unit/context/test_dup.rb +0 -42
- data/spec/unit/context/test_fork.rb +0 -37
- data/spec/unit/context/test_h_factory.rb +0 -31
- data/spec/unit/context/test_middleware.rb +0 -45
- data/spec/unit/context/test_with_world.rb +0 -20
- data/spec/unit/context/test_world.rb +0 -17
- data/spec/unit/event/bus/memory/test_async.rb +0 -43
- data/spec/unit/event/bus/memory/test_sync.rb +0 -43
- data/spec/unit/support/hooks/test_after_hook.rb +0 -54
- data/spec/unit/support/hooks/test_before_hook.rb +0 -54
- data/spec/unit/support/operation_runner/test_around_run.rb +0 -156
- data/spec/unit/support/operation_runner/test_before_after_call.rb +0 -48
- data/spec/unit/support/test_data_object.rb +0 -156
- data/spec/unit/support/test_env.rb +0 -75
- data/spec/unit/support/test_robusteness.rb +0 -229
- data/spec/unit/support/test_transaction_manager.rb +0 -64
- data/spec/unit/support/test_world.rb +0 -72
- data/spec/unit/test_event.rb +0 -62
- data/spec/unit/test_operation.rb +0 -55
- data/spec/unit/test_support.rb +0 -40
- data/spec/unit/web/fixtures/assets/app/hello.es6 +0 -4
- data/spec/unit/web/fixtures/assets/app/hello.html +0 -1
- data/spec/unit/web/fixtures/assets/index.es6 +0 -1
- data/spec/unit/web/test_api.rb +0 -82
- data/spec/unit/web/test_auto_caching.rb +0 -81
- data/spec/unit/web/test_catch_all.rb +0 -77
- data/spec/unit/web/test_cors_headers.rb +0 -88
- data/spec/unit/web/test_healthcheck.rb +0 -59
- data/spec/unit/web/test_magic_assets.rb +0 -82
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6d94ab898d70e35e0d5b188c459af5ab40070ae0e5e7a1d23bbbfc6aad61694f
|
|
4
|
+
data.tar.gz: 333ed1fbf65609758827015d725dbbed14224249141e979204b7203fa3d75405
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 687b5855a911d1d434bbfd6c9569d26a10b9615d7b4c06f10671d5fa645c4b62b053c9c3beacb49d252ebaa595144d9192b8e165ff73f2e4733bc56c677bb99b
|
|
7
|
+
data.tar.gz: 9852ab507c3a9ad302bd7c323914ff248452e39d30a8bd4408e8f7c6407bf820aaafaa2abc7f7a20ec321ff80217a02dbf884bf82ada6be6802af8c505653a9d
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -1,13 +1,68 @@
|
|
|
1
|
-
|
|
1
|
+
## Hub
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The `Hub:App` provides an opinionated protocol that eases
|
|
4
|
+
the handling of real-time, websocket-based, applications.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
2. the operations layer, in charge of the high-level software operations
|
|
7
|
-
3. the database layer, abstracted using the Relations As First Class Citizen pattern
|
|
6
|
+
### Protocol
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
The protocol is mainly based on JSON-serialized messages containing both a `headers` map and a `body` property.
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
#### Rooms
|
|
11
|
+
|
|
12
|
+
Rooms provide an abstraction allowing messages to be broadcasted to a set of `participants`.
|
|
13
|
+
|
|
14
|
+
Messages are sent/received to/from rooms by providing the special `'room' header`, eg:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"header": {
|
|
19
|
+
"room": "a-room-name"
|
|
20
|
+
},
|
|
21
|
+
"body": {} || "" // can be anything
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
#### Commands
|
|
26
|
+
|
|
27
|
+
Commands provide an abstraction allowing the execution of remote processing that can provide a result.
|
|
28
|
+
|
|
29
|
+
Commands are executed by providing the special `'command‘ header` and an additional `'reply-to' header`, eg:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"header": {
|
|
34
|
+
"command": "a-command",
|
|
35
|
+
"reply-to": "cc3ce43a-a494-4c38-bb01-654223ee24a5" // uuid v4
|
|
36
|
+
},
|
|
37
|
+
"body": {} || "" // can be anything
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The protocol contract between the Hub-servers and Hub-clients is that the result of a command execution can be sent back to the client by sending a message re-using the uuid provided in the `reply-to header` in the following way:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"header": {
|
|
46
|
+
// same as the reply-to value of the original message
|
|
47
|
+
"in-reply-to": "cc3ce43a-a494-4c38-bb01-654223ee24a5"
|
|
48
|
+
},
|
|
49
|
+
"body": {} || "" // the result of the command run
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
#### Combination
|
|
54
|
+
|
|
55
|
+
The several aspects of the protocol can be combined.
|
|
56
|
+
|
|
57
|
+
For instance a command can be executed in a specific room:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"header": {
|
|
62
|
+
"command": "a-command",
|
|
63
|
+
"room": "a-certain-room",
|
|
64
|
+
"reply-to": "323b7293-91f4-49ab-b374-006087b56e07"
|
|
65
|
+
},
|
|
66
|
+
"body": {} || "" // can be anything
|
|
67
|
+
}
|
|
68
|
+
```
|
data/lib/startback/ext.rb
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
require_relative 'ext/
|
|
2
|
-
require_relative 'ext/time'
|
|
1
|
+
require_relative 'ext/context'
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
|
|
2
|
+
module Startback
|
|
3
|
+
module Websocket
|
|
4
|
+
#
|
|
5
|
+
# Can be used to easily implement a custom websocket protocol inside a Startback
|
|
6
|
+
# application.
|
|
7
|
+
#
|
|
8
|
+
# Please note that this rack app is not 100% Rack compliant, since it raises
|
|
9
|
+
# any error that the block itself raises. This class aims at being backed up
|
|
10
|
+
# by a Shield and/or CatchAll middleware.
|
|
11
|
+
#
|
|
12
|
+
class App
|
|
13
|
+
|
|
14
|
+
JS_CLIENT = Path.dir/'../../../dist/client.js'
|
|
15
|
+
|
|
16
|
+
def initialize(context)
|
|
17
|
+
@context = context
|
|
18
|
+
@context.websocket_app = self
|
|
19
|
+
@connections = []
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def call(env)
|
|
23
|
+
request = Rack::Request.new(env)
|
|
24
|
+
if Faye::WebSocket.websocket?(env)
|
|
25
|
+
ws = factor_and_keep(env)
|
|
26
|
+
ws.rack_response
|
|
27
|
+
elsif request.path_info === '/client.js'
|
|
28
|
+
[200, { 'Content-Type' => 'application/javascript' }, JS_CLIENT.readlines]
|
|
29
|
+
else
|
|
30
|
+
[400, { 'Content-Type' => 'text/plain' }, ['Websocket only!']]
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def broadcast(data)
|
|
35
|
+
data = data.to_json unless data.is_a?(String)
|
|
36
|
+
|
|
37
|
+
@connections.each do |socket|
|
|
38
|
+
socket.send(data)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def on_open(event, ws, env)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def on_close(event, ws, env)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def on_error(event, ws, env)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def on_message(event, ws, env)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def factor_and_keep(env)
|
|
57
|
+
ws = Faye::WebSocket.new(env)
|
|
58
|
+
|
|
59
|
+
ws.on :open do |event|
|
|
60
|
+
@connections << ws
|
|
61
|
+
on_open(event, ws, env)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
ws.on :close do |event|
|
|
65
|
+
@connections.delete(ws)
|
|
66
|
+
on_close(event, ws, env)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
ws.on :message do |event|
|
|
70
|
+
on_message(event, ws, env)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
ws.on :error do |event|
|
|
74
|
+
on_error(event, ws, env)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
ws
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
end # class App
|
|
81
|
+
end # module Websocket
|
|
82
|
+
end # module Startback
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
module Startback
|
|
3
|
+
module Websocket
|
|
4
|
+
module Hub
|
|
5
|
+
#
|
|
6
|
+
# The Hub is a very opinionated websocket protocol based on room and
|
|
7
|
+
# subscriptions, allowing users to subscribe to rooms etc etc... # TODO
|
|
8
|
+
#
|
|
9
|
+
class App < Websocket::App
|
|
10
|
+
|
|
11
|
+
def initialize(context, rooms, handler)
|
|
12
|
+
super(context)
|
|
13
|
+
@handler = handler
|
|
14
|
+
@rooms = rooms
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def room(name)
|
|
18
|
+
@rooms[name]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def on_message(event, ws, env)
|
|
22
|
+
@handler.call(Message.new(event.data, ws), ws, env)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end # class App
|
|
26
|
+
end # module Hub
|
|
27
|
+
end # module Websocket
|
|
28
|
+
end # module Startback
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
|
|
2
|
+
module Startback
|
|
3
|
+
module Websocket
|
|
4
|
+
module Hub
|
|
5
|
+
class Builder
|
|
6
|
+
|
|
7
|
+
def initialize(context, default_handler = nil, *bl_args, &bl)
|
|
8
|
+
@context = context
|
|
9
|
+
@default_handler = default_handler
|
|
10
|
+
@middlewares = []
|
|
11
|
+
@commands = {}
|
|
12
|
+
@rooms = {}
|
|
13
|
+
@bl_args
|
|
14
|
+
instance_exec *bl_args, &bl
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def use(middleware, *opts)
|
|
18
|
+
@middlewares << proc { |app| middleware.new(app, *opts) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def command(name, &bl)
|
|
22
|
+
cname = name.to_sym
|
|
23
|
+
raise "Command #{name} already defined: #{name}" if @commands[cname]
|
|
24
|
+
@commands[cname] = proc { |app| Middleware::CommandHandler.new(app, { :name => name }, &bl) }
|
|
25
|
+
@middlewares << @commands[cname]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def room(name, &bl)
|
|
29
|
+
raise "Room names must be strings" unless name.is_a? String
|
|
30
|
+
raise "Room '#{name}' already defined" if @rooms[name]
|
|
31
|
+
|
|
32
|
+
@rooms[name] ||= Room.new(name)
|
|
33
|
+
handler = Builder.new(@context, nil, @rooms[name], &bl).to_handler
|
|
34
|
+
middleware = proc { |app| Middleware::RoomHandler.new(app, @rooms[name], handler) }
|
|
35
|
+
|
|
36
|
+
@middlewares << middleware
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def to_handler
|
|
40
|
+
default_handler = @default_handler || proc {}
|
|
41
|
+
@middlewares
|
|
42
|
+
.reverse
|
|
43
|
+
.reduce(default_handler) do |handler, mw|
|
|
44
|
+
mw.call(handler)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def to_websocket_app
|
|
49
|
+
App.new(@context, @rooms, to_handler)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end # class Builder
|
|
53
|
+
end # module Hub
|
|
54
|
+
end # module Websocket
|
|
55
|
+
end # module Startback
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
|
|
2
|
+
module Startback
|
|
3
|
+
module Websocket
|
|
4
|
+
module Hub
|
|
5
|
+
class Message
|
|
6
|
+
|
|
7
|
+
def initialize(data, socket)
|
|
8
|
+
data = JSON.parse(data, symbolize_names: true)
|
|
9
|
+
@headers = data[:headers] || {}
|
|
10
|
+
@body = data[:body] || {}
|
|
11
|
+
@socket = socket
|
|
12
|
+
end
|
|
13
|
+
attr_reader :headers, :body, :socket
|
|
14
|
+
|
|
15
|
+
def reply(message)
|
|
16
|
+
raise "No reply-to header found" unless headers[:'reply-to']
|
|
17
|
+
response = {
|
|
18
|
+
headers: {
|
|
19
|
+
:'in-reply-to' => headers[:'reply-to']
|
|
20
|
+
},
|
|
21
|
+
body: message
|
|
22
|
+
}
|
|
23
|
+
@socket.send(response.to_json)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end # class Message
|
|
27
|
+
end # module Hub
|
|
28
|
+
end # module Websocket
|
|
29
|
+
end # module Startback
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
|
|
2
|
+
module Startback
|
|
3
|
+
module Websocket
|
|
4
|
+
module Hub
|
|
5
|
+
module Middleware
|
|
6
|
+
class CommandHandler
|
|
7
|
+
|
|
8
|
+
def initialize(app, opts, &bl)
|
|
9
|
+
@app = app
|
|
10
|
+
@opts = opts
|
|
11
|
+
@handler = bl
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call(event, socket, env)
|
|
15
|
+
who = matches?(event) ? @handler : @app
|
|
16
|
+
who.call(event, socket, env)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def matches?(event)
|
|
22
|
+
if event.headers[:command]
|
|
23
|
+
event.headers[:command]&.to_sym === @opts[:name]
|
|
24
|
+
else
|
|
25
|
+
false
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end # class CommandHandler
|
|
30
|
+
end # module Middleware
|
|
31
|
+
end # module Hub
|
|
32
|
+
end # module Websocket
|
|
33
|
+
end # module Startback
|
|
34
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
module Startback
|
|
3
|
+
module Websocket
|
|
4
|
+
module Hub
|
|
5
|
+
module Middleware
|
|
6
|
+
class RoomHandler
|
|
7
|
+
|
|
8
|
+
def initialize(app, room, handler)
|
|
9
|
+
@app = app
|
|
10
|
+
@room = room
|
|
11
|
+
@handler = handler
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call(event, socket, env)
|
|
15
|
+
who = matches?(event) ? @handler : @app
|
|
16
|
+
who.call(event, socket, env)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def matches?(event)
|
|
22
|
+
event.headers[:room] === @room.name
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end # class RoomHandler
|
|
26
|
+
end # module Middleware
|
|
27
|
+
end # module Hub
|
|
28
|
+
end # module Websocket
|
|
29
|
+
end # module Startback
|
|
30
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
module Startback
|
|
3
|
+
module Websocket
|
|
4
|
+
module Hub
|
|
5
|
+
module Middleware
|
|
6
|
+
end # module Middleware
|
|
7
|
+
end # module Hub
|
|
8
|
+
end # module Websocket
|
|
9
|
+
end # module Startback
|
|
10
|
+
|
|
11
|
+
require_relative "middleware/command_handler"
|
|
12
|
+
require_relative "middleware/room_handler"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Startback
|
|
2
|
+
module Websocket
|
|
3
|
+
module Hub
|
|
4
|
+
class Participant
|
|
5
|
+
|
|
6
|
+
def initialize(socket, context, metadata={})
|
|
7
|
+
@socket = socket
|
|
8
|
+
@context = context
|
|
9
|
+
@metadata = metadata
|
|
10
|
+
end
|
|
11
|
+
attr_reader :socket, :context, :metadata
|
|
12
|
+
|
|
13
|
+
end # class Participant
|
|
14
|
+
end # module Hub
|
|
15
|
+
end # module Websocket
|
|
16
|
+
end # module Startback
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
|
|
2
|
+
module Startback
|
|
3
|
+
module Websocket
|
|
4
|
+
module Hub
|
|
5
|
+
class Room < App
|
|
6
|
+
|
|
7
|
+
def initialize(name)
|
|
8
|
+
@name = name
|
|
9
|
+
@participants = []
|
|
10
|
+
end
|
|
11
|
+
attr_reader :name, :participants
|
|
12
|
+
|
|
13
|
+
def add(participant)
|
|
14
|
+
raise "Participant instance expected" unless participant.is_a? Participant
|
|
15
|
+
@participants << participant
|
|
16
|
+
participant.socket.on :close do |event|
|
|
17
|
+
remove(participant)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def remove(participant)
|
|
22
|
+
raise "Participant instance expected" unless participant.is_a? Participant
|
|
23
|
+
@participants.delete participant
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def include?(participant)
|
|
27
|
+
raise "Participant instance expected" unless participant.is_a? Participant
|
|
28
|
+
@participants.include? participant
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def broadcast(message)
|
|
32
|
+
puts "Broadcasting to #{@participants.size} participants"
|
|
33
|
+
@participants.each do |p|
|
|
34
|
+
p.socket.send({
|
|
35
|
+
headers: {
|
|
36
|
+
room: @name,
|
|
37
|
+
},
|
|
38
|
+
body: message
|
|
39
|
+
}.to_json)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end # class Room
|
|
44
|
+
end # module Hub
|
|
45
|
+
end # module Websocket
|
|
46
|
+
end # module Startback
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
module Startback
|
|
3
|
+
module Websocket
|
|
4
|
+
module Hub
|
|
5
|
+
end # module Hub
|
|
6
|
+
end # module Websocket
|
|
7
|
+
end # module Startback
|
|
8
|
+
|
|
9
|
+
require_relative "hub/errors"
|
|
10
|
+
require_relative "hub/message"
|
|
11
|
+
require_relative "hub/middleware"
|
|
12
|
+
require_relative "hub/participant"
|
|
13
|
+
require_relative "hub/room"
|
|
14
|
+
require_relative "hub/app"
|
|
15
|
+
require_relative "hub/builder"
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,49 +1,38 @@
|
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
|
1
2
|
require 'startback'
|
|
2
|
-
require 'startback/
|
|
3
|
-
require 'startback/support/fake_logger'
|
|
3
|
+
require 'startback/websocket'
|
|
4
4
|
require 'rack/test'
|
|
5
|
-
require 'ostruct'
|
|
6
5
|
|
|
7
6
|
module SpecHelpers
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
RSpec.configure do |c|
|
|
11
|
-
c.include SpecHelpers
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
class SubContext < Startback::Context
|
|
15
7
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
h_factory do |c,h|
|
|
19
|
-
c.foo = h["foo"]
|
|
8
|
+
class SubContext < Startback::Context
|
|
9
|
+
attr_accessor :websocket_app
|
|
20
10
|
end
|
|
21
11
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
end
|
|
12
|
+
class MockSocket
|
|
13
|
+
attr_reader :last_message
|
|
25
14
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
class SubContext
|
|
15
|
+
def send(msg)
|
|
16
|
+
@last_message = msg
|
|
17
|
+
end
|
|
33
18
|
|
|
34
|
-
|
|
19
|
+
def close()
|
|
20
|
+
end
|
|
35
21
|
|
|
36
|
-
|
|
37
|
-
|
|
22
|
+
def on(event, &bl)
|
|
23
|
+
end
|
|
38
24
|
end
|
|
39
25
|
|
|
40
|
-
|
|
41
|
-
|
|
26
|
+
class MockFayeEvent
|
|
27
|
+
def initialize(event)
|
|
28
|
+
@data = event[:data]
|
|
29
|
+
@headers = event[:headers] || {}
|
|
30
|
+
end
|
|
31
|
+
attr_reader :data, :headers
|
|
42
32
|
end
|
|
43
33
|
|
|
44
34
|
end
|
|
45
35
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
end
|
|
36
|
+
RSpec.configure do |c|
|
|
37
|
+
c.include SpecHelpers
|
|
49
38
|
end
|