raindeer 0.4.0 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a6fa213232ae917a276896434feda9d572209e595d0e04a7d5c7e2ba5244f035
4
- data.tar.gz: cd7e970c62ad742f0024981eca3d8f3d609ad7b2e703283d10d1a82b2995d34b
3
+ metadata.gz: 10abc837f7a79dd6e8807ab756f5d393324ef1643dc7eacfc53fdd09246e5d97
4
+ data.tar.gz: 15eb9638281737ad60f8ce07dd9164f8584ec5ac8a66b0d5f304c734f13155f0
5
5
  SHA512:
6
- metadata.gz: 150cc6803a59f65d411f5441d56b851cd6a9b89de713cdae5a5292fc660e279d8978ac5c49421e4416e3294d05e3334e7a4521fc931bcc44b57d8c7e35dda34e
7
- data.tar.gz: 826b04af2e7ded427657d3c06f9899d80901d8858373286bf85feb003ab56ca81db8f729ee00d8d6b64b4478caf63b76aa85f8f99a7c3fed35d0cbda6c9a4721
6
+ metadata.gz: 27d14ec70fdd565fa5d62833f05672762bce3e050a482b6748d99b524892b473b42cda5658b3aa22dc459fcd3230614e724928d01de5e1d138031ee0a6f4151e
7
+ data.tar.gz: 07761ecc57e9660c719793226e63f6be7ba4d795e660647d8d9540b16b53a857f5e33e27e8d3aa435b2775d2a4222b8df17220008036f77cd64637fb8239d8b4
data/lib/matrix/cursor.rb CHANGED
@@ -4,8 +4,8 @@ module Rain
4
4
  class Cursor
5
5
  attr_accessor :index, :first_update, :last_update
6
6
 
7
- def initialize
8
- @index = -1
7
+ def initialize(index: -1)
8
+ @index = index
9
9
  @first_update = now
10
10
  @last_update = now
11
11
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'effect'
4
+
5
+ module Rain
6
+ class ColorEffect < Effect
7
+ def render(output:, next_output:, **)
8
+ color = next_output ? @config.cell_color : @config.lead_color
9
+
10
+ return Paint[output, '#006ab0'] if @config.leet_keys.invert.key?(output)
11
+ output ? Paint[output, color] : Paint[' ']
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rain
4
+ class Effect
5
+ attr_accessor :last_output
6
+
7
+ def initialize(config:)
8
+ @config = config
9
+ @last_output = {}
10
+ end
11
+
12
+ def save(output:, x:, y:)
13
+ @last_output[x] ||= {}
14
+ @last_output[x][y] = output
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'effect'
4
+
5
+ module Rain
6
+ class LeetEffect < Effect
7
+ def initialize(config:)
8
+ super(config:)
9
+
10
+ @leet_numbers = @config.leet_keys.transform_keys(&:to_s)
11
+ @leet_letters = @leet_numbers.invert
12
+ end
13
+
14
+ def render(output:, next_output:, x:, y:)
15
+ output = filter(output:, next_output:, x:, y:)
16
+ save(output:, x:, y:)
17
+ end
18
+
19
+ private
20
+
21
+ def filter(output:, next_output:, x:, y:)
22
+ return unless output
23
+
24
+ character = output.downcase
25
+ last_character = @last_output.dig(x, y)
26
+
27
+ return @leet_numbers[character] if @leet_letters.key?(last_character) && rand < 0.99
28
+ return @leet_numbers[character] if @leet_numbers.key?(character) && rand < 0.005
29
+
30
+ output
31
+ end
32
+ end
33
+ end
data/lib/matrix/matrix.rb CHANGED
@@ -5,17 +5,20 @@ require 'low_type'
5
5
  require 'observers'
6
6
  require 'paint'
7
7
 
8
- require_relative '../support/config_loader'
8
+ require_relative 'effects/color_effect'
9
+ require_relative 'effects/leet_effect'
9
10
  require_relative 'stream'
11
+ require_relative '../support/config_loader'
10
12
 
11
13
  module Rain
12
14
  class Matrix
13
15
  include LowType
14
16
  include Observers
15
17
 
16
- def initialize(event_pool:, config: ConfigLoader.load('./config/matrix.yaml'))
18
+ def initialize(event_pool:, config: ConfigLoader.load('matrix.yaml'))
17
19
  @event_pool = event_pool
18
20
  @config = config
21
+ @effects = [LeetEffect.new(config:), ColorEffect.new(config:)]
19
22
 
