dm-is-tree 0.9.9 → 0.9.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|