app/models/collection/base.rb

Summary

Maintainability
A
3 hrs
Test Coverage
# encoding: UTF-8

# Copyright 2011-2013 innoQ Deutschland GmbH
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

class Collection::Base < Concept::Base

  Iqvoc::Collection.include_modules.each do |mod|
    include mod
  end

  has_many Note::SKOS::Definition.name.to_relation_name,
           class_name: 'Note::SKOS::Definition',
           as: :owner,
           dependent: :destroy,
           inverse_of: :owner

  has_many :members,
      class_name: 'Collection::Member::Base',
      foreign_key: 'collection_id',
      dependent: :destroy,
      inverse_of: :collection

  has_many :parent_collection_members,
      class_name: 'Collection::Member::Base',
      foreign_key: 'target_id',
      dependent: :destroy,
      inverse_of: :target

  has_many :parent_collections,
      through: :parent_collection_members,
      source: 'collection'

  has_many :concepts,
           -> { where("concepts.type = ?", Iqvoc::Concept.base_class) },
           through: :members,
           source: :target

  has_many :subcollections,
           -> { where("concepts.type = ?", Iqvoc::Collection.base_class) },
           through: :members,
           source: :target

  include_to_deep_cloning(:members)

  # ********** Hooks

  after_initialize do |collection|
    if collection.origin.blank?
      collection.origin = Origin.new.to_s
    end
  end

  after_save :regenerate_concept_members,
             :regenerate_collection_members

  validate :circular_subcollections

  def self.with_pref_labels
    preload(:pref_labels)
      .joins(:pref_labels)
      .references(:collection_members)
      .distinct
  end

  def self.by_origin(origin)
    where(origin: origin)
  end

  def self.by_label_value(val)
    includes(:labels).merge(Label::Base.by_query_value(val))
  end

  def self.tops
    includes(:parent_collection_members).
        where("#{Collection::Member::Base.table_name}.target_id IS NULL")
  end

  def self.by_parent_id(parent_id)
    includes(:parent_collection_members).
        where(Collection::Member::Base.arel_table[:collection_id].eq(parent_id))
  end

  def self.dashboard_path
    'collection_dashboard_path'
  end

  def self.edit_link_partial_name
    'partials/collection/edit_link_base'
  end

  def self.new_link_partial_name
    'partials/collection/new_link_base'
  end

  def class_path
    'collection_path'
  end

  def to_param
    origin
  end

  def label
    pref_label
  end

  def build_rdf_subject(&block)
    IqRdf.build_uri(self.origin, IqRdf::Skos::build_uri('Collection'), &block)
  end

  def inline_member_concept_origins=(origins)
    @member_concept_origins = origins.to_s.
        split(InlineDataHelper::SPLITTER).map(&:strip)
  end

  def inline_member_concept_origins
    @member_concept_origins || concepts.map { |m| m.origin }.uniq
  end

  def inline_member_concepts
    if @member_concept_origins
      Concept::Base.editor_selectable.where(origin: @member_concept_origins)
    else
      concepts.select{ |c| c.editor_selectable? }
    end
  end

  def inline_member_collection_origins=(origins)
    @member_collection_origins = origins.to_s.
        split(InlineDataHelper::SPLITTER).map(&:strip)
  end

  def inline_member_collection_origins
    @member_collection_origins || subcollections.map(&:origin).uniq
  end

  def inline_member_collections
    if @member_collection_origins
      Collection::Base.where(origin: @member_collection_origins)
    else
      subcollections
    end
  end

  def regenerate_members(target_class, target_origins)
    return if target_origins.nil? # There is nothing to do
    existing = self.members.includes(:target)
    existing = if target_class <= Collection::Base
      existing.select { |m| m.target.is_a?(Collection::Base) }
    else
      existing.reject { |m| m.target.is_a?(Collection::Base) }
    end
    new = []
    target_origins.each do |new_origin|
      member = existing.find{ |m| m.target.origin == new_origin }
      unless member
        c = target_class.by_origin(new_origin).first
        member = Iqvoc::Collection.member_class.create(collection: self, target: c) if c
      end
      new << member if member
    end
    (existing - new).each do |m|
      m.destroy
    end
  end

  def regenerate_concept_members
    regenerate_members(Concept::Base, @member_concept_origins)
  end

  def regenerate_collection_members
    regenerate_members(Collection::Base, @member_collection_origins)
  end

  #******** Validation methods

  # This only prevent circles of length 2.
  # TODO: This should be a real circle detector (but still performant) or be
  # removed (seems to me like the better idea).
  def circular_subcollections
    Iqvoc::Collection.base_class.by_origin(@member_collection_origins).includes(members: :target).each do |subcollection|
      if subcollection.subcollections.include?(self)
        errors.add(:base,
          I18n.t('txt.controllers.collections.circular_error', label: subcollection.pref_label))
      end
    end
  end
end