20
23
  @screen_size = nil
21
24
 
@@ -66,6 +69,8 @@ module Rain
66
69
  stream
67
70
  end
68
71
 
72
+ # Where streams become the final output on the matrix.
73
+ # @columns - A column contains a particular stream
69
74
  def render_streams(show_output: true) # rubocop:disable Metrics/AbcSize
70
75
  @streams.each_value(&:render)
71
76
 
@@ -74,17 +79,18 @@ module Rain
74
79
  output = ''.dup
75
80
 
76
81
  (0...@screen_size[:row_count]).each do |row_index|
77
- cell_outputs = []
78
- cell_colors = []
82
+ row_outputs = []
79
83
 
80
84
  # Rendering streams can happen before redrawing streams, so @columns may not be populated yet.
81
85
  (0...column_count).each do |column_index|
82
- cell_colors << (@columns[column_index].nil? ? nil : @columns[column_index].colors[row_index])
83
- cell_outputs << (@columns[column_index].nil? ? nil : @columns[column_index].outputs[row_index])
86
+ row_outputs << (@columns[column_index].nil? ? nil : @columns[column_index].outputs[row_index])
84
87
  end
85
88
 
86
- output << "\n" + cell_outputs.zip(cell_colors).map do |cell, color| # rubocop:disable Style/StringConcatenation
87
- cell ? Paint[cell, color] : Paint[' ']
89
+ output << "\n" + row_outputs.map.with_index do |output, col_index| # rubocop:disable Style/StringConcatenation
90
+ @effects.reduce(output) do |out, effect|
91
+ next_output = @columns[col_index].nil? ? nil : @columns[col_index].outputs[row_index + 1]
92
+ effect.render(output: out, next_output:, x: row_index, y: col_index)
93
+ end
88
94
  end.join(' ')
89
95
  end
90
96
 
data/lib/matrix/stream.rb CHANGED
@@ -8,7 +8,7 @@ require_relative 'cursor'
8
8
  module Rain
9
9
  class Stream
10
10
  include Observers
11
- attr_reader :index, :inputs, :outputs, :delays, :colors, :head_cursor, :tail_cursor
11
+ attr_reader :index, :inputs, :outputs, :delays, :head_cursor, :tail_cursor
12
12
 
13
13
  ARROW = ['│', '▼'].freeze
14
14
 
@@ -19,13 +19,11 @@ module Rain
19
19
 
20
20
  @inputs = []
21
21
  @delays = []
22
- @colors = []
23
22
  @outputs = []
24
23
 
25
24
  @event_cursor = 0
26
25
  @head_cursor = Cursor.new
27
- @tail_cursor = Cursor.new
28
- @tail_cursor.index = 0
26
+ @tail_cursor = Cursor.new(index: 0)
29
27
 
30
28
  @show_cursor = Cursor.new
31
29
  @hide_cursor = Cursor.new
@@ -75,10 +73,7 @@ module Rain
75
73
  # │ s │ 75 │ │ Then sets a "250" delay for the Hide Cursor.
76
74
  # │ t │ 75 │ │
77
75
  # └─────┴─────┴─────┘
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
76
+ def render(duration: nil)
82
77
  @show_cursor.increment(delays:, inputs:, duration:) do |index|
83
78
  prev_index = index.zero? ? @inputs.count - 1 : index - 1
84
79
  next_index = index + 1 >= @inputs.count ? 0 : index + 1
@@ -86,8 +81,6 @@ module Rain
86
81
  if @inputs[index]
87
82
  @outputs[index] = @inputs[index]
88
83
  @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
84
  @inputs[index] = nil
92
85
  end
93
86
  end
@@ -130,7 +123,7 @@ module Rain
130
123
  # │o│ 1. The minimum delay
131
124
  # │u│ 2. The time elapsed between events divided by the number of cells
132
125
  # │t│
133
- # │e│ ◀── A cursor moves to the next cell after a delay and colors the leading cell white.
126
+ # │e│ ◀── A cursor moves to the next cell after a delay.
134
127
  # └─┘
135
128
  def redraw_event(current_event:, past_event:)
136
129
  variable_delay = variable_delay(current_event:, past_event:)
@@ -171,7 +164,6 @@ module Rain
171
164
  @inputs = @inputs.fill(nil, old_index...cell_count)[0...cell_count]
172
165
  @outputs = @outputs.fill(nil, old_index...cell_count)[0...cell_count]
173
166
  @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
167
  end
