Class: LiveCable::Rendering::PartialRenderer

Inherits:
Object
  • Object
show all
Defined in:
lib/live_cable/rendering/partial_renderer.rb

Overview

Base class for partial renderers with common functionality

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(component, parts, view_context) ⇒ PartialRenderer

Returns a new instance of PartialRenderer.



10
11
12
13
14
# File 'lib/live_cable/rendering/partial_renderer.rb', line 10

def initialize(component, parts, view_context)
  @component = component
  @parts = parts
  @view_context = view_context
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/live_cable/rendering/partial_renderer.rb', line 97

def method_missing(method, ...)
  if locals.key?(method)
    return locals[method]
  end

  if component.respond_to?(method)
    return component.public_send(method, ...)
  end

  if view_context.respond_to?(method)
    return view_context.public_send(method, ...)
  end

  super
end

Instance Attribute Details

#componentLiveCable::Component (readonly)



8
9
10
# File 'lib/live_cable/rendering/partial_renderer.rb', line 8

def component
  @component
end

Instance Method Details

#expand_component_method_dependencies(method_names) ⇒ Array<Symbol>

Expand component.method_name calls to their transitive dependencies Results are cached per render cycle to avoid redundant analysis

Parameters:

  • method_names (Array<Symbol>)

    Method names called on component

Returns:

  • (Array<Symbol>)

    Reactive variables these methods depend on



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

def expand_component_method_dependencies(method_names)
  return [] if method_names.empty?

  # Use cache key based on the sorted method names array
  cache_key = method_names.sort

  # Return cached result if available
  return method_deps_cache[cache_key] if method_deps_cache.key?(cache_key)

  # Get the analyzer for the component class
  analyzer = component.class.method_dependencies_analyzer

  expanded = []
  method_names.each do |method_name|
    # Get transitive dependencies for this method
    deps = analyzer.expanded_dependencies(method_name)
    expanded.concat(deps.to_a)
  end

  # Cache and return the result
  method_deps_cache[cache_key] = expanded.uniq
end

#mark_locals_dirty(locals) ⇒ Object



39
40
41
# File 'lib/live_cable/rendering/partial_renderer.rb', line 39

def mark_locals_dirty(locals)
  locals.each { |local| dirty_locals << local }
end

#renderObject

Pass through renders to the view context, otherwise method_missing will pass to the component instead since it also has a render method



35
36
37
# File 'lib/live_cable/rendering/partial_renderer.rb', line 35

def render(*)
  view_context.render(*)
end

#render_changes(changes = :all) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/live_cable/rendering/partial_renderer.rb', line 16

def render_changes(changes = :all)
  # No changes, different from :all (:all means render all)
  # Return an array of nils
  if changes == []
    return Array.new(parts.size, nil)
  end

  @locals = {}
  @dirty_locals = Set.new  # Track which locals were recomputed this render
  @method_deps_cache = {}  # Cache method dependency expansion for this render cycle
  parts.each_with_index.with_object([]) do |item, acc|
    part, index = item
    result = view_context.render_part(index) { send("render_part_#{index}", changes) }
    acc << result unless part[0] == :code
  end
end

#respond_to_missing?(method, _include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


113
114
115
# File 'lib/live_cable/rendering/partial_renderer.rb', line 113

def respond_to_missing?(method, _include_private = false)
  locals.key?(method) || component.respond_to?(method) || view_context.respond_to?(method)
end

#should_skip_part?(changes, component_dependencies, component_method_calls, local_dependencies) ⇒ Boolean

Check if this part should be skipped based on its dependencies

Parameters:

  • changes (Symbol|Array)

    Changed variables, :all, or :dynamic

  • component_dependencies (Array<Symbol>)

    Direct component dependencies

  • component_method_calls (Array<Symbol>)

    Component methods called

  • local_dependencies (Array<Symbol>)

    Local variable dependencies

Returns:

  • (Boolean)

    true if the part should be skipped



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/live_cable/rendering/partial_renderer.rb', line 76

def should_skip_part?(changes, component_dependencies, component_method_calls, local_dependencies)
  # Always render on initial render (all parts)
  return false if changes == :all

  # Always render on template switch (all dynamic parts must render)
  return false if changes == :dynamic

  # Expand component.method_name calls to their transitive dependencies
  expanded_deps = expand_component_method_dependencies(component_method_calls)
  all_component_deps = component_dependencies | expanded_deps

  # Render if any component dependencies changed
  return false if changes.intersect?(all_component_deps)

  # Render if any local dependencies are dirty
  return false if dirty_locals.intersect?(local_dependencies)

  # Otherwise, skip rendering this part
  true
end