datamapper 0.2.5 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. data/CHANGELOG +5 -1
  2. data/FAQ +96 -0
  3. data/QUICKLINKS +12 -0
  4. data/README +57 -155
  5. data/environment.rb +61 -43
  6. data/example.rb +30 -12
  7. data/lib/data_mapper.rb +6 -1
  8. data/lib/data_mapper/adapters/abstract_adapter.rb +0 -57
  9. data/lib/data_mapper/adapters/data_object_adapter.rb +203 -97
  10. data/lib/data_mapper/adapters/mysql_adapter.rb +4 -0
  11. data/lib/data_mapper/adapters/postgresql_adapter.rb +7 -1
  12. data/lib/data_mapper/adapters/sql/coersion.rb +3 -2
  13. data/lib/data_mapper/adapters/sql/commands/load_command.rb +29 -10
  14. data/lib/data_mapper/adapters/sql/mappings/associations_set.rb +4 -0
  15. data/lib/data_mapper/adapters/sql/mappings/column.rb +13 -9
  16. data/lib/data_mapper/adapters/sql/mappings/conditions.rb +172 -0
  17. data/lib/data_mapper/adapters/sql/mappings/table.rb +43 -17
  18. data/lib/data_mapper/adapters/sqlite3_adapter.rb +9 -2
  19. data/lib/data_mapper/associations.rb +75 -3
  20. data/lib/data_mapper/associations/belongs_to_association.rb +70 -36
  21. data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +195 -86
  22. data/lib/data_mapper/associations/has_many_association.rb +168 -61
  23. data/lib/data_mapper/associations/has_n_association.rb +23 -3
  24. data/lib/data_mapper/attributes.rb +73 -0
  25. data/lib/data_mapper/auto_migrations.rb +2 -6
  26. data/lib/data_mapper/base.rb +5 -9
  27. data/lib/data_mapper/database.rb +4 -3
  28. data/lib/data_mapper/embedded_value.rb +66 -30
  29. data/lib/data_mapper/identity_map.rb +1 -3
  30. data/lib/data_mapper/is/tree.rb +121 -0
  31. data/lib/data_mapper/migration.rb +155 -0
  32. data/lib/data_mapper/persistence.rb +532 -218
  33. data/lib/data_mapper/property.rb +306 -0
  34. data/lib/data_mapper/query.rb +164 -0
  35. data/lib/data_mapper/support/blank.rb +2 -2
  36. data/lib/data_mapper/support/connection_pool.rb +5 -6
  37. data/lib/data_mapper/support/enumerable.rb +3 -3
  38. data/lib/data_mapper/support/errors.rb +10 -1
  39. data/lib/data_mapper/support/inflector.rb +174 -238
  40. data/lib/data_mapper/support/object.rb +54 -0
  41. data/lib/data_mapper/support/serialization.rb +19 -1
  42. data/lib/data_mapper/support/string.rb +7 -16
  43. data/lib/data_mapper/support/symbol.rb +3 -15
  44. data/lib/data_mapper/support/typed_set.rb +68 -0
  45. data/lib/data_mapper/types/base.rb +44 -0
  46. data/lib/data_mapper/types/string.rb +34 -0
  47. data/lib/data_mapper/validations/number_validator.rb +40 -0
  48. data/lib/data_mapper/validations/string_validator.rb +20 -0
  49. data/lib/data_mapper/validations/validator.rb +13 -0
  50. data/performance.rb +26 -1
  51. data/profile_data_mapper.rb +1 -1
  52. data/rakefile.rb +42 -2
  53. data/spec/acts_as_tree_spec.rb +11 -3
  54. data/spec/adapters/data_object_adapter_spec.rb +31 -0
  55. data/spec/associations/belongs_to_association_spec.rb +98 -0
  56. data/spec/associations/has_and_belongs_to_many_association_spec.rb +377 -0
  57. data/spec/associations/has_many_association_spec.rb +337 -0
  58. data/spec/attributes_spec.rb +23 -1
  59. data/spec/auto_migrations_spec.rb +86 -29
  60. data/spec/callbacks_spec.rb +107 -0
  61. data/spec/column_spec.rb +5 -2
  62. data/spec/count_command_spec.rb +33 -1
  63. data/spec/database_spec.rb +18 -0
  64. data/spec/dependency_spec.rb +4 -2
  65. data/spec/embedded_value_spec.rb +8 -8
  66. data/spec/fixtures/people.yaml +1 -1
  67. data/spec/fixtures/projects.yaml +10 -1
  68. data/spec/fixtures/tasks.yaml +6 -0
  69. data/spec/fixtures/tasks_tasks.yaml +2 -0
  70. data/spec/fixtures/tomatoes.yaml +1 -0
  71. data/spec/is_a_tree_spec.rb +149 -0
  72. data/spec/load_command_spec.rb +71 -9
  73. data/spec/magic_columns_spec.rb +17 -2
  74. data/spec/migration_spec.rb +267 -0
  75. data/spec/models/animal.rb +1 -1
  76. data/spec/models/candidate.rb +8 -0
  77. data/spec/models/career.rb +1 -1
  78. data/spec/models/chain.rb +8 -0
  79. data/spec/models/comment.rb +1 -1
  80. data/spec/models/exhibit.rb +1 -1
  81. data/spec/models/fence.rb +7 -0
  82. data/spec/models/fruit.rb +2 -2
  83. data/spec/models/job.rb +8 -0
  84. data/spec/models/person.rb +2 -3
  85. data/spec/models/post.rb +1 -1
  86. data/spec/models/project.rb +21 -1
  87. data/spec/models/section.rb +1 -1
  88. data/spec/models/serializer.rb +1 -1
  89. data/spec/models/task.rb +9 -0
  90. data/spec/models/tomato.rb +27 -0
  91. data/spec/models/user.rb +8 -2
  92. data/spec/models/zoo.rb +2 -7
  93. data/spec/paranoia_spec.rb +1 -1
  94. data/spec/{base_spec.rb → persistence_spec.rb} +207 -18
  95. data/spec/postgres_spec.rb +48 -6
  96. data/spec/property_spec.rb +90 -9
  97. data/spec/query_spec.rb +71 -5
  98. data/spec/save_command_spec.rb +11 -0
  99. data/spec/spec_helper.rb +14 -11
  100. data/spec/support/blank_spec.rb +8 -0
  101. data/spec/support/inflector_spec.rb +41 -0
  102. data/spec/support/object_spec.rb +9 -0
  103. data/spec/{serialization_spec.rb → support/serialization_spec.rb} +1 -1
  104. data/spec/support/silence_spec.rb +15 -0
  105. data/spec/{support_spec.rb → support/string_spec.rb} +3 -3
  106. data/spec/support/struct_spec.rb +12 -0
  107. data/spec/support/typed_set_spec.rb +66 -0
  108. data/spec/table_spec.rb +3 -3
  109. data/spec/types/string.rb +81 -0
  110. data/spec/validates_uniqueness_of_spec.rb +17 -0
  111. data/spec/validations/number_validator.rb +59 -0
  112. data/spec/validations/string_validator.rb +14 -0
  113. metadata +59 -17
  114. data/do_performance.rb +0 -153
  115. data/lib/data_mapper/support/active_record_impersonation.rb +0 -103
  116. data/lib/data_mapper/support/weak_hash.rb +0 -46
  117. data/spec/active_record_impersonation_spec.rb +0 -129
  118. data/spec/associations_spec.rb +0 -232
  119. data/spec/conditions_spec.rb +0 -49
  120. data/spec/has_many_association_spec.rb +0 -173
  121. data/spec/models/animals_exhibit.rb +0 -8
