dm-is-tree 0.9.9 → 0.9.10
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +3 -0
- data/README.txt +32 -0
- data/TODO +1 -1
- data/lib/dm-is-tree.rb +1 -1
- data/lib/dm-is-tree/is/tree.rb +33 -25
- data/lib/dm-is-tree/is/version.rb +1 -1
- data/spec/integration/tree_spec.rb +93 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/unit/tree_spec.rb +113 -0
- data/tasks/spec.rb +1 -1
- metadata +6 -3
data/History.txt
CHANGED
data/Manifest.txt
CHANGED
data/README.txt
CHANGED
@@ -1,3 +1,35 @@
|
|
1
1
|
= dm-is-tree
|
2
2
|
|
3
3
|
DataMapper plugin allowing the creation of tree structures from data models.
|
4
|
+
|
5
|
+
Example:
|
6
|
+
|
7
|
+
class Category
|
8
|
+
include DataMapper::Resource
|
9
|
+
|
10
|
+
property :id, Serial
|
11
|
+
property :parent_id, Integer
|
12
|
+
property :name, String
|
13
|
+
|
14
|
+
is :tree, :order => :name
|
15
|
+
end
|
16
|
+
|
17
|
+
root
|
18
|
+
+- child
|
19
|
+
+- grandchild1
|
20
|
+
+- grandchild2
|
21
|
+
|
22
|
+
root = Category.create(:name => "root")
|
23
|
+
child = root.children.create(:name => "child")
|
24
|
+
grandchild1 = child1.children.create(:name => "grandchild1")
|
25
|
+
grandchild2 = child2.children.create(:name => "grandchild2")
|
26
|
+
|
27
|
+
root.parent # => nil
|
28
|
+
child.parent # => root
|
29
|
+
root.children # => [child]
|
30
|
+
root.children.first.children.first # => grandchild1
|
31
|
+
Category.first_root # => root
|
32
|
+
Category.roots # => [root]
|
33
|
+
|
34
|
+
Original Author:: Timothy Bennett (http://lanaer.com)
|
35
|
+
Current Maintainer:: Garrett Heaver (http://www.linkedin.com/pub/dir/garrett/heaver)
|
data/TODO
CHANGED
data/lib/dm-is-tree.rb
CHANGED
data/lib/dm-is-tree/is/tree.rb
CHANGED
@@ -19,25 +19,25 @@ module DataMapper
|
|
19
19
|
# property :parent_id, Integer
|
20
20
|
# property :name, String
|
21
21
|
#
|
22
|
-
# is :tree :order =>
|
22
|
+
# is :tree, :order => :name
|
23
23
|
# end
|
24
24
|
#
|
25
|
-
#
|
26
|
-
#
|
25
|
+
# root
|
26
|
+
# +- child
|
27
27
|
# +- grandchild1
|
28
28
|
# +- grandchild2
|
29
29
|
#
|
30
|
-
# root
|
31
|
-
# child
|
32
|
-
# grandchild1 = child1.children.create(
|
33
|
-
# grandchild2 = child2.children.create(
|
30
|
+
# root = Category.create(:name => "root")
|
31
|
+
# child = root.children.create(:name => "child")
|
32
|
+
# grandchild1 = child1.children.create(:name => "grandchild1")
|
33
|
+
# grandchild2 = child2.children.create(:name => "grandchild2")
|
34
34
|
#
|
35
|
-
# root.parent
|
35
|
+
# root.parent # => nil
|
36
36
|
# child.parent # => root
|
37
|
-
# root.children
|
38
|
-
# root.children.first.children.first
|
37
|
+
# root.children # => [child]
|
38
|
+
# root.children.first.children.first # => grandchild1
|
39
39
|
# Category.first_root # => root
|
40
|
-
# Category.roots
|
40
|
+
# Category.roots # => [root]
|
41
41
|
#
|
42
42
|
# The following instance methods are added:
|
43
43
|
# * <tt>children</tt> - Returns all nodes with the current node as their parent, in the order specified by
|
@@ -52,30 +52,24 @@ module DataMapper
|
|
52
52
|
# when called on <tt>grandchild2</tt>)
|
53
53
|
# * <tt>root</tt> - Returns the root of the current node (<tt>root</tt> when called on <tt>grandchild2</tt>)
|
54
54
|
#
|
55
|
-
# Author:: Timothy Bennett (http://lanaer.com)
|
55
|
+
# Original Author:: Timothy Bennett (http://lanaer.com)
|
56
|
+
# Current Maintainer:: Garrett Heaver (http://www.linkedin.com/pub/dir/garrett/heaver)
|
56
57
|
|
57
58
|
# Configuration options are:
|
58
59
|
#
|
59
60
|
# * <tt>child_key</tt> - specifies the column name to use for tracking of the tree (default: +parent_id+)
|
60
61
|
def is_tree(options = {})
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
belongs_to :parent, :class_name => name, :child_key => [ configuration[:child_key] ]
|
65
|
-
has n, :children, :class_name => name, :child_key => [ configuration[:child_key] ]
|
62
|
+
options = { :class_name => name, :child_key => :parent_id }.merge(options) if Hash === options
|
63
|
+
@tree_options = options
|
66
64
|
|
67
65
|
include DataMapper::Is::Tree::InstanceMethods
|
68
66
|
extend DataMapper::Is::Tree::ClassMethods
|
69
67
|
|
70
|
-
|
71
|
-
|
72
|
-
all :#{configuration[:child_key]} => nil, :order => [#{configuration[:order].inspect}]
|
73
|
-
end
|
68
|
+
assc_options = { :class_name => options[:class_name], :child_key => Array(options[:child_key]) }
|
69
|
+
has_n_options = options[:order] ? { :order => Array(options[:order]) }.merge(assc_options) : assc_options
|
74
70
|
|
75
|
-
|
76
|
-
|
77
|
-
end
|
78
|
-
CLASS
|
71
|
+
belongs_to :parent, assc_options
|
72
|
+
has n, :children, has_n_options
|
79
73
|
|
80
74
|
class << self
|
81
75
|
alias_method :root, :first_root # for people used to the ActiveRecord acts_as_tree
|
@@ -89,9 +83,23 @@ module DataMapper
|
|
89
83
|
alias_method :can_has_tree, :is_tree # just for fun ;)
|
90
84
|
|
91
85
|
module ClassMethods
|
86
|
+
attr_reader :tree_options
|
87
|
+
|
88
|
+
def roots
|
89
|
+
options = { tree_options[:child_key] => nil }
|
90
|
+
options = { :order => Array(tree_options[:order]) }.merge(options) if tree_options[:order]
|
91
|
+
all options
|
92
|
+
end
|
93
|
+
|
94
|
+
def first_root
|
95
|
+
options = { tree_options[:child_key] => nil }
|
96
|
+
options = { :order => Array(tree_options[:order]) }.merge(options) if tree_options[:order]
|
97
|
+
first options
|
98
|
+
end
|
92
99
|
end
|
93
100
|
|
94
101
|
module InstanceMethods
|
102
|
+
|
95
103
|
# Returns list of ancestors, starting with the root.
|
96
104
|
#
|
97
105
|
# grandchild1.ancestors # => [root, child]
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
|
4
|
+
|
5
|
+
describe DataMapper::Is::Tree do
|
6
|
+
|
7
|
+
class Category
|
8
|
+
include DataMapper::Resource
|
9
|
+
|
10
|
+
property :id, Serial
|
11
|
+
property :parent_id, Integer
|
12
|
+
property :name, String
|
13
|
+
end
|
14
|
+
|
15
|
+
before(:all) do
|
16
|
+
Category.auto_migrate!
|
17
|
+
|
18
|
+
@root_a = Category.create(:name => 'a root')
|
19
|
+
@root_b = Category.create(:name => 'b root')
|
20
|
+
|
21
|
+
@child_a = Category.create(:name => 'a child', :parent_id => @root_a.id)
|
22
|
+
@child_b = Category.create(:name => 'b child', :parent_id => @root_a.id)
|
23
|
+
|
24
|
+
@grandchild_a = Category.create(:name => 'a grandchild', :parent_id => @child_a.id)
|
25
|
+
@grandchild_b = Category.create(:name => 'b grandchild', :parent_id => @child_a.id)
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "roots class method" do
|
29
|
+
|
30
|
+
it "should return all instances where the child_key is nil" do
|
31
|
+
Category.is :tree
|
32
|
+
Category.roots.should == [@root_a, @root_b]
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should use the order from the options if it is supplied" do
|
36
|
+
Category.is :tree, :order => :name.desc
|
37
|
+
Category.roots.should == [@root_b, @root_a]
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "first_root class method" do
|
43
|
+
|
44
|
+
it "should return the first instance where the child_key is nil" do
|
45
|
+
Category.is :tree
|
46
|
+
Category.first_root.should == @root_a
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should use the order from the options if it is supplied" do
|
50
|
+
Category.is :tree, :order => :name.desc
|
51
|
+
Category.first_root.should == @root_b
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "ancestors instance method" do
|
57
|
+
|
58
|
+
it "should return the list of ancestors for the current node up to its root" do
|
59
|
+
Category.is :tree
|
60
|
+
@grandchild_a.ancestors.should == [@root_a, @child_a]
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "root instance method" do
|
66
|
+
|
67
|
+
it "should return the root node for the current instance" do
|
68
|
+
Category.is :tree
|
69
|
+
@child_a.root.should == @root_a
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "siblings instance method" do
|
74
|
+
|
75
|
+
it "should return all nodes which have the same parent as this node (excluding this node)" do
|
76
|
+
Category.is :tree
|
77
|
+
@child_a.siblings.should == [@child_b]
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "generation instance method" do
|
83
|
+
|
84
|
+
it "should return all nodes which have the same parent as this node (including this node)" do
|
85
|
+
Category.is :tree
|
86
|
+
@child_a.generation.should == [@child_a, @child_b]
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
gem 'rspec', '~>1.1.11'
|
5
|
+
require 'spec'
|
6
|
+
|
7
|
+
require Pathname(__FILE__).dirname.expand_path.parent + 'lib/dm-is-tree'
|
8
|
+
|
9
|
+
def load_driver(name, default_uri)
|
10
|
+
return false if ENV['ADAPTER'] != name.to_s
|
11
|
+
|
12
|
+
begin
|
13
|
+
DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
|
14
|
+
DataMapper::Repository.adapters[:default] = DataMapper::Repository.adapters[name]
|
15
|
+
true
|
16
|
+
rescue LoadError => e
|
17
|
+
warn "Could not load do_#{name}: #{e}"
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
ENV['ADAPTER'] ||= 'sqlite3'
|
23
|
+
|
24
|
+
HAS_SQLITE3 = load_driver(:sqlite3, 'sqlite3::memory:')
|
25
|
+
HAS_MYSQL = load_driver(:mysql, 'mysql://localhost/dm_core_test')
|
26
|
+
HAS_POSTGRES = load_driver(:postgres, 'postgres://postgres@localhost/dm_core_test')
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe DataMapper::Is::Tree do
|
4
|
+
|
5
|
+
before do
|
6
|
+
class Category
|
7
|
+
include DataMapper::Resource
|
8
|
+
|
9
|
+
property :id, Serial
|
10
|
+
property :parent_id, Integer
|
11
|
+
property :name, String
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should create a parent relationship" do
|
16
|
+
Category.is :tree
|
17
|
+
Category.relationships.should have_key(:parent)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should create a children relationship" do
|
21
|
+
Category.is :tree
|
22
|
+
Category.relationships.should have_key(:children)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should create a class method called roots" do
|
26
|
+
Category.is :tree
|
27
|
+
Category.should respond_to(:roots)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should create a class method called first_root" do
|
31
|
+
Category.is :tree
|
32
|
+
Category.should respond_to(:first_root)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should create an alias of class method first_root called root (ActiveRecord compatability)" do
|
36
|
+
Category.is :tree
|
37
|
+
Category.method(:first_root).should == Category.method(:root)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should create an instance method called ancestors" do
|
41
|
+
Category.is :tree
|
42
|
+
Category.new.should respond_to(:ancestors)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should create an instance method called root" do
|
46
|
+
Category.is :tree
|
47
|
+
Category.new.should respond_to(:root)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should create an instance method called siblings" do
|
51
|
+
Category.is :tree
|
52
|
+
Category.new.should respond_to(:siblings)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should create an instance method called generation" do
|
56
|
+
Category.is :tree
|
57
|
+
Category.new.should respond_to(:generation)
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "parent relationship" do
|
61
|
+
|
62
|
+
it "should set the class_name from the current class" do
|
63
|
+
Category.is :tree
|
64
|
+
Category.relationships[:parent].options[:class_name].should == Category.name
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should use the default child_key of :parent_id if none is supplied in the options" do
|
68
|
+
Category.is :tree
|
69
|
+
Category.relationships[:parent].options[:child_key].should == Array(:parent_id)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should use the child_key from the options if it is supplied" do
|
73
|
+
Category.is :tree, :child_key => :other_id
|
74
|
+
Category.relationships[:parent].options[:child_key].should == Array(:other_id)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should not set any order" do
|
78
|
+
Category.is :tree, :order => :name
|
79
|
+
Category.relationships[:parent].options.should_not have_key(:order)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "children relationship" do
|
85
|
+
|
86
|
+
it "should set the class_name from the current class" do
|
87
|
+
Category.is :tree
|
88
|
+
Category.relationships[:children].options[:class_name].should == Category.name
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should use the default child_key of :parent_id if none is supplied in the options" do
|
92
|
+
Category.is :tree
|
93
|
+
Category.relationships[:children].options[:child_key].should == Array(:parent_id)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should use the child_key from the options if it is supplied" do
|
97
|
+
Category.is :tree, :child_key => :other_id
|
98
|
+
Category.relationships[:children].options[:child_key].should == Array(:other_id)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should not set any order if none is supplied in the options" do
|
102
|
+
Category.is :tree
|
103
|
+
Category.relationships[:children].options.should_not have_key(:order)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should use the order from the options if it is supplied" do
|
107
|
+
Category.is :tree, :order => :name
|
108
|
+
Category.relationships[:children].options[:order].should == Array(:name)
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
data/tasks/spec.rb
CHANGED
@@ -8,7 +8,7 @@ begin
|
|
8
8
|
desc 'Run specifications'
|
9
9
|
Spec::Rake::SpecTask.new(:spec) do |t|
|
10
10
|
t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
|
11
|
-
t.spec_files = Pathname.glob((ROOT + 'spec/**/*_spec.rb').to_s)
|
11
|
+
t.spec_files = Pathname.glob((ROOT + 'spec/**/*_spec.rb').to_s).map { |f| f.to_s }
|
12
12
|
|
13
13
|
begin
|
14
14
|
gem 'rcov', '~>0.8'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dm-is-tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Timothy Bennett
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-01-
|
12
|
+
date: 2009-01-19 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -20,7 +20,7 @@ dependencies:
|
|
20
20
|
requirements:
|
21
21
|
- - ~>
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: 0.9.
|
23
|
+
version: 0.9.10
|
24
24
|
version:
|
25
25
|
description: DataMapper plugin allowing the creation of tree structures from data models
|
26
26
|
email:
|
@@ -44,7 +44,10 @@ files:
|
|
44
44
|
- lib/dm-is-tree.rb
|
45
45
|
- lib/dm-is-tree/is/tree.rb
|
46
46
|
- lib/dm-is-tree/is/version.rb
|
47
|
+
- spec/integration/tree_spec.rb
|
47
48
|
- spec/spec.opts
|
49
|
+
- spec/spec_helper.rb
|
50
|
+
- spec/unit/tree_spec.rb
|
48
51
|
- tasks/install.rb
|
49
52
|
- tasks/spec.rb
|
50
53
|
has_rdoc: true
|