Class: ViewComponent::Serializable::Proxy

Inherits:
Object
  • Object
show all
Defined in:
lib/view_component/serializable/proxy.rb

Overview

A proxy that wraps a component class and its initialization arguments, deferring component instantiation until render time. This allows slot calls and other post-initialize configuration to be captured and replayed, and enables the proxy itself (rather than a live component instance) to be serialized for background jobs (e.g. ActiveJob / Turbo Streams).

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(component_class, *args) ⇒ Proxy

Returns a new instance of Proxy.



30
31
32
33
34
35
36
37
38
# File 'lib/view_component/serializable/proxy.rb', line 30

def initialize(component_class, *args)
  if component_class.name.nil?
    raise UnserializableError, "Cannot serialize anonymous component class #{component_class.inspect}"
  end

  @component_class = component_class
  @initialize_args = args
  @slot_calls = []
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, &block) ⇒ Object



49
50
51
52
53
54
55
# File 'lib/view_component/serializable/proxy.rb', line 49

def method_missing(method_name, *args, &block)
  if slot_method?(method_name)
    capture_slot_call(method_name, args, &block)
  else
    super
  end
end

Instance Attribute Details

#component_classObject (readonly)

Returns the value of attribute component_class.



28
29
30
# File 'lib/view_component/serializable/proxy.rb', line 28

def component_class
  @component_class
end

#initialize_argsObject (readonly)

Returns the value of attribute initialize_args.



28
29
30
# File 'lib/view_component/serializable/proxy.rb', line 28

def initialize_args
  @initialize_args
end

#slot_callsObject (readonly)

Returns the value of attribute slot_calls.



28
29
30
# File 'lib/view_component/serializable/proxy.rb', line 28

def slot_calls
  @slot_calls
end

Class Method Details

.deserialize(hash) ⇒ Object

Rebuilds a Proxy from a serialized hash produced by serialize

Raises:

  • (ArgumentError)


12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/view_component/serializable/proxy.rb', line 12

def self.deserialize(hash)
  klass = hash["component_class"].safe_constantize
  raise ArgumentError, "Cannot deserialize unknown component: #{hash["component_class"]}" unless klass

  args = ActiveJob::Arguments.deserialize(hash["initialize_args"] || [])
  proxy = new(klass, *args)

  Array(hash["slot_calls"]).each do |call|
    method_name = call["method"].to_sym
    slot_args = ActiveJob::Arguments.deserialize(call["args"])
    proxy.public_send(method_name, *slot_args)
  end

  proxy
end

Instance Method Details

#formatObject

Rails expects us to define format on all renderables, but we do not know the format of a ViewComponent until runtime.



81
82
83
# File 'lib/view_component/serializable/proxy.rb', line 81

def format
  nil
end

#render_in(view_context, &block) ⇒ Object

Implements the Rails renderable interface



42
43
44
45
46
47
# File 'lib/view_component/serializable/proxy.rb', line 42

def render_in(view_context, &block)
  if block
    raise UnserializableError, "Cannot serialize render_in with a block"
  end
  build_component.render_in(view_context)
end

#respond_to_missing?(method_name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


58
59
60
# File 'lib/view_component/serializable/proxy.rb', line 58

def respond_to_missing?(method_name, include_private = false)
  slot_method?(method_name) || super
end

#serializeObject

Returns a hash that can be rebuilt into a Proxy instance using deserialize



63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/view_component/serializable/proxy.rb', line 63

def serialize
  serialized_slot_calls = @slot_calls.map do |call|
    {
      "method" => call[:method].to_s,
      "args" => ActiveJob::Arguments.serialize(call[:args])
    }
  end

  {
    "component_class" => @component_class.name,
    "initialize_args" => ActiveJob::Arguments.serialize(@initialize_args),
    "slot_calls" => serialized_slot_calls
  }
end