@@ -76,4 +76,111 @@ describe DataMapper::Callbacks do
76
76
  post.instance_variable_get("@three").should eql('blue_cow')
77
77
  end
78
78
 
79
+ it "should execute materialization callbacks" do
80
+
81
+ $before_materialize = 0
82
+ $after_materialize = 0
83
+
84
+ Zoo.before_materialize do
85
+ $before_materialize += 1
86
+ end
87
+
88
+ Zoo.after_materialize do
89
+ $after_materialize += 1
90
+ end
91
+
92
+ class Zoo
93
+
94
+ # This syntax doesn't work in DM.
95
+ # Which I don't think is necessarily a bad thing...
96
+ # Just FYI -Sam
97
+ def before_materialize
98
+ $before_materialize += 1
99
+ end
100
+
101
+ def call_before_materialize
102
+ $before_materialize += 1
103
+ end
104
+
105
+ # Example of invalid syntax
106
+ def after_materialize
107
+ $after_materialize += 1
108
+ end
109
+
110
+ def call_after_materialize
111
+ $after_materialize += 1
112
+ end
113
+
114
+ end
115
+
116
+ Zoo.before_materialize :call_before_materialize
117
+ Zoo.after_materialize :call_after_materialize
118
+
119
+ Zoo.before_materialize "$before_materialize += 1"
120
+ Zoo.after_materialize "$after_materialize += 1"
121
+
122
+ Zoo.first
123
+
124
+ $before_materialize.should == 3
125
+ $after_materialize.should == 3
126
+
127
+ Zoo[1]
128
+
129
+ $before_materialize.should == 6
130
+ $after_materialize.should == 6
131
+
132
+ end
133
+
134
+ it "should execute creation callbacks" do
135
+
136
+ $before_create = 0
137
+ $after_create = 0
138
+
139
+ Zoo.before_create do
140
+ $before_create += 1
141
+ end
142
+
143
+ Zoo.after_create do
144
+ $after_create += 1
145
+ end
146
+
147
+ class Zoo
148
+
149
+ # Example of invalid syntax
150
+ def before_create
151
+ $before_create += 1
152
+ end
153
+
154
+ def call_before_create
155
+ $before_create += 1
156
+ end
157
+
158
+ # Example of invalid syntax
159
+ def after_create
160
+ $after_create += 1
161
+ end
162
+
163
+ def call_after_create
164
+ $after_create += 1
165
+ end
166
+
167
+ end
168
+
169
+ Zoo.before_create :call_before_create
170
+ Zoo.after_create :call_after_create
171
+
172
+ Zoo.before_create "$before_create += 1"
173
+ Zoo.after_create "$after_create += 1"
174
+
175
+ Zoo.create(:name => 'bob')
176
+
177
+ $before_create.should == 3
178
+ $after_create.should == 3
179
+
180
+ Zoo.new(:name => 'bob2').save
181
+
182
+ $before_create.should == 6
183
+ $after_create.should == 6
184
+ end
185
+
79
186
  end
