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.
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
- See the various examples in the `examples/` directory.
9
+ Please see the [project documentation](https://socketry.github.io/lively/) for more details.
10
10
 
11
- ### Live Coding
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
- You can use `entr` to reload the server when files change:
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.11.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: 2025-05-08 00:00:00.000000000 Z
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.2
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
- });