app/models/concerns/shared/matrix_hooks/dynamic.rb
module Shared
module MatrixHooks
# Code to update (dynamic) matrix content based on what goes on with this instance.
#
# This code handles first creates and changes, not use increments nor destroys. It ensures there
# are rows/columns created when changes happens, that's it.
#
# This module handles a once-removed abstraction edge case.
#
# !! All modules including this code must implement either one or both (row/column) of the in/out "Interface" methods
#
# See spec/models/taxon_name/matrix_hook_spec.rb for specs and exercising.
module Dynamic
extend ActiveSupport::Concern
included do
before_save :dynamic_inspect_matrices
after_save :dynamic_syncronize_matrices, if: -> {dynamic_update_matrix_row_items? || dynamic_update_matrix_column_items?} # must happen after closure_tree updates the hierarchy
attr_accessor :dynamic_row_items_in, :dynamic_row_items_out, :dynamic_row_out_map
attr_accessor :dynamic_column_items_in, :dynamic_column_items_out, :dynamic_column_out_map
end
module ClassMethods
end
# "Interface"
# These methods must be implemented in all classes that include this code
def in_scope_observation_matrix_row_items
[]
end
def out_of_scope_observation_matrix_row_items
[]
end
def in_scope_observation_matrix_column_items
[]
end
def out_of_scope_observation_matrix_column_items
[]
end
# Shared code
def dynamic_update_matrix_row_items?
dynamic_row_items_out && dynamic_row_items_in != dynamic_row_items_out
end
def dynamic_update_matrix_column_items?
dynamic_column_items_out && dynamic_column_items_in != dynamic_column_items_out
end
def dynamic_remove_from_matrix_row_items
out_of_scope_observation_matrix_row_items - (out_of_scope_observation_matrix_row_items & in_scope_observation_matrix_row_items)
end
def dynamic_add_to_matrix_row_items
in_scope_observation_matrix_row_items - (in_scope_observation_matrix_row_items & out_of_scope_observation_matrix_row_items)
end
def dynamic_remove_from_matrix_column_items
out_of_scope_observation_matrix_column_items - (out_of_scope_observation_matrix_column_items & in_scope_observation_matrix_column_items)
end
def dynamic_add_to_matrix_column_items
in_scope_observation_matrix_column_items - (in_scope_observation_matrix_column_items & out_of_scope_observation_matrix_column_items)
end
def dynamic_row_items_in
@dynamic_row_items_in ||= in_scope_observation_matrix_row_items
end
def dynamic_row_items_out
@dynamic_row_items_out ||= out_of_scope_observation_matrix_row_items
end
def dynamic_cleanup_in_scope_row_items(rows)
rows.each do |mri|
mri.observation_objects.each do |o|
p = mri.find_or_build_row(o)
if p.persisted?
#if p.observation_object == o
# p.update_column(:reference_count, p.reference_count + 1)
#end
else
p.reference_count = 1
p.save!
end
end
end
end
def dynamic_cleanup_out_of_scope_row_items(rows)
rows.each do |mri|
dynamic_row_out_map[mri.id].each do |o|
mri.cleanup_single_matrix_row(o)
end
end
end
def dynamic_column_items_in
@dynamic_column_items_in ||= in_scope_observation_matrix_column_items
end
def dynamic_column_items_out
@dynamic_column_items_out ||= out_of_scope_observation_matrix_column_items
end
# adding, incrementing
def dynamic_cleanup_in_scope_column_items(columns)
columns.each do |mci|
mci.column_objects.each do |o|
p = mci.find_or_build_column(o)
if p.persisted?
# See row version.
#if p.descriptor == o
# #p.update_column(:reference_count, p.reference_count + 1)
#end
# see "only added observation_matrix column is incremented" spec # mci.increment_matrix_column_reference_count(p)
else
p.reference_count = 1
p.save!
end
end
end
end
# removing, decrementing
def dynamic_cleanup_out_of_scope_column_items(columns)
columns.each do |mci|
dynamic_column_out_map[mci.id].each do |o|
mci.cleanup_single_matrix_column(o)
end
end
end
def dynamic_syncronize_matrices
dynamic_cleanup_out_of_scope_row_items(@dynamic_row_items_out) if @dynamic_row_items_out
dynamic_cleanup_in_scope_row_items(@dynamic_row_items_in) if @dynamic_row_items_in
dynamic_cleanup_out_of_scope_column_items(@dynamic_column_items_out) if @dynamic_column_items_out
dynamic_cleanup_in_scope_column_items(@dynamic_column_items_in) if @dynamic_column_items_in
end
def prepare_matrix_items
# Rows
@dynamic_row_items_in = in_scope_observation_matrix_row_items
@dynamic_row_items_out = out_of_scope_observation_matrix_row_items
# After save our out maps don't capture items that *were* there,
# so we cache those records that need to be popped off the matrix row stack
@dynamic_row_out_map = {}
@dynamic_row_items_out.each do |mri|
@dynamic_row_out_map[mri.id] = mri.observation_objects
end
# Columns
@dynamic_column_items_in = in_scope_observation_matrix_column_items
@dynamic_column_items_out = out_of_scope_observation_matrix_column_items
# After save our out maps don't capture items that *were* there,
# so we cache those records that need to be popped off the matrix column stack
@dynamic_column_out_map = {}
@dynamic_column_items_out.each do |mci|
@dynamic_column_out_map[mci.id] = mci.column_objects
end
end
def dynamic_inspect_matrices
# TODO: strengthen checks for whether this is necessary
if ObservationMatrix.where(project_id:).any?
prepare_matrix_items
end
end
end
end
end