@@ -46,13 +46,13 @@ describe DataMapper::Adapters::Sql::Mappings::Column do
46
46
  lambda { database.query("SELECT name FROM zoos") }.should_not raise_error
47
47
  lambda { database.query("SELECT moo FROM zoos") }.should raise_error
48
48
 
49
- name_column.rename!(:moo).should eql(true)
49
+ name_column = name_column.rename!(:moo)
50
50
  name_column.name.should eql(:moo)
51
51
 
52
52
  lambda { database.query("SELECT name FROM zoos") }.should raise_error
53
53
  lambda { database.query("SELECT moo FROM zoos") }.should_not raise_error
54
54
 
55
- name_column.rename!(:name)
55
+ name_column = name_column.rename!(:name)
56
56
  name_column.name.should eql(:name)
57
57
 
58
58
  lambda { database.query("SELECT name FROM zoos") }.should_not raise_error
@@ -88,6 +88,7 @@ describe DataMapper::Adapters::Sql::Mappings::Column do
88
88
 
89
89
  Zoo.send(:undef_method, :moo)
90
90
  Zoo.send(:undef_method, :moo=)
91
+ Zoo.properties.delete_if { |x| x.name == :moo }
91
92
 
92
93
  lambda { database.query("SELECT moo FROM zoos") }.should raise_error
