Class: Plushie::Subscription

Inherits:
Object
  • Object
show all
Defined in:
lib/plushie/subscription.rb

Overview

Declarative subscription specifications.

Return subscriptions from your subscribe method. The runtime diffs the list each cycle and starts/stops subscriptions automatically.

def subscribe(model) subs = [Subscription.on_key_press] subs << Subscription.every(1000, :tick) if model.timer_running subs end

Timer subscriptions require a tag (it appears in the Timer event). Renderer subscriptions do not take a tag; they are identified by their kind and optional window scope.

Defined Under Namespace

Classes: Sub

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Attribute Details

#interval [Integer, nil] interval in milliseconds (only for :every)([Integer, nil]) ⇒ Object (readonly)

An immutable subscription specification.

Created via factory methods on Subscription rather than directly. The runtime uses #key to diff subscriptions between cycles, starting new ones and stopping removed ones automatically.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/plushie/subscription.rb', line 31

Sub = Data.define(:type, :tag, :interval, :max_rate, :window_id) do
  def initialize(type:, tag: nil, interval: nil, max_rate: nil, window_id: nil)
    super
  end

  # Returns a key that uniquely identifies this subscription.
  # Used by the runtime to diff subscription lists between cycles.
  # Timer subs include the interval so that changing the interval
  # creates a new subscription rather than updating the existing one.
  # Renderer subs are keyed by type and window_id.
  #
  # @return [Array] unique identity tuple for this subscription
  def key
    if type == :every
      [:every, interval, tag]
    else
      [type, window_id]
    end
  end

  # Derive the wire tag sent to the renderer. Window-scoped
  # subscriptions include the window_id so they don't collide
  # with global subscriptions of the same kind.
  #
  # @return [String]
  def wire_tag
    kind = type.to_s
    window_id ? "#{kind}:#{window_id}" : kind
  end

  # Set the maximum event rate (events per second).
  #
  # @param rate [Integer] max events per second
  # @return [Sub] new Sub with the rate applied
  def with_max_rate(rate)
    self.class.new(**to_h.merge(max_rate: rate))
  end
end

#max_rate [Integer, nil] maximum events per second (nil = unlimited)([Integer, nil](nil = unlimited)) ⇒ Object (readonly)

An immutable subscription specification.

Created via factory methods on Subscription rather than directly. The runtime uses #key to diff subscriptions between cycles, starting new ones and stopping removed ones automatically.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/plushie/subscription.rb', line 31

Sub = Data.define(:type, :tag, :interval, :max_rate, :window_id) do
  def initialize(type:, tag: nil, interval: nil, max_rate: nil, window_id: nil)
    super
  end

  # Returns a key that uniquely identifies this subscription.
  # Used by the runtime to diff subscription lists between cycles.
  # Timer subs include the interval so that changing the interval
  # creates a new subscription rather than updating the existing one.
  # Renderer subs are keyed by type and window_id.
  #
  # @return [Array] unique identity tuple for this subscription
  def key
    if type == :every
      [:every, interval, tag]
    else
      [type, window_id]
    end
  end

  # Derive the wire tag sent to the renderer. Window-scoped
  # subscriptions include the window_id so they don't collide
  # with global subscriptions of the same kind.
  #
  # @return [String]
  def wire_tag
    kind = type.to_s
    window_id ? "#{kind}:#{window_id}" : kind
  end

  # Set the maximum event rate (events per second).
  #
  # @param rate [Integer] max events per second
  # @return [Sub] new Sub with the rate applied
  def with_max_rate(rate)
    self.class.new(**to_h.merge(max_rate: rate))
  end
end

#tag [Symbol, nil] identifier for timer event correlation (timers only)([Symbol, nil]) ⇒ Object (readonly)

An immutable subscription specification.

Created via factory methods on Subscription rather than directly. The runtime uses #key to diff subscriptions between cycles, starting new ones and stopping removed ones automatically.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/plushie/subscription.rb', line 31

Sub = Data.define(:type, :tag, :interval, :max_rate, :window_id) do
  def initialize(type:, tag: nil, interval: nil, max_rate: nil, window_id: nil)
    super
  end

  # Returns a key that uniquely identifies this subscription.
  # Used by the runtime to diff subscription lists between cycles.
  # Timer subs include the interval so that changing the interval
  # creates a new subscription rather than updating the existing one.
  # Renderer subs are keyed by type and window_id.
  #
  # @return [Array] unique identity tuple for this subscription
  def key
    if type == :every
      [:every, interval, tag]
    else
      [type, window_id]
    end
  end

  # Derive the wire tag sent to the renderer. Window-scoped
  # subscriptions include the window_id so they don't collide
  # with global subscriptions of the same kind.
  #
  # @return [String]
  def wire_tag
    kind = type.to_s
    window_id ? "#{kind}:#{window_id}" : kind
  end

  # Set the maximum event rate (events per second).
  #
  # @param rate [Integer] max events per second
  # @return [Sub] new Sub with the rate applied
  def with_max_rate(rate)
    self.class.new(**to_h.merge(max_rate: rate))
  end
