Module: Rooibos::Command::Custom
- Defined in:
- lib/rooibos/command/custom.rb
Overview
Mixin for user-defined custom commands.
Custom commands extend Rooibos with side effects: WebSockets, gRPC, database polls, background tasks. The runtime dispatches them in threads and routes results back as messages.
Include this module to identify your class as a command. The runtime uses rooibos_command? to distinguish commands from plain models. Override rooibos_cancellation_grace_period if your cleanup takes longer than 100 milliseconds.
Use it to build real-time features, long-polling connections, or background workers.
Example
– SPDX-SnippetBegin SPDX-FileCopyrightText: 2026 Kerrick Long SPDX-License-Identifier: MIT-0 ++
class WebSocketCommand
include Rooibos::Command::Custom
def initialize(url)
@url = url
end
def call(out, token)
ws = WebSocket::Client.new(@url)
ws. { |msg| out.put(:ws_message, msg) }
ws.connect
until token.canceled?
ws.ping
sleep 1
end
ws.close
end
# WebSocket close handshake needs extra time
def rooibos_cancellation_grace_period
5.0
end
end
– SPDX-SnippetEnd ++
Instance Method Summary collapse
-
#deconstruct_keys(keys) ⇒ Hash{Symbol => Object}
Deconstructs for hash-based pattern matching.
-
#rooibos_cancellation_grace_period ⇒ Object
Cleanup time after cancellation is requested.
-
#rooibos_command? ⇒ Boolean
Brand predicate for command identification.
Instance Method Details
#deconstruct_keys(keys) ⇒ Hash{Symbol => Object}
Deconstructs for hash-based pattern matching.
Introspects public query methods (CQS: zero-arity, no side effects) and returns a hash suitable for case/in matching. Excludes infrastructure methods from Object, Data, and Struct.
Always includes :type as a snake_case symbol of the class name. Anonymous classes default to :custom.
Data.define members are automatically included since they generate public accessor methods.
This is a naive but practical default. Override for:
-
Hot paths (introspects methods on every call)
-
Ghost methods via
method_missing/respond_to_missing? -
Methods with optional arguments (only zero-arity detected)
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/rooibos/command/custom.rb', line 152 def deconstruct_keys(keys) class_name = self.class.name&.split("::")&.last type_name = if class_name class_name .gsub(/([a-z])([A-Z])/, '\1_\2') .downcase .to_sym else :custom end result = { type: type_name } # Include Data.define/Struct members if self.class.respond_to?(:members) klass = self.class #: Class & _HasMembers klass.members.each do |member| next if keys && !keys.include?(member) result[member] = public_send(member) end end # Include public zero-arity query methods (excluding infrastructure) # Use Kernel#public_method to avoid collision with Data.define :method member get_method = Kernel.instance_method(:public_method) (public_methods - INFRASTRUCTURE_METHODS).each do |method_name| next if method_name.to_s.end_with?("=", "!") next unless get_method.bind_call(self, method_name).arity.zero? next if keys && !keys.include?(method_name) result[method_name] = public_send(method_name) end result end |
#rooibos_cancellation_grace_period ⇒ Object
Cleanup time after cancellation is requested. In seconds.
When the runtime cancels your command (app exit, navigation, explicit cancel), it calls token.cancel! and waits this long for your command to stop. If your command does not exit within this window, it is orphaned until process exit. There is no safe way to force-kill a Ruby thread.
*This is NOT a lifetime limit.* Your command runs indefinitely until canceled. A WebSocket open for 15 minutes is fine. This timeout only applies to the cleanup phase after cancellation is requested.
Override this method to specify how long your cleanup takes:
-
0.5— Quick HTTP abort, no cleanup needed -
2.0— Default, suitable for most commands -
5.0— WebSocket close handshake with remote server -
Float::INFINITY— Wait indefinitely for cooperative exit (database transactions)
Example
– SPDX-SnippetBegin SPDX-FileCopyrightText: 2026 Kerrick Long SPDX-License-Identifier: MIT-0 ++
# Database transactions should never be interrupted mid-write
def rooibos_cancellation_grace_period
Float::INFINITY
end
– SPDX-SnippetEnd ++
100 101 102 |
# File 'lib/rooibos/command/custom.rb', line 100 def rooibos_cancellation_grace_period 0.1 end |
#rooibos_command? ⇒ Boolean
Brand predicate for command identification.
The runtime calls this to distinguish commands from plain models. Returns true unconditionally.
You do not need to override this method.
64 65 66 |
# File 'lib/rooibos/command/custom.rb', line 64 def rooibos_command? true end |