raindeer 0.1.1 → 0.2.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/lib/boot.rb +21 -10
- data/lib/matrix/cursor.rb +32 -0
- data/lib/matrix/matrix.rb +87 -0
- data/lib/matrix/stream.rb +144 -0
- data/lib/raindeer.rb +1 -1
- data/lib/router/route_event.rb +1 -1
- data/lib/router/router.rb +9 -13
- data/lib/router/trie.rb +1 -1
- data/lib/support/config_loader.rb +28 -0
- data/lib/system/dashboard_node.rbx +11 -0
- data/lib/system/error_404_node.rbx +11 -0
- data/lib/system/events_node.rbx +34 -0
- data/lib/system/layout_node.rbx +40 -0
- data/lib/system/routes_node.rbx +34 -0
- data/lib/system/system.rb +4 -2
- data/lib/version.rb +1 -1
- metadata +53 -4
- data/lib/system/page_not_found_node.rb +0 -13
- data/lib/system/system_node.rb +0 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8783b509655bf58e51f0e7afc0aa320832d8aee1b298d50771d4121c418b28d1
|
|
4
|
+
data.tar.gz: 1eaf600341059527fc7dc013df6cc24a3bc1ba7d606a34b5544b73630e10b0ea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6328d1ed634f9e501e8abd946deccfe13e39ee68b863273bc5c6aa8b4822e449b9462bf3027a5147382802f0af2367995af807ac2144a18e66247f0c2603ceb0
|
|
7
|
+
data.tar.gz: 2268752ea3dbb8e0eb8190f007fa8263ac37e0eff6f4d1e737fbf04fc9760788e15af59e426d46e4bdd2105075a3795396f95a5d837136f6138a61c4263dc8db
|
data/lib/boot.rb
CHANGED
|
@@ -1,24 +1,35 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'low_dependency'
|
|
4
|
+
require 'low_event'
|
|
4
5
|
require 'low_loop'
|
|
6
|
+
require 'low_node'
|
|
7
|
+
require 'low_type'
|
|
8
|
+
require 'lowload'
|
|
5
9
|
|
|
6
|
-
require_relative '
|
|
10
|
+
require_relative 'matrix/matrix'
|
|
11
|
+
require_relative 'support/config_loader'
|
|
7
12
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
env = {
|
|
14
|
+
host: ENV.fetch('RAIN_HOST', nil),
|
|
15
|
+
port: ENV.fetch('RAIN_PORT', nil),
|
|
16
|
+
web_root: ENV.fetch('RAIN_WEB_ROOT', nil),
|
|
17
|
+
matrix_mode: Rain::ConfigLoader.parse_boolean(ENV.fetch('RAIN_MATRIX', nil)),
|
|
18
|
+
mirror_mode: Rain::ConfigLoader.parse_boolean(ENV.fetch('RAIN_MIRROR', nil))
|
|
19
|
+
}
|
|
12
20
|
|
|
13
|
-
config =
|
|
21
|
+
config = Rain::ConfigLoader.load('./config/config.yaml', env)
|
|
14
22
|
|
|
15
23
|
LowDependency.provide('rain.router') do
|
|
16
|
-
|
|
24
|
+
Rain::Router.new
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
LowDependency.provide('rain.matrix') do
|
|
28
|
+
Rain::Matrix.new(event_pool: Low::Providers['low.event.pool'])
|
|
17
29
|
end
|
|
18
30
|
|
|
19
31
|
LowDependency.provide('low.loop') do
|
|
20
|
-
|
|
21
|
-
LowLoop.new(config: config.new(host, port, matrix_mode, mirror_mode), router: Low::Providers['rain.router'])
|
|
32
|
+
LowLoop.new(config:, router: Low::Providers['rain.router'], renderer: Low::Providers['rain.matrix'])
|
|
22
33
|
end
|
|
23
34
|
|
|
24
|
-
|
|
35
|
+
LowLoad.dirload(File.expand_path('../system', __FILE__))
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rain
|
|
4
|
+
class Cursor
|
|
5
|
+
attr_accessor :index, :first_update, :last_update
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@index = -1
|
|
9
|
+
@first_update = now
|
|
10
|
+
@last_update = now
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def increment(delays:, inputs:, duration: nil)
|
|
14
|
+
next_index = @index + 1
|
|
15
|
+
duration = now - @last_update if duration.nil?
|
|
16
|
+
|
|
17
|
+
if delays[next_index] && duration >= delays[next_index]
|
|
18
|
+
@index += 1
|
|
19
|
+
@index = 0 if index >= inputs.count
|
|
20
|
+
@last_update = now
|
|
21
|
+
|
|
22
|
+
yield index
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def now
|
|
29
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'paint'
|
|
4
|
+
require 'low_event'
|
|
5
|
+
require 'observers'
|
|
6
|
+
|
|
7
|
+
require_relative '../support/config_loader'
|
|
8
|
+
require_relative 'stream'
|
|
9
|
+
|
|
10
|
+
module Rain
|
|
11
|
+
class Matrix
|
|
12
|
+
include Observers
|
|
13
|
+
|
|
14
|
+
observe Low::Events::EventPool
|
|
15
|
+
|
|
16
|
+
def initialize(event_pool:, config: Rain::ConfigLoader.load('./config/matrix.yaml'))
|
|
17
|
+
@event_pool = event_pool
|
|
18
|
+
@config = config
|
|
19
|
+
|
|
20
|
+
@screen_size = nil
|
|
21
|
+
|
|
22
|
+
@last_stream_index = -1
|
|
23
|
+
@streams = {}
|
|
24
|
+
@columns = []
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def render(screen_size:, show_output: true)
|
|
28
|
+
if screen_size != @screen_size
|
|
29
|
+
@screen_size = screen_size
|
|
30
|
+
redraw_streams
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
render_streams(show_output:)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def update(stream_id:, event_tree:)
|
|
37
|
+
upsert_stream(stream_id:, event_tree:)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def redraw_streams
|
|
43
|
+
@event_pool.event_trees.each do |stream_id, event_tree|
|
|
44
|
+
stream = upsert_stream(stream_id:, event_tree:)
|
|
45
|
+
stream.redraw(cell_count: @screen_size[:row_count])
|
|
46
|
+
|
|
47
|
+
@columns[stream.index] = upsert_stream(stream_id:, event_tree:)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def render_streams(show_output:)
|
|
52
|
+
@streams.each_value { |stream| stream.render }
|
|
53
|
+
|
|
54
|
+
(0...@screen_size[:row_count]).each do |row_index|
|
|
55
|
+
cell_outputs = []
|
|
56
|
+
cell_colors = []
|
|
57
|
+
|
|
58
|
+
# Rendering streams can happen before redrawing streams and @columns haven't been populated yet (empty event pool).
|
|
59
|
+
(0...@screen_size[:column_count]).each do |column_index|
|
|
60
|
+
cell_colors << (@columns[column_index].nil? ? nil : @columns[column_index].colors[row_index])
|
|
61
|
+
cell_outputs << (@columns[column_index].nil? ? nil : @columns[column_index].outputs[row_index])
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
output = cell_outputs.zip(cell_colors).map do |cell, color|
|
|
65
|
+
cell ? Paint[cell, color] : Paint[' ', nil]
|
|
66
|
+
end.join(' ')
|
|
67
|
+
|
|
68
|
+
puts output if show_output
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def upsert_stream(stream_id:, event_tree:)
|
|
73
|
+
@streams[stream_id] ||= Stream.new(index:, config: @config, event_tree:)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def index
|
|
77
|
+
case @config.start_col
|
|
78
|
+
when :random
|
|
79
|
+
rand(0...@screen_size[:column_count])
|
|
80
|
+
when :latest
|
|
81
|
+
@last_stream_index += 1
|
|
82
|
+
return @last_stream_index = 0 if @last_stream_index >= @screen_size[:column_count]
|
|
83
|
+
@last_stream_index
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'cursor'
|
|
4
|
+
|
|
5
|
+
module Rain
|
|
6
|
+
class Stream
|
|
7
|
+
attr_reader :index, :colors, :outputs
|
|
8
|
+
|
|
9
|
+
ARROW = ['│', '▼']
|
|
10
|
+
|
|
11
|
+
def initialize(index:, config:, event_tree:)
|
|
12
|
+
@index = index
|
|
13
|
+
@config = config
|
|
14
|
+
|
|
15
|
+
@event_tree = event_tree
|
|
16
|
+
@event_cursor = 0
|
|
17
|
+
@redraw_cursor = 0
|
|
18
|
+
|
|
19
|
+
@inputs = []
|
|
20
|
+
@delays = []
|
|
21
|
+
@colors = []
|
|
22
|
+
@outputs = []
|
|
23
|
+
|
|
24
|
+
@head_cursor = Cursor.new
|
|
25
|
+
@tail_cursor = Cursor.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Layout cells to fit the current cell count. Called on matrix initialization, screen size changes and on new events.
|
|
29
|
+
def redraw(cell_count:)
|
|
30
|
+
@inputs.fill(nil, 0...cell_count)[0...cell_count]
|
|
31
|
+
@delays.fill(@config.min_delay, 0...cell_count)[0...cell_count]
|
|
32
|
+
@colors.fill(@config.cell_color, 0...cell_count)[0...cell_count]
|
|
33
|
+
|
|
34
|
+
(@event_cursor...@event_tree.sequence.count).each do |event_index|
|
|
35
|
+
current_event = @event_tree.sequence[event_index]
|
|
36
|
+
past_event = @event_tree.sequence[event_index - 1]
|
|
37
|
+
|
|
38
|
+
redraw_event(current_event:, past_event:)
|
|
39
|
+
@event_cursor += 1
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
@inputs
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Render a cell's input as output after a delay, using cursors. Called on every frame.
|
|
46
|
+
# ┌─┐
|
|
47
|
+
# │R│ <-- Each cell is represented as an input, delay and output.
|
|
48
|
+
# │e│
|
|
49
|
+
# │q│ <-- The head cursor outputs the cell's input after a delay and colors the leading cell white.
|
|
50
|
+
# │ │
|
|
51
|
+
# │ │ <-- The tail cursor does the same thing but the input (and therefore output) will be empty.
|
|
52
|
+
# └─┘ The tail cursor can be before or after the head cursor depending on whether events wrap around.
|
|
53
|
+
#
|
|
54
|
+
# Unit tests use "duration" to skip forwards in time, while matrix spec and the real world use old fashioned linear time.
|
|
55
|
+
def render(duration: nil)
|
|
56
|
+
@head_cursor.increment(delays:, inputs:, duration:) do |index|
|
|
57
|
+
prev_index = (index - 1).clamp(0, nil)
|
|
58
|
+
next_index = index >= @inputs.count ? 0 : index + 1
|
|
59
|
+
|
|
60
|
+
if @inputs[index]
|
|
61
|
+
@outputs[index] = @inputs[index]
|
|
62
|
+
@delays[index] = @config.fade_delay
|
|
63
|
+
@colors[prev_index] = @config.cell_color if @colors[prev_index]
|
|
64
|
+
@colors[index] = @outputs[next_index] ? @config.cell_color : @config.lead_color
|
|
65
|
+
@inputs[index] = nil
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
fade(duration:) if @config.fade
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
attr_reader :inputs, :delays
|
|
75
|
+
|
|
76
|
+
def fade(duration: nil)
|
|
77
|
+
fade_start = rand(5_000..10_000)
|
|
78
|
+
|
|
79
|
+
if (now - @head_cursor.first_update) >= fade_start || (duration && duration >= fade_start)
|
|
80
|
+
@tail_cursor.increment(delays:, inputs:, duration:) do |index|
|
|
81
|
+
if @inputs[index].nil? && @outputs[index]
|
|
82
|
+
@outputs[index] = @inputs[index]
|
|
83
|
+
@delays[index] = 0
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def now
|
|
90
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# A column of cells representing sequential events.
|
|
94
|
+
# ┌─┐
|
|
95
|
+
# │R│ FIRST EVENT
|
|
96
|
+
# │e│ Each cell will render input as output after a minimum delay (since there's no prior event).
|
|
97
|
+
# │q│
|
|
98
|
+
# │u│
|
|
99
|
+
# │e│
|
|
100
|
+
# │s│
|
|
101
|
+
# │t│
|
|
102
|
+
# └─┘ <-- Time has passed between events.
|
|
103
|
+
# ┌─┐
|
|
104
|
+
# │││ SECOND EVENT
|
|
105
|
+
# │▼│ The next event has data to work with, it can represent the time it took to get from the previous event to the next.
|
|
106
|
+
# │R│ Each cell will render after the largest duration of the following values:
|
|
107
|
+
# │o│ 1. The minimum delay
|
|
108
|
+
# │u│ 2. The time elapsed between events divided by the number of inputs
|
|
109
|
+
# │t│
|
|
110
|
+
# │e│ <-- A cursor moves to the next cell after a delay and colors the leading cell white.
|
|
111
|
+
# └─┘
|
|
112
|
+
def redraw_event(current_event:, past_event:)
|
|
113
|
+
if @event_cursor == 0
|
|
114
|
+
inputs = event_name(current_event:)
|
|
115
|
+
delay = @config.min_delay
|
|
116
|
+
randomize_start_row
|
|
117
|
+
else
|
|
118
|
+
inputs = [*ARROW, *event_name(current_event:)]
|
|
119
|
+
difference = current_event.created_at - past_event.created_at
|
|
120
|
+
delay = difference == 0 ? @config.min_delay : (difference / inputs.count).to_i.clamp(@config.min_delay, nil)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
inputs.each do |character|
|
|
124
|
+
@inputs[@redraw_cursor] = character
|
|
125
|
+
@delays[@redraw_cursor] = @redraw_cursor == 0 ? 0 : delay # Don't add delay to the first cell, looks stuck.
|
|
126
|
+
|
|
127
|
+
@redraw_cursor += 1
|
|
128
|
+
@redraw_cursor = 0 if @redraw_cursor >= @inputs.count
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def event_name(current_event:)
|
|
133
|
+
current_event.class.name.split('::').last.delete_suffix('Event').chars
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def randomize_start_row
|
|
137
|
+
return unless @config.start_row == :random
|
|
138
|
+
|
|
139
|
+
random_index = rand(0..2)
|
|
140
|
+
@redraw_cursor = random_index
|
|
141
|
+
@head_cursor.index = random_index - 1 # Head cursor always 1 index behind to make "increment" method's logic simple.
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
data/lib/raindeer.rb
CHANGED
data/lib/router/route_event.rb
CHANGED
data/lib/router/router.rb
CHANGED
|
@@ -33,19 +33,19 @@ module Rain
|
|
|
33
33
|
@breadcrumbs.pop
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
def get(path
|
|
36
|
+
def get(path, &block)
|
|
37
37
|
route(path, 'GET', &block)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
def post(path
|
|
40
|
+
def post(path, &block)
|
|
41
41
|
route(path, 'POST', &block)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
def update(path
|
|
44
|
+
def update(path, &block)
|
|
45
45
|
route(path, 'UPDATE', &block)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
def delete(path
|
|
48
|
+
def delete(path, &block)
|
|
49
49
|
route(path, 'DELETE', &block)
|
|
50
50
|
end
|
|
51
51
|
|
|
@@ -54,18 +54,14 @@ module Rain
|
|
|
54
54
|
def handle(event:)
|
|
55
55
|
response_event = nil
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
# The last route event will render a response event which we want to return to the request event.
|
|
58
|
+
@trie.match(path: event.request.path.delete_suffix('/')).each do |route_event|
|
|
59
|
+
response_event = route_event.trigger
|
|
59
60
|
end
|
|
60
61
|
|
|
61
|
-
if response_event
|
|
62
|
-
status = LowType::Status[404]
|
|
63
|
-
response_event = trigger status, action: :render, event: Low::Events::StatusEvent.new(status:, request: event.request)
|
|
64
|
-
end
|
|
62
|
+
return response_event if response_event
|
|
65
63
|
|
|
66
|
-
|
|
64
|
+
Low::Events::StatusEvent.trigger(status: Low::Types::Status[404], request: event.request)
|
|
67
65
|
end
|
|
68
66
|
end
|
|
69
67
|
end
|
|
70
|
-
|
|
71
|
-
RainRouter = Rain::Router
|
data/lib/router/trie.rb
CHANGED
|
@@ -66,7 +66,7 @@ module Rain
|
|
|
66
66
|
|
|
67
67
|
private
|
|
68
68
|
|
|
69
|
-
#
|
|
69
|
+
# Mid nodes handle events, end nodes render events.
|
|
70
70
|
def route_event(next_index:, params:, path:, route:)
|
|
71
71
|
action = path[next_index].nil? ? :render : :handle
|
|
72
72
|
RouteEvent.new(action:, route:, params:)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'ostruct'
|
|
4
|
+
require 'yaml'
|
|
5
|
+
|
|
6
|
+
module Rain
|
|
7
|
+
class ConfigLoader
|
|
8
|
+
class << self
|
|
9
|
+
def load(filepath, overrides = {})
|
|
10
|
+
config_file = YAML.safe_load_file(filepath, permitted_classes: [Symbol], symbolize_names: true)
|
|
11
|
+
|
|
12
|
+
# Environment variables override config file.
|
|
13
|
+
config_data = config_file.merge(overrides) do |_key, old_value, new_value|
|
|
14
|
+
new_value.nil? ? old_value : new_value
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
OpenStruct.new(config_data)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def parse_boolean(value)
|
|
21
|
+
return true if value == '1'
|
|
22
|
+
return false if value == '0'
|
|
23
|
+
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class EventsNode < LowNode
|
|
4
|
+
observe '/system/events'
|
|
5
|
+
|
|
6
|
+
def initialize(event:)
|
|
7
|
+
@events = 'TODO: Populate via LowEvent inherited hook.'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def render(event:)
|
|
11
|
+
<{ LayoutNode: }>
|
|
12
|
+
<h1>{"Events"}</h1>
|
|
13
|
+
|
|
14
|
+
{@events}
|
|
15
|
+
|
|
16
|
+
<table>
|
|
17
|
+
<thead>
|
|
18
|
+
<tr>
|
|
19
|
+
<th scope="col">HTTP Verbs</th>
|
|
20
|
+
<th scope="col">Route</th>
|
|
21
|
+
<th scope="col">Observers</th>
|
|
22
|
+
</tr>
|
|
23
|
+
</thead>
|
|
24
|
+
<tbody>
|
|
25
|
+
<tr>
|
|
26
|
+
<td>1</td>
|
|
27
|
+
<td>2</td>
|
|
28
|
+
<td>3</td>
|
|
29
|
+
</tr>
|
|
30
|
+
</tbody>
|
|
31
|
+
</table>
|
|
32
|
+
<{ :LayoutNode }>
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class LayoutNode < LowNode
|
|
4
|
+
def render(event:)
|
|
5
|
+
<html>
|
|
6
|
+
<head>
|
|
7
|
+
<link rel="stylesheet" href="/assets/pico.min.css">
|
|
8
|
+
<link rel="stylesheet" href="/assets/system.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<header>
|
|
12
|
+
<div class="container">
|
|
13
|
+
<a id="favicon" href="/system"><img src="/assets/favicon.svg"/></a>
|
|
14
|
+
<nav id="navbar">
|
|
15
|
+
<ul>
|
|
16
|
+
<li><a href="/system">{"Dashboard"}</a>
|
|
17
|
+
<li><a href="/system/events">{"Events"}</a>
|
|
18
|
+
<li><a href="/system/routes">{"Routes"}</a>
|
|
19
|
+
</ul>
|
|
20
|
+
</nav>
|
|
21
|
+
</div>
|
|
22
|
+
</header>
|
|
23
|
+
|
|
24
|
+
<main class="container">
|
|
25
|
+
<{ :slot }>
|
|
26
|
+
</main>
|
|
27
|
+
|
|
28
|
+
<footer>
|
|
29
|
+
<div class="container">
|
|
30
|
+
<ul>
|
|
31
|
+
<li><a href="https://raindeer.dev">{"Website"}</a></li>
|
|
32
|
+
<li><a href="https://raindeer.dev/docs">{"Docs"}</a></li>
|
|
33
|
+
<li><a href="https://github.com/raindeer-rb/raindeer">{"Source code"}</a></li>
|
|
34
|
+
</ul>
|
|
35
|
+
</div>
|
|
36
|
+
</footer>
|
|
37
|
+
</body>
|
|
38
|
+
</html>
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class RoutesNode < LowNode
|
|
4
|
+
observe '/system/routes'
|
|
5
|
+
|
|
6
|
+
def initialize(event:)
|
|
7
|
+
@routes = Low::Providers['rain.router'].routes
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def render(event:)
|
|
11
|
+
<{ LayoutNode: }>
|
|
12
|
+
<h1>{"Routes"}</h1>
|
|
13
|
+
|
|
14
|
+
{@routes}
|
|
15
|
+
|
|
16
|
+
<table>
|
|
17
|
+
<thead>
|
|
18
|
+
<tr>
|
|
19
|
+
<th scope="col">HTTP Verbs</th>
|
|
20
|
+
<th scope="col">Route</th>
|
|
21
|
+
<th scope="col">Observers</th>
|
|
22
|
+
</tr>
|
|
23
|
+
</thead>
|
|
24
|
+
<tbody>
|
|
25
|
+
<tr>
|
|
26
|
+
<td>1</td>
|
|
27
|
+
<td>2</td>
|
|
28
|
+
<td>3</td>
|
|
29
|
+
</tr>
|
|
30
|
+
</tbody>
|
|
31
|
+
</table>
|
|
32
|
+
<{ :LayoutNode }>
|
|
33
|
+
end
|
|
34
|
+
end
|
data/lib/system/system.rb
CHANGED
data/lib/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: raindeer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- maedi
|
|
@@ -9,6 +9,34 @@ bindir: exe
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: ostruct
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: paint
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
12
40
|
- !ruby/object:Gem::Dependency
|
|
13
41
|
name: low_dependency
|
|
14
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -93,6 +121,20 @@ dependencies:
|
|
|
93
121
|
- - "~>"
|
|
94
122
|
- !ruby/object:Gem::Version
|
|
95
123
|
version: '1.0'
|
|
124
|
+
- !ruby/object:Gem::Dependency
|
|
125
|
+
name: lowload
|
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - ">="
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: '0'
|
|
131
|
+
type: :runtime
|
|
132
|
+
prerelease: false
|
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - ">="
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '0'
|
|
96
138
|
- !ruby/object:Gem::Dependency
|
|
97
139
|
name: antlers
|
|
98
140
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -143,15 +185,22 @@ extensions: []
|
|
|
143
185
|
extra_rdoc_files: []
|
|
144
186
|
files:
|
|
145
187
|
- lib/boot.rb
|
|
188
|
+
- lib/matrix/cursor.rb
|
|
189
|
+
- lib/matrix/matrix.rb
|
|
190
|
+
- lib/matrix/stream.rb
|
|
146
191
|
- lib/raindeer.rb
|
|
147
192
|
- lib/router/route.rb
|
|
148
193
|
- lib/router/route_event.rb
|
|
149
194
|
- lib/router/router.rb
|
|
150
195
|
- lib/router/trie.rb
|
|
151
196
|
- lib/router/trie_node.rb
|
|
152
|
-
- lib/
|
|
197
|
+
- lib/support/config_loader.rb
|
|
198
|
+
- lib/system/dashboard_node.rbx
|
|
199
|
+
- lib/system/error_404_node.rbx
|
|
200
|
+
- lib/system/events_node.rbx
|
|
201
|
+
- lib/system/layout_node.rbx
|
|
202
|
+
- lib/system/routes_node.rbx
|
|
153
203
|
- lib/system/system.rb
|
|
154
|
-
- lib/system/system_node.rb
|
|
155
204
|
- lib/version.rb
|
|
156
205
|
homepage: https://codeberg.org/raindeer/raindeer
|
|
157
206
|
licenses: []
|
|
@@ -172,7 +221,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
172
221
|
- !ruby/object:Gem::Version
|
|
173
222
|
version: '0'
|
|
174
223
|
requirements: []
|
|
175
|
-
rubygems_version:
|
|
224
|
+
rubygems_version: 4.0.6
|
|
176
225
|
specification_version: 4
|
|
177
226
|
summary: Deer to be different
|
|
178
227
|
test_files: []
|