lively 0.18.0 → 0.19.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 +21 -13
- data/lib/lively/application.rb +22 -7
- data/lib/lively/page.rb +103 -0
- data/lib/lively/page.xrb +30 -0
- data/lib/lively/pages/index.rb +22 -22
- data/lib/lively/router.rb +138 -0
- data/lib/lively/version.rb +1 -1
- data/lib/lively.rb +2 -0
- data/readme.md +4 -7
- data/releases.md +4 -0
- data.tar.gz.sig +0 -0
- metadata +19 -3
- metadata.gz.sig +1 -5
- data/lib/lively/pages/index.xrb +0 -29
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d4f90097ca71e02dffa056e9251b96a63f0d45262ad5eddd0f073d2d02d42b6e
|
|
4
|
+
data.tar.gz: 214a19575d3f3fa624a3d71494b888feccebc18774fa1e38255ec4ceb90c84a6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 23cc87763d59471eaabdfee09f234f389c3576f7bb6d59df85f65e0205d7f6244710c20cfed77c3e0543513f4db581664e776062383c9a05491673ca8bfc88b8
|
|
7
|
+
data.tar.gz: 5b905206f71fcea3f15da3f6db3f0e8fcd9f1bd85f06278654d621affb4d72cf42828b828a1f6821602a98f8af5b8441fe701a4785a7626b4327f972dad8b03d
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/context/getting-started.md
CHANGED
|
@@ -99,7 +99,7 @@ Application = Lively::Application[GameView, game_state: GameState.new]
|
|
|
99
99
|
|
|
100
100
|
The `game_state:` keyword is passed to every `GameView` instance — whether created by the initial page load or by a WebSocket reconnection. This means all connected browsers share the same `GameState` object.
|
|
101
101
|
|
|
102
|
-
For more complex applications, subclass {ruby Lively::Application} and
|
|
102
|
+
For more complex applications, subclass {ruby Lively::Application} and add routes with `#configure_routes`:
|
|
103
103
|
|
|
104
104
|
```ruby
|
|
105
105
|
class Application < Lively::Application
|
|
@@ -116,26 +116,34 @@ class Application < Lively::Application
|
|
|
116
116
|
super
|
|
117
117
|
end
|
|
118
118
|
|
|
119
|
-
def
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
119
|
+
def configure_routes(router)
|
|
120
|
+
super
|
|
121
|
+
|
|
122
|
+
router.get("/") do
|
|
123
|
+
render(DisplayView.new(**state))
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
router.get("/control") do |_request, parameters|
|
|
127
|
+
render(ControlView.new(**state, mode: parameters["mode"]))
|
|
125
128
|
end
|
|
126
129
|
end
|
|
127
130
|
|
|
131
|
+
# Unmatched routes are passed here:
|
|
128
132
|
def handle(request)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
delegate.call(request)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
private
|
|
137
|
+
|
|
138
|
+
def render(body)
|
|
139
|
+
page = Lively::Pages::Index.new(title: "My App", body: body)
|
|
140
|
+
Protocol::HTTP::Response[200, [], [page.call]]
|
|
135
141
|
end
|
|
136
142
|
end
|
|
137
143
|
```
|
|
138
144
|
|
|
145
|
+
Routes match exact paths and may accept one or more HTTP methods. Query parameters are decoded using `protocol-url` and passed to the handler as its second argument. Routes without an explicit method accept every method.
|
|
146
|
+
|
|
139
147
|
## Live Reloading
|
|
140
148
|
|
|
141
149
|
To enable live reloading, add the `io-watch` gem to your `gems.rb` file:
|
data/lib/lively/application.rb
CHANGED
|
@@ -8,6 +8,7 @@ require "protocol/http/middleware"
|
|
|
8
8
|
require "async/websocket/adapters/http"
|
|
9
9
|
|
|
10
10
|
require_relative "resolver"
|
|
11
|
+
require_relative "router"
|
|
11
12
|
require_relative "pages/index"
|
|
12
13
|
require_relative "hello_world"
|
|
13
14
|
|
|
@@ -22,7 +23,7 @@ module Lively
|
|
|
22
23
|
#
|
|
23
24
|
# Use {.[]} to create a simple application class for a single view, optionally
|
|
24
25
|
# with shared state. For more complex applications, subclass and override
|
|
25
|
-
# {#allowed_views}, {#state}, and {#
|
|
26
|
+
# {#allowed_views}, {#state}, and {#configure_routes}.
|
|
26
27
|
class Application < Protocol::HTTP::Middleware
|
|
27
28
|
VIEWS = [HelloWorld].freeze
|
|
28
29
|
STATE = {}.freeze
|
|
@@ -98,22 +99,36 @@ module Lively
|
|
|
98
99
|
Pages::Index.new(title: self.title, body: self.body)
|
|
99
100
|
end
|
|
100
101
|
|
|
101
|
-
# Handle a standard HTTP request.
|
|
102
|
+
# Handle a standard HTTP request which did not match a configured route.
|
|
102
103
|
# @parameter request [Protocol::HTTP::Request] The incoming HTTP request.
|
|
103
104
|
# @returns [Protocol::HTTP::Response] The HTTP response with the rendered page.
|
|
104
105
|
def handle(request)
|
|
105
106
|
return Protocol::HTTP::Response[200, [], [self.index.call]]
|
|
106
107
|
end
|
|
107
108
|
|
|
109
|
+
# Add the standard application routes to the given router.
|
|
110
|
+
# Override this method and call `super` to add application-specific routes.
|
|
111
|
+
# @parameter router [Router] The router to configure.
|
|
112
|
+
def configure_routes(router)
|
|
113
|
+
router.route("/live") do |request|
|
|
114
|
+
Async::WebSocket::Adapters::HTTP.open(request, &self.method(:live)) || Protocol::HTTP::Response[400]
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# The router for this application. Unmatched requests are handled separately
|
|
119
|
+
# by {#handle}.
|
|
120
|
+
# @returns [Router] The configured router.
|
|
121
|
+
def router
|
|
122
|
+
@router ||= Router.new.tap do |router|
|
|
123
|
+
configure_routes(router)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
108
127
|
# Process an incoming HTTP request.
|
|
109
128
|
# @parameter request [Protocol::HTTP::Request] The incoming HTTP request.
|
|
110
129
|
# @returns [Protocol::HTTP::Response] The appropriate response for the request.
|
|
111
130
|
def call(request)
|
|
112
|
-
|
|
113
|
-
return Async::WebSocket::Adapters::HTTP.open(request, &self.method(:live)) || Protocol::HTTP::Response[400]
|
|
114
|
-
else
|
|
115
|
-
return handle(request)
|
|
116
|
-
end
|
|
131
|
+
return router.call(request) || handle(request)
|
|
117
132
|
end
|
|
118
133
|
end
|
|
119
134
|
end
|
data/lib/lively/page.rb
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require "json"
|
|
7
|
+
require "xrb/markup"
|
|
8
|
+
require "xrb/tag"
|
|
9
|
+
require "xrb/template"
|
|
10
|
+
|
|
11
|
+
module Lively
|
|
12
|
+
# Represents a complete HTML document.
|
|
13
|
+
#
|
|
14
|
+
# A page combines application content with the stylesheets, import map,
|
|
15
|
+
# JavaScript modules, and body attributes required to present it. Applications
|
|
16
|
+
# can use this class directly or subclass it to provide shared defaults.
|
|
17
|
+
class Page
|
|
18
|
+
TEMPLATE = XRB::Template.load_file(File.expand_path("page.xrb", __dir__))
|
|
19
|
+
|
|
20
|
+
# Initialize a new page.
|
|
21
|
+
# @parameter title [String] The document title.
|
|
22
|
+
# @parameter body [Object | Nil] The document body. The result of `to_html` is interpolated into the template.
|
|
23
|
+
# @parameter icon [String | Nil] The favicon URL.
|
|
24
|
+
# @parameter stylesheets [Array(String | Hash)] Stylesheets in document order. Hash entries specify link attributes.
|
|
25
|
+
# @parameter imports [Hash] JavaScript import map entries.
|
|
26
|
+
# @parameter modules [Array(String)] JavaScript module URLs in document order.
|
|
27
|
+
# @parameter body_attributes [Hash] Attributes applied to the body element.
|
|
28
|
+
def initialize(title: "Lively", body: nil, icon: nil, stylesheets: [], imports: {}, modules: [], body_attributes: {})
|
|
29
|
+
@title = title
|
|
30
|
+
@body = body
|
|
31
|
+
@icon = icon
|
|
32
|
+
@stylesheets = stylesheets
|
|
33
|
+
@imports = imports
|
|
34
|
+
@modules = modules
|
|
35
|
+
@body_attributes = body_attributes
|
|
36
|
+
@template = TEMPLATE
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @attribute [String] The document title.
|
|
40
|
+
attr :title
|
|
41
|
+
|
|
42
|
+
# @attribute [Object | Nil] The document body.
|
|
43
|
+
attr :body
|
|
44
|
+
|
|
45
|
+
# @attribute [String | Nil] The favicon URL.
|
|
46
|
+
attr :icon
|
|
47
|
+
|
|
48
|
+
# @attribute [Array(String | Hash)] Stylesheets in document order.
|
|
49
|
+
attr :stylesheets
|
|
50
|
+
|
|
51
|
+
# @attribute [Hash] JavaScript import map entries.
|
|
52
|
+
attr :imports
|
|
53
|
+
|
|
54
|
+
# @attribute [Array(String)] JavaScript module URLs in document order.
|
|
55
|
+
attr :modules
|
|
56
|
+
|
|
57
|
+
# @attribute [Hash] Attributes applied to the body element.
|
|
58
|
+
attr :body_attributes
|
|
59
|
+
|
|
60
|
+
# @attribute [XRB::Template] The document template.
|
|
61
|
+
attr :template
|
|
62
|
+
|
|
63
|
+
# The opening body tag including configured attributes.
|
|
64
|
+
# @returns [XRB::Tag]
|
|
65
|
+
def body_tag
|
|
66
|
+
XRB::Tag.opened("body", @body_attributes)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# A stylesheet link tag for the given URL or attributes.
|
|
70
|
+
# @parameter stylesheet [String | Hash] The stylesheet URL or link attributes.
|
|
71
|
+
# @returns [XRB::Tag]
|
|
72
|
+
def stylesheet_tag(stylesheet)
|
|
73
|
+
attributes = if stylesheet.respond_to?(:to_hash)
|
|
74
|
+
stylesheet.to_hash
|
|
75
|
+
else
|
|
76
|
+
{href: stylesheet}
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
XRB::Tag.closed("link", {rel: "stylesheet", type: "text/css"}.merge(attributes))
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# The rendered body content.
|
|
83
|
+
# @returns [Object]
|
|
84
|
+
def body_content
|
|
85
|
+
@body&.to_html || "No body specified!"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# The serialized JavaScript import map.
|
|
89
|
+
# @returns [XRB::MarkupString]
|
|
90
|
+
def import_map
|
|
91
|
+
json = JSON.pretty_generate(imports: @imports)
|
|
92
|
+
json = json.gsub("<", "\\u003c").gsub(">", "\\u003e").gsub("&", "\\u0026")
|
|
93
|
+
|
|
94
|
+
XRB::MarkupString.raw(json)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Render this page to a string.
|
|
98
|
+
# @returns [String]
|
|
99
|
+
def call
|
|
100
|
+
@template.to_string(self)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
data/lib/lively/page.xrb
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>#{self.title}</title>
|
|
5
|
+
|
|
6
|
+
<meta charset="UTF-8" />
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
8
|
+
|
|
9
|
+
<?r if self.icon ?>
|
|
10
|
+
<link rel="icon" type="image/png" href="#{self.icon}" />
|
|
11
|
+
<?r end ?>
|
|
12
|
+
<?r self.stylesheets.each do |stylesheet| ?>
|
|
13
|
+
#{self.stylesheet_tag(stylesheet)}
|
|
14
|
+
<?r end ?>
|
|
15
|
+
|
|
16
|
+
<?r unless self.imports.empty? ?>
|
|
17
|
+
<script type="importmap">
|
|
18
|
+
#{self.import_map}
|
|
19
|
+
</script>
|
|
20
|
+
<?r end ?>
|
|
21
|
+
|
|
22
|
+
<?r self.modules.each do |source| ?>
|
|
23
|
+
<script type="module" src="#{source}"></script>
|
|
24
|
+
<?r end ?>
|
|
25
|
+
</head>
|
|
26
|
+
|
|
27
|
+
#{self.body_tag}
|
|
28
|
+
#{self.body_content}
|
|
29
|
+
</body>
|
|
30
|
+
</html>
|
data/lib/lively/pages/index.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
4
|
# Copyright, 2021-2026, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
require_relative "../page"
|
|
7
7
|
|
|
8
8
|
# @namespace
|
|
9
9
|
module Lively
|
|
@@ -14,31 +14,31 @@ module Lively
|
|
|
14
14
|
# This class renders the initial HTML page that users see when they visit
|
|
15
15
|
# a Lively application. It uses an XRB template to generate the page structure
|
|
16
16
|
# and embeds the Live view component for dynamic content.
|
|
17
|
-
class Index
|
|
17
|
+
class Index < Page
|
|
18
|
+
ICON = "/_static/icon.png"
|
|
19
|
+
STYLESHEETS = [
|
|
20
|
+
{href: "/_static/site.css", media: "screen"}.freeze,
|
|
21
|
+
{href: "/_static/index.css", media: "screen"}.freeze,
|
|
22
|
+
].freeze
|
|
23
|
+
IMPORTS = {
|
|
24
|
+
"live" => "/_components/@socketry/live/Live.js",
|
|
25
|
+
"live-audio" => "/_components/@socketry/live-audio/Live/Audio.js",
|
|
26
|
+
"morphdom" => "/_components/morphdom/morphdom-esm.js",
|
|
27
|
+
}.freeze
|
|
28
|
+
MODULES = ["/application.js"].freeze
|
|
29
|
+
|
|
18
30
|
# Initialize a new index page.
|
|
19
31
|
# @parameter title [String] The title of the page.
|
|
20
32
|
# @parameter body [Object] The body content of the page.
|
|
21
33
|
def initialize(title: "Lively", body: nil)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
attr :title
|
|
31
|
-
|
|
32
|
-
# @attribute [Object] The body content of the page.
|
|
33
|
-
attr :body
|
|
34
|
-
|
|
35
|
-
# @attribute [XRB::Template] The XRB template for rendering the page.
|
|
36
|
-
attr :template
|
|
37
|
-
|
|
38
|
-
# Render this page to a string.
|
|
39
|
-
# @returns [String] The rendered HTML for this page.
|
|
40
|
-
def call
|
|
41
|
-
@template.to_string(self)
|
|
34
|
+
super(
|
|
35
|
+
title: title,
|
|
36
|
+
body: body,
|
|
37
|
+
icon: ICON,
|
|
38
|
+
stylesheets: STYLESHEETS,
|
|
39
|
+
imports: IMPORTS,
|
|
40
|
+
modules: MODULES,
|
|
41
|
+
)
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
44
|
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require "protocol/http"
|
|
7
|
+
require "protocol/url"
|
|
8
|
+
|
|
9
|
+
module Lively
|
|
10
|
+
# Dispatches HTTP requests to handlers using exact path and method matches.
|
|
11
|
+
#
|
|
12
|
+
# Request targets are parsed with {Protocol::URL::Reference}. Handlers receive
|
|
13
|
+
# the original request and the decoded query parameters.
|
|
14
|
+
class Router
|
|
15
|
+
EMPTY_PARAMETERS = {}.freeze
|
|
16
|
+
ANY_METHOD = nil
|
|
17
|
+
private_constant :EMPTY_PARAMETERS, :ANY_METHOD
|
|
18
|
+
|
|
19
|
+
# Initialize a router.
|
|
20
|
+
# @yields {|router| ...} The router to configure.
|
|
21
|
+
def initialize
|
|
22
|
+
@routes = {}
|
|
23
|
+
|
|
24
|
+
yield self if block_given?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Add a route.
|
|
28
|
+
#
|
|
29
|
+
# When `methods` is omitted, the handler accepts every HTTP method. Otherwise,
|
|
30
|
+
# it may be a single method or an array of methods.
|
|
31
|
+
#
|
|
32
|
+
# @parameter path [String] The absolute path to match.
|
|
33
|
+
# @parameter methods [String | Symbol | Array(String | Symbol) | Nil] The accepted HTTP methods.
|
|
34
|
+
# @yields {|request, parameters| ...} The route handler.
|
|
35
|
+
# @parameter request [Protocol::HTTP::Request] The original request.
|
|
36
|
+
# @parameter parameters [Hash] The decoded query parameters.
|
|
37
|
+
# @returns [Router] The router.
|
|
38
|
+
def route(path, methods: nil, &handler)
|
|
39
|
+
raise ArgumentError, "A route handler is required!" unless handler
|
|
40
|
+
|
|
41
|
+
path = route_path(path)
|
|
42
|
+
methods = route_methods(methods)
|
|
43
|
+
handlers = (@routes[path] ||= {})
|
|
44
|
+
|
|
45
|
+
methods.each do |method|
|
|
46
|
+
if handlers.key?(method)
|
|
47
|
+
raise ArgumentError, "Route already defined for #{method || "any method"} #{path}!"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
handlers[method] = handler
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
return self
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
Protocol::HTTP::Methods.each do |name, method|
|
|
57
|
+
# Add a route for this HTTP method.
|
|
58
|
+
# @parameter path [String] The absolute path to match.
|
|
59
|
+
# @yields {|request, parameters| ...} The route handler.
|
|
60
|
+
# @returns [Router] The router.
|
|
61
|
+
define_method(name) do |path, &handler|
|
|
62
|
+
route(path, methods: method, &handler)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Dispatch a request to a matching route.
|
|
67
|
+
# @parameter request [Protocol::HTTP::Request] The request to dispatch.
|
|
68
|
+
# @returns [Protocol::HTTP::Response | Nil] The handler or error response, or `nil` when no path matches.
|
|
69
|
+
def call(request)
|
|
70
|
+
reference = parse_reference(request.path)
|
|
71
|
+
return Protocol::HTTP::Response[400] unless reference
|
|
72
|
+
|
|
73
|
+
unless handlers = @routes[reference.path]
|
|
74
|
+
return nil
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
unless handler = handlers[request.method] || handlers[ANY_METHOD]
|
|
78
|
+
allowed_methods = handlers.keys.compact.sort.join(", ")
|
|
79
|
+
return Protocol::HTTP::Response[405, [["allow", allowed_methods]]]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
parameters = parse_query(reference)
|
|
83
|
+
return Protocol::HTTP::Response[400] unless parameters
|
|
84
|
+
|
|
85
|
+
return handler.call(request, parameters)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def route_path(path)
|
|
91
|
+
url = Protocol::URL[path]
|
|
92
|
+
|
|
93
|
+
unless url && !url.is_a?(Protocol::URL::Absolute)
|
|
94
|
+
raise ArgumentError, "Route must be an absolute path: #{path.inspect}!"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
reference = Protocol::URL::Reference[url]
|
|
98
|
+
|
|
99
|
+
unless reference.path.absolute?
|
|
100
|
+
raise ArgumentError, "Route must be an absolute path: #{path.inspect}!"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
if reference.query? || reference.fragment?
|
|
104
|
+
raise ArgumentError, "Route path cannot include a query or fragment: #{path.inspect}!"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
return reference.path.freeze
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def route_methods(methods)
|
|
111
|
+
return [ANY_METHOD] unless methods
|
|
112
|
+
|
|
113
|
+
methods = Array(methods)
|
|
114
|
+
raise ArgumentError, "At least one HTTP method is required!" if methods.empty?
|
|
115
|
+
|
|
116
|
+
return methods.map do |method|
|
|
117
|
+
raise ArgumentError, "HTTP method cannot be nil!" unless method
|
|
118
|
+
|
|
119
|
+
method.to_s.upcase
|
|
120
|
+
end.uniq
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def parse_reference(path)
|
|
124
|
+
reference = Protocol::URL::Reference[path]
|
|
125
|
+
return if reference&.fragment?
|
|
126
|
+
|
|
127
|
+
return reference
|
|
128
|
+
rescue ArgumentError
|
|
129
|
+
nil
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def parse_query(reference)
|
|
133
|
+
reference.parse_query! || EMPTY_PARAMETERS
|
|
134
|
+
rescue ArgumentError
|
|
135
|
+
nil
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
data/lib/lively/version.rb
CHANGED
data/lib/lively.rb
CHANGED
data/readme.md
CHANGED
|
@@ -20,6 +20,10 @@ Please see the [project documentation](https://socketry.github.io/lively/) for m
|
|
|
20
20
|
|
|
21
21
|
Please see the [project releases](https://socketry.github.io/lively/releases/index) for all releases.
|
|
22
22
|
|
|
23
|
+
### v0.18.0
|
|
24
|
+
|
|
25
|
+
- Add support for HTTY.
|
|
26
|
+
|
|
23
27
|
### v0.17.0
|
|
24
28
|
|
|
25
29
|
- Expose shared application state via `Application[..., controller: Controller.new]`.
|
|
@@ -57,13 +61,6 @@ Please see the [project releases](https://socketry.github.io/lively/releases/ind
|
|
|
57
61
|
|
|
58
62
|
- Tidied up gem dependencies.
|
|
59
63
|
|
|
60
|
-
### v0.13.0
|
|
61
|
-
|
|
62
|
-
- Added `live-audio` support for background and positional audio in applications.
|
|
63
|
-
- Added game audio example demonstrating audio playback.
|
|
64
|
-
- Fixed serving non-existent asset paths gracefully.
|
|
65
|
-
- Achieved 100% test and documentation coverage.
|
|
66
|
-
|
|
67
64
|
## See Also
|
|
68
65
|
|
|
69
66
|
- [live](https://github.com/socketry/live) — Provides client-server communication using websockets.
|
data/releases.md
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.19.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -122,6 +122,20 @@ dependencies:
|
|
|
122
122
|
- - "~>"
|
|
123
123
|
- !ruby/object:Gem::Version
|
|
124
124
|
version: '0.18'
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: protocol-url
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - "~>"
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '0.18'
|
|
132
|
+
type: :runtime
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - "~>"
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0.18'
|
|
125
139
|
- !ruby/object:Gem::Dependency
|
|
126
140
|
name: xrb
|
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -155,9 +169,11 @@ files:
|
|
|
155
169
|
- lib/lively/environment/htty.rb
|
|
156
170
|
- lib/lively/environment/middleware.rb
|
|
157
171
|
- lib/lively/hello_world.rb
|
|
172
|
+
- lib/lively/page.rb
|
|
173
|
+
- lib/lively/page.xrb
|
|
158
174
|
- lib/lively/pages/index.rb
|
|
159
|
-
- lib/lively/pages/index.xrb
|
|
160
175
|
- lib/lively/resolver.rb
|
|
176
|
+
- lib/lively/router.rb
|
|
161
177
|
- lib/lively/version.rb
|
|
162
178
|
- license.md
|
|
163
179
|
- public/_components/@socketry/live-audio/Live/Audio.js
|
|
@@ -203,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
203
219
|
- !ruby/object:Gem::Version
|
|
204
220
|
version: '0'
|
|
205
221
|
requirements: []
|
|
206
|
-
rubygems_version: 4.0.
|
|
222
|
+
rubygems_version: 4.0.10
|
|
207
223
|
specification_version: 4
|
|
208
224
|
summary: A simple client-server SPA framework.
|
|
209
225
|
test_files: []
|
metadata.gz.sig
CHANGED
|
@@ -1,5 +1 @@
|
|
|
1
|
-
|
|
2
|
-
pլ�U �*M�\Z��)�X�N�U�_Z�Rj��7�D^���×lb�� �)��p�8P
|
|
3
|
-
��
|
|
4
|
-
W�����jA����D���F��0���,�
|
|
5
|
-
�W|؛~�zV�w̤�m���C
|
|
1
|
+
Rg�D��O�(���>W�`C=W`�R��T:=�����*-��y�;�}@l��!>K��n�K��_�@*�E���B�����r��aO�9��o=�cx�������z�l'Hm�(�/�*@�o����K��ל�T��`UOӕ�z�H��u�_t�����x��F�֥�<;u4x2��G;��E�s��`��V�S�۾��Yw7p�Z�D����Z����
|
data/lib/lively/pages/index.xrb
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<title>#{self.title}</title>
|
|
5
|
-
|
|
6
|
-
<meta charset="UTF-8" />
|
|
7
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
8
|
-
|
|
9
|
-
<link rel="icon" type="image/png" href="/_static/icon.png" />
|
|
10
|
-
<link rel="stylesheet" href="/_static/site.css" type="text/css" media="screen" />
|
|
11
|
-
<link rel="stylesheet" href="/_static/index.css" type="text/css" media="screen" />
|
|
12
|
-
|
|
13
|
-
<script type="importmap">
|
|
14
|
-
{
|
|
15
|
-
"imports": {
|
|
16
|
-
"live": "/_components/@socketry/live/Live.js",
|
|
17
|
-
"live-audio": "/_components/@socketry/live-audio/Live/Audio.js",
|
|
18
|
-
"morphdom": "/_components/morphdom/morphdom-esm.js"
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
</script>
|
|
22
|
-
|
|
23
|
-
<script type="module" src="/application.js"></script>
|
|
24
|
-
</head>
|
|
25
|
-
|
|
26
|
-
<body>
|
|
27
|
-
#{self.body&.to_html || "No body specified!"}
|
|
28
|
-
</body>
|
|
29
|
-
</html>
|