app/models/concerns/course/discussion/post/ordering_concern.rb
# frozen_string_literal: true
module Course::Discussion::Post::OrderingConcern
extend ActiveSupport::Concern
# Sorts all posts in a collection in topological order.
#
# By convention, each post is represented by an array. The first element is the post itself,
# the second is the children of the array.
class PostSort
include Enumerable
delegate :each, to: :@sorted
delegate :length, to: :@sorted
delegate :flatten, to: :@sorted
alias_method :size, :length
# Constructor.
#
# @param [Array<Course::Discussion::Post>] posts The posts to sort.
def initialize(posts)
@posts = posts
@sorted = sort(nil)
end
# Retrieves the last post topologically -- the last post at every branch.
#
# @return [Course::Discussion::Post] The last post topologically.
# @return [nil] When there are no posts.
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
# Returns a set of recursive arrays indicating the parent-child relationships of post ids.
#
# @return [Enumerable]
# @return [[]] When there are no posts.
def sorted_ids
retrieve_id(@sorted)
end
private
def sort(post_id)
children_posts, @posts = @posts.partition { |child_post| child_post.parent_id == post_id }
children_posts.map do |child_post|
[child_post].push(sort(child_post.id))
end
end
def retrieve_id(sorted_enum)
sorted_ids = []
sorted_enum.each do |element|
sorted_ids.push(element.id) if element.instance_of?(Course::Discussion::Post)
sorted_ids.push(retrieve_id(element)) if element.instance_of?(Array)
end
sorted_ids
end
end
# Returns a set of recursive arrays indicating the parent-child relationships of posts.
#
# @return [Enumerable]
def ordered_topologically
PostSort.new(self)
end
end