Module: Charming::Controller::ClassMethods

Included in:
Charming::Controller
Defined in:
lib/charming/controller/class_methods.rb

Overview

DSL for declaring controller-level event bindings and configuration: keys, commands, timers, task handlers, the auto-rendered action, layout wrapper, and focus ring. Mixed into Controller as class methods; also exposed for tests and shared base controllers.

Instance Method Summary collapse

Instance Method Details

#animate(name, action:, fps: 30) ⇒ Object

Declares an animation: a stopped timer ticking action at fps frames per second. Begin motion with start_timer(name); the action calls stop_timer(name) once the motion settles, returning the app to zero idle cost.

Raises:

  • (ArgumentError)


38
39
40
41
42
# File 'lib/charming/controller/class_methods.rb', line 38

def animate(name, action:, fps: 30)
  raise ArgumentError, "fps must be positive (got #{fps.inspect})" unless fps.is_a?(Numeric) && fps.positive?

  timer(name, every: 1.0 / fps, action: action, autostart: false)
end

#auto_render(action = :show) ⇒ Object

Sets the action that the controller should auto-render after a non-rendering action runs. Defaults to :show when unset.



59
60
61
# File 'lib/charming/controller/class_methods.rb', line 59

def auto_render(action = :show)
  @auto_render_action = action.to_sym
end

#auto_render_actionObject

Returns the configured auto-render action, walking the superclass chain when undefined locally.



64
65
66
67
68
69
# File 'lib/charming/controller/class_methods.rb', line 64

def auto_render_action
  return @auto_render_action if instance_variable_defined?(:@auto_render_action)
  return superclass.auto_render_action if superclass.respond_to?(:auto_render_action)

  nil
end

#command(label, action = nil, &block) ⇒ Object

Adds a CommandPalette entry with the given label. action is a method name to send on the controller, or a block to instance_exec when selected.



21
22
23
# File 'lib/charming/controller/class_methods.rb', line 21

def command(label, action = nil, &block)
  command_bindings << Components::CommandPalette::Command.new(label: label, value: block || action)
end

#command_bindingsObject

Array of registered command palette entries, inherited from superclass when undefined.



102
103
104
# File 'lib/charming/controller/class_methods.rb', line 102

def command_bindings
  @command_bindings ||= superclass.respond_to?(:command_bindings) ? superclass.command_bindings.dup : []
end

#focus_ring(*slots) ⇒ Object

Defines the named focus slots cycled by Tab/Shift+Tab traversal.



92
93
94
# File 'lib/charming/controller/class_methods.rb', line 92

def focus_ring(*slots)
  @focus_ring_slots = slots
end

#focus_ring_slotsObject

Returns the focus ring slots, inherited from superclass when undefined.



97
98
99
# File 'lib/charming/controller/class_methods.rb', line 97

def focus_ring_slots
  @focus_ring_slots ||= superclass.respond_to?(:focus_ring_slots) ? superclass.focus_ring_slots.dup : []
end

#key(name, action, scope: :content) ⇒ Object

Binds a key press to a controller action. name is the normalized key symbol (e.g., "up", "q", "ctrl+c"). scope is :content (default) for content-pane keys or :global for app-wide shortcuts that fire regardless of focus. Raises ArgumentError for any other scope.



12
13
14
15
16
17
# File 'lib/charming/controller/class_methods.rb', line 12

def key(name, action, scope: :content)
  normalized_scope = validate_key_scope(scope)
  key_name = Charming.key_binding_name(name)
  key_bindings[key_name] = action
  key_binding_scopes[key_name] = normalized_scope
end

#key_binding_scopesObject

Hash of key scopes paralleling key_bindings (symbol key name => :content or :global).



87
88
89
# File 'lib/charming/controller/class_methods.rb', line 87

def key_binding_scopes
  @key_binding_scopes ||= superclass.respond_to?(:key_binding_scopes) ? superclass.key_binding_scopes.dup : {}
end

#key_bindingsObject

Hash of registered key bindings (symbol key name => action method name), inherited from superclass controllers.



82
83
84
# File 'lib/charming/controller/class_methods.rb', line 82

def key_bindings
  @key_bindings ||= superclass.respond_to?(:key_bindings) ? superclass.key_bindings.dup : {}
end

#layout(layout_class = :__charming_layout_reader__) ⇒ Object

Sets or returns the controller's layout. Pass a layout class (instantiated per request), a String/Symbol template name (resolved through Templates), or false to disable inherited layout wrapping. Called with no arguments returns the resolved layout.



74
75
76
77
78
# File 'lib/charming/controller/class_methods.rb', line 74

def layout(layout_class = :__charming_layout_reader__)
  return resolved_layout if layout_class == :__charming_layout_reader__

  @layout = layout_class
end

#on_task(name, action:) ⇒ Object

Declares a task handler for async work submitted via run_task(:name). When the task emits a TaskEvent with the matching name, the runtime dispatches action on the controller.



46
47
48
# File 'lib/charming/controller/class_methods.rb', line 46

def on_task(name, action:)
  task_bindings[name.to_sym] = TaskBinding.new(name: name.to_sym, action: action)
end

#on_task_progress(name, action:) ⇒ Object

Declares a progress handler for a task: while run_task(:name) runs, each progress.report(...) dispatches action on the controller (the event is available as event — a TaskProgressEvent with current/total/message).



53
54
55
# File 'lib/charming/controller/class_methods.rb', line 53

def on_task_progress(name, action:)
  task_progress_bindings[name.to_sym] = TaskBinding.new(name: name.to_sym, action: action)
end

#task_bindingsObject

Hash of task name => TaskBinding, inherited from superclass when undefined.



112
113
114
# File 'lib/charming/controller/class_methods.rb', line 112

def task_bindings
  @task_bindings ||= superclass.respond_to?(:task_bindings) ? superclass.task_bindings.dup : {}
end

#task_progress_bindingsObject

Hash of task name => TaskBinding for progress handlers, inherited from superclass.



117
118
119
# File 'lib/charming/controller/class_methods.rb', line 117

def task_progress_bindings
  @task_progress_bindings ||= superclass.respond_to?(:task_progress_bindings) ? superclass.task_progress_bindings.dup : {}
end

#timer(name, every:, action:, autostart: true) ⇒ Object

Declares a timer that fires every every seconds and dispatches action on the controller. The runtime builds a TimerEvent and routes it to the active controller's dispatch_timer. Timers run from boot by default; declare autostart: false to schedule one only when an action calls start_timer.

Raises:

  • (ArgumentError)


29
30
31
32
33
# File 'lib/charming/controller/class_methods.rb', line 29

def timer(name, every:, action:, autostart: true)
  raise ArgumentError, "timer interval must be positive (got #{every.inspect})" unless every.is_a?(Numeric) && every.positive?

  timer_bindings[name.to_sym] = TimerBinding.new(name: name.to_sym, interval: every, action: action, autostart: autostart)
end

#timer_bindingsObject

Hash of timer name => TimerBinding, inherited from superclass when undefined.



107
108
109
# File 'lib/charming/controller/class_methods.rb', line 107

def timer_bindings
  @timer_bindings ||= superclass.respond_to?(:timer_bindings) ? superclass.timer_bindings.dup : {}
end