end

#type [Symbol] subscription type (:every, :on_key_press, etc.)([Symbol](: every, :on_key_press, etc.)) ⇒ Object (readonly)

An immutable subscription specification.

Created via factory methods on Subscription rather than directly. The runtime uses #key to diff subscriptions between cycles, starting new ones and stopping removed ones automatically.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/plushie/subscription.rb', line 31

Sub = Data.define(:type, :tag, :interval, :max_rate, :window_id) do
  def initialize(type:, tag: nil, interval: nil, max_rate: nil, window_id: nil)
    super
  end

  # Returns a key that uniquely identifies this subscription.
  # Used by the runtime to diff subscription lists between cycles.
  # Timer subs include the interval so that changing the interval
  # creates a new subscription rather than updating the existing one.
  # Renderer subs are keyed by type and window_id.
  #
  # @return [Array] unique identity tuple for this subscription
  def key
    if type == :every
      [:every, interval, tag]
    else
      [type, window_id]
    end
  end

  # Derive the wire tag sent to the renderer. Window-scoped
  # subscriptions include the window_id so they don't collide
  # with global subscriptions of the same kind.
  #
  # @return [String]
  def wire_tag
    kind = type.to_s
    window_id ? "#{kind}:#{window_id}" : kind
  end

  # Set the maximum event rate (events per second).
  #
  # @param rate [Integer] max events per second
  # @return [Sub] new Sub with the rate applied
  def with_max_rate(rate)
    self.class.new(**to_h.merge(max_rate: rate))
  end
end

#window_id [String, nil] window scope (nil = all windows)([String, nil](nil = all windows)) ⇒ Object (readonly)

An immutable subscription specification.

Created via factory methods on Subscription rather than directly. The runtime uses #key to diff subscriptions between cycles, starting new ones and stopping removed ones automatically.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/plushie/subscription.rb', line 31

Sub = Data.define(:type, :tag, :interval, :max_rate, :window_id) do
  def initialize(type:, tag: nil, interval: nil, max_rate: nil, window_id: nil)
    super
  end

  # Returns a key that uniquely identifies this subscription.
  # Used by the runtime to diff subscription lists between cycles.
  # Timer subs include the interval so that changing the interval
  # creates a new subscription rather than updating the existing one.
  # Renderer subs are keyed by type and window_id.
  #
  # @return [Array] unique identity tuple for this subscription
  def key
    if type == :every
      [:every, interval, tag]
    else
      [type, window_id]
    end
  end

  # Derive the wire tag sent to the renderer. Window-scoped
  # subscriptions include the window_id so they don't collide
  # with global subscriptions of the same kind.
  #
  # @return [String]
  def wire_tag
    kind = type.to_s
    window_id ? "#{kind}:#{window_id}" : kind
  end

  # Set the maximum event rate (events per second).
  #
  # @param rate [Integer] max events per second
  # @return [Sub] new Sub with the rate applied
  def with_max_rate(rate)
    self.class.new(**to_h.merge(max_rate: rate))
  end
end

Class Method Details

.every(interval_ms, tag) ⇒ Sub

Subscribe to a periodic timer. Delivers Event::Timer[tag: tag, timestamp: ms] to update at the given interval.

Parameters:

  • interval_ms (Integer)

    interval between ticks in milliseconds

  • tag (Symbol)

    tag that appears in the delivered Timer event

Returns:



76
77
78
# File 'lib/plushie/subscription.rb', line 76

def self.every(interval_ms, tag)
  Sub.new(type: :every, tag:, interval: interval_ms)
end

.for_window(window_id, subscriptions) ⇒ Array<Sub>

Scope a list of subscriptions to a specific window.

Window-scoped subscriptions tell the renderer to only deliver events from the given window. Without a window scope, subscriptions receive events from all windows.

Subscription.for_window("editor", [ Subscription.on_key_press(max_rate: 60), Subscription.on_pointer_move(max_rate: 60) ])

Parameters:

  • window_id (String)
  • subscriptions (Array<Sub>)

Returns:



275
276
277
# File 'lib/plushie/subscription.rb', line 275

def self.for_window(window_id, subscriptions)
  subscriptions.map { |sub| sub.with(window_id: window_id) }
