class ViewModel::TraversalContext
Abstract base for Serialize and DeserializeContexts.
def self.new_child(...)
def self.new_child(...) self.allocate.tap { |c| c.initialize_as_child(...) } end
def self.shared_context_class
def self.shared_context_class SharedContext end
def for_child(parent_viewmodel, association_name:, **rest)
def for_child(parent_viewmodel, association_name:, **rest) self.class.new_child( shared_context: shared_context, parent_context: self, parent_viewmodel: parent_viewmodel, parent_association: association_name, **rest) end
def for_references
context (since a shared reference could equally have been reached via any
keep the same shared context, but drop any tree location specific local
Obtain a semi-independent context for descending through a shared reference:
def for_references self.class.new(shared_context: shared_context) end
def initialize(shared_context: nil, **shared_context_params)
def initialize(shared_context: nil, **shared_context_params) super() @shared_context = shared_context || self.class.shared_context_class.new(**shared_context_params) @parent_context = nil @parent_viewmodel = nil @parent_association = nil @root = true end
def initialize_as_child(shared_context:, parent_context:, parent_viewmodel:, parent_association:)
Shared context is the same, ancestry is established, and subclasses can
Overloaded constructor for initialization of descendent node contexts.
def initialize_as_child(shared_context:, parent_context:, parent_viewmodel:, parent_association:) @shared_context = shared_context @parent_context = parent_context @parent_viewmodel = parent_viewmodel @parent_association = parent_association @root = false end
def nearest_root
def nearest_root if root? self else parent_context&.nearest_root end end
def nearest_root_viewmodel
def nearest_root_viewmodel if root? raise RuntimeError.new('Attempted to find nearest root from a root context. This is probably not what you wanted.') elsif parent_context.root? parent_viewmodel else parent_context.nearest_root_viewmodel end end
def parent_association(idx = 0)
def parent_association(idx = 0) if idx == 0 @parent_association else parent_context(idx - 1)&.parent_association end end
def parent_context(idx = 0)
def parent_context(idx = 0) if idx == 0 @parent_context else @parent_context&.parent_context(idx - 1) end end
def parent_ref(idx = 0)
def parent_ref(idx = 0) parent_viewmodel(idx)&.to_reference end
def parent_viewmodel(idx = 0)
def parent_viewmodel(idx = 0) if idx == 0 @parent_viewmodel else parent_context(idx - 1)&.parent_viewmodel end end
def root?
def root? @root end
def run_callback(hook, view, **args)
def run_callback(hook, view, **args) # Run in-viewmodel callback hooks before context hooks, as they are # permitted to alter the model. if view.respond_to?(hook.dsl_viewmodel_callback_method) view.public_send(hook.dsl_viewmodel_callback_method, hook.context_name => self, **args) end callbacks.each do |callback| callback.run_callback(hook, view, self, **args) end end