176
168
 
177
169
  def randomize_start_index
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'low_event'
4
+ require 'low_node'
5
+ require 'low_type'
6
+ require 'observers'
7
+ require 'providers'
8
+
9
+ require_relative '../router/router'
10
+ require_relative '../support/config_loader'
11
+
12
+ module Rain
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
+ debug_mode: ConfigLoader.parse_boolean(ENV.fetch('RAIN_DEBUG', true)),
18
+ matrix_mode: ConfigLoader.parse_boolean(ENV.fetch('RAIN_MATRIX', nil)),
19
+ mirror_mode: ConfigLoader.parse_boolean(ENV.fetch('RAIN_MIRROR', nil))
20
+ }
21
+
22
+ config = ConfigLoader.load('config.yaml', env)
23
+
24
+ Providers.define('rain.router') do
25
+ require_relative '../router/router'
26
+ Router.new
27
+ end
28
+
29
+ Providers.define('rain.matrix') do
30
+ require_relative '../matrix/matrix'
31
+ Matrix.new(event_pool: Providers['low.event.pool'])
32
+ end
33
+
34
+ Providers.define('low.loop') do
35
+ require 'low_loop'
36
+ LowLoop.new(config:, router: Providers['rain.router'], renderer: Providers['rain.matrix'])
37
+ end
38
+ end
39
+
40
+ module Raindeer
41
+ class << self
42
+ def router(&block)
43
+ Providers['rain.router'].instance_eval(&block)
44
+ end
45
+ end
46
+ end
47
+
48
+ require 'lowload'
49
+ LowLoad.dirload(File.expand_path('../system', __dir__))
@@ -6,8 +6,9 @@ require 'yaml'
6
6
  module Rain
7
7
  class ConfigLoader
8
8
  class << self
9
- def load(filepath, overrides = {})
10
- config_file = YAML.safe_load_file(filepath, permitted_classes: [Symbol], symbolize_names: true)
9
+ def load(file_path, overrides = {})
10
+ file_path = File.expand_path("../../../config/#{file_path}", __FILE__) unless File.exist?(file_path)
11
+ config_file = YAML.safe_load_file(file_path, permitted_classes: [Symbol], symbolize_names: true)
11
12
 
12
13
  # Environment variables override config file.
13
14
  config_data = config_file.merge(overrides) do |_key, old_value, new_value|
@@ -1,21 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class DashboardNode < LowNode
4
- observe '/system'
3
+ module System
4
+ class DashboardNode < LowNode
5
+ observe '/system'
5
6
 
6
- def initialize(event:)
7
- @node_count = LowNode.count
8
- @event_count = LowEvent.count
9
- end
7
+ def initialize(event:)
8
+ @node_count = LowNode.count
9
+ @event_count = LowEvent.count
10
+ end
10
11
 
11
- def render(event:) # rubocop:disable Lint/UnusedMethodArgument
12
- <{ LayoutNode: }>
13
- <h1>{"Dashboard"}</h1>
12
+ def render(event:) # rubocop:disable Lint/UnusedMethodArgument
13
+ <{ LayoutNode: }>
14
+ <h1>{"Dashboard"}</h1>
14
15
 
15
- <div class="grid">
16
- <{ StatFormatter title='Nodes' stat=@node_count }>
17
- <{ StatFormatter title='Events' stat=@event_count }>
18
- </div>
19
- <{ :LayoutNode }>
16
+ <div class="grid">
17
+ <{ StatFormatter title='Nodes' stat=@node_count }>
18
+ <{ StatFormatter title='Events' stat=@event_count }>
19
+ </div>
20
+ <{ :LayoutNode }>
21
+ end
20
22
  end
21
23
  end
@@ -1,35 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class EventsNode < LowNode
4
- observe '/system/events'
3
+ module System
4
+ class EventsNode < LowNode
5
+ observe '/system/events'
5
6
 
6
- def initialize(event:)
7
- # TODO: Include types that can be observed keys too like Status and Status[404].
8
- @event_types = LowEvent.events
9
- end
7
+ def initialize(event:)
8
+ # TODO: Include types that can be observed keys too like Status and Status[404].
9
+ @event_types = LowEvent.events
10
+ end
10
11
 
11
- def render(event:)
12
- <{ LayoutNode: }>
13
- <h1>{"Events"}</h1>
12
+ def render(event:)
13
+ <{ LayoutNode: }>
14
+ <h1>{"Events"}</h1>
14
15
 
