turbo-train 0.2.0 → 0.4.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.
- checksums.yaml +4 -4
- data/README.md +44 -10
- data/app/assets/javascripts/package.json +1 -1
- data/app/assets/javascripts/turbo-train.js +2 -10
- data/app/assets/javascripts/turbo-train.min.js +1 -1
- data/app/helpers/turbo/train/streams_helper.rb +1 -3
- data/lib/install/create_initializer.rb +13 -3
- data/lib/turbo/train/anycable_server.rb +36 -0
- data/lib/turbo/train/base_server.rb +27 -0
- data/lib/turbo/train/broadcasts.rb +3 -3
- data/lib/turbo/train/config.rb +81 -3
- data/lib/turbo/train/fanout_server.rb +36 -0
- data/lib/turbo/train/{server.rb → mercure_server.rb} +7 -9
- data/lib/turbo/train/test_helper.rb +58 -2
- data/lib/turbo/train/test_server.rb +5 -4
- data/lib/turbo/train/train.rb +29 -6
- data/lib/turbo/train/version.rb +1 -1
- data/lib/turbo/train.rb +4 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ad7e01b88eb8b09c2eecdffee7a4ede7b383c35573a194eec411a24674ae44e
|
4
|
+
data.tar.gz: 26289d3f1a518cc8208f7b0b1db5ad8b06b00f8489d2bfffe3fd89d90ad10a93
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5f000e44026c4e4d675eb5e6fa09ffab8b1f370ffd8f242f8582276542b9f5860b4ee8513a9f9f8350a4f5094b44d032894a10ca044f1d78c710e6a9ce42bad1
|
7
|
+
data.tar.gz: 84fd805f8e22ef1f1686cbe50a0cc7e4c3753f9f1f91225fab5c73be392d2a710acf2e9fe563166c1e28af7e9cf56e5b1f06a8c277d89486b7199f876ee951fd
|
data/README.md
CHANGED
@@ -4,19 +4,19 @@
|
|
4
4
|
|
5
5
|
<img align="right" width="220" title="Turbo::Train logo"
|
6
6
|
src="https://user-images.githubusercontent.com/3010927/210603861-4b265489-a4a7-4d2a-bceb-40ceccebcd96.jpg">
|
7
|
-
|
8
7
|
|
9
|
-
|
8
|
+
|
9
|
+
Real-time page updates for your Rails app over SSE with [Mercure](https://mercure.rocks), [Fanout Cloud](https://fanout.io/cloud) or [AnyCable](https://anycable.io/) and [Hotwire Turbo](https://turbo.hotwired.dev/handbook/streams#integration-with-server-side-frameworks).
|
10
10
|
|
11
11
|
* **Uses [SSE](https://html.spec.whatwg.org/multipage/server-sent-events.html)**. No more websockets, client libraries, JS code and handling reconnects. Just an HTTP connection. Let the [browser](https://caniuse.com/eventsource) do the work.
|
12
12
|
* **Seamless Hotwire integration.** Use it exactly like [ActionCable](https://github.com/hotwired/turbo-rails#come-alive-with-turbo-streams). Drop-in replacement for `broadcast_action_to` and usual helpers.
|
13
|
-
* **Simple.** Get running in minutes, scale easily in production 🚀
|
13
|
+
* **Simple.** Get running in minutes, scale easily in production 🚀
|
14
14
|
|
15
15
|
## Before your proceed
|
16
16
|
|
17
17
|
Using this gem requires some knowledge of ActionCable and broadcasting turbo streams. Turbo::Train is designed to mimic those, so it is highly recommended to first try the original to understand the concept.
|
18
18
|
|
19
|
-
You can start [here](https://hotwired.dev/) and proceed with the [Turbo Handbook](https://turbo.hotwired.dev/handbook/introduction). One of its chapters will be covering [Turbo Streams](https://turbo.hotwired.dev/handbook/streams). Specifically [this section](https://turbo.hotwired.dev/handbook/streams#integration-with-server-side-frameworks) would be the main prerequisite to understanding what this gem is about: it covers [Broadcastable](https://github.com/hotwired/turbo-rails/blob/main/app/models/concerns/turbo/broadcastable.rb) and the overall idea of working with Mercure.
|
19
|
+
You can start [here](https://hotwired.dev/) and proceed with the [Turbo Handbook](https://turbo.hotwired.dev/handbook/introduction). One of its chapters will be covering [Turbo Streams](https://turbo.hotwired.dev/handbook/streams). Specifically [this section](https://turbo.hotwired.dev/handbook/streams#integration-with-server-side-frameworks) would be the main prerequisite to understanding what this gem is about: it covers [Broadcastable](https://github.com/hotwired/turbo-rails/blob/main/app/models/concerns/turbo/broadcastable.rb) and the overall idea of working with Mercure or Fanout Cloud.
|
20
20
|
|
21
21
|
## Prerequisites
|
22
22
|
|
@@ -40,7 +40,8 @@ Instructions for Rails 6
|
|
40
40
|
1. Install [turbo-rails](https://github.com/hotwired/turbo-rails#installation)
|
41
41
|
2. Repeat steps for Rails 7 above
|
42
42
|
|
43
|
-
### Step 2.
|
43
|
+
### Step 2. Server
|
44
|
+
#### Mercure
|
44
45
|
|
45
46
|
Mercure is installed as a plugin to [Caddy](https://github.com/caddyserver/caddy) server. For mac users everything is pretty easy:
|
46
47
|
|
@@ -57,6 +58,21 @@ Now you are ready to run 🚀
|
|
57
58
|
```
|
58
59
|
caddy run
|
59
60
|
```
|
61
|
+
#### Fanout Cloud
|
62
|
+
|
63
|
+
We only support the cloud version today. To use [Fanout](https://fanout.io/cloud/) you must purchase a paid account with a contract for Fastly's services.
|
64
|
+
|
65
|
+
#### Fanout self-hosted (Pushpin)
|
66
|
+
|
67
|
+
Coming soon.
|
68
|
+
|
69
|
+
#### AnyCable
|
70
|
+
|
71
|
+
```
|
72
|
+
anycable-go --host=localhost --port=8080 --sse --broadcast_adapter=http --broadcast_key=test --public_streams --noauth
|
73
|
+
```
|
74
|
+
|
75
|
+
Coming soon.
|
60
76
|
|
61
77
|
## Usage
|
62
78
|
|
@@ -98,17 +114,35 @@ You have the same options as original Rails Turbo helpers: rendering partials, p
|
|
98
114
|
|
99
115
|
## Configuration
|
100
116
|
|
101
|
-
To specify different Mercure server settings, please adjust the generated `config/initializers/turbo_train.rb` file:
|
117
|
+
To specify different Mercure or Fanout server settings, please adjust the generated `config/initializers/turbo_train.rb` file:
|
102
118
|
|
103
119
|
```ruby
|
104
120
|
Turbo::Train.configure do |config|
|
105
|
-
config.
|
106
|
-
config.
|
107
|
-
|
121
|
+
config.skip_ssl_verification = true # Development only; don't do this in production
|
122
|
+
config.default_server = :fanout # Default value is :mercure
|
123
|
+
|
124
|
+
config.server :mercure do |mercure|
|
125
|
+
mercure.mercure_domain = ...
|
126
|
+
mercure.publisher_key = ...
|
127
|
+
mercure.subscriber_key = ...
|
128
|
+
end
|
129
|
+
|
130
|
+
config.server :fanout do |fanout|
|
131
|
+
fanout.service_url = ...
|
132
|
+
fanout.service_id = ...
|
133
|
+
fanout.fastly_key = ...
|
134
|
+
end
|
135
|
+
|
136
|
+
config.server :anycable do |fanout|
|
137
|
+
ac.anycable_url = 'http://0.0.0.0:8080'
|
138
|
+
ac.broadcast_key = 'test'
|
139
|
+
end
|
108
140
|
end
|
109
141
|
```
|
110
142
|
|
111
|
-
|
143
|
+
### Mercure
|
144
|
+
|
145
|
+
* Your SSE will connect to `https://#{configuration.mercure_domain}/.well-known`.
|
112
146
|
* The publisher/subscriber key correspond to the [configuration](https://mercure.rocks/docs/hub/config) or your Mercure server.
|
113
147
|
|
114
148
|
By default, these are set to `localhost`/`test`/`testing` to match the configuration of the local development server from the installation instructions above.
|
@@ -2,7 +2,7 @@ import { Turbo } from "@hotwired/turbo-rails"
|
|
2
2
|
|
3
3
|
export default class TurboTrain extends HTMLElement {
|
4
4
|
static get observedAttributes() {
|
5
|
-
return [ 'href'
|
5
|
+
return [ 'href' ];
|
6
6
|
}
|
7
7
|
|
8
8
|
constructor() {
|
@@ -10,7 +10,7 @@ export default class TurboTrain extends HTMLElement {
|
|
10
10
|
}
|
11
11
|
|
12
12
|
connectedCallback() {
|
13
|
-
this.eventSource = new EventSource(
|
13
|
+
this.eventSource = new EventSource(this.href);
|
14
14
|
Turbo.connectStreamSource(this.eventSource);
|
15
15
|
}
|
16
16
|
|
@@ -21,14 +21,6 @@ export default class TurboTrain extends HTMLElement {
|
|
21
21
|
get href() {
|
22
22
|
return this.getAttribute('href');
|
23
23
|
}
|
24
|
-
|
25
|
-
get session() {
|
26
|
-
return this.getAttribute('session');
|
27
|
-
}
|
28
|
-
|
29
|
-
get name() {
|
30
|
-
return this.getAttribute('name');
|
31
|
-
}
|
32
24
|
}
|
33
25
|
|
34
26
|
if (
|
@@ -1 +1 @@
|
|
1
|
-
import{Turbo as e}from"@hotwired/turbo-rails";export default class t extends HTMLElement{static get observedAttributes(){return["href"
|
1
|
+
import{Turbo as e}from"@hotwired/turbo-rails";export default class t extends HTMLElement{static get observedAttributes(){return["href"]}constructor(){super()}connectedCallback(){this.eventSource=new EventSource(this.href),e.connectStreamSource(this.eventSource)}disconnectedCallback(){e.disconnectStreamSource(this.eventSource)}get href(){return this.getAttribute("href")}};"undefined"==typeof window||window.customElements.get("turbo-train-stream-source")||window.customElements.define("turbo-train-stream-source",t);
|
@@ -1,8 +1,6 @@
|
|
1
1
|
module Turbo::Train::StreamsHelper
|
2
2
|
def turbo_train_from(*streamables, **attributes)
|
3
|
-
attributes[:
|
4
|
-
attributes[:session] = Turbo::Train.encode({ platform: "web" })
|
5
|
-
attributes[:href] = Turbo::Train.configuration.url
|
3
|
+
attributes[:href] = Turbo::Train.server(attributes[:server]&.to_sym).listen_url(streamables, platform: "web")
|
6
4
|
tag.turbo_train_stream_source(**attributes)
|
7
5
|
end
|
8
6
|
end
|
@@ -2,10 +2,20 @@ say "Creating initializer"
|
|
2
2
|
create_file Rails.root.join("config/initializers/turbo_train.rb") do
|
3
3
|
%{
|
4
4
|
Turbo::Train.configure do |config|
|
5
|
-
config.mercure_domain = 'localhost'
|
6
|
-
config.publisher_key = 'testing'
|
7
|
-
config.subscriber_key = 'test'
|
8
5
|
config.skip_ssl_verification = true # Development only; don't do this in production
|
6
|
+
config.default_server = # Default value is :mercure
|
7
|
+
|
8
|
+
config.server :mercure do |mercure|
|
9
|
+
mercure.mercure_domain = 'localhost'
|
10
|
+
mercure.publisher_key = 'testing'
|
11
|
+
mercure.subscriber_key = 'test'
|
12
|
+
end
|
13
|
+
|
14
|
+
config.server :fanout do |fanout|
|
15
|
+
fanout.service_url = ''
|
16
|
+
fanout.service_id = ''
|
17
|
+
fanout.fastly_key = ''
|
18
|
+
end
|
9
19
|
end
|
10
20
|
}
|
11
21
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Turbo
|
2
|
+
module Train
|
3
|
+
class AnycableServer < BaseServer
|
4
|
+
def publish(topics:, data:)
|
5
|
+
uri = URI(server_config.publish_url)
|
6
|
+
req = Net::HTTP::Post.new(uri)
|
7
|
+
req['Content-Type'] = 'application/json'
|
8
|
+
req['Authorization'] = "Bearer #{server_config.broadcast_key}"
|
9
|
+
|
10
|
+
message = data[:data].gsub("\n", '')
|
11
|
+
|
12
|
+
opts = {
|
13
|
+
use_ssl: uri.scheme == 'https'
|
14
|
+
}
|
15
|
+
|
16
|
+
payload = []
|
17
|
+
|
18
|
+
Array(topics).each do |topic|
|
19
|
+
payload << { stream: topic, data: message }
|
20
|
+
end
|
21
|
+
|
22
|
+
req.body = payload.to_json
|
23
|
+
|
24
|
+
opts[:verify_mode] = OpenSSL::SSL::VERIFY_NONE if configuration.skip_ssl_verification
|
25
|
+
|
26
|
+
Net::HTTP.start(uri.host, uri.port, opts) do |http|
|
27
|
+
http.request(req)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def server_config
|
32
|
+
configuration.anycable
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Turbo
|
2
|
+
module Train
|
3
|
+
class BaseServer
|
4
|
+
attr_reader :configuration
|
5
|
+
|
6
|
+
def initialize(configuration)
|
7
|
+
@configuration = configuration
|
8
|
+
end
|
9
|
+
|
10
|
+
def publish(topics:, data:)
|
11
|
+
raise NotImplementedError
|
12
|
+
end
|
13
|
+
|
14
|
+
def server_config
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
|
18
|
+
def publish_url
|
19
|
+
server_config.publish_url
|
20
|
+
end
|
21
|
+
|
22
|
+
def listen_url(topic, **options)
|
23
|
+
server_config.listen_url(topic, **options)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Turbo::Train::Broadcasts
|
2
|
-
def broadcast(streamables, content:)
|
2
|
+
def broadcast(streamables, content:, server: nil)
|
3
3
|
topics = if streamables.is_a?(Array)
|
4
4
|
streamables.map { |s| signed_stream_name(s) }
|
5
5
|
else
|
@@ -11,13 +11,13 @@ module Turbo::Train::Broadcasts
|
|
11
11
|
data: content
|
12
12
|
}
|
13
13
|
|
14
|
-
Turbo::Train.server.publish(topics: topics, data: data)
|
14
|
+
Turbo::Train.server(server).publish(topics: topics, data: data)
|
15
15
|
end
|
16
16
|
|
17
17
|
def broadcast_action_to(*streamables, action:, target: nil, targets: nil, **rendering)
|
18
18
|
broadcast(streamables, content: turbo_stream_action_tag(action, target: target, targets: targets, template:
|
19
19
|
rendering.delete(:content) || rendering.delete(:html) || (rendering.any? ? render_format(:html, **rendering) : nil)
|
20
|
-
))
|
20
|
+
), server: rendering.delete(:server))
|
21
21
|
end
|
22
22
|
|
23
23
|
def broadcast_render_to(*streamables, **rendering)
|
data/lib/turbo/train/config.rb
CHANGED
@@ -1,18 +1,96 @@
|
|
1
1
|
module Turbo
|
2
2
|
module Train
|
3
|
-
class
|
4
|
-
attr_accessor :mercure_domain, :publisher_key, :subscriber_key
|
3
|
+
class MercureConfiguration
|
4
|
+
attr_accessor :mercure_domain, :publisher_key, :subscriber_key
|
5
5
|
|
6
6
|
def initialize
|
7
|
+
super
|
7
8
|
@mercure_domain = 'localhost'
|
8
9
|
@publisher_key = 'test'
|
9
10
|
@subscriber_key = 'testing'
|
10
|
-
@skip_ssl_verification = Rails.env.development? || Rails.env.test?
|
11
11
|
end
|
12
12
|
|
13
13
|
def url
|
14
14
|
"https://#{mercure_domain}/.well-known"
|
15
15
|
end
|
16
|
+
|
17
|
+
def publish_url
|
18
|
+
"#{url}/mercure"
|
19
|
+
end
|
20
|
+
|
21
|
+
def listen_url(topic, platform: 'web')
|
22
|
+
"#{url}/mercure?topic=#{Turbo::Train.signed_stream_name(topic)}&authorization=#{jwt_auth_token({ platform: platform })}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def jwt_auth_token(payload)
|
26
|
+
structured_payload = { mercure: { payload: payload } }
|
27
|
+
JWT.encode structured_payload, subscriber_key, ALGORITHM
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class FanoutConfiguration
|
32
|
+
attr_accessor :fastly_api_url, :service_url, :fastly_key, :service_id
|
33
|
+
|
34
|
+
def initialize
|
35
|
+
super
|
36
|
+
@fastly_api_url = 'https://api.fastly.com'
|
37
|
+
@service_url = 'https://johnny-cage-fake-url.edgecompute.app'
|
38
|
+
@fastly_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
|
39
|
+
end
|
40
|
+
|
41
|
+
def publish_url
|
42
|
+
"#{@fastly_api_url}/service/#{@service_id}/publish/"
|
43
|
+
end
|
44
|
+
|
45
|
+
def listen_url(topic, **)
|
46
|
+
"#{service_url}/stream/sse?topic=#{Turbo::Train.signed_stream_name(topic)}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class AnycableConfiguration
|
51
|
+
attr_accessor :anycable_url, :broadcast_key
|
52
|
+
|
53
|
+
def initialize
|
54
|
+
super
|
55
|
+
@anycable_url = 'http://localhost:8080'
|
56
|
+
@broadcast_key = 'test'
|
57
|
+
end
|
58
|
+
|
59
|
+
def publish_url
|
60
|
+
"#{@anycable_url}/_broadcast"
|
61
|
+
end
|
62
|
+
|
63
|
+
def listen_url(topic, **)
|
64
|
+
"#{@anycable_url}/events?stream=#{Turbo::Train.signed_stream_name(topic)}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Configuration
|
69
|
+
attr_accessor :skip_ssl_verification, :mercure, :fanout, :anycable, :default_server
|
70
|
+
|
71
|
+
def initialize
|
72
|
+
@skip_ssl_verification = Rails.env.development? || Rails.env.test?
|
73
|
+
@mercure = nil
|
74
|
+
@fanout = nil
|
75
|
+
@anycable = nil
|
76
|
+
@default_server = :mercure
|
77
|
+
end
|
78
|
+
|
79
|
+
def server(server_name)
|
80
|
+
case server_name
|
81
|
+
when :mercure
|
82
|
+
@mercure ||= MercureConfiguration.new
|
83
|
+
yield(@mercure)
|
84
|
+
when :fanout
|
85
|
+
@fanout ||= FanoutConfiguration.new
|
86
|
+
yield(@fanout)
|
87
|
+
when :anycable
|
88
|
+
@anycable ||= AnycableConfiguration.new
|
89
|
+
yield(@anycable)
|
90
|
+
else
|
91
|
+
raise ArgumentError, "Unknown server name: #{server_name}"
|
92
|
+
end
|
93
|
+
end
|
16
94
|
end
|
17
95
|
|
18
96
|
class << self
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Turbo
|
2
|
+
module Train
|
3
|
+
class FanoutServer < BaseServer
|
4
|
+
def publish(topics:, data:)
|
5
|
+
uri = URI(server_config.publish_url)
|
6
|
+
req = Net::HTTP::Post.new(uri)
|
7
|
+
req['Fastly-Key'] = server_config.fastly_key
|
8
|
+
|
9
|
+
message = data[:data].gsub("\n", "")
|
10
|
+
payload = {items: []}
|
11
|
+
|
12
|
+
Array(topics).each do |topic|
|
13
|
+
payload[:items] << {channel: topic, formats: { 'http-stream': { content: "event: message\ndata: #{message}\n\n" } } }
|
14
|
+
end
|
15
|
+
|
16
|
+
req.body = payload.to_json
|
17
|
+
|
18
|
+
opts = {
|
19
|
+
use_ssl: uri.scheme == 'https'
|
20
|
+
}
|
21
|
+
|
22
|
+
if configuration.skip_ssl_verification
|
23
|
+
opts[:verify_mode] = OpenSSL::SSL::VERIFY_NONE
|
24
|
+
end
|
25
|
+
|
26
|
+
Net::HTTP.start(uri.host, uri.port, opts) do |http|
|
27
|
+
http.request(req)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def server_config
|
32
|
+
configuration.fanout
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,17 +1,11 @@
|
|
1
1
|
module Turbo
|
2
2
|
module Train
|
3
|
-
class
|
4
|
-
attr_reader :configuration
|
5
|
-
|
6
|
-
def initialize(configuration)
|
7
|
-
@configuration = configuration
|
8
|
-
end
|
9
|
-
|
3
|
+
class MercureServer < BaseServer
|
10
4
|
def publish(topics:, data:)
|
11
5
|
payload = { mercure: { publish: topics } }
|
12
|
-
token = JWT.encode payload,
|
6
|
+
token = JWT.encode payload, server_config.publisher_key, ALGORITHM
|
13
7
|
|
14
|
-
uri = URI(
|
8
|
+
uri = URI(publish_url)
|
15
9
|
|
16
10
|
req = Net::HTTP::Post.new(uri)
|
17
11
|
req['Content-Type'] = 'application/x-www-form-urlencoded'
|
@@ -30,6 +24,10 @@ module Turbo
|
|
30
24
|
http.request(req)
|
31
25
|
end
|
32
26
|
end
|
27
|
+
|
28
|
+
def server_config
|
29
|
+
configuration.mercure
|
30
|
+
end
|
33
31
|
end
|
34
32
|
end
|
35
33
|
end
|
@@ -3,13 +3,35 @@ module Turbo
|
|
3
3
|
module Train
|
4
4
|
module TestHelper
|
5
5
|
def before_setup
|
6
|
-
|
6
|
+
test_server = case ENV.fetch('TURBO_TRAIN_TEST_SERVER', 'mercure').to_sym
|
7
|
+
when :mercure
|
8
|
+
Turbo::Train::TestServer.new(Turbo::Train.mercure_server, Turbo::Train.configuration)
|
9
|
+
when :fanout
|
10
|
+
Turbo::Train::TestServer.new(Turbo::Train.fanout_server, Turbo::Train.configuration)
|
11
|
+
when :anycable
|
12
|
+
Turbo::Train::TestServer.new(Turbo::Train.anycable_server, Turbo::Train.configuration)
|
13
|
+
else
|
14
|
+
raise "Unknown test server: #{ENV['TURBO_TRAIN_TEST_SERVER']}"
|
15
|
+
end
|
16
|
+
|
17
|
+
Turbo::Train.instance_variable_set(:@server, test_server)
|
7
18
|
super
|
8
19
|
end
|
9
20
|
|
10
21
|
def after_teardown
|
22
|
+
test_server = case ENV.fetch('TURBO_TRAIN_TEST_SERVER', 'mercure').to_sym
|
23
|
+
when :mercure
|
24
|
+
Turbo::Train.mercure_server
|
25
|
+
when :fanout
|
26
|
+
Turbo::Train.fanout_server
|
27
|
+
when :anycable
|
28
|
+
Turbo::Train.anycable_server
|
29
|
+
else
|
30
|
+
raise "Unknown test server: #{ENV['TURBO_TRAIN_TEST_SERVER']}"
|
31
|
+
end
|
32
|
+
|
11
33
|
super
|
12
|
-
Turbo::Train.instance_variable_set(:@server,
|
34
|
+
Turbo::Train.instance_variable_set(:@server, test_server)
|
13
35
|
end
|
14
36
|
|
15
37
|
def assert_broadcast_on(stream, data, &block)
|
@@ -36,6 +58,40 @@ module Turbo
|
|
36
58
|
|
37
59
|
assert message, "No messages sent with #{data} to #{Turbo::Train.stream_name_from(stream)}"
|
38
60
|
end
|
61
|
+
|
62
|
+
def assert_body_match(r)
|
63
|
+
if Turbo::Train.server.real_server.is_a?(Turbo::Train::FanoutServer)
|
64
|
+
assert_match "Published\n", r.body
|
65
|
+
elsif Turbo::Train.server.real_server.is_a?(Turbo::Train::MercureServer)
|
66
|
+
assert_match /urn:uuid:.*/, r.body
|
67
|
+
elsif Turbo::Train.server.real_server.is_a?(Turbo::Train::AnycableServer)
|
68
|
+
assert_match '', r.body
|
69
|
+
else
|
70
|
+
raise "Unknown server type"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def assert_code_ok(r)
|
75
|
+
if Turbo::Train.server.real_server.is_a?(Turbo::Train::FanoutServer)
|
76
|
+
assert_equal r.code, '200'
|
77
|
+
elsif Turbo::Train.server.real_server.is_a?(Turbo::Train::MercureServer)
|
78
|
+
assert_equal r.code, '200'
|
79
|
+
elsif Turbo::Train.server.real_server.is_a?(Turbo::Train::AnycableServer)
|
80
|
+
assert_equal r.code, '201'
|
81
|
+
else
|
82
|
+
raise "Unknown server type"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def assert_response_from_mercure_server(r)
|
87
|
+
assert_equal r.code, '200'
|
88
|
+
assert_match /urn:uuid:.*/, r.body
|
89
|
+
end
|
90
|
+
|
91
|
+
def assert_response_from_fanout_server(r)
|
92
|
+
assert_equal r.code, '200'
|
93
|
+
assert_match "Published\n", r.body
|
94
|
+
end
|
39
95
|
end
|
40
96
|
end
|
41
97
|
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
module Turbo
|
2
2
|
module Train
|
3
|
-
class TestServer <
|
4
|
-
attr_reader :configuration, :channels_data
|
3
|
+
class TestServer < BaseServer
|
4
|
+
attr_reader :configuration, :channels_data, :real_server
|
5
5
|
|
6
|
-
def initialize(configuration)
|
6
|
+
def initialize(real_server, configuration)
|
7
7
|
@configuration = configuration
|
8
8
|
@channels_data = {}
|
9
|
+
@real_server = real_server
|
9
10
|
end
|
10
11
|
|
11
12
|
def publish(topics:, data:)
|
@@ -14,7 +15,7 @@ module Turbo
|
|
14
15
|
@channels_data[topic] << data[:data]
|
15
16
|
end
|
16
17
|
|
17
|
-
|
18
|
+
real_server.publish(topics: topics, data: data) if real_server
|
18
19
|
end
|
19
20
|
|
20
21
|
def broadcasts(channel)
|
data/lib/turbo/train/train.rb
CHANGED
@@ -18,8 +18,35 @@ module Turbo
|
|
18
18
|
Turbo.signed_stream_verifier.generate stream_name_from(streamables)
|
19
19
|
end
|
20
20
|
|
21
|
-
def server
|
22
|
-
@server ||=
|
21
|
+
def server(server = nil)
|
22
|
+
@server ||= case server || configuration.default_server
|
23
|
+
when :mercure
|
24
|
+
mercure_server
|
25
|
+
when :fanout
|
26
|
+
fanout_server
|
27
|
+
when :anycable
|
28
|
+
anycable_server
|
29
|
+
else
|
30
|
+
raise ArgumentError, "Unknown server: #{server}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def mercure_server
|
35
|
+
raise ArgumentError, "Mercure configuration is missing" unless configuration.mercure
|
36
|
+
|
37
|
+
@mercure_server ||= MercureServer.new(configuration)
|
38
|
+
end
|
39
|
+
|
40
|
+
def fanout_server
|
41
|
+
raise ArgumentError, "Fanout configuration is missing" unless configuration.fanout
|
42
|
+
|
43
|
+
@fanout_server ||= FanoutServer.new(configuration)
|
44
|
+
end
|
45
|
+
|
46
|
+
def anycable_server
|
47
|
+
raise ArgumentError, "Anycable configuration is missing" unless configuration.anycable
|
48
|
+
|
49
|
+
@anycable_server ||= AnycableServer.new(configuration)
|
23
50
|
end
|
24
51
|
|
25
52
|
def stream_name_from(streamables)
|
@@ -30,10 +57,6 @@ module Turbo
|
|
30
57
|
end
|
31
58
|
end
|
32
59
|
|
33
|
-
def url
|
34
|
-
configuration.url
|
35
|
-
end
|
36
|
-
|
37
60
|
private
|
38
61
|
|
39
62
|
def render_format(format, **rendering)
|
data/lib/turbo/train/version.rb
CHANGED
data/lib/turbo/train.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
require "turbo/train/version"
|
2
2
|
require "turbo/train/config"
|
3
3
|
require 'turbo/train/broadcasts'
|
4
|
-
require 'turbo/train/
|
4
|
+
require 'turbo/train/base_server'
|
5
|
+
require 'turbo/train/mercure_server'
|
6
|
+
require 'turbo/train/fanout_server'
|
7
|
+
require 'turbo/train/anycable_server'
|
5
8
|
require 'turbo/train/test_server'
|
6
9
|
require 'turbo/train/test_helper'
|
7
10
|
require "turbo/train/engine"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: turbo-train
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Savrov
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2024-05-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -84,10 +84,13 @@ files:
|
|
84
84
|
- lib/install/install_node.rb
|
85
85
|
- lib/tasks/install_tasks.rake
|
86
86
|
- lib/turbo/train.rb
|
87
|
+
- lib/turbo/train/anycable_server.rb
|
88
|
+
- lib/turbo/train/base_server.rb
|
87
89
|
- lib/turbo/train/broadcasts.rb
|
88
90
|
- lib/turbo/train/config.rb
|
89
91
|
- lib/turbo/train/engine.rb
|
90
|
-
- lib/turbo/train/
|
92
|
+
- lib/turbo/train/fanout_server.rb
|
93
|
+
- lib/turbo/train/mercure_server.rb
|
91
94
|
- lib/turbo/train/test_helper.rb
|
92
95
|
- lib/turbo/train/test_server.rb
|
93
96
|
- lib/turbo/train/train.rb
|