lively 0.5.0 → 0.6.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/bin/lively +16 -0
- data/lib/lively/application.rb +29 -5
- data/lib/lively/environment/application.rb +6 -1
- data/lib/lively/hello_world.rb +59 -0
- data/lib/lively/version.rb +1 -1
- data/public/_components/@socketry/live/Live.js +109 -84
- data/public/_components/@socketry/live/package.json +1 -1
- data/public/_components/@socketry/live/test/Live.js +46 -17
- data/public/_static/Falcon.png +0 -0
- data/public/_static/index.css +5 -1
- data/public/_static/site.css +1 -1
- data.tar.gz.sig +0 -0
- metadata +8 -4
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5792cd3cb017762c79c0c8efcf99724db21dd444aa0e6e1f0c7f4f8e0e61774a
|
4
|
+
data.tar.gz: 0275c27630c5e0af1408771737e9fa87276ce3b6e2f06de788eaa9e45d60a6a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c0f91c91d6d25d138aa78332cfb9c51d4f5793f5134c059bed8d1b6d1dd4b327a70f3d8e47106a49965534157d58913952b60f7508e903b461d3e15d48381bb
|
7
|
+
data.tar.gz: c4b7817e61109d157bb68c157e8c50590776db6c1eca1c4d7f65936339ba2d02da62f07dfc903cd939d8807231dc382e6b7f18702ef0096266b3bd458b25f4cd
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/bin/lively
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'async/service'
|
4
|
+
require_relative '../lib/lively/environment/application'
|
5
|
+
|
6
|
+
ARGV.each do |path|
|
7
|
+
require(path)
|
8
|
+
end
|
9
|
+
|
10
|
+
configuration = Async::Service::Configuration.build do
|
11
|
+
service "lively" do
|
12
|
+
include Lively::Environment::Application
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Async::Service::Controller.run(configuration)
|
data/lib/lively/application.rb
CHANGED
@@ -4,12 +4,32 @@
|
|
4
4
|
# Copyright, 2021-2024, by Samuel Williams.
|
5
5
|
|
6
6
|
require 'live'
|
7
|
+
require 'protocol/http/middleware'
|
7
8
|
require 'async/websocket/adapters/http'
|
8
9
|
|
9
10
|
require_relative 'pages/index'
|
11
|
+
require_relative 'hello_world'
|
10
12
|
|
11
13
|
module Lively
|
12
14
|
class Application < Protocol::HTTP::Middleware
|
15
|
+
def self.[](tag)
|
16
|
+
klass = Class.new(self)
|
17
|
+
|
18
|
+
klass.define_singleton_method(:resolver) do
|
19
|
+
Live::Resolver.allow(tag)
|
20
|
+
end
|
21
|
+
|
22
|
+
klass.define_method(:body) do
|
23
|
+
tag.new
|
24
|
+
end
|
25
|
+
|
26
|
+
return klass
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.resolver
|
30
|
+
Live::Resolver.allow(HelloWorld)
|
31
|
+
end
|
32
|
+
|
13
33
|
def initialize(delegate, resolver: self.class.resolver)
|
14
34
|
super(delegate)
|
15
35
|
|
@@ -24,19 +44,23 @@ module Lively
|
|
24
44
|
self.class.name
|
25
45
|
end
|
26
46
|
|
27
|
-
def body
|
28
|
-
|
47
|
+
def body(...)
|
48
|
+
HelloWorld.new(...)
|
49
|
+
end
|
50
|
+
|
51
|
+
def index(...)
|
52
|
+
Pages::Index.new(title: self.title, body: self.body(...))
|
29
53
|
end
|
30
54
|
|
31
|
-
def
|
32
|
-
|
55
|
+
def handle(request, ...)
|
56
|
+
return Protocol::HTTP::Response[200, [], [self.index(...).call]]
|
33
57
|
end
|
34
58
|
|
35
59
|
def call(request)
|
36
60
|
if request.path == '/live'
|
37
61
|
return Async::WebSocket::Adapters::HTTP.open(request, &self.method(:live)) || Protocol::HTTP::Response[400]
|
38
62
|
else
|
39
|
-
return
|
63
|
+
return handle(request)
|
40
64
|
end
|
41
65
|
end
|
42
66
|
end
|
@@ -3,13 +3,18 @@
|
|
3
3
|
# Released under the MIT License.
|
4
4
|
# Copyright, 2021-2024, by Samuel Williams.
|
5
5
|
|
6
|
+
require_relative '../application'
|
7
|
+
require_relative '../assets'
|
8
|
+
|
9
|
+
require 'falcon/environment/server'
|
10
|
+
|
6
11
|
module Lively
|
7
12
|
module Environment
|
8
13
|
module Application
|
9
14
|
include Falcon::Environment::Server
|
10
15
|
|
11
16
|
def application
|
12
|
-
if Object.const_defined?(:Application
|
17
|
+
if Object.const_defined?(:Application)
|
13
18
|
::Application
|
14
19
|
else
|
15
20
|
Console.warn(self, "No Application class defined, using default.")
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Lively
|
2
|
+
class HelloWorld < Live::View
|
3
|
+
def initialize(...)
|
4
|
+
super
|
5
|
+
|
6
|
+
@clock = nil
|
7
|
+
end
|
8
|
+
|
9
|
+
def bind(page)
|
10
|
+
super
|
11
|
+
|
12
|
+
@clock ||= Async do
|
13
|
+
while true
|
14
|
+
self.update!
|
15
|
+
|
16
|
+
sleep 1
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def close
|
22
|
+
@clock&.stop
|
23
|
+
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
def render(builder)
|
28
|
+
builder.tag(:h1) do
|
29
|
+
builder.text("Hello, I'm Lively!")
|
30
|
+
end
|
31
|
+
|
32
|
+
builder.tag(:p) do
|
33
|
+
builder.text("The time is #{Time.now}.")
|
34
|
+
end
|
35
|
+
|
36
|
+
builder.tag(:p) do
|
37
|
+
builder.text(<<~TEXT)
|
38
|
+
Lively is a simple client-server SPA framework. It is designed to be easy to use and understand, while providing a solid foundation for building interactive web applications. Create an `application.rb` file and define your own `Application` class to get started.
|
39
|
+
TEXT
|
40
|
+
end
|
41
|
+
|
42
|
+
builder.inline_tag(:pre) do
|
43
|
+
builder.text(<<~TEXT)
|
44
|
+
#!/usr/bin/env lively
|
45
|
+
|
46
|
+
class Application < Lively::Application
|
47
|
+
def body
|
48
|
+
Lively::HelloWorld.new
|
49
|
+
end
|
50
|
+
end
|
51
|
+
TEXT
|
52
|
+
end
|
53
|
+
|
54
|
+
builder.tag(:p) do
|
55
|
+
builder.text("Check the `examples/` directory for... you guessed it... more examples.")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/lively/version.rb
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
import morphdom from 'morphdom';
|
2
2
|
|
3
3
|
export class Live {
|
4
|
+
#window;
|
5
|
+
#document;
|
6
|
+
#server;
|
7
|
+
#events;
|
8
|
+
#failures;
|
9
|
+
#reconnectTimer;
|
10
|
+
|
4
11
|
static start(options = {}) {
|
5
12
|
let window = options.window || globalThis;
|
6
13
|
let path = options.path || 'live'
|
@@ -13,35 +20,37 @@ export class Live {
|
|
13
20
|
}
|
14
21
|
|
15
22
|
constructor(window, url) {
|
16
|
-
this
|
17
|
-
this
|
23
|
+
this.#window = window;
|
24
|
+
this.#document = window.document;
|
18
25
|
|
19
26
|
this.url = url;
|
20
|
-
this
|
27
|
+
this.#server = null;
|
28
|
+
this.#events = [];
|
21
29
|
|
22
|
-
this
|
23
|
-
this
|
30
|
+
this.#failures = 0;
|
31
|
+
this.#reconnectTimer = null;
|
24
32
|
|
25
33
|
// Track visibility state and connect if required:
|
26
|
-
this
|
27
|
-
|
34
|
+
this.#document.addEventListener("visibilitychange", () => this.#handleVisibilityChange());
|
35
|
+
|
36
|
+
this.#handleVisibilityChange();
|
28
37
|
|
29
|
-
const elementNodeType = this
|
38
|
+
const elementNodeType = this.#window.Node.ELEMENT_NODE;
|
30
39
|
|
31
40
|
// Create a MutationObserver to watch for removed nodes
|
32
|
-
this.observer = new this
|
41
|
+
this.observer = new this.#window.MutationObserver((mutationsList, observer) => {
|
33
42
|
for (let mutation of mutationsList) {
|
34
43
|
if (mutation.type === 'childList') {
|
35
44
|
for (let node of mutation.removedNodes) {
|
36
45
|
if (node.nodeType !== elementNodeType) continue;
|
37
46
|
|
38
47
|
if (node.classList?.contains('live')) {
|
39
|
-
this
|
48
|
+
this.#unbind(node);
|
40
49
|
}
|
41
50
|
|
42
51
|
// Unbind any child nodes:
|
43
52
|
for (let child of node.getElementsByClassName('live')) {
|
44
|
-
this
|
53
|
+
this.#unbind(child);
|
45
54
|
}
|
46
55
|
}
|
47
56
|
|
@@ -49,65 +58,65 @@ export class Live {
|
|
49
58
|
if (node.nodeType !== elementNodeType) continue;
|
50
59
|
|
51
60
|
if (node.classList.contains('live')) {
|
52
|
-
this
|
61
|
+
this.#bind(node);
|
53
62
|
}
|
54
63
|
|
55
64
|
// Bind any child nodes:
|
56
65
|
for (let child of node.getElementsByClassName('live')) {
|
57
|
-
this
|
66
|
+
this.#bind(child);
|
58
67
|
}
|
59
68
|
}
|
60
69
|
}
|
61
70
|
}
|
62
71
|
});
|
63
72
|
|
64
|
-
this.observer.observe(this
|
73
|
+
this.observer.observe(this.#document.body, {childList: true, subtree: true});
|
65
74
|
}
|
66
75
|
|
67
76
|
// -- Connection Handling --
|
68
77
|
|
69
78
|
connect() {
|
70
|
-
if (this
|
71
|
-
return this
|
79
|
+
if (this.#server) {
|
80
|
+
return this.#server;
|
72
81
|
}
|
73
82
|
|
74
|
-
let server = this
|
83
|
+
let server = this.#server = new this.#window.WebSocket(this.url);
|
75
84
|
|
76
|
-
if (this
|
77
|
-
clearTimeout(this
|
78
|
-
this
|
85
|
+
if (this.#reconnectTimer) {
|
86
|
+
clearTimeout(this.#reconnectTimer);
|
87
|
+
this.#reconnectTimer = null;
|
79
88
|
}
|
80
89
|
|
81
90
|
server.onopen = () => {
|
82
|
-
this
|
83
|
-
this
|
84
|
-
this
|
91
|
+
this.#failures = 0;
|
92
|
+
this.#flush();
|
93
|
+
this.#attach();
|
85
94
|
};
|
86
95
|
|
87
96
|
server.onmessage = (message) => {
|
88
|
-
const [name, ...
|
97
|
+
const [name, ...args] = JSON.parse(message.data);
|
89
98
|
|
90
|
-
this[name](...
|
99
|
+
this[name](...args);
|
91
100
|
};
|
92
101
|
|
93
102
|
// The remote end has disconnected:
|
94
103
|
server.addEventListener('error', () => {
|
95
|
-
this
|
104
|
+
this.#failures += 1;
|
96
105
|
});
|
97
106
|
|
98
107
|
server.addEventListener('close', () => {
|
99
|
-
// Explicit disconnect will clear `this
|
100
|
-
if (this
|
108
|
+
// Explicit disconnect will clear `this.#server`:
|
109
|
+
if (this.#server && !this.#reconnectTimer) {
|
101
110
|
// We need a minimum delay otherwise this can end up immediately invoking the callback:
|
102
|
-
const delay = Math.max(100 * (this
|
103
|
-
this
|
104
|
-
this
|
111
|
+
const delay = Math.max(100 * (this.#failures + 1) ** 2, 60000);
|
112
|
+
this.#reconnectTimer = setTimeout(() => {
|
113
|
+
this.#reconnectTimer = null;
|
105
114
|
this.connect();
|
106
115
|
}, delay);
|
107
116
|
}
|
108
117
|
|
109
|
-
if (this
|
110
|
-
this
|
118
|
+
if (this.#server === server) {
|
119
|
+
this.#server = null;
|
111
120
|
}
|
112
121
|
});
|
113
122
|
|
@@ -115,133 +124,149 @@ export class Live {
|
|
115
124
|
}
|
116
125
|
|
117
126
|
disconnect() {
|
118
|
-
if (this
|
119
|
-
const server = this
|
120
|
-
this
|
127
|
+
if (this.#server) {
|
128
|
+
const server = this.#server;
|
129
|
+
this.#server = null;
|
121
130
|
server.close();
|
122
131
|
}
|
123
132
|
|
124
|
-
if (this
|
125
|
-
clearTimeout(this
|
126
|
-
this
|
133
|
+
if (this.#reconnectTimer) {
|
134
|
+
clearTimeout(this.#reconnectTimer);
|
135
|
+
this.#reconnectTimer = null;
|
127
136
|
}
|
128
137
|
}
|
129
138
|
|
130
|
-
send(message) {
|
131
|
-
if (this
|
139
|
+
#send(message) {
|
140
|
+
if (this.#server) {
|
132
141
|
try {
|
133
|
-
return this
|
142
|
+
return this.#server.send(message);
|
134
143
|
} catch (error) {
|
135
144
|
// console.log("Live.send", "failed to send message to server", error);
|
136
145
|
}
|
137
146
|
}
|
138
147
|
|
139
|
-
this
|
148
|
+
this.#events.push(message);
|
140
149
|
}
|
141
150
|
|
142
|
-
flush() {
|
143
|
-
if (this
|
151
|
+
#flush() {
|
152
|
+
if (this.#events.length === 0) return;
|
144
153
|
|
145
|
-
let events = this
|
146
|
-
this
|
154
|
+
let events = this.#events;
|
155
|
+
this.#events = [];
|
147
156
|
|
148
157
|
for (var event of events) {
|
149
|
-
this
|
158
|
+
this.#send(event);
|
150
159
|
}
|
151
160
|
}
|
152
161
|
|
153
|
-
handleVisibilityChange() {
|
154
|
-
if (this
|
162
|
+
#handleVisibilityChange() {
|
163
|
+
if (this.#document.hidden) {
|
155
164
|
this.disconnect();
|
156
165
|
} else {
|
157
166
|
this.connect();
|
158
167
|
}
|
159
168
|
}
|
160
169
|
|
161
|
-
bind(element) {
|
170
|
+
#bind(element) {
|
162
171
|
console.log("bind", element.id, element.dataset);
|
163
172
|
|
164
|
-
this
|
173
|
+
this.#send(JSON.stringify(['bind', element.id, element.dataset]));
|
165
174
|
}
|
166
175
|
|
167
|
-
unbind(element) {
|
176
|
+
#unbind(element) {
|
168
177
|
console.log("unbind", element.id, element.dataset);
|
169
178
|
|
170
|
-
if (this
|
171
|
-
this
|
179
|
+
if (this.#server) {
|
180
|
+
this.#send(JSON.stringify(['unbind', element.id]));
|
172
181
|
}
|
173
182
|
}
|
174
183
|
|
175
|
-
attach() {
|
176
|
-
for (let node of this
|
177
|
-
this
|
184
|
+
#attach() {
|
185
|
+
for (let node of this.#document.getElementsByClassName('live')) {
|
186
|
+
this.#bind(node);
|
178
187
|
}
|
179
188
|
}
|
180
189
|
|
181
|
-
createDocumentFragment(html) {
|
182
|
-
return this
|
190
|
+
#createDocumentFragment(html) {
|
191
|
+
return this.#document.createRange().createContextualFragment(html);
|
183
192
|
}
|
184
193
|
|
185
|
-
reply(options) {
|
194
|
+
#reply(options, ...args) {
|
186
195
|
if (options?.reply) {
|
187
|
-
this
|
196
|
+
this.#send(JSON.stringify(['reply', options.reply, ...args]));
|
188
197
|
}
|
189
198
|
}
|
190
199
|
|
191
200
|
// -- RPC Methods --
|
192
201
|
|
202
|
+
script(id, code, options) {
|
203
|
+
let element = this.#document.getElementById(id);
|
204
|
+
|
205
|
+
try {
|
206
|
+
let result = this.#window.Function(code).call(element);
|
207
|
+
|
208
|
+
this.#reply(options, result);
|
209
|
+
} catch (error) {
|
210
|
+
this.#reply(options, null, {name: error.name, message: error.message, stack: error.stack});
|
211
|
+
}
|
212
|
+
}
|
213
|
+
|
193
214
|
update(id, html, options) {
|
194
|
-
let element = this
|
195
|
-
let fragment = this
|
215
|
+
let element = this.#document.getElementById(id);
|
216
|
+
let fragment = this.#createDocumentFragment(html);
|
196
217
|
|
197
218
|
morphdom(element, fragment);
|
198
219
|
|
199
|
-
this
|
220
|
+
this.#reply(options);
|
200
221
|
}
|
201
222
|
|
202
223
|
replace(selector, html, options) {
|
203
|
-
let elements = this
|
204
|
-
let fragment = this
|
224
|
+
let elements = this.#document.querySelectorAll(selector);
|
225
|
+
let fragment = this.#createDocumentFragment(html);
|
205
226
|
|
206
227
|
elements.forEach(element => morphdom(element, fragment.cloneNode(true)));
|
207
228
|
|
208
|
-
this
|
229
|
+
this.#reply(options);
|
209
230
|
}
|
210
231
|
|
211
232
|
prepend(selector, html, options) {
|
212
|
-
let elements = this
|
213
|
-
let fragment = this
|
233
|
+
let elements = this.#document.querySelectorAll(selector);
|
234
|
+
let fragment = this.#createDocumentFragment(html);
|
214
235
|
|
215
236
|
elements.forEach(element => element.prepend(fragment.cloneNode(true)));
|
216
237
|
|
217
|
-
this
|
238
|
+
this.#reply(options);
|
218
239
|
}
|
219
240
|
|
220
241
|
append(selector, html, options) {
|
221
|
-
let elements = this
|
222
|
-
let fragment = this
|
242
|
+
let elements = this.#document.querySelectorAll(selector);
|
243
|
+
let fragment = this.#createDocumentFragment(html);
|
223
244
|
|
224
245
|
elements.forEach(element => element.append(fragment.cloneNode(true)));
|
225
246
|
|
226
|
-
this
|
247
|
+
this.#reply(options);
|
227
248
|
}
|
228
249
|
|
229
250
|
remove(selector, options) {
|
230
|
-
let elements = this
|
251
|
+
let elements = this.#document.querySelectorAll(selector);
|
231
252
|
|
232
253
|
elements.forEach(element => element.remove());
|
233
254
|
|
234
|
-
this
|
255
|
+
this.#reply(options);
|
235
256
|
}
|
236
257
|
|
237
258
|
dispatchEvent(selector, type, options) {
|
238
|
-
let elements = this
|
259
|
+
let elements = this.#document.querySelectorAll(selector);
|
239
260
|
|
240
261
|
elements.forEach(element => element.dispatchEvent(
|
241
|
-
new this
|
262
|
+
new this.#window.CustomEvent(type, options)
|
242
263
|
));
|
243
264
|
|
244
|
-
this
|
265
|
+
this.#reply(options);
|
266
|
+
}
|
267
|
+
|
268
|
+
error(message) {
|
269
|
+
console.error("Live.error", ...arguments);
|
245
270
|
}
|
246
271
|
|
247
272
|
// -- Event Handling --
|
@@ -249,19 +274,19 @@ export class Live {
|
|
249
274
|
forward(id, event) {
|
250
275
|
this.connect();
|
251
276
|
|
252
|
-
this
|
277
|
+
this.#send(
|
253
278
|
JSON.stringify(['event', id, event])
|
254
279
|
);
|
255
280
|
}
|
256
281
|
|
257
|
-
forwardEvent(id, event, detail) {
|
258
|
-
event.preventDefault();
|
282
|
+
forwardEvent(id, event, detail, preventDefault = false) {
|
283
|
+
if (preventDefault) event.preventDefault();
|
259
284
|
|
260
285
|
this.forward(id, {type: event.type, detail: detail});
|
261
286
|
}
|
262
287
|
|
263
|
-
forwardFormEvent(id, event, detail) {
|
264
|
-
event.preventDefault();
|
288
|
+
forwardFormEvent(id, event, detail, preventDefault = true) {
|
289
|
+
if (preventDefault) event.preventDefault();
|
265
290
|
|
266
291
|
let form = event.form;
|
267
292
|
let formData = new FormData(form);
|
@@ -85,9 +85,9 @@ describe('Live', function () {
|
|
85
85
|
const live = Live.start({window: dom.window, base: 'http://localhost/'});
|
86
86
|
ok(live);
|
87
87
|
|
88
|
-
strictEqual(live.window, dom.window);
|
89
|
-
strictEqual(live.document, dom.window.document);
|
90
88
|
strictEqual(live.url.href, 'ws://localhost/live');
|
89
|
+
|
90
|
+
live.disconnect();
|
91
91
|
});
|
92
92
|
|
93
93
|
it('should connect to the WebSocket server', function () {
|
@@ -102,26 +102,46 @@ describe('Live', function () {
|
|
102
102
|
it('should handle visibility changes', async function () {
|
103
103
|
const live = new Live(dom.window, webSocketServerURL);
|
104
104
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
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', {}]);
|
109
111
|
|
110
|
-
|
111
|
-
live.handleVisibilityChange();
|
112
|
+
live.disconnect();
|
112
113
|
|
113
|
-
|
114
|
+
live.connect()
|
114
115
|
deepStrictEqual(await messages.pop(), ['bind', 'my', {}]);
|
115
116
|
|
116
|
-
|
117
|
-
|
118
|
-
|
117
|
+
live.disconnect();
|
118
|
+
});
|
119
|
+
|
120
|
+
it('can execute scripts', async function () {
|
121
|
+
const live = new Live(dom.window, webSocketServerURL);
|
119
122
|
|
120
|
-
|
121
|
-
live.handleVisibilityChange();
|
122
|
-
ok(live.server);
|
123
|
+
live.connect();
|
123
124
|
|
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);
|
125
145
|
|
126
146
|
live.disconnect();
|
127
147
|
});
|
@@ -178,7 +198,10 @@ describe('Live', function () {
|
|
178
198
|
|
179
199
|
dom.window.document.getElementById('my').remove();
|
180
200
|
|
181
|
-
let payload = await messages.popUntil(message =>
|
201
|
+
let payload = await messages.popUntil(message => {
|
202
|
+
return message[0] == 'unbind' && message[1] == 'my';
|
203
|
+
});
|
204
|
+
|
182
205
|
deepStrictEqual(payload, ['unbind', 'my']);
|
183
206
|
|
184
207
|
live.disconnect();
|
@@ -325,4 +348,10 @@ describe('Live', function () {
|
|
325
348
|
|
326
349
|
live.disconnect();
|
327
350
|
});
|
351
|
+
|
352
|
+
it ('can log errors', function () {
|
353
|
+
const live = new Live(dom.window, webSocketServerURL);
|
354
|
+
|
355
|
+
live.error('my', 'Test Error');
|
356
|
+
});
|
328
357
|
});
|
Binary file
|
data/public/_static/index.css
CHANGED
data/public/_static/site.css
CHANGED
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.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -59,14 +59,14 @@ dependencies:
|
|
59
59
|
requirements:
|
60
60
|
- - "~>"
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version: '0.
|
62
|
+
version: '0.9'
|
63
63
|
type: :runtime
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
67
|
- - "~>"
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: '0.
|
69
|
+
version: '0.9'
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: xrb
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
@@ -83,14 +83,17 @@ dependencies:
|
|
83
83
|
version: '0'
|
84
84
|
description:
|
85
85
|
email:
|
86
|
-
executables:
|
86
|
+
executables:
|
87
|
+
- lively
|
87
88
|
extensions: []
|
88
89
|
extra_rdoc_files: []
|
89
90
|
files:
|
91
|
+
- bin/lively
|
90
92
|
- lib/lively.rb
|
91
93
|
- lib/lively/application.rb
|
92
94
|
- lib/lively/assets.rb
|
93
95
|
- lib/lively/environment/application.rb
|
96
|
+
- lib/lively/hello_world.rb
|
94
97
|
- lib/lively/pages/index.rb
|
95
98
|
- lib/lively/pages/index.xrb
|
96
99
|
- lib/lively/version.rb
|
@@ -104,6 +107,7 @@ files:
|
|
104
107
|
- public/_components/morphdom/morphdom-umd.js
|
105
108
|
- public/_components/morphdom/morphdom-umd.min.js
|
106
109
|
- public/_components/morphdom/morphdom.js
|
110
|
+
- public/_static/Falcon.png
|
107
111
|
- public/_static/icon.png
|
108
112
|
- public/_static/index.css
|
109
113
|
- public/_static/site.css
|
metadata.gz.sig
CHANGED
Binary file
|