Class: Charming::Application
- Inherits:
-
Object
- Object
- Charming::Application
- Defined in:
- lib/charming/application.rb
Overview
Application is a lightweight, Rails-inspired application base for building terminal-based apps. It provides routing (via a DSL), session storage, and task execution for managing async operations.
Constant Summary collapse
- LOGGER_READER =
Object.new.freeze
- THEME_READER =
Object.new.freeze
- COALESCE_READER =
Object.new.freeze
Instance Attribute Summary collapse
-
#logger ⇒ Object
Returns the value of attribute logger.
-
#session ⇒ Object
readonly
Returns the value of attribute session.
-
#task_executor ⇒ Object
Returns the value of attribute task_executor.
Class Method Summary collapse
-
.coalesce_input(value = COALESCE_READER) ⇒ Object
When true, the runtime collapses bursts of identical key events — the flood a terminal emits while a key is held (OS auto-repeat) — into a single dispatch, so holding a key can't queue a backlog that keeps acting after release.
-
.default_theme(name = THEME_READER) ⇒ Object
Returns the default theme name, or sets it when name is given.
-
.logger(value = LOGGER_READER) ⇒ Object
Returns or sets the app logger.
-
.mouse_motion(value = COALESCE_READER) ⇒ Object
The mouse motion reporting mode: :drag (default) reports movement only while a button is held; :all also reports buttonless hover movement.
-
.namespace ⇒ Object
Derives the module namespace from the class name — e.g., Admin::HomeController yields "Admin".
-
.persist_session(to:) ⇒ Object
Opts into session persistence: the session hash is serialized as JSON to to when the app quits and reloaded on boot.
-
.root(path = THEME_READER) ⇒ Object
Returns the app's filesystem root, used to resolve relative theme and template paths.
-
.routes(&block) ⇒ Object
Registers or returns the app's Router.
-
.session_path ⇒ Object
The configured session file path, walking the superclass chain.
-
.theme(name, from: nil, built_in: nil, extends: nil, overrides: nil) ⇒ Object
Registers a named theme.
-
.theme_for(name = nil) ⇒ Object
Resolves a theme by name (or the default theme when name is nil).
-
.themes ⇒ Object
Hash of all registered themes keyed by symbol, including those inherited from superclasses.
Instance Method Summary collapse
-
#coalesce_input? ⇒ Boolean
Whether the runtime should coalesce held-key auto-repeat for this app (see the class-level
coalesce_inputDSL). -
#initialize ⇒ Application
constructor
Initializes the session hash for per-request state storage, restoring a previously persisted session when
persist_sessionis configured. -
#mouse_motion ⇒ Object
The mouse motion reporting mode for this app (see the class-level
mouse_motionDSL). -
#routes ⇒ Object
Delegates to the class-level Router, providing instance access to route definitions.
-
#save_session ⇒ Object
Serializes the session to the configured
persist_sessionpath. - #theme ⇒ Object
- #use_theme(name) ⇒ Object
Constructor Details
#initialize ⇒ Application
Initializes the session hash for per-request state storage, restoring a
previously persisted session when persist_session is configured.
170 171 172 173 |
# File 'lib/charming/application.rb', line 170 def initialize @logger = self.class.logger @session = load_session end |
Instance Attribute Details
#logger ⇒ Object
Returns the value of attribute logger.
165 166 167 |
# File 'lib/charming/application.rb', line 165 def logger @logger end |
#session ⇒ Object (readonly)
Returns the value of attribute session.
166 167 168 |
# File 'lib/charming/application.rb', line 166 def session @session end |
#task_executor ⇒ Object
Returns the value of attribute task_executor.
165 166 167 |
# File 'lib/charming/application.rb', line 165 def task_executor @task_executor end |
Class Method Details
.coalesce_input(value = COALESCE_READER) ⇒ Object
When true, the runtime collapses bursts of identical key events — the flood a terminal emits while a key is held (OS auto-repeat) — into a single dispatch, so holding a key can't queue a backlog that keeps acting after release. Off by default: it also merges intentional fast repeats of the same key (e.g. tab tab), so enable it only for movement-style apps. Pass a boolean to set; call without args to read (inherited).
117 118 119 120 121 |
# File 'lib/charming/application.rb', line 117 def coalesce_input(value = COALESCE_READER) return configured_coalesce_input if value == COALESCE_READER @coalesce_input = value end |
.default_theme(name = THEME_READER) ⇒ Object
Returns the default theme name, or sets it when name is given. When unset, falls back
to the first registered theme. Used by theme_for when no name is provided.
80 81 82 83 84 |
# File 'lib/charming/application.rb', line 80 def default_theme(name = THEME_READER) return @default_theme || themes.keys.first if name == THEME_READER @default_theme = name.to_sym end |
.logger(value = LOGGER_READER) ⇒ Object
Returns or sets the app logger. Defaults to a null-device logger so app and framework code can safely call logging methods without writing into the terminal UI.
32 33 34 35 36 |
# File 'lib/charming/application.rb', line 32 def logger(value = LOGGER_READER) return configured_logger if value == LOGGER_READER @logger = value end |
.mouse_motion(value = COALESCE_READER) ⇒ Object
The mouse motion reporting mode: :drag (default) reports movement only while a button is held; :all also reports buttonless hover movement. Pass a symbol to set; call without args to read (inherited).
126 127 128 129 130 131 |
# File 'lib/charming/application.rb', line 126 def mouse_motion(value = COALESCE_READER) return configured_mouse_motion if value == COALESCE_READER raise ArgumentError, "mouse_motion must be :drag or :all" unless %i[drag all].include?(value) @mouse_motion = value end |
.namespace ⇒ Object
Derives the module namespace from the class name — e.g., Admin::HomeController yields "Admin". Mirrors Rails' engine-style namespacing.
26 27 28 |
# File 'lib/charming/application.rb', line 26 def namespace ActiveSupport::Inflector.deconstantize(name.to_s) end |
.persist_session(to:) ⇒ Object
Opts into session persistence: the session hash is serialized as JSON to to when the app quits and reloaded on boot. Only JSON-safe values survive the round-trip (hash keys come back as symbols); non-serializable entries (state objects, procs) are skipped with a warning in the log.
99 100 101 |
# File 'lib/charming/application.rb', line 99 def persist_session(to:) @session_path = to end |
.root(path = THEME_READER) ⇒ Object
Returns the app's filesystem root, used to resolve relative theme and template paths. Pass path to set it; without arguments it returns the current value (or nil if unset).
40 41 42 43 44 |
# File 'lib/charming/application.rb', line 40 def root(path = THEME_READER) return @root if path == THEME_READER @root = File.(path) end |
.routes(&block) ⇒ Object
Registers or returns the app's Router. Accepts an optional block to define routes via DSL (screen, root). Lazily initializes a new Router per namespace.
18 19 20 21 22 |
# File 'lib/charming/application.rb', line 18 def routes(&block) @routes ||= Router.new(namespace: namespace) @routes.draw(&block) if block @routes end |
.session_path ⇒ Object
The configured session file path, walking the superclass chain. Nil when persistence is not enabled.
105 106 107 108 109 110 |
# File 'lib/charming/application.rb', line 105 def session_path return @session_path if instance_variable_defined?(:@session_path) return superclass.session_path if superclass.respond_to?(:session_path) nil end |
.theme(name, from: nil, built_in: nil, extends: nil, overrides: nil) ⇒ Object
Registers a named theme. Provide one of:
-
from: — path to a JSON theme file relative to the app root
-
built_in: — name of a bundled theme ("phosphor", "catppuccin-mocha", "catppuccin-latte", "gruvbox-dark", "nord", "tokyonight")
-
extends: — name of an already-registered theme to derive from, with overrides: (token name → style spec) merged on top:
theme :dark, built_in: "tokyonight" theme :high_contrast, extends: :dark, overrides: {text: {foreground: "#ffffff"}}
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/charming/application.rb', line 55 def theme(name, from: nil, built_in: nil, extends: nil, overrides: nil) sources = [from, built_in, extends].compact raise ArgumentError, "theme expects from:, built_in:, or extends:" if sources.empty? raise ArgumentError, "theme expects only one of from:, built_in:, or extends:" if sources.length > 1 raise ArgumentError, "overrides: requires extends:" if overrides && !extends themes[name.to_sym] = if extends parent = themes.fetch(extends.to_sym) do raise ArgumentError, "unknown parent theme: #{extends.inspect} (register it before extending)" end parent.merge(overrides || {}) elsif built_in UI::Theme.load_builtin(built_in) else UI::Theme.load_file(resolve_theme_path(from)) end end |
.theme_for(name = nil) ⇒ Object
Resolves a theme by name (or the default theme when name is nil). Returns the default built-in theme if no name is given and no default is registered.
88 89 90 91 92 93 |
# File 'lib/charming/application.rb', line 88 def theme_for(name = nil) theme_name = name || default_theme return UI::Theme.default unless theme_name themes.fetch(theme_name.to_sym) end |
.themes ⇒ Object
Hash of all registered themes keyed by symbol, including those inherited from superclasses.
74 75 76 |
# File 'lib/charming/application.rb', line 74 def themes @themes ||= superclass.respond_to?(:themes) ? superclass.themes.dup : {} end |
Instance Method Details
#coalesce_input? ⇒ Boolean
Whether the runtime should coalesce held-key auto-repeat for this app (see the class-level
coalesce_input DSL). Read by the Runtime at startup.
204 205 206 |
# File 'lib/charming/application.rb', line 204 def coalesce_input? self.class.coalesce_input == true end |
#mouse_motion ⇒ Object
The mouse motion reporting mode for this app (see the class-level mouse_motion
DSL). Read by the Runtime at startup.
210 211 212 |
# File 'lib/charming/application.rb', line 210 def mouse_motion self.class.mouse_motion end |
#routes ⇒ Object
Delegates to the class-level Router, providing instance access to route definitions.
189 190 191 |
# File 'lib/charming/application.rb', line 189 def routes self.class.routes end |
#save_session ⇒ Object
Serializes the session to the configured persist_session path. Entries that
don't survive a JSON round-trip (state objects, procs, focus scopes) are skipped.
No-op when persistence isn't configured. Called by the Runtime on exit.
178 179 180 181 182 183 184 185 186 |
# File 'lib/charming/application.rb', line 178 def save_session path = self.class.session_path return unless path FileUtils.mkdir_p(File.dirname(path)) File.write(path, JSON.generate(serializable_session)) rescue => e logger.warn("session not saved: #{e.class}: #{e.}") end |
#theme ⇒ Object
193 194 195 |
# File 'lib/charming/application.rb', line 193 def theme self.class.theme_for(session[:theme]) end |
#use_theme(name) ⇒ Object
197 198 199 200 |
# File 'lib/charming/application.rb', line 197 def use_theme(name) self.class.theme_for(name) session[:theme] = name.to_sym end |