lively 0.11.0 → 0.13.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
- checksums.yaml.gz.sig +0 -0
- data/context/getting-started.md +77 -0
- data/context/index.yaml +16 -0
- data/context/worms-tutorial.md +842 -0
- data/lib/lively/application.rb +38 -0
- data/lib/lively/assets.rb +66 -14
- data/lib/lively/environment/application.rb +20 -5
- data/lib/lively/hello_world.rb +16 -0
- data/lib/lively/pages/index.rb +19 -1
- data/lib/lively/pages/index.xrb +2 -4
- data/lib/lively/version.rb +3 -2
- data/public/_components/@socketry/live/Live.js +42 -48
- data/public/_components/@socketry/live/package.json +4 -1
- data/public/_components/@socketry/live/readme.md +147 -31
- data/public/_components/@socketry/live-audio/Live/Audio/Controller.js +168 -0
- data/public/_components/@socketry/live-audio/Live/Audio/Library.js +748 -0
- data/public/_components/@socketry/live-audio/Live/Audio/Output.js +87 -0
- data/public/_components/@socketry/live-audio/Live/Audio/Sound.js +34 -0
- data/public/_components/@socketry/live-audio/Live/Audio/Visualizer.js +265 -0
- data/public/_components/@socketry/live-audio/Live/Audio.js +24 -0
- data/public/_components/@socketry/live-audio/package.json +35 -0
- data/public/_components/@socketry/live-audio/readme.md +250 -0
- data/public/application.js +4 -0
- data/readme.md +3 -7
- data.tar.gz.sig +0 -0
- metadata +15 -4
- metadata.gz.sig +0 -0
- data/public/_components/@socketry/live/test/Live.js +0 -357
data/readme.md
CHANGED
@@ -6,15 +6,11 @@ A Ruby framework for building interactive web applications for creative coding.
|
|
6
6
|
|
7
7
|
## Usage
|
8
8
|
|
9
|
-
|
9
|
+
Please see the [project documentation](https://socketry.github.io/lively/) for more details.
|
10
10
|
|
11
|
-
|
11
|
+
- [Getting Started](https://socketry.github.io/lively/guides/getting-started/index) - This guide will help you get started with Lively, a framework for building real-time applications in Ruby.
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
``` bash
|
16
|
-
$ ls **/*.rb | entr -r bundle exec ./application.rb
|
17
|
-
```
|
13
|
+
- [Building a Worms Game with Lively](https://socketry.github.io/lively/guides/worms-tutorial/index) - This tutorial will guide you through creating a Worms-style game using Lively, a Ruby framework for building real-time applications. We'll build the game step by step, starting with simple concepts and gradually adding complexity.
|
18
14
|
|
19
15
|
## Contributing
|
20
16
|
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lively
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -36,7 +36,7 @@ cert_chain:
|
|
36
36
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
37
37
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
38
38
|
-----END CERTIFICATE-----
|
39
|
-
date:
|
39
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
40
40
|
dependencies:
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: falcon
|
@@ -86,6 +86,9 @@ extensions: []
|
|
86
86
|
extra_rdoc_files: []
|
87
87
|
files:
|
88
88
|
- bin/lively
|
89
|
+
- context/getting-started.md
|
90
|
+
- context/index.yaml
|
91
|
+
- context/worms-tutorial.md
|
89
92
|
- lib/lively.rb
|
90
93
|
- lib/lively/application.rb
|
91
94
|
- lib/lively/assets.rb
|
@@ -95,10 +98,17 @@ files:
|
|
95
98
|
- lib/lively/pages/index.xrb
|
96
99
|
- lib/lively/version.rb
|
97
100
|
- license.md
|
101
|
+
- public/_components/@socketry/live-audio/Live/Audio.js
|
102
|
+
- public/_components/@socketry/live-audio/Live/Audio/Controller.js
|
103
|
+
- public/_components/@socketry/live-audio/Live/Audio/Library.js
|
104
|
+
- public/_components/@socketry/live-audio/Live/Audio/Output.js
|
105
|
+
- public/_components/@socketry/live-audio/Live/Audio/Sound.js
|
106
|
+
- public/_components/@socketry/live-audio/Live/Audio/Visualizer.js
|
107
|
+
- public/_components/@socketry/live-audio/package.json
|
108
|
+
- public/_components/@socketry/live-audio/readme.md
|
98
109
|
- public/_components/@socketry/live/Live.js
|
99
110
|
- public/_components/@socketry/live/package.json
|
100
111
|
- public/_components/@socketry/live/readme.md
|
101
|
-
- public/_components/@socketry/live/test/Live.js
|
102
112
|
- public/_components/morphdom/morphdom-esm.js
|
103
113
|
- public/_components/morphdom/morphdom-factory.js
|
104
114
|
- public/_components/morphdom/morphdom-umd.js
|
@@ -108,6 +118,7 @@ files:
|
|
108
118
|
- public/_static/icon.png
|
109
119
|
- public/_static/index.css
|
110
120
|
- public/_static/site.css
|
121
|
+
- public/application.js
|
111
122
|
- readme.md
|
112
123
|
homepage: https://github.com/socketry/lively
|
113
124
|
licenses:
|
@@ -129,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
129
140
|
- !ruby/object:Gem::Version
|
130
141
|
version: '0'
|
131
142
|
requirements: []
|
132
|
-
rubygems_version: 3.6.
|
143
|
+
rubygems_version: 3.6.9
|
133
144
|
specification_version: 4
|
134
145
|
summary: A simple client-server SPA framework.
|
135
146
|
test_files: []
|
metadata.gz.sig
CHANGED
Binary file
|
@@ -1,357 +0,0 @@
|
|
1
|
-
import {describe, before, beforeEach, after, it} from 'node:test';
|
2
|
-
import {ok, strict, strictEqual, deepStrictEqual} from 'node:assert';
|
3
|
-
|
4
|
-
import {WebSocket} from 'ws';
|
5
|
-
import {JSDOM} from 'jsdom';
|
6
|
-
import {Live} from '../Live.js';
|
7
|
-
|
8
|
-
class Queue {
|
9
|
-
constructor() {
|
10
|
-
this.items = [];
|
11
|
-
this.waiting = [];
|
12
|
-
}
|
13
|
-
|
14
|
-
push(item) {
|
15
|
-
if (this.waiting.length > 0) {
|
16
|
-
let resolve = this.waiting.shift();
|
17
|
-
resolve(item);
|
18
|
-
} else {
|
19
|
-
this.items.push(item);
|
20
|
-
}
|
21
|
-
}
|
22
|
-
|
23
|
-
pop() {
|
24
|
-
return new Promise(resolve => {
|
25
|
-
if (this.items.length > 0) {
|
26
|
-
resolve(this.items.shift());
|
27
|
-
} else {
|
28
|
-
this.waiting.push(resolve);
|
29
|
-
}
|
30
|
-
});
|
31
|
-
}
|
32
|
-
|
33
|
-
async popUntil(callback) {
|
34
|
-
while (true) {
|
35
|
-
let item = await this.pop();
|
36
|
-
if (callback(item)) return item;
|
37
|
-
}
|
38
|
-
}
|
39
|
-
|
40
|
-
clear() {
|
41
|
-
this.items = [];
|
42
|
-
this.waiting = [];
|
43
|
-
}
|
44
|
-
}
|
45
|
-
|
46
|
-
describe('Live', function () {
|
47
|
-
let dom;
|
48
|
-
let webSocketServer;
|
49
|
-
let messages = new Queue();
|
50
|
-
|
51
|
-
const webSocketServerConfig = {port: 3000};
|
52
|
-
const webSocketServerURL = `ws://localhost:${webSocketServerConfig.port}/live`;
|
53
|
-
|
54
|
-
before(async function () {
|
55
|
-
const listening = new Promise((resolve, reject) => {
|
56
|
-
webSocketServer = new WebSocket.Server(webSocketServerConfig, resolve);
|
57
|
-
webSocketServer.on('error', reject);
|
58
|
-
});
|
59
|
-
|
60
|
-
dom = new JSDOM('<!DOCTYPE html><html><body><div id="my" class="live"><p>Hello World</p></div></body></html>');
|
61
|
-
// Ensure the WebSocket class is available:
|
62
|
-
dom.window.WebSocket = WebSocket;
|
63
|
-
|
64
|
-
await new Promise(resolve => dom.window.addEventListener('load', resolve));
|
65
|
-
|
66
|
-
await listening;
|
67
|
-
|
68
|
-
webSocketServer.on('connection', socket => {
|
69
|
-
socket.on('message', message => {
|
70
|
-
let payload = JSON.parse(message);
|
71
|
-
messages.push(payload);
|
72
|
-
});
|
73
|
-
});
|
74
|
-
});
|
75
|
-
|
76
|
-
beforeEach(function () {
|
77
|
-
messages.clear();
|
78
|
-
});
|
79
|
-
|
80
|
-
after(function () {
|
81
|
-
webSocketServer.close();
|
82
|
-
});
|
83
|
-
|
84
|
-
it('should start the live connection', function () {
|
85
|
-
const live = Live.start({window: dom.window, base: 'http://localhost/'});
|
86
|
-
ok(live);
|
87
|
-
|
88
|
-
strictEqual(live.url.href, 'ws://localhost/live');
|
89
|
-
|
90
|
-
live.disconnect();
|
91
|
-
});
|
92
|
-
|
93
|
-
it('should connect to the WebSocket server', function () {
|
94
|
-
const live = new Live(dom.window, webSocketServerURL);
|
95
|
-
|
96
|
-
const server = live.connect();
|
97
|
-
ok(server);
|
98
|
-
|
99
|
-
live.disconnect();
|
100
|
-
});
|
101
|
-
|
102
|
-
it('should handle visibility changes', async function () {
|
103
|
-
const live = new Live(dom.window, webSocketServerURL);
|
104
|
-
|
105
|
-
// It's tricky to test the method directly.
|
106
|
-
// - Changing document.hidden is a hack.
|
107
|
-
// - Sending custom events seems to cause a hang.
|
108
|
-
|
109
|
-
live.connect();
|
110
|
-
deepStrictEqual(await messages.pop(), ['bind', 'my', {}]);
|
111
|
-
|
112
|
-
live.disconnect();
|
113
|
-
|
114
|
-
live.connect()
|
115
|
-
deepStrictEqual(await messages.pop(), ['bind', 'my', {}]);
|
116
|
-
|
117
|
-
live.disconnect();
|
118
|
-
});
|
119
|
-
|
120
|
-
it('can execute scripts', async function () {
|
121
|
-
const live = new Live(dom.window, webSocketServerURL);
|
122
|
-
|
123
|
-
live.connect();
|
124
|
-
|
125
|
-
const connected = new Promise(resolve => {
|
126
|
-
webSocketServer.on('connection', resolve);
|
127
|
-
});
|
128
|
-
|
129
|
-
let socket = await connected;
|
130
|
-
|
131
|
-
socket.send(
|
132
|
-
JSON.stringify(['script', 'my', 'return 1+2', {reply: true}])
|
133
|
-
);
|
134
|
-
|
135
|
-
let successReply = await messages.popUntil(message => message[0] == 'reply');
|
136
|
-
strictEqual(successReply[2], 3);
|
137
|
-
|
138
|
-
socket.send(
|
139
|
-
JSON.stringify(['script', 'my', 'throw new Error("Test Error")', {reply: true}])
|
140
|
-
);
|
141
|
-
|
142
|
-
let errorReply = await messages.popUntil(message => message[0] == 'reply');
|
143
|
-
strictEqual(errorReply[2], null);
|
144
|
-
console.log(errorReply);
|
145
|
-
|
146
|
-
live.disconnect();
|
147
|
-
});
|
148
|
-
|
149
|
-
it('should handle updates', async function () {
|
150
|
-
const live = new Live(dom.window, webSocketServerURL);
|
151
|
-
|
152
|
-
live.connect();
|
153
|
-
|
154
|
-
const connected = new Promise(resolve => {
|
155
|
-
webSocketServer.on('connection', resolve);
|
156
|
-
});
|
157
|
-
|
158
|
-
let socket = await connected;
|
159
|
-
|
160
|
-
socket.send(
|
161
|
-
JSON.stringify(['update', 'my', '<div id="my"><p>Goodbye World!</p></div>', {reply: true}])
|
162
|
-
);
|
163
|
-
|
164
|
-
await messages.popUntil(message => message[0] == 'reply');
|
165
|
-
|
166
|
-
strictEqual(dom.window.document.getElementById('my').innerHTML, '<p>Goodbye World!</p>');
|
167
|
-
|
168
|
-
live.disconnect();
|
169
|
-
});
|
170
|
-
|
171
|
-
it('should handle updates with child live elements', async function () {
|
172
|
-
const live = new Live(dom.window, webSocketServerURL);
|
173
|
-
|
174
|
-
live.connect();
|
175
|
-
|
176
|
-
const connected = new Promise(resolve => {
|
177
|
-
webSocketServer.on('connection', resolve);
|
178
|
-
});
|
179
|
-
|
180
|
-
let socket = await connected;
|
181
|
-
|
182
|
-
socket.send(
|
183
|
-
JSON.stringify(['update', 'my', '<div id="my"><div id="mychild" class="live"></div></div>'])
|
184
|
-
);
|
185
|
-
|
186
|
-
let payload = await messages.popUntil(message => message[0] == 'bind');
|
187
|
-
deepStrictEqual(payload, ['bind', 'mychild', {}]);
|
188
|
-
|
189
|
-
live.disconnect();
|
190
|
-
});
|
191
|
-
|
192
|
-
it('can unbind removed elements', async function () {
|
193
|
-
dom.window.document.body.innerHTML = '<div id="my" class="live"><p>Hello World</p></div>';
|
194
|
-
|
195
|
-
const live = new Live(dom.window, webSocketServerURL);
|
196
|
-
|
197
|
-
live.connect();
|
198
|
-
|
199
|
-
dom.window.document.getElementById('my').remove();
|
200
|
-
|
201
|
-
let payload = await messages.popUntil(message => {
|
202
|
-
return message[0] == 'unbind' && message[1] == 'my';
|
203
|
-
});
|
204
|
-
|
205
|
-
deepStrictEqual(payload, ['unbind', 'my']);
|
206
|
-
|
207
|
-
live.disconnect();
|
208
|
-
});
|
209
|
-
|
210
|
-
it('should handle replacements', async function () {
|
211
|
-
dom.window.document.body.innerHTML = '<div id="my"><p>Hello World</p></div>';
|
212
|
-
|
213
|
-
const live = new Live(dom.window, webSocketServerURL);
|
214
|
-
|
215
|
-
live.connect();
|
216
|
-
|
217
|
-
const connected = new Promise(resolve => {
|
218
|
-
webSocketServer.on('connection', resolve);
|
219
|
-
});
|
220
|
-
|
221
|
-
let socket = await connected;
|
222
|
-
|
223
|
-
socket.send(
|
224
|
-
JSON.stringify(['replace', '#my p', '<p>Replaced!</p>', {reply: true}])
|
225
|
-
);
|
226
|
-
|
227
|
-
await messages.popUntil(message => message[0] == 'reply');
|
228
|
-
strictEqual(dom.window.document.getElementById('my').innerHTML, '<p>Replaced!</p>');
|
229
|
-
|
230
|
-
live.disconnect();
|
231
|
-
});
|
232
|
-
|
233
|
-
it('should handle prepends', async function () {
|
234
|
-
const live = new Live(dom.window, webSocketServerURL);
|
235
|
-
|
236
|
-
live.connect();
|
237
|
-
|
238
|
-
const connected = new Promise(resolve => {
|
239
|
-
webSocketServer.on('connection', resolve);
|
240
|
-
});
|
241
|
-
|
242
|
-
let socket = await connected;
|
243
|
-
|
244
|
-
socket.send(
|
245
|
-
JSON.stringify(['update', 'my', '<div id="my"><p>Middle</p></div>'])
|
246
|
-
);
|
247
|
-
|
248
|
-
socket.send(
|
249
|
-
JSON.stringify(['prepend', '#my', '<p>Prepended!</p>', {reply: true}])
|
250
|
-
);
|
251
|
-
|
252
|
-
await messages.popUntil(message => message[0] == 'reply');
|
253
|
-
strictEqual(dom.window.document.getElementById('my').innerHTML, '<p>Prepended!</p><p>Middle</p>');
|
254
|
-
|
255
|
-
live.disconnect();
|
256
|
-
});
|
257
|
-
|
258
|
-
it('should handle appends', async function () {
|
259
|
-
const live = new Live(dom.window, webSocketServerURL);
|
260
|
-
|
261
|
-
live.connect();
|
262
|
-
|
263
|
-
const connected = new Promise(resolve => {
|
264
|
-
webSocketServer.on('connection', resolve);
|
265
|
-
});
|
266
|
-
|
267
|
-
let socket = await connected;
|
268
|
-
|
269
|
-
socket.send(
|
270
|
-
JSON.stringify(['update', 'my', '<div id="my"><p>Middle</p></div>'])
|
271
|
-
);
|
272
|
-
|
273
|
-
socket.send(
|
274
|
-
JSON.stringify(['append', '#my', '<p>Appended!</p>', {reply: true}])
|
275
|
-
);
|
276
|
-
|
277
|
-
await messages.popUntil(message => message[0] == 'reply');
|
278
|
-
strictEqual(dom.window.document.getElementById('my').innerHTML, '<p>Middle</p><p>Appended!</p>');
|
279
|
-
|
280
|
-
live.disconnect();
|
281
|
-
});
|
282
|
-
|
283
|
-
it ('should handle removals', async function () {
|
284
|
-
const live = new Live(dom.window, webSocketServerURL);
|
285
|
-
|
286
|
-
live.connect();
|
287
|
-
|
288
|
-
const connected = new Promise(resolve => {
|
289
|
-
webSocketServer.on('connection', resolve);
|
290
|
-
});
|
291
|
-
|
292
|
-
let socket = await connected;
|
293
|
-
|
294
|
-
socket.send(
|
295
|
-
JSON.stringify(['update', 'my', '<div id="my"><p>Middle</p></div>'])
|
296
|
-
);
|
297
|
-
|
298
|
-
socket.send(
|
299
|
-
JSON.stringify(['remove', '#my p', {reply: true}])
|
300
|
-
);
|
301
|
-
|
302
|
-
await messages.popUntil(message => message[0] == 'reply');
|
303
|
-
strictEqual(dom.window.document.getElementById('my').innerHTML, '');
|
304
|
-
|
305
|
-
live.disconnect();
|
306
|
-
});
|
307
|
-
|
308
|
-
it ('can dispatch custom events', async function () {
|
309
|
-
const live = new Live(dom.window, webSocketServerURL);
|
310
|
-
|
311
|
-
live.connect();
|
312
|
-
|
313
|
-
const connected = new Promise(resolve => {
|
314
|
-
webSocketServer.on('connection', resolve);
|
315
|
-
});
|
316
|
-
|
317
|
-
let socket = await connected;
|
318
|
-
|
319
|
-
socket.send(
|
320
|
-
JSON.stringify(['dispatchEvent', '#my', 'click', {reply: true}])
|
321
|
-
);
|
322
|
-
|
323
|
-
await messages.popUntil(message => message[0] == 'reply');
|
324
|
-
|
325
|
-
live.disconnect();
|
326
|
-
});
|
327
|
-
|
328
|
-
it ('can forward events', async function () {
|
329
|
-
const live = new Live(dom.window, webSocketServerURL);
|
330
|
-
|
331
|
-
live.connect();
|
332
|
-
|
333
|
-
const connected = new Promise(resolve => {
|
334
|
-
webSocketServer.on('connection', resolve);
|
335
|
-
});
|
336
|
-
|
337
|
-
let socket = await connected;
|
338
|
-
|
339
|
-
dom.window.document.getElementById('my').addEventListener('click', event => {
|
340
|
-
live.forwardEvent('my', event);
|
341
|
-
});
|
342
|
-
|
343
|
-
dom.window.document.getElementById('my').click();
|
344
|
-
|
345
|
-
let payload = await messages.popUntil(message => message[0] == 'event');
|
346
|
-
strictEqual(payload[1], 'my');
|
347
|
-
strictEqual(payload[2].type, 'click');
|
348
|
-
|
349
|
-
live.disconnect();
|
350
|
-
});
|
351
|
-
|
352
|
-
it ('can log errors', function () {
|
353
|
-
const live = new Live(dom.window, webSocketServerURL);
|
354
|
-
|
355
|
-
live.error('my', 'Test Error');
|
356
|
-
});
|
357
|
-
});
|