sanger/sequencescape

View on GitHub
app/models/study/poly_metadata_handler.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
92%
# frozen_string_literal: true

# Handles the processing of polymorphic metadata for a study.
class Study::PolyMetadataHandler
  include ActiveModel::Validations

  # Add the attribute accessor for each polymorphic metadata key here.

  # @!attribute [rw] scrna_core_pbmc_donor_pooling_required_number_of_cells
  #   @return [Integer] The number of cells required for PBMC donor pooling.
  # :reek:Attribute
  attr_accessor :scrna_core_pbmc_donor_pooling_required_number_of_cells

  # Add validations for each polymorphic metadata key here.

  validates :scrna_core_pbmc_donor_pooling_required_number_of_cells,
            numericality: {
              greater_than: 0,
              allow_blank: true
            }

  # Initializes a new instance of the PolyMetadataHandler class. The studies
  # controller creates an instance of this class in the create and update
  # actions and passes the study instance.
  #
  # @param study [Study] The study to handle polymorphic metadata for.
  # @return [void]
  def initialize(study)
    @study = study
  end

  # Processes the provided poly_metadta parameters. This involves three steps:
  # 1. Assigning the parameters to corresponding attributes.
  # 2. Validating the assigned attributes.
  # 3. Dispatching the parameters to their specific handler methods.
  # The parameters passed from the controller are solely poly_metadata keys and values.
  # This is because they are nested under the poly_metadata key in the form fields,
  # which allows for straightforward iterations within this class.
  #
  # @param params [Hash] The poly_metadata parameters to process.
  # @return [void]
  def process(params)
    assign_attributes(params)
    validate_attributes
    dispatch(params)
  end

  # Assigns the given parameters to attributes if they are defined.
  #
  # @param params [Hash] The parameters to assign.
  # @return [void]
  def assign_attributes(params)
    params.each { |key, value| send(:"#{key}=", value) if self.class.method_defined?(key) }
  end

  # Validates the assigned attributes. If any attributes are invalid, their
  # errors are added to the study's errors, and an ActiveRecord::RecordInvalid
  # exception is raised with the study as its record. Adding errors to the
  # study is important to render messages in the UI. Raising the specific
  # exception is important to rollback the active transaction.
  #
  # @return [void]
  # @raise [ActiveRecord::RecordInvalid] If any attributes are invalid.
  def validate_attributes
    return if valid?
    errors.each { |error| @study.errors.add(error.attribute, error.message) }
    raise ActiveRecord::RecordInvalid, @study
  end

  # Dispatches the given parameters by calling a handler method for each one
  # if it exists. The convention for the handler methods is to prefix the key
  # with 'handle_'. For example, if the key is
  # 'scrna_core_pbmc_donor_pooling_required_number_of_cells', the handler method
  # would be 'handle_scrna_core_pbmc_donor_pooling_required_number_of_cells'.
  #
  # @param params [Hash] The parameters to dispatch.
  # @return [void]
  # :reek:ManualDispatch
  def dispatch(params)
    params.each do |key, value|
      method = "handle_#{key}"
      send(method, key, value) if respond_to?(method)
    end
  end

  # Handles 'scrna_core_pbmc_donor_pooling_required_number_of_cells' parameter.
  # A blank value defaults to Limber's configuration. Limber will warn but allow
  # proceeding with the default value for the study. If a matching PolyMetadatum
  # exists with the same value as the parameter, the method exits early to avoid
  # redundant updates. Otherwise, a new PolyMetadatum is created or updated with
  # the new value, followed by a save operation.
  #
  # @param value [String] The value of the
  #   scrna_core_pbmc_donor_pooling_required_number_of_cells parameter.
  # @return [void]
  def handle_scrna_core_pbmc_donor_pooling_required_number_of_cells(key, value)
    poly_metadatum = @study.poly_metadatum_by_key(key)

    if value.blank?
      poly_metadatum&.destroy!
    elsif poly_metadatum&.value != value
      poly_metadatum ||= PolyMetadatum.new(key: key, metadatable: @study)
      poly_metadatum.value = value
      poly_metadatum.save!
    end
  end
end