Skip to content

Instantly share code, notes, and snippets.

@cmrichards
Created April 17, 2012 15:54
Show Gist options
  • Save cmrichards/2407071 to your computer and use it in GitHub Desktop.
Save cmrichards/2407071 to your computer and use it in GitHub Desktop.
Data Model + Context + Role
The Context + Roles :
module Contexts
# Move an area to different points on the tree.
#
# This context has multiple entry points for each deviation of moving an area around the tree.
#
# The source area can be moved in any of these 3 ways :
#
# - Before an existing area.
# - After an existing area.
# - Within, ie in the subtree of, an existing area
#
class MoveArea < Contexts::Base
def initialize(area_id, target_area_id)
area = Area.find area_id
target_area = Area.find target_area_id
target_area_parent = target_area.parent
add_role :source_area, area, MoveableArea
add_role :target_area, target_area, MoveableAreaTarget
add_role :parent_of_target_area, target_area_parent, MoveableAreaTarget
end
def move_after_target_area
roles.source_area.move_after_target_area
end
def move_before_target_area
roles.source_area.move_before_target_area
end
def move_within_target_area
roles.source_area.move_within_target_area
end
end
module MoveableAreaTarget
def add_existing_sub_area(area)
area.parent = self
area.save!
end
end
# These role methods use some methods that are provided
# by the 'resort' gem that is used in the Area model :
# delete_from_list, append_to, prepend.
#
module MoveableArea
class CantMoveRoot < StandardError ; end
class InvalidMoveTarget < StandardError ; end
def move_after_target_area
check_target_area_not_in_subtree
check_not_root!(self)
check_not_root!(roles.target_area)
add_to_parent_of_target_area
self.append_to(roles.target_area)
end
def move_before_target_area
check_target_area_not_in_subtree
check_not_root!(self)
check_not_root!(roles.target_area)
add_to_parent_of_target_area
if roles.target_area.previous
self.append_to(roles.target_area.previous)
else
prepend
end
end
def move_within_target_area
check_target_area_not_in_subtree
check_not_root!(self)
delete_from_list
roles.target_area.add_existing_sub_area(self)
prepend # This method is from resort! gem, it moves the area to the top of the list.
end
private
#Add to the parent of the target area if the parent is different to the current parent.
def add_to_parent_of_target_area
if self.parent != roles.target_area.parent
delete_from_list
roles.parent_of_target_area.add_existing_sub_area(self)
end
end
#Check that area is not the root of the tree
def check_not_root!(area = self)
raise CantMoveRoot if area.parent.blank?
end
#Check that the target area is not within the subtree of the source area.
def check_target_area_not_in_subtree
raise InvalidMoveTarget if self.find_subtree(roles.target_area.id).exists?
end
end
------------------------
The Domain Model :
class Area < ActiveRecord::Base
#https://github.com/codegram/resort
#Resort sorts elements within a list using a linked list format
resort!
# https://github.com/stefankroes/ancestry
# Ancestry arranges areas into a hierachy/tree of areas.
# has_ancestry provides the children and subtree methods.
has_ancestry
validates :name, :presence => true
has_many :sections
default_scope where(:expired=>false)
def find_section(section_id)
self.sections.where(:id=>section_id).first
end
def find_children(area_id)
self.children.where(:id=>area_id).first
end
def find_subtree(area_id)
self.subtree.where(:id=>area_id).first
end
def ordered_sections
self.sections.ordered
end
def ordered_areas
self.children.ordered
end
def expire
self.update_attribute :expired, true
end
def arranged_subtree
self.subtree.arrange
end
#TODO : make this nicer. The resort! gem automatically calls this #siblings method to determine
#what elements are in the same list.
#This method is required by the resort! gem.
def siblings
if self.ancestry.blank?
#Root children have no siblings, do don't return anything. Fix this ugliness.
super.where(:ancestry=>"xxxxxxxxxxxxxxxx")
else
#Call the siblings method that is added by the has_ancestry gem.
super
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment