module ViewModel::ActiveRecord::NestedControllerBase
Controller
mixin defining machinery for accessing viewmodels nested under a parent. Used by Singular- and CollectionNestedControllers
Protected Instance Methods
association_data()
click to toggle source
# File lib/view_model/active_record/nested_controller_base.rb, line 175 def association_data @association_data ||= owner_viewmodel._association_data(association_name) end
association_name()
click to toggle source
# File lib/view_model/active_record/nested_controller_base.rb, line 202 def association_name params.fetch(:association_name) { raise ArgumentError.new('No association name from routes') } end
destroy_association(collection, serialize_context: new_serialize_context, deserialize_context: new_deserialize_context, lock_owner: nil)
click to toggle source
# File lib/view_model/active_record/nested_controller_base.rb, line 162 def destroy_association(collection, serialize_context: new_serialize_context, deserialize_context: new_deserialize_context, lock_owner: nil) require_external_referenced_association! if lock_owner owner_viewmodel.find(owner_viewmodel_id, eager_include: false, lock: lock_owner) end empty_update = collection ? [] : nil owner_viewmodel.deserialize_from_view(owner_update_hash(empty_update), deserialize_context: deserialize_context) render_viewmodel(empty_update, serialize_context: serialize_context) end
owner_update_hash(update)
click to toggle source
# File lib/view_model/active_record/nested_controller_base.rb, line 179 def owner_update_hash(update) { ViewModel::ID_ATTRIBUTE => owner_viewmodel_id, ViewModel::TYPE_ATTRIBUTE => owner_viewmodel.view_name, association_name.to_s => update, } end
owner_viewmodel()
click to toggle source
# File lib/view_model/active_record/nested_controller_base.rb, line 197 def owner_viewmodel name = params.fetch(:owner_viewmodel) { raise ArgumentError.new("No owner viewmodel present") } owner_viewmodel_class_for_name(name.to_s.camelize) end
owner_viewmodel_class_for_name(name)
click to toggle source
# File lib/view_model/active_record/nested_controller_base.rb, line 193 def owner_viewmodel_class_for_name(name) ViewModel::Registry.for_view_name(name) end
owner_viewmodel_id(required: true)
click to toggle source
# File lib/view_model/active_record/nested_controller_base.rb, line 187 def owner_viewmodel_id(required: true) id_param_name = owner_viewmodel.view_name.underscore + '_id' default = required ? {} : { default: nil } parse_param(id_param_name, **default) end
require_external_referenced_association!()
click to toggle source
# File lib/view_model/active_record/nested_controller_base.rb, line 206 def require_external_referenced_association! unless association_data.referenced? && association_data.external? raise ArgumentError.new("Expected referenced external association: '#{association_name}'") end end
show_association(scope: nil, serialize_context: new_serialize_context, lock_owner: nil) { |associated_views| ... }
click to toggle source
# File lib/view_model/active_record/nested_controller_base.rb, line 46 def show_association(scope: nil, serialize_context: new_serialize_context, lock_owner: nil) require_external_referenced_association! associated_views = nil pre_rendered = owner_viewmodel.transaction do owner_view = owner_viewmodel.find(owner_viewmodel_id, eager_include: false, lock: lock_owner) ViewModel::Callbacks.wrap_serialize(owner_view, context: serialize_context) do # Association manipulation methods construct child contexts internally associated_views = owner_view.load_associated(association_name, scope: scope, serialize_context: serialize_context) associated_views = yield(associated_views) if block_given? child_context = owner_view.context_for_child(association_name, context: serialize_context) prerender_viewmodel(associated_views, serialize_context: child_context) end end render_json_string(pre_rendered) associated_views end
write_association(serialize_context: new_serialize_context, deserialize_context: new_deserialize_context, lock_owner: nil) { |association_view| ... }
click to toggle source
This method always takes direct update hashes, and returns viewmodels directly.
There's no multi membership, so when viewing the children of a single parent each child can only appear once. This means it's safe to use update hashes directly.
# File lib/view_model/active_record/nested_controller_base.rb, line 72 def write_association(serialize_context: new_serialize_context, deserialize_context: new_deserialize_context, lock_owner: nil) require_external_referenced_association! association_view = nil pre_rendered = owner_viewmodel.transaction do update_hash, refs = parse_viewmodel_updates update_hash = ViewModel::ActiveRecord.add_reference_indirection( update_hash, association_data: association_data, references: refs, key: 'write-association', ) owner_view = owner_viewmodel.find(owner_viewmodel_id, eager_include: false, lock: lock_owner) association_view = owner_view.replace_associated(association_name, update_hash, references: refs, deserialize_context: deserialize_context) ViewModel::Callbacks.wrap_serialize(owner_view, context: serialize_context) do child_context = owner_view.context_for_child(association_name, context: serialize_context) ViewModel.preload_for_serialization(association_view) association_view = yield(association_view) if block_given? prerender_viewmodel(association_view, serialize_context: child_context) end end render_json_string(pre_rendered) association_view end
write_association_bulk(serialize_context: new_serialize_context, deserialize_context: new_deserialize_context, lock_owner: nil) { |updated_by_parent_viewmodel| ... }
click to toggle source
This method takes direct update hashes for owned associations, and reference hashes for shared associations. The return value matches the input structure.
If an association is referenced and owned, each child may only appear once so each is guaranteed to have a unique update hash. This means it's only safe to use update hashes directly in this case.
# File lib/view_model/active_record/nested_controller_base.rb, line 112 def write_association_bulk(serialize_context: new_serialize_context, deserialize_context: new_deserialize_context, lock_owner: nil) require_external_referenced_association! updated_by_parent_viewmodel = nil pre_rendered = owner_viewmodel.transaction do updates_by_parent_id, references = parse_bulk_update if association_data.owned? updates_by_parent_id.transform_values!.with_index do |update_hash, index| ViewModel::ActiveRecord.add_reference_indirection( update_hash, association_data: association_data, references: references, key: "write-association-bulk-#{index}", ) end end updated_by_parent_viewmodel = owner_viewmodel.replace_associated_bulk( association_name, updates_by_parent_id, references: references, deserialize_context: deserialize_context, ) views = updated_by_parent_viewmodel.flat_map { |_parent_viewmodel, updated_views| Array.wrap(updated_views) } ViewModel.preload_for_serialization(views) updated_by_parent_viewmodel = yield(updated_by_parent_viewmodel) if block_given? return_updates = updated_by_parent_viewmodel.map do |owner_view, updated_views| ParentProxyModel.new(owner_view, association_data, updated_views) end return_structure = { ViewModel::TYPE_ATTRIBUTE => ViewModel::BULK_UPDATE_TYPE, ViewModel::BULK_UPDATES_ATTRIBUTE => return_updates, } prerender_viewmodel(return_structure, serialize_context: serialize_context) end render_json_string(pre_rendered) updated_by_parent_viewmodel end