end

.on_animation_frame(max_rate: nil, window: nil) ⇒ Sub

Subscribe to animation frame ticks for smooth animations. Delivers Event::System[type: :animation_frame, data: delta_ms] to update.

Parameters:

  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

  • window (String, nil) (defaults to: nil)

    window scope (nil = all windows)

Returns:



236
237
238
# File 'lib/plushie/subscription.rb', line 236

def self.on_animation_frame(max_rate: nil, window: nil)
  Sub.new(type: :on_animation_frame, max_rate:, window_id: window)
end

.on_event(max_rate: nil, window: nil) ⇒ Sub

Subscribe to all renderer events (catch-all). Delivers the raw event to update without filtering by type. Useful for debugging or handling event types not covered by specific subscriptions.

Parameters:

  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

  • window (String, nil) (defaults to: nil)

    window scope (nil = all windows)

Returns:



257
258
259
# File 'lib/plushie/subscription.rb', line 257

def self.on_event(max_rate: nil, window: nil)
  Sub.new(type: :on_event, max_rate:, window_id: window)
end

.on_file_drop(max_rate: nil, window: nil) ⇒ Sub

Subscribe to file drag and drop events. Delivers Event::Window[type: :file_dropped/:file_hovered, path:] to update.

Parameters:

  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

  • window (String, nil) (defaults to: nil)

    window scope (nil = all windows)

Returns:



246
247
248
# File 'lib/plushie/subscription.rb', line 246

def self.on_file_drop(max_rate: nil, window: nil)
  Sub.new(type: :on_file_drop, max_rate:, window_id: window)
end

.on_ime(max_rate: nil, window: nil) ⇒ Sub

Subscribe to IME (Input Method Editor) composition events. Delivers Event::Ime[type: :enabled/:preedit/:commit/:disabled, ...] to update.

Parameters:

  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

  • window (String, nil) (defaults to: nil)

    window scope (nil = all windows)

Returns:



216
217
218
# File 'lib/plushie/subscription.rb', line 216

def self.on_ime(max_rate: nil, window: nil)
  Sub.new(type: :on_ime, max_rate:, window_id: window)
end

.on_key_press(max_rate: nil, window: nil) ⇒ Sub

Subscribe to keyboard press events. Delivers Event::Key[type: :press, ...] to update.

Parameters:

  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

  • window (String, nil) (defaults to: nil)

    window scope (nil = all windows)

Returns:



86
87
88
# File 'lib/plushie/subscription.rb', line 86

def self.on_key_press(max_rate: nil, window: nil)
  Sub.new(type: :on_key_press, max_rate:, window_id: window)
end

.on_key_release(max_rate: nil, window: nil) ⇒ Sub

Subscribe to keyboard release events. Delivers Event::Key[type: :release, ...] to update.

Parameters:

  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

  • window (String, nil) (defaults to: nil)

    window scope (nil = all windows)

Returns:



96
97
98
# File 'lib/plushie/subscription.rb', line 96

def self.on_key_release(max_rate: nil, window: nil)
  Sub.new(type: :on_key_release, max_rate:, window_id: window)
end

.on_modifiers_changed(max_rate: nil, window: nil) ⇒ Sub

Subscribe to modifier key state changes. Delivers Event::Modifiers with the current modifier state.

Parameters:

  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

  • window (String, nil) (defaults to: nil)

    window scope (nil = all windows)

Returns:



106
107
108
# File 'lib/plushie/subscription.rb', line 106

def self.on_modifiers_changed(max_rate: nil, window: nil)
  Sub.new(type: :on_modifiers_changed, max_rate:, window_id: window)
end

.on_pointer_button(max_rate: nil, window: nil) ⇒ Sub

Subscribe to pointer button press/release events (mouse or touch). Delivers Event::Widget with type: :press/:release, pointer data in data map.

Parameters:

  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

  • window (String, nil) (defaults to: nil)

    window scope (nil = all windows)

Returns:



126
127
128
# File 'lib/plushie/subscription.rb', line 126

def self.on_pointer_button(max_rate: nil, window: nil)
  Sub.new(type: :on_pointer_button, max_rate:, window_id: window)
end

.on_pointer_move(max_rate: nil, window: nil) ⇒ Sub

Subscribe to pointer movement events (mouse or touch). Delivers Event::Widget with type: :move, pointer data in data map.

Parameters:

  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

  • window (String, nil) (defaults to: nil)

    window scope (nil = all windows)

Returns:



116
117
118
# File 'lib/plushie/subscription.rb', line 116

def self.on_pointer_move(max_rate: nil, window: nil)
  Sub.new(type: :on_pointer_move, max_rate:, window_id: window)
end

.on_pointer_scroll(max_rate: nil, window: nil) ⇒ Sub

Subscribe to pointer scroll events. Delivers Event::Widget with type: :scroll, pointer data in data map.

Parameters:

  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

  • window (String, nil) (defaults to: nil)

    window scope (nil = all windows)

Returns:



136
137
138
# File 'lib/plushie/subscription.rb', line 136

def self.on_pointer_scroll(max_rate: nil, window: nil)
  Sub.new(type: :on_pointer_scroll, max_rate:, window_id: window)
end

.on_pointer_touch(max_rate: nil, window: nil) ⇒ Sub

Subscribe to touch events. Delivers Event::Widget with type: :press/:move/:release, pointer: :touch in data.

Parameters:

  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

  • window (String, nil) (defaults to: nil)

    window scope (nil = all windows)

Returns:



206
207
208
# File 'lib/plushie/subscription.rb', line 206

def self.on_pointer_touch(max_rate: nil, window: nil)
  Sub.new(type: :on_pointer_touch, max_rate:, window_id: window)
end

.on_theme_change(max_rate: nil, window: nil) ⇒ Sub

Subscribe to OS theme changes (light/dark mode). Delivers Event::System[type: :theme_changed, data: theme_name] to update.

Parameters:

  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

  • window (String, nil) (defaults to: nil)

    window scope (nil = all windows)

Returns:



226
227
228
# File 'lib/plushie/subscription.rb', line 226

def self.on_theme_change(max_rate: nil, window: nil)
  Sub.new(type: :on_theme_change, max_rate:, window_id: window)
end

.on_window_close(max_rate: nil, window: nil) ⇒ Sub

Subscribe to window close request events. Delivers Event::Window[type: :close_requested, window_id:] to update.

Parameters:

  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

  • window (String, nil) (defaults to: nil)

    window scope (nil = all windows)

Returns:



146
147
148
# File 'lib/plushie/subscription.rb', line 146

def self.on_window_close(max_rate: nil, window: nil)
  Sub.new(type: :on_window_close, max_rate:, window_id: window)
end

.on_window_focus(max_rate: nil, window: nil) ⇒ Sub

Subscribe to window focus events. Delivers Event::Window[type: :focused, window_id:] to update.

Parameters:

  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

  • window (String, nil) (defaults to: nil)

    window scope (nil = all windows)

Returns:



176
177
178
# File 'lib/plushie/subscription.rb', line 176

def self.on_window_focus(max_rate: nil, window: nil)
  Sub.new(type: :on_window_focus, max_rate:, window_id: window)
end

.on_window_move(max_rate: nil, window: nil) ⇒ Sub

Subscribe to window move events. Delivers Event::Window[type: :moved, window_id:, x:, y:] to update.

Parameters:

  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

  • window (String, nil) (defaults to: nil)

    window scope (nil = all windows)

Returns:



196
197
198
# File 'lib/plushie/subscription.rb', line 196

def self.on_window_move(max_rate: nil, window: nil)
  Sub.new(type: :on_window_move, max_rate:, window_id: window)
end

.on_window_open(max_rate: nil, window: nil) ⇒ Sub

Subscribe to window opened events. Delivers Event::Window[type: :opened, window_id:, width:, height:] to update.

Parameters:

  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

  • window (String, nil) (defaults to: nil)

    window scope (nil = all windows)

Returns:



156
157
158
# File 'lib/plushie/subscription.rb', line 156

def self.on_window_open(max_rate: nil, window: nil)
  Sub.new(type: :on_window_open, max_rate:, window_id: window)
end

.on_window_resize(max_rate: nil, window: nil) ⇒ Sub

Subscribe to window resize events. Delivers Event::Window[type: :resized, window_id:, width:, height:] to update.

Parameters:

  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

  • window (String, nil) (defaults to: nil)

    window scope (nil = all windows)

Returns:



166
167
168
# File 'lib/plushie/subscription.rb', line 166

def self.on_window_resize(max_rate: nil, window: nil)
  Sub.new(type: :on_window_resize, max_rate:, window_id: window)
end

.on_window_unfocus(max_rate: nil, window: nil) ⇒ Sub

Subscribe to window unfocus events. Delivers Event::Window[type: :unfocused, window_id:] to update.

Parameters:

  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

  • window (String, nil) (defaults to: nil)

    window scope (nil = all windows)

Returns:



186
187
188
# File 'lib/plushie/subscription.rb', line 186

def self.on_window_unfocus(max_rate: nil, window: nil)
  Sub.new(type: :on_window_unfocus, max_rate:, window_id: window)
end