93
94
  end
@@ -100,6 +101,8 @@ describe DataMapper::Adapters::Sql::Mappings::Column do
100
101
  it "should be able to create a column with unique index" do
101
102
  column = table.add_column("name", :string, :index => :unique)
102
103
  column.unique?.should be_true
104
+ column.index?.should be_nil
105
+ table.to_create_index_sql.should == []
103
106
  table.to_create_sql.should match(/UNIQUE/)
104
107
  end
105
108
 
@@ -1,13 +1,45 @@
1
1
  require File.dirname(__FILE__) + "/spec_helper"
2
2
 
3
- describe DataMapper::Adapters::AbstractAdapter do
3
+ describe DataMapper::Adapters::AbstractAdapter, "count()" do
4
4
 
5
5
  before(:all) do
6
6
  fixtures(:zoos)
7
+ fixtures(:projects)
7
8
  end
8
9
 
9
10
  it "should return a count of the selected table" do
10
11
  Zoo.count.should be_a_kind_of(Integer)
11
12
  Zoo.count.should == Zoo.all.size
12
13
  end
14
+
15
+ it "should accept finder style options" do
16
+ # Hash-style (with SymbolOperators)
17
+ Zoo.count(:name => 'Dallas').should == Zoo.all(:name => 'Dallas').length
18
+ Zoo.count(:name.not => nil).should == Zoo.all(:name.not => nil).length
19
+ Zoo.count(:name.not => nil, :notes => nil).should == Zoo.all(:name.not => nil, :notes => nil).length
20
+ Zoo.count(:name.like => '%.%').should == Zoo.all(:name.like => '%.%').length
21
+
22
+ # :conditions
23
+ Zoo.count(:conditions => ["name = ?", 'Dallas']).should == Zoo.all(:conditions => ["name = ?", 'Dallas']).length
24
+
25
+ # mix and match
26
+ Zoo.count(:notes => nil, :conditions => ["name = ?", 'Dallas']).should == Zoo.all(:notes => nil, :conditions => ["name = ?", 'Dallas']).length
27
+ end
28
+
29
+ it "should respect paranoia" do
30
+ p = Project[3]
31
+ p.destroy!
32
+
33
+ Project.count.should == Project.all.length
34
+ # clean up
35
+ p.deleted_at = nil
36
+ p.save
37
+ end
38
+
39
+ #This won't work at the moment, hopefully before 0.3.0
40
+ it "should do distinct counting" do
41
+ #Zoo.count(:distinct => :name).should == Zoo.all.length # there all distinct in the fixtures
42
+ #Zoo.count(:distinct => :name, :notes.not => nil).should == Zoo.all(:notes.not => nil).length
43
+ #Zoo.count(:distinct => :name, :conditions => ["notes is not null"]).should == Zoo.all(:notes.not => nil).length
44
+ end
13
45
  end
@@ -0,0 +1,18 @@
1
+ require File.dirname(__FILE__) + "/spec_helper"
2
+
3
+ describe('Multiple Databases') do
4
+
5
+ it "should scope model creation and lookup" do
6
+ database(:secondary) do
7
+ Zoo.create :name => 'secondary'
8
+ end
9
+
10
+ Zoo.first(:name => 'secondary').should be_nil
11
+
12
+ database(:secondary) do
13
+ Zoo.first(:name => 'secondary').should_not be_nil
14
+ Zoo.first(:name => 'secondary').destroy!
15
+ end
16
+ end
17
+
18
+ end
@@ -1,6 +1,8 @@
1
1
  require File.dirname(__FILE__) + "/spec_helper"
2
2
 
3
- describe DataMapper::Persistence do
3
+ # Can't describe DataMapper::Persistence because
4
+ # rspec will include it for some crazy reason!
5
+ describe "DataMapper::Persistence" do
4
6
 
5
7
  it "should be able to add a dependency for a class not yet defined" do
6
8
 
@@ -12,7 +14,7 @@ describe DataMapper::Persistence do
12
14
  $happy_cow_defined = true
13
15
  end
14
16
 
15
- class HappyCow
17
+ class HappyCow #< DataMapper::Base # please do not remove this
16
18
  include DataMapper::Persistence
17
19
 
18
20
  property :name, :string, :key => true
@@ -19,7 +19,7 @@ describe DataMapper::EmbeddedValue do
19
19
  end
20
20
 
21
21
  it 'should not require prefix' do
22
- class PointyHeadedBoss
22
+ class PointyHeadedBoss #< DataMapper::Base # please do not remove this
23
23
  include DataMapper::Persistence
24
24
 
25
25
  set_table_name 'people'
@@ -38,7 +38,7 @@ describe DataMapper::EmbeddedValue do
38
38
  end
39
39
 
40
40
  it 'should add convenience methods to the non-embedded base' do
41
- class Employee
41
+ class Employee #< DataMapper::Base # please do not remove this
42
42
  include DataMapper::Persistence
43
43
 
44
44
  set_table_name 'people'
@@ -55,7 +55,7 @@ describe DataMapper::EmbeddedValue do
55
55
  end
56
56
 
57
57
  it 'should support lazy loading of embedded properties' do
58
- class Human
58
+ class Human #< DataMapper::Base # please do not remove this
59
59
  include DataMapper::Persistence
60
60
 
61
61
  set_table_name 'people'
@@ -72,7 +72,7 @@ describe DataMapper::EmbeddedValue do
72
72
  end
73
73
 
74
74
  it 'should default to public method visibility for all' do
75
- class SoftwareEngineer
75
+ class SoftwareEngineer #< DataMapper::Base # please do not remove this
76
76
  include DataMapper::Persistence
77
77
 
78
78
  set_table_name 'people'
@@ -89,7 +89,7 @@ describe DataMapper::EmbeddedValue do
89
89
  end
90
90
 
91
91
  it 'should respect protected property options for all' do
92
- class SanitationEngineer
92
+ class SanitationEngineer #< DataMapper::Base # please do not remove this
93
93
  include DataMapper::Persistence
94
94
 
95
95
  set_table_name 'people'
@@ -107,7 +107,7 @@ describe DataMapper::EmbeddedValue do
107
107
  end
108
108
 
109
109
  it 'should respect private property options for all' do
110
- class ElectricalEngineer
110
+ class ElectricalEngineer #< DataMapper::Base # please do not remove this
111
111
  include DataMapper::Persistence
112
112
 
113
113
  set_table_name 'people'
@@ -125,7 +125,7 @@ describe DataMapper::EmbeddedValue do
125
125
  end
126
126
 
127
127
  it 'should set both reader and writer visibiliy for all when accessor option is passed' do
128
- class TrainEngineer
128
+ class TrainEngineer #< DataMapper::Base # please do not remove this
129
129
  include DataMapper::Persistence
130
130
 
131
131
  set_table_name 'people'
@@ -142,7 +142,7 @@ describe DataMapper::EmbeddedValue do
142
142
  end
143
143
 
144
144
  it 'should allow individual properties to override method visibility options passed on the block' do
145
- class ChemicalEngineer
145
+ class ChemicalEngineer #< DataMapper::Base # please do not remove this
146
146
  include DataMapper::Persistence
147
147
 
148
148
  set_table_name 'people'
@@ -31,7 +31,7 @@
31
31
  address_street: 123 Happy Ln.
32
32
  address_city: Dallas
33
33
  address_state: TX
34
- address_zip_code: 75000
34
+ address_zip_code: "75000"
35
35
  type: SalesPerson
36
36
  commission: 40
37
37
  career_name: Peon
@@ -1,4 +1,13 @@
1
1
  ---
2
2
  - id: 1
3
3
  title: Test
4
- description:
4
+ description: test
5
+ - id: 2
6
+ title: The Empire State Building
7
+ description: "The Empire State Building is a 102-story Art Deco skyscraper in New York City, New York"
8
+ - id: 3
9
+ title: World Trade Center
10
+ description: "The World Trade Center in New York City, United States"
11
+ - id: 4
12
+ title: The Statue Of Liberty
13
+ description: "Liberty Enlightening the World (French: La liberte eclairant le monde), known more commonly as the Statue of Liberty"
@@ -0,0 +1,6 @@
1
+ - name: task_relax
2
+ notes: I am the relaxed task an should be related to task_vacation
3
+ - name: task_drink_heartily
4
+ notes: I am the drink heartily task and should be related to task_relax
5
+ - name: task_vacation
6
+ notes: I am the vacation task and should be related to task_relax
@@ -0,0 +1,2 @@
1
+ - task_id: 1
2
+ related_task_id: 2
@@ -0,0 +1 @@
1
+ --- []
@@ -0,0 +1,149 @@
1
+ require File.dirname(__FILE__) + "/spec_helper"
2
+
3
+ class DataMapper::MockBase
4
+ include DataMapper::Is::Tree
5
+ end
6
+
7
+ class DefaultTree < DataMapper::MockBase
8
+ end
9
+
10
+ describe "DataMapper::Is::Tree receiving various is_a_tree configurations" do
11
+ setup do
12
+ DefaultTree.stub!(:has_many)
13
+ DefaultTree.stub!(:belongs_to)
14
+ end
15
+
16
+ it "should setup a belongs_to relationship with the parent with the default configurations" do
17
+ DefaultTree.should_receive(:belongs_to).with(:parent, :foreign_key => "parent_id", :counter_cache => nil,
18
+ :class_name => "DefaultTree")
19
+ DefaultTree.send(:is_a_tree)
20
+ end
21
+
22
+ it "should setup a has_many relationship with the children with default configurations" do
23
+ DefaultTree.should_receive(:has_many).with(:children, :foreign_key => "parent_id", :order => nil,
24
+ :class_name => "DefaultTree")
25
+ DefaultTree.send(:is_a_tree)
26
+ end
27
+
28
+ it "should setup a belongs_to relationship with the correct foreign key" do
29
+ DefaultTree.should_receive(:belongs_to).with(:parent, :foreign_key => "something_id", :counter_cache => nil,
30
+ :class_name => "DefaultTree")
31
+ DefaultTree.send(:is_a_tree, :foreign_key => "something_id")
32
+ end
33
+
34
+ it "should setup a has_many relationship with the children with the correct foreign key" do
35
+ DefaultTree.should_receive(:has_many).with(:children, :foreign_key => "something_id", :order => nil,
36
+ :class_name => "DefaultTree")
37
+ DefaultTree.send(:is_a_tree, :foreign_key => "something_id")
38
+ end
39
+
40
+ it "should setup a has_many relationship with the children with the correct order" do
41
+ DefaultTree.should_receive(:has_many).with(:children, :foreign_key => "parent_id", :order => 'position',
42
+ :class_name => "DefaultTree")
43
+ DefaultTree.send(:is_a_tree, :order => 'position')
44
+ end
45
+ end
46
+
47
+ describe "Default DataMapper::Is::Tree class methods" do
48
+ setup do
49
+ DefaultTree.stub!(:has_many)
50
+ DefaultTree.stub!(:belongs_to)
51
+ DefaultTree.send(:can_has_tree)
52
+ DefaultTree.stub!(:all).and_return([])
53
+ DefaultTree.stub!(:first).and_return(nil)
54
+ end
55
+
56
+ it "should return an empty array for .roots" do
57
+ DefaultTree.roots.should == []
58
+ end
59
+
60
+ it "should find with the correct options on .roots" do
61
+ DefaultTree.should_receive(:all).with(:parent_id => nil, :order => nil)
62
+ DefaultTree.roots
63
+ end
64
+
65
+ it "should return nil for .first_root" do
66
+ DefaultTree.root.should be_nil
67
+ end
68
+
69
+ it "should find with the correct options on .first_root" do
70
+ DefaultTree.should_receive(:first).with(:parent_id => nil, :order => nil)
71
+ DefaultTree.first_root
72
+ end
73
+ end
74
+
75
+ describe "Configured DataMapper::Is::Tree class methods" do
76
+ setup do
77
+ DefaultTree.stub!(:has_many)
78
+ DefaultTree.stub!(:belongs_to)
79
+ DefaultTree.send(:can_has_tree, :foreign_key => 'mew_id', :order => 'mew')
80
+ DefaultTree.stub!(:all).and_return([])
81
+ DefaultTree.stub!(:first).and_return(nil)
82
+ end
83
+
84
+ it "should return an empty array for .roots" do
85
+ DefaultTree.roots.should == []
86
+ end
87
+
88
+ it "should find with the correct options on .roots" do
89
+ DefaultTree.should_receive(:all).with(:mew_id => nil, :order => 'mew')
90
+ DefaultTree.roots
91
+ end
92
+
93
+ it "should return nil for .first_root" do
94
+ DefaultTree.root.should be_nil
95
+ end
96
+
97
+ it "should find with the correct options on .first_root" do
98
+ DefaultTree.should_receive(:first).with(:mew_id => nil, :order => 'mew')
99
+ DefaultTree.first_root
100
+ end
101
+ end
102
+
103
+ describe "Default DataMapper::Is::Tree instance methods" do
104
+ setup do
105
+ DefaultTree.stub!(:has_many)
106
+ DefaultTree.stub!(:belongs_to)
107
+ DefaultTree.send(:is_a_tree)
108
+ @root = DefaultTree.new
109
+ @child = DefaultTree.new
110
+ @grandchild1 = DefaultTree.new
111
+ @grandchild2 = DefaultTree.new
112
+
113
+ # Mocking the belongs_to & has_many relationships (part of the not-needing-a-db-to-test plan)
114
+ @root.stub!(:parent).and_return nil
115
+ @child.stub!(:parent).and_return @root
116
+ @grandchild1.stub!(:parent).and_return(@child)
117
+ @grandchild2.stub!(:parent).and_return(@child)
118
+ @child.stub!(:children).and_return([@grandchild1, @grandchild2])
119
+ @root.stub!(:children).and_return([@child])
120
+ @grandchild1.stub!(:children).and_return []
121
+ @grandchild2.stub!(:children).and_return []
122
+ end
123
+
124
+ it "should return an array of parents, furthest parent first, for #ancestors" do
125
+ @grandchild1.ancestors.should == [@root, @child]
126
+ end
127
+
128
+ it "should return an array of siblings for #siblings" do
129
+ @grandchild1.siblings.should == [@grandchild2]
130
+ end
131
+
132
+ it "should return all children of the node's parent for #generation" do
133
+ @grandchild1.generation.should == [@grandchild1, @grandchild2]
134
+ end
135
+
136
+ it "should return roots on #generation when there is no parent" do
137
+ DefaultTree.should_receive(:roots).and_return [@root]
138
+ @root.generation.should == [@root]
139
+ end
140
+
141
+ it "should return the top-most parent on #root" do
142
+ @grandchild1.root.should == @root
143
+ end
144
+
145
+ it "should return self on #root if self has no parent" do
146
+ @root.root.should == @root
147
+ end
148
+
149
+ end