15
- <table>
16
- <thead>
17
- <tr>
18
- <th scope="col">Event</th>
19
- <th scope="col">Observers</th>
20
- </tr>
21
- </thead>
22
- <tbody>
23
- <{ for: observer_key in: @event_types }>
16
+ <table>
17
+ <thead>
24
18
  <tr>
25
- <td>{observer_key}</td>
26
- <td>
27
- <{ ObserversFormatter observer_key=observer_key }>
28
- </td>
19
+ <th scope="col">Event</th>
20
+ <th scope="col">Observers</th>
29
21
  </tr>
30
- <{ :for }>
31
- </tbody>
32
- </table>
33
- <{ :LayoutNode }>
22
+ </thead>
23
+ <tbody>
24
+ <{ for: observer_key in: @event_types }>
25
+ <tr>
26
+ <td>{observer_key}</td>
27
+ <td>
28
+ <{ ObserversFormatter observer_key=observer_key }>
29
+ </td>
30
+ </tr>
31
+ <{ :for }>
32
+ </tbody>
33
+ </table>
34
+ <{ :LayoutNode }>
35
+ end
34
36
  end
35
37
  end
@@ -1,41 +1,42 @@
1
1
  # frozen_string_literal: true
2
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
- <link rel="stylesheet" href="/assets/stats.css">
10
- </head>
11
- <body>
12
- <header>
13
- <div class="container">
14
- <a id="favicon" href="/system"><img src="/assets/favicon.svg"/></a>
15
- <nav id="navbar">
16
- <ul>
17
- <li><a href="/system">{"Dashboard"}</a>
18
- <li><a href="/system/events">{"Events"}</a>
19
- <li><a href="/system/routes">{"Routes"}</a>
20
- </ul>
21
- </nav>
22
- </div>
23
- </header>
3
+ module System
4
+ class LayoutNode < LowNode
5
+ def render(event:)
6
+ <html>
7
+ <head>
8
+ <link rel="stylesheet" href="/system/pico.min.css">
9
+ <link rel="stylesheet" href="/system/system.css">
10
+ </head>
11
+ <body>
12
+ <header>
13
+ <div class="container">
14
+ <a id="favicon" href="/system"><img src="/system/favicon.svg"/></a>
15
+ <nav id="navbar">
16
+ <ul>
17
+ <li><a href="/system">{"Dashboard"}</a>
18
+ <li><a href="/system/events">{"Events"}</a>
19
+ <li><a href="/system/routes">{"Routes"}</a>
20
+ </ul>
21
+ </nav>
22
+ </div>
23
+ </header>
24
24
 
25
- <main class="container">
26
- <{ :slot }>
27
- </main>
25
+ <main class="container">
26
+ <{ :slot }>
27
+ </main>
28
28
 
29
- <footer>
30
- <div class="container">
31
- <ul>
32
- <li><a href="https://raindeer.dev">{"Website"}</a></li>
33
- <li><a href="https://raindeer.dev/docs">{"Docs"}</a></li>
34
- <li><a href="https://github.com/raindeer-rb/raindeer">{"Source code"}</a></li>
35
- </ul>
36
- </div>
37
- </footer>
38
- </body>
39
- </html>
29
+ <footer>
30
+ <div class="container">
31
+ <ul>
32
+ <li><a href="https://raindeer.dev">{"Website"}</a></li>
33
+ <li><a href="https://raindeer.dev/docs">{"Docs"}</a></li>
34
+ <li><a href="https://github.com/raindeer-rb/raindeer">{"Source code"}</a></li>
35
+ </ul>
36
+ </div>
37
+ </footer>
38
+ </body>
39
+ </html>
40
+ end
40
41
  end
41
42
  end
@@ -1,34 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class RoutesNode < LowNode
4
- observe '/system/routes'
3
+ module System
4
+ class RoutesNode < LowNode
5
+ observe '/system/routes'
5
6
 
6
- def initialize(event:)
7
- @routes = Providers['rain.router'].routes
8
- end
7
+ def initialize(event:)
8
+ @routes = Providers['rain.router'].routes
9
+ end
9
10
 
10
- def render(event:)
11
- <{ LayoutNode: }>
12
- <h1>{"Routes"}</h1>
11
+ def render(event:)
12
+ <{ LayoutNode: }>
13
+ <h1>{"Routes"}</h1>
13
14
 
