engine/app/models/locomotive/extensions/page/tree.rb

126 lines
3.4 KiB
Ruby

module Locomotive
module Extensions
module Page
module Tree
extend ActiveSupport::Concern
included do
include ::Mongoid::Tree
include ::Mongoid::Tree::Ordering
## fields ##
field :depth, :type => Integer, :default => 0
## callbacks ##
before_save :persist_depth
before_destroy :delete_descendants
## indexes ##
index :position
index [[:depth, Mongo::ASCENDING], [:position, Mongo::ASCENDING]]
alias_method_chain :rearrange, :identity_map
alias_method_chain :rearrange_children, :identity_map
end
module ClassMethods
# Returns the tree of pages from the site with the most minimal amount of queries.
# This method should only be used for read-only purpose since
# the mongodb returns the minimal set of required attributes to build
# the tree.
#
# @param [ Locomotive::Site ] site The site owning the pages
#
# @return [ Array ] The first array of pages (depth = 0)
#
def quick_tree(site, minimal_attributes = true)
pages = (minimal_attributes ? site.pages.unscoped.minimal_attributes : site.pages.unscoped).order_by([:depth.asc, :position.asc]).to_a
tmp = []
while !pages.empty?
tmp << _quick_tree(pages.delete_at(0), pages)
end
tmp
end
#:nodoc:
def _quick_tree(current_page, pages)
i, children = 0, []
while !pages.empty?
page = pages[i]
break if page.nil?
if page.parent_id == current_page.id
page = pages.delete_at(i)
children << _quick_tree(page, pages)
else
i += 1
end
end
current_page.instance_eval do
def children=(list); @children = list; end
def children; @children || []; end
end
current_page.children = children
current_page
end
end
# Returns the children of this node but with the minimal set of required attributes
#
# @return [ Array ] The children pages ordered by their position
#
def children_with_minimal_attributes( attrs = [] )
self.children.minimal_attributes( attrs )
end
# Assigns the new position of each child of this node.
#
# @param [ Array ] ids The ordered list of page ids (string)
#
def sort_children!(ids)
cached_children = self.children.to_a
ids.each_with_index do |id, position|
child = cached_children.detect { |p| p._id == BSON::ObjectId(id) }
child.position = position
child.save
end
end
def depth
self.parent_ids.count
end
protected
def rearrange_with_identity_map
::Mongoid::IdentityMap.clear
rearrange_without_identity_map
end
def rearrange_children_with_identity_map
self.children.reset
rearrange_children_without_identity_map
end
def persist_depth
self.attributes['depth'] = self.depth
self.depth_will_change!
end
end
end
end
end