af83/chouette-core

View on GitHub
app/models/concerns/custom_fields_support.rb

Summary

Maintainability
A
3 hrs
Test Coverage
# frozen_string_literal: true

module CustomFieldsSupport
  extend ActiveSupport::Concern

  THREAD_VARIABLE_NAME = "current_workgroup"
  def self.within_workgroup workgroup
    if current_workgroup
      if current_workgroup != workgroup
        raise "Two different current workgroups: current:#{current_workgroup.id} wanted:#{workgroup.id}"
      end
      Rails.logger.info "within_workgroup used twice"
      return yield
    end

    Rails.logger.debug "Set current workgroup: #{workgroup&.id}"

    value = nil
    begin
      Thread.current.thread_variable_set(THREAD_VARIABLE_NAME, workgroup)
      value = yield
    ensure
      Thread.current.thread_variable_set(THREAD_VARIABLE_NAME, nil)
    end

    Rails.logger.debug "Reset current workgroup"

    value
  end

  def self.current_workgroup
    Thread.current.thread_variable_get(THREAD_VARIABLE_NAME)
  end

  class_methods do
    def without_custom_fields(&block)
      old_skip_custom_fields_initialization = skip_custom_fields_initialization
      self.skip_custom_fields_initialization = true
      block.call
    ensure
      self.skip_custom_fields_initialization = old_skip_custom_fields_initialization
    end
  end

  included do
    validate :custom_fields_values_are_valid
    after_initialize :initialize_custom_fields

    mattr_accessor :skip_custom_fields_initialization, instance_accessor: false, default: false
    attr_writer :skip_custom_fields_initialization

    # Support both class and instance option
    def skip_custom_fields_initialization
      @skip_custom_fields_initialization || self.class.skip_custom_fields_initialization
    end

    def self.reset_custom_fields
      @_custom_fields = nil
    end

    def self._get_custom_fields(workgroup)
      fields = CustomField.where(resource_type: self.name.split("::").last)
      fields = fields.where(workgroup_id: workgroup.id)
      fields
    end

    def self.custom_fields workgroup
      return CustomField.none unless workgroup
      @_custom_fields ||= {}
      unless @_custom_fields.has_key?(workgroup.id) && @_custom_fields[workgroup.id].first == workgroup.updated_at
        @_custom_fields[workgroup.id] = [workgroup.updated_at, _get_custom_fields(workgroup)]
      end
      @_custom_fields[workgroup.id].last
    end

    def self.custom_fields_definitions workgroup
      Hash[*custom_fields(workgroup).map{|cf| [cf.code, cf]}.flatten]
    end

    def self.within_workgroup workgroup, &block
      ActiveSupport::Deprecation.warn('Use CustomFieldsSupport.within_workgroup')
      CustomFieldsSupport.within_workgroup workgroup, &block
    end

    def self.current_workgroup
      CustomFieldsSupport.current_workgroup
    end

    def method_missing method_name, *args
      if !@custom_fields_initialized && (method_name =~ /custom_field_*/ || method_name =~ /remove_custom_field_*/) && method_name.to_sym != :custom_field_values
        initialize_custom_fields
        send method_name, *args
      else
        super method_name, *args
      end
    end

    def custom_fields deprecated_workgroup=nil
      _workgroup = deprecated_workgroup || self.workgroup
      CustomField::Collection.new self, _workgroup
    end

    def custom_fields_checksum
      custom_fields.values.sort_by(&:code).map(&:checksum).compact
    end

    def custom_field_values= vals
      if custom_fields_initialized?
        out = {}
        custom_fields.each do |code, field|
          out[code] = field.preprocess_value_for_assignment(vals.symbolize_keys[code.to_sym])
        end
        @custom_fields_values_initialized = true
      else
        @raw_custom_fields_values = vals
        out = vals
      end
      write_attribute :custom_field_values, out
    end

    def custom_fields_initialized?
      !!@custom_fields_initialized
    end

    def custom_fields_values_initialized?
      !!@custom_fields_values_initialized
    end

    def initialize_custom_fields
      return if skip_custom_fields_initialization
      return if custom_fields_initialized?
      return unless self.attributes.has_key?("custom_field_values")

      Rails.logger.debug 'Initialize custom fields'
      return unless self.workgroup.present?

      self.custom_field_values ||= {}
      custom_fields.values.each &:initialize_custom_field
      custom_fields.each do |k, v|
        custom_field_values[k] ||= v.default_value
      end
      @custom_fields_initialized = true
      self.custom_field_values = (@raw_custom_fields_values || self.custom_field_values) unless custom_fields_values_initialized?
    end

    def custom_field_value key
      (custom_field_values&.stringify_keys || {})[key.to_s]
    end

    private
    def custom_fields_values_are_valid
      return if skip_custom_fields_initialization

      custom_fields.values.all?{|cf| cf.valid?}
    end
  end
end