app/models/concerns/course/material/folder/ordering_concern.rb
# frozen_string_literal: true
module Course::Material::Folder::OrderingConcern
extend ActiveSupport::Concern
# Sorts all folders in a collection in topological order.
#
# By convention, each folder is represented by an array. The first element is the folder itself,
# the second is the children of the array.
class FolderSort
include Enumerable
delegate :each, to: :@sorted
delegate :length, to: :@sorted
delegate :flatten, to: :@sorted
alias_method :size, :length
# Constructor.
#
# @param [Array<Course::Material::Folder>] folders The folders to sort.
def initialize(folders)
@folders = folders
@sorted = sort(nil)
end
# Retrieves the last folder topologically -- the last folder at every branch.
#
# @return [Course::Material::Folder] The last folder topologically.
# @return [nil] When there are no folders.
def last
current_thread = @sorted.last
return nil unless current_thread
current_thread = current_thread.second.last until current_thread.second.empty?
current_thread.first
end
private
def sort(folder_id)
children_folders, @folders = @folders.partition { |child_folder| child_folder.parent_id == folder_id }
children_folders.map do |child_folder|
[child_folder].push(sort(child_folder.id))
end
end
end
# Returns a set of recursive arrays indicating the parent-child relationships of folders.
#
# @return [Enumerable]
def ordered_topologically
FolderSort.new(self)
end
end