raindeer 0.1.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8ff951a5eafc6d661e188c1eb78fb01ae2a148544fd897da8731800305335f2f
4
- data.tar.gz: d821af08d8333a88da073564db2f699e905de22fa825ed89ac41dca5dd48fd4b
3
+ metadata.gz: 8783b509655bf58e51f0e7afc0aa320832d8aee1b298d50771d4121c418b28d1
4
+ data.tar.gz: 1eaf600341059527fc7dc013df6cc24a3bc1ba7d606a34b5544b73630e10b0ea
5
5
  SHA512:
6
- metadata.gz: 6c8cc7383ae033d78736e5c7db85057f4a2ab4bbd8b5634d4efc7164b96112dd25ac94e31216f9cbe2ab06a99fa70f1b34a9f742da6725ff3f0fe7295b99f0f2
7
- data.tar.gz: 52bd7181b0e4bb0a9a4cf17a0802428849a1a0e5543f1443721661c58c229f528e6e49fc0f97866a5092d2e42772a2ca26cbdd67d61da7b5bca50857ade3bd0c
6
+ metadata.gz: 6328d1ed634f9e501e8abd946deccfe13e39ee68b863273bc5c6aa8b4822e449b9462bf3027a5147382802f0af2367995af807ac2144a18e66247f0c2603ceb0
7
+ data.tar.gz: 2268752ea3dbb8e0eb8190f007fa8263ac37e0eff6f4d1e737fbf04fc9760788e15af59e426d46e4bdd2105075a3795396f95a5d837136f6138a61c4263dc8db
data/lib/boot.rb CHANGED
@@ -1,8 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'low_loop'
4
3
  require 'low_dependency'
4
+ require 'low_event'
5
+ require 'low_loop'
6
+ require 'low_node'
7
+ require 'low_type'
8
+ require 'lowload'
5
9
 
10
+ require_relative 'matrix/matrix'
6
11
  require_relative 'support/config_loader'
7
12
 
8
13
  env = {
@@ -16,11 +21,15 @@ env = {
16
21
  config = Rain::ConfigLoader.load('./config/config.yaml', env)
17
22
 
18
23
  LowDependency.provide('rain.router') do
19
- RainRouter.new
24
+ Rain::Router.new
25
+ end
26
+
27
+ LowDependency.provide('rain.matrix') do
28
+ Rain::Matrix.new(event_pool: Low::Providers['low.event.pool'])
20
29
  end
21
30
 
22
31
  LowDependency.provide('low.loop') do
23
- LowLoop.new(config:, router: Low::Providers['rain.router'])
32
+ LowLoop.new(config:, router: Low::Providers['rain.router'], renderer: Low::Providers['rain.matrix'])
24
33
  end
25
34
 
26
- require_relative 'system/system'
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
@@ -8,7 +8,7 @@ require 'observers'
8
8
 
9
9
  require_relative 'router/router'
10
10
 
11
- class Raindeer
11
+ module Raindeer
12
12
  class << self
13
13
  def router(&block)
14
14
  Low::Providers['rain.router'].instance_eval(&block)
@@ -7,7 +7,7 @@ module Rain
7
7
  attr_reader :route, :params
8
8
 
9
9
  def initialize(route:, action: :render, params: Hash | nil)
10
- super(action:)
10
+ super(key: route.path, action:)
11
11
 
12
12
  @route = route
13
13
  @params = params
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 = String, &block)
36
+ def get(path, &block)
37
37
  route(path, 'GET', &block)
38
38
  end
39
39
 
40
- def post(path = String, &block)
40
+ def post(path, &block)
41
41
  route(path, 'POST', &block)
42
42
  end
43
43
 
44
- def update(path = String, &block)
44
+ def update(path, &block)
45
45
  route(path, 'UPDATE', &block)
46
46
  end
47
47
 
48
- def delete(path = String, &block)
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
- @trie.match(path: event.request.path).each do |route_event|
58
- response_event = trigger route_event.route.path, event: route_event
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.nil?
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
- response_event
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
- # End nodes render events, mid nodes handle events.
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, env = {})
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(env) do |_key, old_value, new_value|
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,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DashboardNode < LowNode
4
+ observe '/system'
5
+
6
+ def render(event:) # rubocop:disable Lint/UnusedMethodArgument
7
+ <{ LayoutNode: }>
8
+ <h1>{"Dashboard"}</h1>
9
+ <{ :LayoutNode }>
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Error404Node < LowNode
4
+ observe Low::Types::Status[404]
5
+
6
+ def render(event:) # rubocop:disable Lint/UnusedMethodArgument
7
+ <div class="page-not-found">
8
+ <em>404</em>
9
+ </div>
10
+ end
11
+ 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
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../raindeer'
4
- require_relative 'system_node'
5
4
 
6
5
  Raindeer.router do
7
- get '/system'
6
+ get '/system' do
7
+ get '/events'
8
+ get '/routes'
9
+ end
8
10
  end
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Raindeer
4
- VERSION = '0.1.2'
4
+ VERSION = '0.2.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.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - maedi
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
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'
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: low_dependency
28
42
  requirement: !ruby/object:Gem::Requirement
@@ -107,6 +121,20 @@ dependencies:
107
121
  - - "~>"
108
122
  - !ruby/object:Gem::Version
109
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'
110
138
  - !ruby/object:Gem::Dependency
111
139
  name: antlers
112
140
  requirement: !ruby/object:Gem::Requirement
@@ -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/page_not_found_node.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
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: 3.7.2
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
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'page_not_found_node'
4
-
5
- class SystemNode < LowNode
6
- observe '/system'
7
-
8
- def render(event: RouteEvent) # rubocop:disable Lint/UnusedMethodArgument
9
- 'SYSTEM!'
10
- end
11
- end