raindeer 0.1.2 → 0.3.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 +17 -7
- data/lib/matrix/cursor.rb +51 -0
- data/lib/matrix/matrix.rb +111 -0
- data/lib/matrix/stream.rb +186 -0
- data/lib/raindeer.rb +3 -3
- data/lib/router/route_event.rb +1 -1
- data/lib/router/router.rb +10 -14
- data/lib/router/trie.rb +1 -1
- data/lib/support/config_loader.rb +3 -3
- 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 +43 -9
- 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: 8d79ce92ce79c7e46122adb4e9ba39968d9bf220cca424deba580f3a74ac26c6
|
|
4
|
+
data.tar.gz: 6970876678afd4bf8395135d52a9c1ff20858170282e3c97c540f1458338d4ea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 947dd01220faa492c9abf83e78aae9ec07d5e6c5794724aa24dda43be39e8fe82822734c068612f1176246c64b3e5f34e205d5b2fbb7d5517be598d3e927b1f2
|
|
7
|
+
data.tar.gz: bb4fff4358eae10a7b3ee1085ad9bd35bd7ac795c639dcb48b8e7c7deea3055e592a1f196931fc98ad64161631b5107bd1ca75fe29086d415891e7cfc23d84d3
|
data/lib/boot.rb
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require '
|
|
4
|
-
require '
|
|
3
|
+
require 'low_event'
|
|
4
|
+
require 'low_node'
|
|
5
|
+
require 'low_type'
|
|
6
|
+
require 'providers'
|
|
5
7
|
|
|
6
8
|
require_relative 'support/config_loader'
|
|
7
9
|
|
|
@@ -15,12 +17,20 @@ env = {
|
|
|
15
17
|
|
|
16
18
|
config = Rain::ConfigLoader.load('./config/config.yaml', env)
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
Providers.define('rain.router') do
|
|
21
|
+
require_relative 'router/router'
|
|
22
|
+
Rain::Router.new
|
|
20
23
|
end
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
Providers.define('rain.matrix') do
|
|
26
|
+
require_relative 'matrix/matrix'
|
|
27
|
+
Rain::Matrix.new(event_pool: Providers['low.event.pool'])
|
|
24
28
|
end
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
Providers.define('low.loop') do
|
|
31
|
+
require 'low_loop'
|
|
32
|
+
LowLoop.new(config:, router: Providers['rain.router'], renderer: Providers['rain.matrix'])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
require 'lowload'
|
|
36
|
+
LowLoad.dirload(File.expand_path('system', __dir__))
|
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
# Unit tests use "duration" to skip forward in time, while feature tests and the real world use old fashioned linear time.
|
|
14
|
+
def increment(delays:, inputs:, duration: nil)
|
|
15
|
+
next_index = next_index(loop_count: inputs.count)
|
|
16
|
+
|
|
17
|
+
return unless delays[next_index] && (duration || now - @last_update) >= delays[next_index]
|
|
18
|
+
|
|
19
|
+
@index = next_index
|
|
20
|
+
@last_update = now
|
|
21
|
+
|
|
22
|
+
yield index
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def iterate(inputs:, loop_count:)
|
|
26
|
+
inputs.each do |input|
|
|
27
|
+
@index += 1
|
|
28
|
+
@index = 0 if index >= loop_count
|
|
29
|
+
|
|
30
|
+
yield index, input
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def increase_index(loop_count:)
|
|
35
|
+
@index += 1
|
|
36
|
+
@index = 0 if @index >= loop_count
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def next_index(loop_count:)
|
|
40
|
+
next_index = @index + 1
|
|
41
|
+
next_index = 0 if next_index >= loop_count
|
|
42
|
+
next_index
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def now
|
|
48
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'low_event'
|
|
4
|
+
require 'observers'
|
|
5
|
+
require 'paint'
|
|
6
|
+
|
|
7
|
+
require_relative '../support/config_loader'
|
|
8
|
+
require_relative 'stream'
|
|
9
|
+
|
|
10
|
+
module Rain
|
|
11
|
+
class Matrix
|
|
12
|
+
include Observers
|
|
13
|
+
|
|
14
|
+
def initialize(event_pool:, config: ConfigLoader.load('./config/matrix.yaml'))
|
|
15
|
+
@event_pool = event_pool
|
|
16
|
+
@config = config
|
|
17
|
+
|
|
18
|
+
@screen_size = nil
|
|
19
|
+
|
|
20
|
+
@last_stream_index = -1
|
|
21
|
+
@streams = {} # TODO: Could be a "stream pool" like event pool (a pool hash).
|
|
22
|
+
@columns = []
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def redraw(screen_size:)
|
|
26
|
+
@setup ||= setup
|
|
27
|
+
|
|
28
|
+
@streams.each_value do |stream|
|
|
29
|
+
stream.redraw(cell_count: screen_size[:row_count])
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def render(screen_size:, show_output: true)
|
|
34
|
+
if screen_size != @screen_size
|
|
35
|
+
@screen_size = screen_size
|
|
36
|
+
redraw(screen_size:)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
render_streams(show_output:)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# TODO: Introduce "on :new_event_tree do |event|" block construct in LowEvent for making event handlers more obvious.
|
|
43
|
+
# TODO: Observers should allow arbitrary params when triggering and observing.
|
|
44
|
+
def new_event_tree(event: Low::Events::EventTree)
|
|
45
|
+
stream = upsert_stream(event_tree: event)
|
|
46
|
+
stream.redraw(cell_count: @screen_size[:row_count])
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def setup
|
|
52
|
+
@event_pool.event_trees.each_value do |event_tree|
|
|
53
|
+
upsert_stream(event_tree:)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
observe @event_pool
|
|
57
|
+
|
|
58
|
+
true
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def upsert_stream(event_tree:)
|
|
62
|
+
stream = @streams[event_tree.request_id] ||= Stream.new(index: generate_index, config: @config, event_tree:)
|
|
63
|
+
@columns[stream.index] = stream
|
|
64
|
+
stream
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def render_streams(show_output: true) # rubocop:disable Metrics/AbcSize
|
|
68
|
+
@streams.each_value(&:render)
|
|
69
|
+
|
|
70
|
+
return unless show_output
|
|
71
|
+
|
|
72
|
+
output = ''.dup
|
|
73
|
+
|
|
74
|
+
(0...@screen_size[:row_count]).each do |row_index|
|
|
75
|
+
cell_outputs = []
|
|
76
|
+
cell_colors = []
|
|
77
|
+
|
|
78
|
+
# Rendering streams can happen before redrawing streams, so @columns may not be populated yet.
|
|
79
|
+
(0...column_count).each do |column_index|
|
|
80
|
+
cell_colors << (@columns[column_index].nil? ? nil : @columns[column_index].colors[row_index])
|
|
81
|
+
cell_outputs << (@columns[column_index].nil? ? nil : @columns[column_index].outputs[row_index])
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
output << "\n" + cell_outputs.zip(cell_colors).map do |cell, color| # rubocop:disable Style/StringConcatenation
|
|
85
|
+
cell ? Paint[cell, color] : Paint[' ']
|
|
86
|
+
end.join(' ')
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
system 'clear'
|
|
90
|
+
print output.delete_prefix("\n").delete_suffix("\n")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def generate_index
|
|
94
|
+
case @config.start_col
|
|
95
|
+
when :random
|
|
96
|
+
rand(0...column_count)
|
|
97
|
+
when :latest
|
|
98
|
+
@last_stream_index += 1
|
|
99
|
+
return @last_stream_index = 0 if @last_stream_index >= column_count
|
|
100
|
+
|
|
101
|
+
@last_stream_index
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def column_count
|
|
106
|
+
return @screen_size[:column_count] if @screen_size[:column_count] < 10
|
|
107
|
+
|
|
108
|
+
(@screen_size[:column_count] / 2).to_i.clamp(1, nil)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'low_event'
|
|
4
|
+
require 'observers'
|
|
5
|
+
|
|
6
|
+
require_relative 'cursor'
|
|
7
|
+
|
|
8
|
+
module Rain
|
|
9
|
+
class Stream
|
|
10
|
+
include Observers
|
|
11
|
+
attr_reader :index, :inputs, :outputs, :delays, :colors, :head_cursor, :tail_cursor
|
|
12
|
+
|
|
13
|
+
ARROW = ['│', '▼'].freeze
|
|
14
|
+
|
|
15
|
+
def initialize(index:, config:, event_tree:)
|
|
16
|
+
@index = index
|
|
17
|
+
@config = config
|
|
18
|
+
@event_tree = event_tree
|
|
19
|
+
|
|
20
|
+
@inputs = []
|
|
21
|
+
@delays = []
|
|
22
|
+
@colors = []
|
|
23
|
+
@outputs = []
|
|
24
|
+
|
|
25
|
+
@event_cursor = 0
|
|
26
|
+
@head_cursor = Cursor.new
|
|
27
|
+
@tail_cursor = Cursor.new
|
|
28
|
+
@tail_cursor.index = 0
|
|
29
|
+
|
|
30
|
+
@show_cursor = Cursor.new
|
|
31
|
+
@hide_cursor = Cursor.new
|
|
32
|
+
|
|
33
|
+
observe event_tree
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# TODO: Use "on :branch do |event|" syntax.
|
|
37
|
+
def branch(event: Low::Events::BranchEvent) # rubocop:disable Lint/UnusedMethodArgument
|
|
38
|
+
redraw(cell_count: @inputs.count)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Draw event names onto the current amount of cells in a stream, using cursors. Called when there's a new event.
|
|
42
|
+
#
|
|
43
|
+
# INPUT DELAY OUTPUT
|
|
44
|
+
# ┌─────┬─────┬─────┐
|
|
45
|
+
# │ R │ 75 │ │ ◀── 2. Tail Cursor begins at index zero or a random starting index.
|
|
46
|
+
# │ e │ 75 │ │ A Head Cursor that wraps around will push the Tail Cursor down to be just beneath it.
|
|
47
|
+
# │ q │ 75 │ │ The Show Cursor will start at the Tail Cursor index.
|
|
48
|
+
# │ u │ 75 │ │
|
|
49
|
+
# │ │ 75 │ │ ◀── 1. Head Cursor begins at index zero or a random starting index.
|
|
50
|
+
# │ │ 75 │ │ It populates input for every character in an event name.
|
|
51
|
+
# │ │ 75 │ │ Then sets a "75" delay for the Show Cursor.
|
|
52
|
+
# └─────┴─────┴─────┘
|
|
53
|
+
def redraw(cell_count:)
|
|
54
|
+
randomize_start_index if first_cell_redraw? && @config.start_row == :random
|
|
55
|
+
resize_cells(cell_count:) if cell_count != @inputs.count
|
|
56
|
+
|
|
57
|
+
(@event_cursor...@event_tree.sequence.count).each do |event_index|
|
|
58
|
+
current_event = @event_tree.sequence[event_index]
|
|
59
|
+
past_event = @event_tree.sequence[event_index - 1]
|
|
60
|
+
|
|
61
|
+
redraw_event(current_event:, past_event:)
|
|
62
|
+
@event_cursor += 1
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Render a cell's input as output after a delay, using cursors. Called on every frame.
|
|
67
|
+
#
|
|
68
|
+
# INPUT DELAY OUTPUT
|
|
69
|
+
# ┌─────┬─────┬─────┐
|
|
70
|
+
# │ │ 0 │ │ ◀── 2. Hide Cursor moves the input to the output after a delay.
|
|
71
|
+
# │ │ 250 │ e │ The nil input replaces the previous output of "R".
|
|
72
|
+
# │ │ 250 │ q │
|
|
73
|
+
# │ │ 250 │ u │ ◀── 1. Show Cursor moves the input to the output after a delay.
|
|
74
|
+
# │ e │ 75 │ │ Leaving behind nil input.
|
|
75
|
+
# │ s │ 75 │ │ Then sets a "250" delay for the Hide Cursor.
|
|
76
|
+
# │ t │ 75 │ │
|
|
77
|
+
# └─────┴─────┴─────┘
|
|
78
|
+
#
|
|
79
|
+
# TODO: Refactor "@colors" into an Effect that happens dynamically on render rather than stored as a column of data.
|
|
80
|
+
# This will reduce the "Metrics/AbcSize" complexity.
|
|
81
|
+
def render(duration: nil) # rubocop:disable Metrics/AbcSize
|
|
82
|
+
@show_cursor.increment(delays:, inputs:, duration:) do |index|
|
|
83
|
+
prev_index = index.zero? ? @inputs.count - 1 : index - 1
|
|
84
|
+
next_index = index + 1 >= @inputs.count ? 0 : index + 1
|
|
85
|
+
|
|
86
|
+
if @inputs[index]
|
|
87
|
+
@outputs[index] = @inputs[index]
|
|
88
|
+
@delays[index] = @config.fade_delay
|
|
89
|
+
@colors[prev_index] = @config.cell_color if @colors[prev_index]
|
|
90
|
+
@colors[index] = @outputs[next_index] ? @config.cell_color : @config.lead_color
|
|
91
|
+
@inputs[index] = nil
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
fade(duration:) if @config.fade
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
|
|
100
|
+
def fade(duration: nil)
|
|
101
|
+
fade_start_delay = rand(5_000..10_000)
|
|
102
|
+
return unless (now - @show_cursor.first_update) >= fade_start_delay || duration
|
|
103
|
+
|
|
104
|
+
@hide_cursor.increment(delays:, inputs:, duration:) do |index|
|
|
105
|
+
if @inputs[index].nil? && @outputs[index]
|
|
106
|
+
@outputs[index] = @inputs[index]
|
|
107
|
+
@delays[index] = 0
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def now
|
|
113
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# A column of cells representing sequential events.
|
|
117
|
+
# ┌─┐
|
|
118
|
+
# │R│ FIRST EVENT
|
|
119
|
+
# │e│ Each cell will render input as output after a minimum delay (since there's no prior event).
|
|
120
|
+
# │q│
|
|
121
|
+
# │u│
|
|
122
|
+
# │e│
|
|
123
|
+
# │s│
|
|
124
|
+
# │t│
|
|
125
|
+
# └─┘ ◀── Time has passed between events.
|
|
126
|
+
# ┌─┐
|
|
127
|
+
# │││ SECOND EVENT
|
|
128
|
+
# │▼│ The next event has data to work with, it can represent the time it took to get from the previous event to the next.
|
|
129
|
+
# │R│ Each cell will delay render for the larger duration of either:
|
|
130
|
+
# │o│ 1. The minimum delay
|
|
131
|
+
# │u│ 2. The time elapsed between events divided by the number of cells
|
|
132
|
+
# │t│
|
|
133
|
+
# │e│ ◀── A cursor moves to the next cell after a delay and colors the leading cell white.
|
|
134
|
+
# └─┘
|
|
135
|
+
def redraw_event(current_event:, past_event:)
|
|
136
|
+
variable_delay = variable_delay(current_event:, past_event:)
|
|
137
|
+
|
|
138
|
+
characters = event_name(current_event:)
|
|
139
|
+
characters = [*ARROW, *characters] if @event_cursor > 0
|
|
140
|
+
|
|
141
|
+
@head_cursor.iterate(inputs: characters, loop_count: @inputs.count) do |index, input|
|
|
142
|
+
@inputs[index] = input
|
|
143
|
+
@delays[index] = first_cell_redraw? ? 0 : variable_delay # Don't add delay to the first cell, looks stuck.
|
|
144
|
+
|
|
145
|
+
@tail_cursor.increase_index(loop_count: @inputs.count) if @head_cursor.index == @tail_cursor.index
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
@hide_cursor.index = @head_cursor.index # Everything after me is old so it can fade away.
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def variable_delay(current_event:, past_event:)
|
|
152
|
+
if @event_cursor.zero?
|
|
153
|
+
@config.min_delay
|
|
154
|
+
else
|
|
155
|
+
difference = current_event.created_at - past_event.created_at
|
|
156
|
+
difference.zero? ? @config.min_delay : (difference / inputs.count).to_i.clamp(@config.min_delay, nil)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def event_name(current_event:)
|
|
161
|
+
current_event.class.name.split('::').last.delete_suffix('Event').chars
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def first_cell_redraw?
|
|
165
|
+
@head_cursor.index == -1
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def resize_cells(cell_count:)
|
|
169
|
+
old_index = (@inputs.count - 1).clamp(0, nil)
|
|
170
|
+
|
|
171
|
+
@inputs = @inputs.fill(nil, old_index...cell_count)[0...cell_count]
|
|
172
|
+
@outputs = @outputs.fill(nil, old_index...cell_count)[0...cell_count]
|
|
173
|
+
@delays = @delays.fill(@config.min_delay, old_index...cell_count)[0...cell_count]
|
|
174
|
+
@colors = @colors.fill(@config.cell_color, old_index...cell_count)[0...cell_count]
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def randomize_start_index
|
|
178
|
+
random_index = rand(0..2)
|
|
179
|
+
|
|
180
|
+
@head_cursor.index = random_index
|
|
181
|
+
@tail_cursor.index = random_index
|
|
182
|
+
|
|
183
|
+
@show_cursor.index = random_index
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
data/lib/raindeer.rb
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'low_dependency'
|
|
4
3
|
require 'low_event'
|
|
5
4
|
require 'low_node'
|
|
6
5
|
require 'low_type'
|
|
7
6
|
require 'observers'
|
|
7
|
+
require 'providers'
|
|
8
8
|
|
|
9
9
|
require_relative 'router/router'
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
module Raindeer
|
|
12
12
|
class << self
|
|
13
13
|
def router(&block)
|
|
14
|
-
|
|
14
|
+
Providers['rain.router'].instance_eval(&block)
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
end
|
data/lib/router/route_event.rb
CHANGED
data/lib/router/router.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'low_dependency'
|
|
4
3
|
require 'low_event'
|
|
4
|
+
require 'providers'
|
|
5
5
|
|
|
6
6
|
require_relative 'route'
|
|
7
7
|
require_relative 'route_event'
|
|
@@ -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 = Low::Types::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:)
|
|
@@ -6,11 +6,11 @@ require 'yaml'
|
|
|
6
6
|
module Rain
|
|
7
7
|
class ConfigLoader
|
|
8
8
|
class << self
|
|
9
|
-
def load(filepath,
|
|
10
|
-
config_file = YAML.safe_load_file(filepath, symbolize_names: true)
|
|
9
|
+
def load(filepath, overrides = {})
|
|
10
|
+
config_file = YAML.safe_load_file(filepath, permitted_classes: [Symbol], symbolize_names: true)
|
|
11
11
|
|
|
12
12
|
# Environment variables override config file.
|
|
13
|
-
config_data = config_file.merge(
|
|
13
|
+
config_data = config_file.merge(overrides) do |_key, old_value, new_value|
|
|
14
14
|
new_value.nil? ? old_value : new_value
|
|
15
15
|
end
|
|
16
16
|
|
|
@@ -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 = 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.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- maedi
|
|
@@ -24,7 +24,7 @@ dependencies:
|
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
|
-
name:
|
|
27
|
+
name: paint
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
30
|
- - ">="
|
|
@@ -39,6 +39,20 @@ dependencies:
|
|
|
39
39
|
version: '0'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: low_event
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.5'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0.5'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: lowload
|
|
42
56
|
requirement: !ruby/object:Gem::Requirement
|
|
43
57
|
requirements:
|
|
44
58
|
- - ">="
|
|
@@ -55,16 +69,16 @@ dependencies:
|
|
|
55
69
|
name: low_loop
|
|
56
70
|
requirement: !ruby/object:Gem::Requirement
|
|
57
71
|
requirements:
|
|
58
|
-
- - "
|
|
72
|
+
- - "~>"
|
|
59
73
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '0'
|
|
74
|
+
version: '0.5'
|
|
61
75
|
type: :runtime
|
|
62
76
|
prerelease: false
|
|
63
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
78
|
requirements:
|
|
65
|
-
- - "
|
|
79
|
+
- - "~>"
|
|
66
80
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '0'
|
|
81
|
+
version: '0.5'
|
|
68
82
|
- !ruby/object:Gem::Dependency
|
|
69
83
|
name: low_node
|
|
70
84
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -149,6 +163,20 @@ dependencies:
|
|
|
149
163
|
- - ">="
|
|
150
164
|
- !ruby/object:Gem::Version
|
|
151
165
|
version: '0'
|
|
166
|
+
- !ruby/object:Gem::Dependency
|
|
167
|
+
name: providers
|
|
168
|
+
requirement: !ruby/object:Gem::Requirement
|
|
169
|
+
requirements:
|
|
170
|
+
- - ">="
|
|
171
|
+
- !ruby/object:Gem::Version
|
|
172
|
+
version: '0'
|
|
173
|
+
type: :runtime
|
|
174
|
+
prerelease: false
|
|
175
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
176
|
+
requirements:
|
|
177
|
+
- - ">="
|
|
178
|
+
- !ruby/object:Gem::Version
|
|
179
|
+
version: '0'
|
|
152
180
|
description: A new Ruby framework. Coming soon...
|
|
153
181
|
email:
|
|
154
182
|
- maediprichard@gmail.com
|
|
@@ -157,6 +185,9 @@ extensions: []
|
|
|
157
185
|
extra_rdoc_files: []
|
|
158
186
|
files:
|
|
159
187
|
- lib/boot.rb
|
|
188
|
+
- lib/matrix/cursor.rb
|
|
189
|
+
- lib/matrix/matrix.rb
|
|
190
|
+
- lib/matrix/stream.rb
|
|
160
191
|
- lib/raindeer.rb
|
|
161
192
|
- lib/router/route.rb
|
|
162
193
|
- lib/router/route_event.rb
|
|
@@ -164,9 +195,12 @@ files:
|
|
|
164
195
|
- lib/router/trie.rb
|
|
165
196
|
- lib/router/trie_node.rb
|
|
166
197
|
- lib/support/config_loader.rb
|
|
167
|
-
- lib/system/
|
|
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
|
|
168
203
|
- lib/system/system.rb
|
|
169
|
-
- lib/system/system_node.rb
|
|
170
204
|
- lib/version.rb
|
|
171
205
|
homepage: https://codeberg.org/raindeer/raindeer
|
|
172
206
|
licenses: []
|
|
@@ -187,7 +221,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
187
221
|
- !ruby/object:Gem::Version
|
|
188
222
|
version: '0'
|
|
189
223
|
requirements: []
|
|
190
|
-
rubygems_version:
|
|
224
|
+
rubygems_version: 4.0.6
|
|
191
225
|
specification_version: 4
|
|
192
226
|
summary: Deer to be different
|
|
193
227
|
test_files: []
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class PageNotFoundNode < LowNode
|
|
4
|
-
observe Status[404]
|
|
5
|
-
|
|
6
|
-
def render(event: StatusEvent) # rubocop:disable Lint/UnusedMethodArgument
|
|
7
|
-
<<~HTML
|
|
8
|
-
<div class="page-not-found">
|
|
9
|
-
<em>404</em>
|
|
10
|
-
</div>
|
|
11
|
-
HTML
|
|
12
|
-
end
|
|
13
|
-
end
|
data/lib/system/system_node.rb
DELETED