Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a setter for parents=. #3

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 25 additions & 21 deletions ancestry.gemspec
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
# -*- encoding: utf-8 -*-
# stub: ancestry 2.1.4 ruby lib

Gem::Specification.new do |s|
s.name = 'ancestry'
s.description = 'Organise ActiveRecord model into a tree structure'
s.summary = 'Ancestry allows the records of a ActiveRecord model to be organised in a tree structure, using a single, intuitively formatted database column. It exposes all the standard tree structure relations (ancestors, parent, root, children, siblings, descendants) and all of them can be fetched in a single sql query. Additional features are named_scopes, integrity checking, integrity restoration, arrangement of (sub)tree into hashes and different strategies for dealing with orphaned records.'
s.name = "ancestry"
s.version = "2.1.4"

s.version = '2.1.4'
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib"]
s.authors = ["Stefan Kroes [enhancements for multi-parent nodes by tjchambers]"]
s.date = "2015-01-22"
s.description = "Organise ActiveRecord model into a tree structure"
s.email = "[email protected]"
s.files = ["MIT-LICENSE", "README.rdoc", "ancestry.gemspec", "init.rb", "install.rb", "lib/ancestry.rb", "lib/ancestry/class_methods.rb", "lib/ancestry/exceptions.rb", "lib/ancestry/has_ancestry.rb", "lib/ancestry/instance_methods.rb"]
s.homepage = "http://github.com/tjchambers/ancestry"
s.rubygems_version = "2.4.3"
s.summary = "Ancestry allows the records of a ActiveRecord model to be organised in a tree structure, using a single, intuitively formatted database column. It exposes all the standard tree structure relations (ancestors, parent, root, children, siblings, descendants) and all of them can be fetched in a single sql query. Additional features are named_scopes, integrity checking, integrity restoration, arrangement of (sub)tree into hashes and different strategies for dealing with orphaned records."

s.author = 'Stefan Kroes [enhancements for multi-parent nodes by tjchambers]'
s.email = '[email protected]'
s.homepage = 'http://github.com/tjchambers/ancestry'
if s.respond_to? :specification_version then
s.specification_version = 4

s.files = [
'ancestry.gemspec',
'init.rb',
'install.rb',
'lib/ancestry.rb',
'lib/ancestry/has_ancestry.rb',
'lib/ancestry/exceptions.rb',
'lib/ancestry/class_methods.rb',
'lib/ancestry/instance_methods.rb',
'MIT-LICENSE',
'README.rdoc'
]

s.add_dependency 'activerecord', '>= 4.0.0'
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<activerecord>, [">= 4.0.0"])
else
s.add_dependency(%q<activerecord>, [">= 4.0.0"])
end
else
s.add_dependency(%q<activerecord>, [">= 4.0.0"])
end
end
4 changes: 3 additions & 1 deletion lib/ancestry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@
require File.join(File.expand_path(File.dirname(__FILE__)), 'ancestry/has_ancestry')

module Ancestry
ANCESTRY_PATTERN = /\A[0-9]+(\/[0-9]+)*(,[0-9]+(\/[0-9]+)*)*\Z/
# ANCESTRY_PATTERN = /\A[0-9]+(\/[0-9]+)*(,[0-9]+(\/[0-9]+)*)*\Z/
# This was modified to cycle through an array of values that may match this pattern, rather than 1 value
ANCESTRY_PATTERN = /[\A[0-9]+(\/[0-9]+)*([0-9]+(\/[0-9]+)*)*\Z]/
end
97 changes: 95 additions & 2 deletions lib/ancestry/instance_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,18 @@ def parent_id=(parent_id)
end
end

## These were re-added to give single parent functionality

def parent
if parent_id.blank? then nil else unscoped_find(parent_id) end
end

def parent_id
if ancestor_ids.empty? then nil else ancestor_ids.last end
end

## These were re-added to give single parent functionality

# branches are the multiple parents of the current node
def branches
if ancestor_ids.empty? then
Expand All @@ -150,11 +162,30 @@ def parents
unscoped_find(parent_ids)
end
end

def parents=(new_parents)
ancestry = if new_parents.nil?
nil
else
# This is for the single ID case
# (self.base_class.find(new_parents).child_ancestry.split("/") - [self.id.to_s]).join("/")

# What this does is finds the new record in the DB, gets the child_ancestry value for that new
# record - which is the ancestry path of the entire tree, but that includes the ID for the current
# node, so we remove the current node from the child_ancestry path, convert it back into an
# ancestry-friendly string and return that entire thing.

# This is for an list of ancestry values
(new_parents.collect { |x| (self.base_class.find(x).child_ancestry.split("/") - [self.id.to_s]).join("/") }).join(",")
end
# binding.pry
write_attribute(self.base_class.ancestry_column, ancestry)
end

def has_parent?
!is_root?
end

# Root - the topmost SINGULAR node of the current tree
def root_id
if ancestor_ids.empty? then
Expand All @@ -175,6 +206,56 @@ def root
def is_root?
read_attribute(self.base_class.ancestry_column).blank?
end

# Children

def child_conditions
t = get_arel_table
t[get_ancestry_column].eq(child_ancestry)
end

def children
self.base_class.where child_conditions
end

def child_ids
children.select(self.base_class.primary_key).map(&self.base_class.primary_key.to_sym)
end

def has_children?
self.children.exists?({})
end
alias_method :children?, :has_children?

def is_childless?
!has_children?
end
alias_method :childless?, :is_childless?

# Siblings

def sibling_conditions
t = get_arel_table
t[get_ancestry_column].eq(read_attribute(self.base_class.ancestry_column))
end

def siblings
self.base_class.where sibling_conditions
end

def sibling_ids
siblings.select(self.base_class.primary_key).collect(&self.base_class.primary_key.to_sym)
end

def has_siblings?
self.siblings.count > 1
end
alias_method :siblings?, :has_siblings?

def is_only_child?
!has_siblings?
end
alias_method :only_child?, :is_only_child?


# Descendants = all the nodes below and NOT including the current node
Expand Down Expand Up @@ -262,11 +343,23 @@ def unscoped_descendants
# basically validates the ancestry, but also applied if validation is
# bypassed to determine if children should be affected
def sane_ancestry?
ancestry.nil? || (ancestry.to_s =~ Ancestry::ANCESTRY_PATTERN && !ancestor_ids.include?(self.id))
ancestry.nil? || (ancestry.to_s =~ Ancestry::ANCESTRY_PATTERN && !ancestor_ids.include?(self.id))
end

def unscoped_find(id)
self.base_class.unscoped { self.base_class.find(id) }
end

def get_arel_table
self.base_class.arel_table
end

def get_primary_key_column
self.base_class.primary_key.to_sym
end

def get_ancestry_column
self.base_class.ancestry_column.to_sym
end
end
end