Class: LiveCable::Container

Inherits:
Hash
  • Object
show all
Defined in:
lib/live_cable/container.rb

Overview

Storage for reactive variable values with automatic change tracking.

A Container is a Hash subclass that stores the values of reactive variables for a single component instance. It automatically wraps supported types (Arrays, Hashes, ActiveRecord models) in Delegators that track mutations.

Architecture:

  • Each component instance has its own Container (identified by live_id)
  • Shared reactive variables use a special SHARED_CONTAINER
  • During message processing, mutations are tracked in a changeset
  • After message processing, components with dirty changesets are re-rendered
  • Changesets are reset after broadcasting updates

Change Tracking:

  1. Value is stored via []=
  2. If value is an Array/Hash/Model, it's wrapped in a Delegator
  3. Delegator attaches observers to the value
  4. When value is mutated, observers mark the variable as dirty
  5. Component re-renders if changeset contains the variable

Examples:

Basic usage

container = Container.new
container[:username] = "john"           # Stored as-is
container[:tags] = ['ruby', 'rails']    # Wrapped in Delegator
container[:tags] << 'rspec'             # Automatically marks :tags as dirty

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeContainer

Returns a new instance of Container.

Parameters:

  • args (Array)

    Arguments passed to Hash.new



31
32
33
34
# File 'lib/live_cable/container.rb', line 31

def initialize(...)
  super
  @changeset = []
end

Instance Attribute Details

#changesetArray<Symbol> (readonly)

Returns the list of variables that have changed during this message cycle.

Returns:

  • (Array<Symbol>)

    List of dirty variable names



81
82
83
# File 'lib/live_cable/container.rb', line 81

def changeset
  @changeset
end

Instance Method Details

#[]=(key, value) ⇒ Object

Store a value in the container, automatically wrapping supported types in Delegators for change tracking.

Examples:

Storing different types

container[:count] = 0                    # Number - stored as-is
container[:tags] = ['ruby']              # Array - wrapped in Delegator
container[:user] = User.new              # ActiveRecord - wrapped in Delegator

Parameters:

  • key (Symbol)

    The reactive variable name

  • value (Object)

    The value to store

Returns:

  • (Object)

    The stored value (possibly wrapped in a Delegator)



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/live_cable/container.rb', line 47

def []=(key, value)
  # Remove observer from the old value so it stops notifying this container
  self[key].try(:remove_live_cable_observer, observer, key)

  # ActiveRecord models get observers attached directly
  if value.class < ModelObserver
    value.add_live_cable_observer(observer, key)
  end

  # If value is already a Delegator, add observer and store as-is
  if value.is_a?(Delegator)
    value.add_live_cable_observer(observer, key)
    super
  else
    # Wrap supported types in Delegators for change tracking
    super(key, Delegator.create_if_supported(value, key, observer))
  end
end

#changed?Boolean

Check if any variables have changed.

Returns:

  • (Boolean)

    true if changeset is not empty



86
87
88
# File 'lib/live_cable/container.rb', line 86

def changed?
  !@changeset.empty?
end

#cleanupvoid

This method returns an undefined value.

Clean up all observer references to prevent memory leaks. This breaks the reference chain between delegators, observers, and the container.

Should be called when the component is destroyed.



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/live_cable/container.rb', line 112

def cleanup
  # Remove this container's observer from all delegated values
  # Note: We only remove this specific observer, not all observers,
  # because the same object might be shared across multiple containers
  each do |variable, value|
    if value.respond_to?(:remove_live_cable_observer)
      value.remove_live_cable_observer(observer, variable)
    end
  end

  # Clear the container's data
  clear
  @changeset&.clear
  @observer = nil
end

#mark_dirty(*variables) ⇒ void

This method returns an undefined value.

Mark one or more variables as dirty (changed). Dirty variables will trigger a re-render of their component.

Examples:

container.mark_dirty(:username, :email)

Parameters:

  • variables (Array<Symbol>)

    Variable names to mark as dirty



74
75
76
# File 'lib/live_cable/container.rb', line 74

def mark_dirty(*variables)
  @changeset |= variables # Union operator keeps values unique
end

#observerLiveCable::Observer

Get or create the observer for this container. Each container has exactly one observer instance.

Returns:



102
103
104
# File 'lib/live_cable/container.rb', line 102

def observer
  @observer ||= Observer.new(self)
end

#reset_changesetvoid

This method returns an undefined value.

Clear the changeset after broadcasting updates. Called by Connection after all components have been re-rendered.



94
95
96
# File 'lib/live_cable/container.rb', line 94

def reset_changeset
  @changeset = []
end