14
- <table>
15
- <thead>
16
- <tr>
17
- <th scope="col">HTTP Verbs</th>
18
- <th scope="col">Route</th>
19
- <th scope="col">Observers</th>
20
- </tr>
21
- </thead>
22
- <tbody>
23
- <{ for: path, route in: @routes }>
15
+ <table>
16
+ <thead>
24
17
  <tr>
25
- <td><{ LabelFormatter labels=route.verbs }></td>
26
- <td>{route.path}</td>
27
- <td><{ ObserversFormatter observer_key=route.path }></td>
18
+ <th scope="col">HTTP Verbs</th>
19
+ <th scope="col">Route</th>
20
+ <th scope="col">Observers</th>
28
21
  </tr>
29
- <{ :for }>
30
- </tbody>
31
- </table>
32
- <{ :LayoutNode }>
22
+ </thead>
23
+ <tbody>
24
+ <{ for: path, route in: @routes }>
25
+ <tr>
26
+ <td><{ LabelFormatter labels=route.verbs }></td>
27
+ <td>{route.path}</td>
28
+ <td><{ ObserversFormatter observer_key=route.path }></td>
29
+ </tr>
30
+ <{ :for }>
31
+ </tbody>
32
+ </table>
33
+ <{ :LayoutNode }>
34
+ end
33
35
  end
34
36
  end
data/lib/system/system.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../raindeer'
4
-
5
3
  Raindeer.router do
6
4
  get '/system' do
7
5
  get '/events'
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Raindeer
4
- VERSION = '0.4.0'
4
+ VERSION = '0.5.0'
5
5
  end
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.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - maedi
@@ -184,11 +184,13 @@ executables: []
184
184
  extensions: []
185
185
  extra_rdoc_files: []
186
186
  files:
187
- - lib/boot.rb
188
187
  - lib/matrix/cursor.rb
188
+ - lib/matrix/effects/color_effect.rb
189
+ - lib/matrix/effects/effect.rb
190
+ - lib/matrix/effects/leet_effect.rb
189
191
  - lib/matrix/matrix.rb
190
192
  - lib/matrix/stream.rb
191
- - lib/raindeer.rb
193
+ - lib/raindeer/boot.rb
192
194
  - lib/router/route.rb
193
195
  - lib/router/route_event.rb
194
196
  - lib/router/router.rb
@@ -225,7 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
225
227
  - !ruby/object:Gem::Version
226
228
  version: '0'
227
229
  requirements: []
228
- rubygems_version: 4.0.10
230
+ rubygems_version: 4.0.6
229
231
  specification_version: 4
230
232
  summary: Deer to be different
231
233
  test_files: []
data/lib/boot.rb DELETED
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'low_event'
4
- require 'low_node'
5
- require 'low_type'
6
- require 'providers'
7
-
8
- require_relative 'support/config_loader'
9
-
10
- env = {
11
- host: ENV.fetch('RAIN_HOST', nil),
12
- port: ENV.fetch('RAIN_PORT', nil),
13
- web_root: ENV.fetch('RAIN_WEB_ROOT', nil),
14
- debug_mode: Rain::ConfigLoader.parse_boolean(ENV.fetch('RAIN_DEBUG', true)),
15
- matrix_mode: Rain::ConfigLoader.parse_boolean(ENV.fetch('RAIN_MATRIX', nil)),
16
- mirror_mode: Rain::ConfigLoader.parse_boolean(ENV.fetch('RAIN_MIRROR', nil))
17
- }
18
-
19
- config = Rain::ConfigLoader.load('./config/config.yaml', env)
20
-
21
- Providers.define('rain.router') do
22
- require_relative 'router/router'
23
- Rain::Router.new
24
- end
25
-
26
- Providers.define('rain.matrix') do
27
- require_relative 'matrix/matrix'
28
- Rain::Matrix.new(event_pool: Providers['low.event.pool'])
29
- end
30
-
31
- Providers.define('low.loop') do
32
- require 'low_loop'
33
- LowLoop.new(config:, router: Providers['rain.router'], renderer: Providers['rain.matrix'])
34
- end
35
-
36
- require 'lowload'
37
- LowLoad.dirload(File.expand_path('system', __dir__))
data/lib/raindeer.rb DELETED
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'low_event'
4
- require 'low_node'
5
- require 'low_type'
6
- require 'observers'
7
- require 'providers'
8
-
9
- require_relative 'router/router'
10
-
11
- module Raindeer
12
- class << self
13
- def router(&block)
14
- Providers['rain.router'].instance_eval(&block)
15
- end
16
- end
17
- end