sequel 3.9.0 → 3.10.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +56 -0
- data/README.rdoc +1 -1
- data/Rakefile +1 -1
- data/doc/advanced_associations.rdoc +7 -10
- data/doc/release_notes/3.10.0.txt +286 -0
- data/lib/sequel/adapters/do/mysql.rb +4 -0
- data/lib/sequel/adapters/jdbc.rb +5 -0
- data/lib/sequel/adapters/jdbc/as400.rb +58 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +30 -0
- data/lib/sequel/adapters/shared/mssql.rb +23 -9
- data/lib/sequel/adapters/shared/mysql.rb +12 -1
- data/lib/sequel/adapters/shared/postgres.rb +7 -18
- data/lib/sequel/adapters/shared/sqlite.rb +5 -0
- data/lib/sequel/adapters/sqlite.rb +5 -0
- data/lib/sequel/connection_pool/single.rb +3 -3
- data/lib/sequel/database.rb +3 -2
- data/lib/sequel/dataset.rb +6 -5
- data/lib/sequel/dataset/convenience.rb +3 -3
- data/lib/sequel/dataset/query.rb +13 -0
- data/lib/sequel/dataset/sql.rb +31 -1
- data/lib/sequel/extensions/schema_dumper.rb +3 -3
- data/lib/sequel/model.rb +8 -6
- data/lib/sequel/model/associations.rb +144 -102
- data/lib/sequel/model/base.rb +21 -1
- data/lib/sequel/model/plugins.rb +3 -1
- data/lib/sequel/plugins/association_dependencies.rb +14 -7
- data/lib/sequel/plugins/caching.rb +4 -0
- data/lib/sequel/plugins/composition.rb +138 -0
- data/lib/sequel/plugins/identity_map.rb +2 -2
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/nested_attributes.rb +3 -2
- data/lib/sequel/plugins/rcte_tree.rb +281 -0
- data/lib/sequel/plugins/typecast_on_load.rb +16 -5
- data/lib/sequel/sql.rb +18 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +4 -0
- data/spec/adapters/mysql_spec.rb +4 -0
- data/spec/adapters/postgres_spec.rb +55 -5
- data/spec/core/database_spec.rb +5 -3
- data/spec/core/dataset_spec.rb +86 -15
- data/spec/core/expression_filters_spec.rb +23 -6
- data/spec/extensions/association_dependencies_spec.rb +24 -5
- data/spec/extensions/association_proxies_spec.rb +3 -0
- data/spec/extensions/composition_spec.rb +194 -0
- data/spec/extensions/identity_map_spec.rb +16 -0
- data/spec/extensions/nested_attributes_spec.rb +44 -1
- data/spec/extensions/rcte_tree_spec.rb +205 -0
- data/spec/extensions/schema_dumper_spec.rb +6 -0
- data/spec/extensions/spec_helper.rb +6 -0
- data/spec/extensions/typecast_on_load_spec.rb +9 -0
- data/spec/extensions/validation_helpers_spec.rb +5 -5
- data/spec/integration/dataset_test.rb +13 -9
- data/spec/integration/eager_loader_test.rb +56 -1
- data/spec/integration/model_test.rb +8 -0
- data/spec/integration/plugin_test.rb +270 -0
- data/spec/integration/schema_test.rb +1 -1
- data/spec/model/associations_spec.rb +541 -118
- data/spec/model/eager_loading_spec.rb +24 -3
- data/spec/model/record_spec.rb +34 -0
- metadata +9 -2
@@ -82,6 +82,8 @@ describe "Sequel::Database dump methods" do
|
|
82
82
|
[[:c1, {:db_type=>'blahblah', :allow_null=>true}]]
|
83
83
|
when :t6
|
84
84
|
[[:c1, {:db_type=>'bigint', :primary_key=>true, :allow_null=>true}]]
|
85
|
+
when :t7
|
86
|
+
[[:c1, {:db_type=>'somedbspecifictype', :primary_key=>true, :allow_null=>false}]]
|
85
87
|
end
|
86
88
|
end
|
87
89
|
end
|
@@ -94,6 +96,10 @@ describe "Sequel::Database dump methods" do
|
|
94
96
|
@d.dump_table_schema(:t6).should == "create_table(:t6) do\n primary_key :c1, :type=>Bignum\nend"
|
95
97
|
end
|
96
98
|
|
99
|
+
it "should dump primary key columns with explicit :type equal to the database type when :same_db option is passed" do
|
100
|
+
@d.dump_table_schema(:t7, :same_db => true).should == "create_table(:t7) do\n primary_key :c1, :type=>\"somedbspecifictype\"\nend"
|
101
|
+
end
|
102
|
+
|
97
103
|
it "should use a composite primary_key calls if there is a composite primary key" do
|
98
104
|
@d.dump_table_schema(:t2).should == "create_table(:t2) do\n Integer :c1, :null=>false\n BigDecimal :c2, :null=>false\n \n primary_key [:c1, :c2]\nend"
|
99
105
|
end
|
@@ -7,6 +7,7 @@ describe Sequel::Model, "TypecastOnLoad plugin" do
|
|
7
7
|
[[:id, {}], [:y, {:type=>:boolean, :db_type=>'tinyint(1)'}], [:b, {:type=>:integer, :db_type=>'integer'}]]
|
8
8
|
end
|
9
9
|
@c = Class.new(Sequel::Model(@db[:items])) do
|
10
|
+
include(Module.new{def _refresh(ds); values[:b] = b.to_s; self; end})
|
10
11
|
attr_accessor :bset
|
11
12
|
def b=(x)
|
12
13
|
self.bset = true
|
@@ -26,6 +27,14 @@ describe Sequel::Model, "TypecastOnLoad plugin" do
|
|
26
27
|
o.bset.should == true
|
27
28
|
end
|
28
29
|
|
30
|
+
specify "should call setter method with value when reloading the object, for all given columns" do
|
31
|
+
@c.plugin :typecast_on_load, :b
|
32
|
+
o = @c.new({:id=>1, :b=>"1", :y=>"0"}, true)
|
33
|
+
o.refresh
|
34
|
+
o.values.should == {:id=>1, :b=>1, :y=>"0"}
|
35
|
+
o.bset.should == true
|
36
|
+
end
|
37
|
+
|
29
38
|
specify "should allowing setting columns separately via add_typecast_on_load_columns" do
|
30
39
|
@c.plugin :typecast_on_load
|
31
40
|
@c.load(:id=>1, :b=>"1", :y=>"0").values.should == {:id=>1, :b=>"1", :y=>"0"}
|
@@ -377,7 +377,7 @@ describe "Sequel::Plugins::ValidationHelpers" do
|
|
377
377
|
|
378
378
|
@user = @c.load(:id=>1, :username => "0records", :password => "anothertest")
|
379
379
|
@user.should be_valid
|
380
|
-
MODEL_DB.sqls.last.should == "SELECT COUNT(*) AS count FROM items WHERE ((
|
380
|
+
MODEL_DB.sqls.last.should == "SELECT COUNT(*) AS count FROM items WHERE ((username = '0records') AND (password = 'anothertest') AND (id != 1)) LIMIT 1"
|
381
381
|
@user = @c.new(:username => "0records", :password => "anothertest")
|
382
382
|
@user.should be_valid
|
383
383
|
MODEL_DB.sqls.last.should == "SELECT COUNT(*) AS count FROM items WHERE ((username = '0records') AND (password = 'anothertest')) LIMIT 1"
|
@@ -398,7 +398,7 @@ describe "Sequel::Plugins::ValidationHelpers" do
|
|
398
398
|
@c.new(:username => "0records", :password => "anothertest").should be_valid
|
399
399
|
@c.load(:id=>3, :username => "0records", :password => "anothertest").should be_valid
|
400
400
|
MODEL_DB.sqls.should == ["SELECT COUNT(*) AS count FROM items WHERE ((username = '0records') AND active) LIMIT 1",
|
401
|
-
"SELECT COUNT(*) AS count FROM items WHERE ((
|
401
|
+
"SELECT COUNT(*) AS count FROM items WHERE ((username = '0records') AND active AND (id != 3)) LIMIT 1"]
|
402
402
|
end
|
403
403
|
|
404
404
|
it "should support :only_if_modified option for validates_unique, and not check uniqueness for existing records if values haven't changed" do
|
@@ -423,16 +423,16 @@ describe "Sequel::Plugins::ValidationHelpers" do
|
|
423
423
|
|
424
424
|
m.username = '1'
|
425
425
|
m.should be_valid
|
426
|
-
MODEL_DB.sqls.should == ["SELECT COUNT(*) AS count FROM items WHERE ((
|
426
|
+
MODEL_DB.sqls.should == ["SELECT COUNT(*) AS count FROM items WHERE ((username = '1') AND (password = 'anothertest') AND (id != 3)) LIMIT 1"]
|
427
427
|
|
428
428
|
m = @c.load(:id=>3, :username => "0records", :password => "anothertest")
|
429
429
|
MODEL_DB.reset
|
430
430
|
m.password = '1'
|
431
431
|
m.should be_valid
|
432
|
-
MODEL_DB.sqls.should == ["SELECT COUNT(*) AS count FROM items WHERE ((
|
432
|
+
MODEL_DB.sqls.should == ["SELECT COUNT(*) AS count FROM items WHERE ((username = '0records') AND (password = '1') AND (id != 3)) LIMIT 1"]
|
433
433
|
MODEL_DB.reset
|
434
434
|
m.username = '2'
|
435
435
|
m.should be_valid
|
436
|
-
MODEL_DB.sqls.should == ["SELECT COUNT(*) AS count FROM items WHERE ((
|
436
|
+
MODEL_DB.sqls.should == ["SELECT COUNT(*) AS count FROM items WHERE ((username = '2') AND (password = '1') AND (id != 3)) LIMIT 1"]
|
437
437
|
end
|
438
438
|
end
|
@@ -266,6 +266,10 @@ describe "Simple Dataset operations in transactions" do
|
|
266
266
|
@ds.order(:id).all.should == [{:id=>1, :number=>20}]
|
267
267
|
end
|
268
268
|
end
|
269
|
+
|
270
|
+
specify "should support for_update" do
|
271
|
+
INTEGRATION_DB.transaction{@ds.for_update.all.should == []}
|
272
|
+
end
|
269
273
|
end
|
270
274
|
|
271
275
|
describe "Dataset UNION, EXCEPT, and INTERSECT" do
|
@@ -536,23 +540,23 @@ describe "Sequel::Dataset convenience methods" do
|
|
536
540
|
end
|
537
541
|
|
538
542
|
it "#group_and_count should return a grouping by count" do
|
539
|
-
@ds.group_and_count(:a).all.should == []
|
543
|
+
@ds.group_and_count(:a).order(:count).all.should == []
|
540
544
|
@ds.insert(20, 10)
|
541
|
-
@ds.group_and_count(:a).all.each{|h| h[:count] = h[:count].to_i}.should == [{:a=>20, :count=>1}]
|
545
|
+
@ds.group_and_count(:a).order(:count).all.each{|h| h[:count] = h[:count].to_i}.should == [{:a=>20, :count=>1}]
|
542
546
|
@ds.insert(20, 30)
|
543
|
-
@ds.group_and_count(:a).all.each{|h| h[:count] = h[:count].to_i}.should == [{:a=>20, :count=>2}]
|
547
|
+
@ds.group_and_count(:a).order(:count).all.each{|h| h[:count] = h[:count].to_i}.should == [{:a=>20, :count=>2}]
|
544
548
|
@ds.insert(30, 30)
|
545
|
-
@ds.group_and_count(:a).all.each{|h| h[:count] = h[:count].to_i}.should == [{:a=>30, :count=>1}, {:a=>20, :count=>2}]
|
549
|
+
@ds.group_and_count(:a).order(:count).all.each{|h| h[:count] = h[:count].to_i}.should == [{:a=>30, :count=>1}, {:a=>20, :count=>2}]
|
546
550
|
end
|
547
551
|
|
548
552
|
it "#group_and_count should support column aliases" do
|
549
|
-
@ds.group_and_count(:a___c).all.should == []
|
553
|
+
@ds.group_and_count(:a___c).order(:count).all.should == []
|
550
554
|
@ds.insert(20, 10)
|
551
|
-
@ds.group_and_count(:a___c).all.each{|h| h[:count] = h[:count].to_i}.should == [{:c=>20, :count=>1}]
|
555
|
+
@ds.group_and_count(:a___c).order(:count).all.each{|h| h[:count] = h[:count].to_i}.should == [{:c=>20, :count=>1}]
|
552
556
|
@ds.insert(20, 30)
|
553
|
-
@ds.group_and_count(:a___c).all.each{|h| h[:count] = h[:count].to_i}.should == [{:c=>20, :count=>2}]
|
557
|
+
@ds.group_and_count(:a___c).order(:count).all.each{|h| h[:count] = h[:count].to_i}.should == [{:c=>20, :count=>2}]
|
554
558
|
@ds.insert(30, 30)
|
555
|
-
@ds.group_and_count(:a___c).all.each{|h| h[:count] = h[:count].to_i}.should == [{:c=>30, :count=>1}, {:c=>20, :count=>2}]
|
559
|
+
@ds.group_and_count(:a___c).order(:count).all.each{|h| h[:count] = h[:count].to_i}.should == [{:c=>30, :count=>1}, {:c=>20, :count=>2}]
|
556
560
|
end
|
557
561
|
|
558
562
|
cspecify "#range should return the range between the maximum and minimum values", :sqlite do
|
@@ -596,7 +600,7 @@ describe "Sequel::Dataset main SQL methods" do
|
|
596
600
|
@ds.filter("b < ?", 15).invert.all.should == [{:a=>20, :b=>30}]
|
597
601
|
end
|
598
602
|
|
599
|
-
it "#and and #or should work
|
603
|
+
it "#and and #or should work correctly" do
|
600
604
|
@ds.insert(20, 30)
|
601
605
|
@ds.filter(:a=>20).and(:b=>30).all.should == [{:a=>20, :b=>30}]
|
602
606
|
@ds.filter(:a=>20).and(:b=>15).all.should == []
|
@@ -624,7 +624,7 @@ describe "statistics associations" do
|
|
624
624
|
String :name
|
625
625
|
end
|
626
626
|
class ::Project < Sequel::Model
|
627
|
-
many_to_one :ticket_hours, :read_only=>true, :key=>:id,
|
627
|
+
many_to_one :ticket_hours, :read_only=>true, :key=>:id, :class=>:Ticket,
|
628
628
|
:dataset=>proc{Ticket.filter(:project_id=>id).select{sum(hours).as(hours)}},
|
629
629
|
:eager_loader=>(proc do |kh, projects, a|
|
630
630
|
projects.each{|p| p.associations[:ticket_hours] = nil}
|
@@ -677,3 +677,58 @@ describe "statistics associations" do
|
|
677
677
|
p2.ticket_hours.to_i.should == 22
|
678
678
|
end
|
679
679
|
end
|
680
|
+
|
681
|
+
describe "one to one associations" do
|
682
|
+
before do
|
683
|
+
INTEGRATION_DB.create_table!(:books) do
|
684
|
+
primary_key :id
|
685
|
+
end
|
686
|
+
class ::Book < Sequel::Model
|
687
|
+
one_to_one :first_page, :class=>:Page, :conditions=>{:page_number=>1}
|
688
|
+
one_to_one :second_page, :class=>:Page, :conditions=>{:page_number=>2}
|
689
|
+
end
|
690
|
+
|
691
|
+
INTEGRATION_DB.create_table!(:pages) do
|
692
|
+
primary_key :id
|
693
|
+
foreign_key :book_id, :books
|
694
|
+
Integer :page_number
|
695
|
+
end
|
696
|
+
class ::Page < Sequel::Model
|
697
|
+
many_to_one :book
|
698
|
+
end
|
699
|
+
|
700
|
+
@book1 = Book.create
|
701
|
+
@book2 = Book.create
|
702
|
+
@page1 = Page.create(:book=>@book1, :page_number=>1)
|
703
|
+
@page2 = Page.create(:book=>@book1, :page_number=>2)
|
704
|
+
@page3 = Page.create(:book=>@book2, :page_number=>1)
|
705
|
+
@page4 = Page.create(:book=>@book2, :page_number=>2)
|
706
|
+
clear_sqls
|
707
|
+
end
|
708
|
+
|
709
|
+
after do
|
710
|
+
INTEGRATION_DB.drop_table :pages, :books
|
711
|
+
Object.send(:remove_const, :Book)
|
712
|
+
Object.send(:remove_const, :Page)
|
713
|
+
end
|
714
|
+
|
715
|
+
it "should be eager loadable" do
|
716
|
+
bk1, bk2 = Book.filter(:books__id=>[1,2]).eager(:first_page).all
|
717
|
+
bk1.first_page.should == @page1
|
718
|
+
bk2.first_page.should == @page3
|
719
|
+
end
|
720
|
+
|
721
|
+
it "should be eager graphable" do
|
722
|
+
bk1, bk2 = Book.filter(:books__id=>[1,2]).eager_graph(:first_page).all
|
723
|
+
bk1.first_page.should == @page1
|
724
|
+
bk2.first_page.should == @page3
|
725
|
+
end
|
726
|
+
|
727
|
+
it "should be eager graphable two at once" do
|
728
|
+
bk1, bk2 = Book.filter(:books__id=>[1,2]).eager_graph(:first_page, :second_page).all
|
729
|
+
bk1.first_page.should == @page1
|
730
|
+
bk1.second_page.should == @page2
|
731
|
+
bk2.first_page.should == @page3
|
732
|
+
bk2.second_page.should == @page4
|
733
|
+
end
|
734
|
+
end
|
@@ -107,4 +107,12 @@ describe "Sequel::Model basic support" do
|
|
107
107
|
proc{i2 = Marshal.load(s)}.should_not raise_error
|
108
108
|
i2.should == i
|
109
109
|
end
|
110
|
+
|
111
|
+
specify "#lock! should lock records" do
|
112
|
+
Item.db.transaction do
|
113
|
+
i = Item.create(:name=>'J')
|
114
|
+
i.lock!
|
115
|
+
i.update(:name=>'K')
|
116
|
+
end
|
117
|
+
end
|
110
118
|
end
|
@@ -482,3 +482,273 @@ describe "OptimisticLocking plugin" do
|
|
482
482
|
proc{p1.update(:name=>'Bob')}.should_not raise_error
|
483
483
|
end
|
484
484
|
end
|
485
|
+
|
486
|
+
describe "Composition plugin" do
|
487
|
+
before do
|
488
|
+
@db = INTEGRATION_DB
|
489
|
+
@db.create_table!(:events) do
|
490
|
+
primary_key :id
|
491
|
+
Integer :year
|
492
|
+
Integer :month
|
493
|
+
Integer :day
|
494
|
+
end
|
495
|
+
class ::Event < Sequel::Model(@db)
|
496
|
+
plugin :composition
|
497
|
+
composition :date, :composer=>proc{Date.new(year, month, day) if year && month && day}, :decomposer=>(proc do
|
498
|
+
if date
|
499
|
+
self.year = date.year
|
500
|
+
self.month = date.month
|
501
|
+
self.day = date.day
|
502
|
+
else
|
503
|
+
self.year, self.month, self.day = nil
|
504
|
+
end
|
505
|
+
end)
|
506
|
+
composition :date, :mapping=>[:year, :month, :day]
|
507
|
+
end
|
508
|
+
@e1 = Event.create(:year=>2010, :month=>2, :day=>15)
|
509
|
+
@e2 = Event.create({})
|
510
|
+
end
|
511
|
+
after do
|
512
|
+
@db.drop_table(:events)
|
513
|
+
Object.send(:remove_const, :Event)
|
514
|
+
end
|
515
|
+
|
516
|
+
specify "should return a composed object if the underlying columns have a value" do
|
517
|
+
@e1.date.should == Date.civil(2010, 2, 15)
|
518
|
+
@e2.date.should == nil
|
519
|
+
end
|
520
|
+
|
521
|
+
specify "should decompose the object when saving the record" do
|
522
|
+
@e1.date = Date.civil(2009, 1, 2)
|
523
|
+
@e1.save
|
524
|
+
@e1.year.should == 2009
|
525
|
+
@e1.month.should == 1
|
526
|
+
@e1.day.should == 2
|
527
|
+
end
|
528
|
+
|
529
|
+
specify "should save all columns when saving changes" do
|
530
|
+
@e2.date = Date.civil(2009, 10, 2)
|
531
|
+
@e2.save_changes
|
532
|
+
@e2.reload
|
533
|
+
@e2.year.should == 2009
|
534
|
+
@e2.month.should == 10
|
535
|
+
@e2.day.should == 2
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
if INTEGRATION_DB.dataset.supports_cte?
|
540
|
+
describe "RcteTree Plugin" do
|
541
|
+
before do
|
542
|
+
@db = INTEGRATION_DB
|
543
|
+
@db.create_table!(:nodes) do
|
544
|
+
primary_key :id
|
545
|
+
Integer :parent_id
|
546
|
+
String :name
|
547
|
+
end
|
548
|
+
class ::Node < Sequel::Model(@db)
|
549
|
+
plugin :rcte_tree, :order=>:name
|
550
|
+
end
|
551
|
+
|
552
|
+
@a = Node.create(:name=>'a')
|
553
|
+
@b = Node.create(:name=>'b')
|
554
|
+
@aa = Node.create(:name=>'aa', :parent=>@a)
|
555
|
+
@ab = Node.create(:name=>'ab', :parent=>@a)
|
556
|
+
@ba = Node.create(:name=>'ba', :parent=>@b)
|
557
|
+
@bb = Node.create(:name=>'bb', :parent=>@b)
|
558
|
+
@aaa = Node.create(:name=>'aaa', :parent=>@aa)
|
559
|
+
@aab = Node.create(:name=>'aab', :parent=>@aa)
|
560
|
+
@aba = Node.create(:name=>'aba', :parent=>@ab)
|
561
|
+
@abb = Node.create(:name=>'abb', :parent=>@ab)
|
562
|
+
@aaaa = Node.create(:name=>'aaaa', :parent=>@aaa)
|
563
|
+
@aaab = Node.create(:name=>'aaab', :parent=>@aaa)
|
564
|
+
@aaaaa = Node.create(:name=>'aaaaa', :parent=>@aaaa)
|
565
|
+
end
|
566
|
+
after do
|
567
|
+
@db.drop_table :nodes
|
568
|
+
Object.send(:remove_const, :Node)
|
569
|
+
end
|
570
|
+
|
571
|
+
specify "should load all standard (not-CTE) methods correctly" do
|
572
|
+
@a.children.should == [@aa, @ab]
|
573
|
+
@b.children.should == [@ba, @bb]
|
574
|
+
@aa.children.should == [@aaa, @aab]
|
575
|
+
@ab.children.should == [@aba, @abb]
|
576
|
+
@ba.children.should == []
|
577
|
+
@bb.children.should == []
|
578
|
+
@aaa.children.should == [@aaaa, @aaab]
|
579
|
+
@aab.children.should == []
|
580
|
+
@aba.children.should == []
|
581
|
+
@abb.children.should == []
|
582
|
+
@aaaa.children.should == [@aaaaa]
|
583
|
+
@aaab.children.should == []
|
584
|
+
@aaaaa.children.should == []
|
585
|
+
|
586
|
+
@a.parent.should == nil
|
587
|
+
@b.parent.should == nil
|
588
|
+
@aa.parent.should == @a
|
589
|
+
@ab.parent.should == @a
|
590
|
+
@ba.parent.should == @b
|
591
|
+
@bb.parent.should == @b
|
592
|
+
@aaa.parent.should == @aa
|
593
|
+
@aab.parent.should == @aa
|
594
|
+
@aba.parent.should == @ab
|
595
|
+
@abb.parent.should == @ab
|
596
|
+
@aaaa.parent.should == @aaa
|
597
|
+
@aaab.parent.should == @aaa
|
598
|
+
@aaaaa.parent.should == @aaaa
|
599
|
+
end
|
600
|
+
|
601
|
+
specify "should load all ancestors and descendants lazily for a given instance" do
|
602
|
+
@a.descendants.should == [@aa, @aaa, @aaaa, @aaaaa, @aaab, @aab, @ab, @aba, @abb]
|
603
|
+
@b.descendants.should == [@ba, @bb]
|
604
|
+
@aa.descendants.should == [@aaa, @aaaa, @aaaaa, @aaab, @aab]
|
605
|
+
@ab.descendants.should == [@aba, @abb]
|
606
|
+
@ba.descendants.should == []
|
607
|
+
@bb.descendants.should == []
|
608
|
+
@aaa.descendants.should == [@aaaa, @aaaaa, @aaab]
|
609
|
+
@aab.descendants.should == []
|
610
|
+
@aba.descendants.should == []
|
611
|
+
@abb.descendants.should == []
|
612
|
+
@aaaa.descendants.should == [@aaaaa]
|
613
|
+
@aaab.descendants.should == []
|
614
|
+
@aaaaa.descendants.should == []
|
615
|
+
|
616
|
+
@a.ancestors.should == []
|
617
|
+
@b.ancestors.should == []
|
618
|
+
@aa.ancestors.should == [@a]
|
619
|
+
@ab.ancestors.should == [@a]
|
620
|
+
@ba.ancestors.should == [@b]
|
621
|
+
@bb.ancestors.should == [@b]
|
622
|
+
@aaa.ancestors.should == [@a, @aa]
|
623
|
+
@aab.ancestors.should == [@a, @aa]
|
624
|
+
@aba.ancestors.should == [@a, @ab]
|
625
|
+
@abb.ancestors.should == [@a, @ab]
|
626
|
+
@aaaa.ancestors.should == [@a, @aa, @aaa]
|
627
|
+
@aaab.ancestors.should == [@a, @aa, @aaa]
|
628
|
+
@aaaaa.ancestors.should == [@a, @aa, @aaa, @aaaa]
|
629
|
+
end
|
630
|
+
|
631
|
+
specify "should eagerly load all ancestors and descendants for a dataset" do
|
632
|
+
nodes = Node.filter(:id=>[@a.id, @b.id, @aaa.id]).order(:name).eager(:ancestors, :descendants).all
|
633
|
+
nodes.should == [@a, @aaa, @b]
|
634
|
+
nodes[0].descendants.should == [@aa, @aaa, @aaaa, @aaaaa, @aaab, @aab, @ab, @aba, @abb]
|
635
|
+
nodes[1].descendants.should == [@aaaa, @aaaaa, @aaab]
|
636
|
+
nodes[2].descendants.should == [@ba, @bb]
|
637
|
+
nodes[0].ancestors.should == []
|
638
|
+
nodes[1].ancestors.should == [@a, @aa]
|
639
|
+
nodes[2].ancestors.should == []
|
640
|
+
end
|
641
|
+
|
642
|
+
specify "should eagerly load descendants to a given level" do
|
643
|
+
nodes = Node.filter(:id=>[@a.id, @b.id, @aaa.id]).order(:name).eager(:descendants=>1).all
|
644
|
+
nodes.should == [@a, @aaa, @b]
|
645
|
+
nodes[0].descendants.should == [@aa, @ab]
|
646
|
+
nodes[1].descendants.should == [@aaaa, @aaab]
|
647
|
+
nodes[2].descendants.should == [@ba, @bb]
|
648
|
+
|
649
|
+
nodes = Node.filter(:id=>[@a.id, @b.id, @aaa.id]).order(:name).eager(:descendants=>2).all
|
650
|
+
nodes.should == [@a, @aaa, @b]
|
651
|
+
nodes[0].descendants.should == [@aa, @aaa, @aab, @ab, @aba, @abb]
|
652
|
+
nodes[1].descendants.should == [@aaaa, @aaaaa, @aaab]
|
653
|
+
nodes[2].descendants.should == [@ba, @bb]
|
654
|
+
end
|
655
|
+
|
656
|
+
specify "should populate all :children associations when eagerly loading descendants for a dataset" do
|
657
|
+
nodes = Node.filter(:id=>[@a.id, @b.id, @aaa.id]).order(:name).eager(:descendants).all
|
658
|
+
nodes[0].associations[:children].should == [@aa, @ab]
|
659
|
+
nodes[1].associations[:children].should == [@aaaa, @aaab]
|
660
|
+
nodes[2].associations[:children].should == [@ba, @bb]
|
661
|
+
nodes[0].associations[:children].map{|c1| c1.associations[:children]}.should == [[@aaa, @aab], [@aba, @abb]]
|
662
|
+
nodes[1].associations[:children].map{|c1| c1.associations[:children]}.should == [[@aaaaa], []]
|
663
|
+
nodes[2].associations[:children].map{|c1| c1.associations[:children]}.should == [[], []]
|
664
|
+
nodes[0].associations[:children].map{|c1| c1.associations[:children].map{|c2| c2.associations[:children]}}.should == [[[@aaaa, @aaab], []], [[], []]]
|
665
|
+
nodes[1].associations[:children].map{|c1| c1.associations[:children].map{|c2| c2.associations[:children]}}.should == [[[]], []]
|
666
|
+
nodes[0].associations[:children].map{|c1| c1.associations[:children].map{|c2| c2.associations[:children].map{|c3| c3.associations[:children]}}}.should == [[[[@aaaaa], []], []], [[], []]]
|
667
|
+
nodes[0].associations[:children].map{|c1| c1.associations[:children].map{|c2| c2.associations[:children].map{|c3| c3.associations[:children].map{|c4| c4.associations[:children]}}}}.should == [[[[[]], []], []], [[], []]]
|
668
|
+
end
|
669
|
+
|
670
|
+
specify "should not populate :children associations for final level when loading descendants to a given level" do
|
671
|
+
nodes = Node.filter(:id=>[@a.id, @b.id, @aaa.id]).order(:name).eager(:descendants=>1).all
|
672
|
+
nodes[0].associations[:children].should == [@aa, @ab]
|
673
|
+
nodes[0].associations[:children].map{|c1| c1.associations[:children]}.should == [nil, nil]
|
674
|
+
nodes[1].associations[:children].should == [@aaaa, @aaab]
|
675
|
+
nodes[1].associations[:children].map{|c1| c1.associations[:children]}.should == [nil, nil]
|
676
|
+
nodes[2].associations[:children].should == [@ba, @bb]
|
677
|
+
nodes[2].associations[:children].map{|c1| c1.associations[:children]}.should == [nil, nil]
|
678
|
+
|
679
|
+
nodes[0].associations[:children].map{|c1| c1.children}.should == [[@aaa, @aab], [@aba, @abb]]
|
680
|
+
nodes[1].associations[:children].map{|c1| c1.children}.should == [[@aaaaa], []]
|
681
|
+
nodes[2].associations[:children].map{|c1| c1.children}.should == [[], []]
|
682
|
+
|
683
|
+
nodes = Node.filter(:id=>[@a.id, @b.id, @aaa.id]).order(:name).eager(:descendants=>2).all
|
684
|
+
nodes[0].associations[:children].should == [@aa, @ab]
|
685
|
+
nodes[0].associations[:children].map{|c1| c1.associations[:children]}.should == [[@aaa, @aab], [@aba, @abb]]
|
686
|
+
nodes[0].associations[:children].map{|c1| c1.associations[:children].map{|c2| c2.associations[:children]}}.should == [[[@aaaa, @aaab], nil], [nil, nil]]
|
687
|
+
nodes[0].associations[:children].map{|c1| c1.associations[:children].map{|c2| (cc2 = c2.associations[:children]) ? cc2.map{|c3| c3.associations[:children]} : nil}}.should == [[[[@aaaaa], []], nil], [nil, nil]]
|
688
|
+
nodes[0].associations[:children].map{|c1| c1.associations[:children].map{|c2| (cc2 = c2.associations[:children]) ? cc2.map{|c3| (cc3 = c3.associations[:children]) ? cc3.map{|c4| c4.associations[:children]} : nil} : nil}}.should == [[[[nil], []], nil], [nil, nil]]
|
689
|
+
|
690
|
+
nodes[1].associations[:children].should == [@aaaa, @aaab]
|
691
|
+
nodes[1].associations[:children].map{|c1| c1.associations[:children]}.should == [[@aaaaa], []]
|
692
|
+
nodes[1].associations[:children].map{|c1| c1.associations[:children].map{|c2| c2.associations[:children]}}.should == [[nil], []]
|
693
|
+
|
694
|
+
nodes[2].associations[:children].should == [@ba, @bb]
|
695
|
+
nodes[2].associations[:children].map{|c1| c1.associations[:children]}.should == [[], []]
|
696
|
+
|
697
|
+
nodes[0].associations[:children].map{|c1| c1.associations[:children].map{|c2| c2.children}}.should == [[[@aaaa, @aaab], []], [[], []]]
|
698
|
+
nodes[0].associations[:children].map{|c1| c1.associations[:children].map{|c2| c2.children.map{|c3| c3.children}}}.should == [[[[@aaaaa], []], []], [[], []]]
|
699
|
+
nodes[0].associations[:children].map{|c1| c1.associations[:children].map{|c2| c2.children.map{|c3| c3.children.map{|c4| c4.children}}}}.should == [[[[[]], []], []], [[], []]]
|
700
|
+
nodes[1].associations[:children].map{|c1| c1.associations[:children].map{|c2| c2.children}}.should == [[[]], []]
|
701
|
+
end
|
702
|
+
|
703
|
+
specify "should populate all :children associations when lazily loading descendants" do
|
704
|
+
@a.descendants
|
705
|
+
@a.associations[:children].should == [@aa, @ab]
|
706
|
+
@a.associations[:children].map{|c1| c1.associations[:children]}.should == [[@aaa, @aab], [@aba, @abb]]
|
707
|
+
@a.associations[:children].map{|c1| c1.associations[:children].map{|c2| c2.associations[:children]}}.should == [[[@aaaa, @aaab], []], [[], []]]
|
708
|
+
@a.associations[:children].map{|c1| c1.associations[:children].map{|c2| c2.associations[:children].map{|c3| c3.associations[:children]}}}.should == [[[[@aaaaa], []], []], [[], []]]
|
709
|
+
@a.associations[:children].map{|c1| c1.associations[:children].map{|c2| c2.associations[:children].map{|c3| c3.associations[:children].map{|c4| c4.associations[:children]}}}}.should == [[[[[]], []], []], [[], []]]
|
710
|
+
|
711
|
+
@b.descendants
|
712
|
+
@b.associations[:children].should == [@ba, @bb]
|
713
|
+
@b.associations[:children].map{|c1| c1.associations[:children]}.should == [[], []]
|
714
|
+
|
715
|
+
@aaa.descendants
|
716
|
+
@aaa.associations[:children].map{|c1| c1.associations[:children]}.should == [[@aaaaa], []]
|
717
|
+
@aaa.associations[:children].map{|c1| c1.associations[:children].map{|c2| c2.associations[:children]}}.should == [[[]], []]
|
718
|
+
end
|
719
|
+
|
720
|
+
specify "should populate all :parent associations when eagerly loading ancestors for a dataset" do
|
721
|
+
nodes = Node.filter(:id=>[@a.id, @ba.id, @aaa.id, @aaaaa.id]).order(:name).eager(:ancestors).all
|
722
|
+
nodes[0].associations.fetch(:parent, 1).should == nil
|
723
|
+
nodes[1].associations[:parent].should == @aa
|
724
|
+
nodes[1].associations[:parent].associations[:parent].should == @a
|
725
|
+
nodes[1].associations[:parent].associations[:parent].associations.fetch(:parent, 1) == nil
|
726
|
+
nodes[2].associations[:parent].should == @aaaa
|
727
|
+
nodes[2].associations[:parent].associations[:parent].should == @aaa
|
728
|
+
nodes[2].associations[:parent].associations[:parent].associations[:parent].should == @aa
|
729
|
+
nodes[2].associations[:parent].associations[:parent].associations[:parent].associations[:parent].should == @a
|
730
|
+
nodes[2].associations[:parent].associations[:parent].associations[:parent].associations[:parent].associations.fetch(:parent, 1).should == nil
|
731
|
+
nodes[3].associations[:parent].should == @b
|
732
|
+
nodes[3].associations[:parent].associations.fetch(:parent, 1).should == nil
|
733
|
+
end
|
734
|
+
|
735
|
+
specify "should populate all :parent associations when lazily loading ancestors" do
|
736
|
+
@a.reload
|
737
|
+
@a.ancestors
|
738
|
+
@a.associations[:parent].should == nil
|
739
|
+
|
740
|
+
@ba.reload
|
741
|
+
@ba.ancestors
|
742
|
+
@ba.associations[:parent].should == @b
|
743
|
+
@ba.associations[:parent].associations.fetch(:parent, 1).should == nil
|
744
|
+
|
745
|
+
@ba.reload
|
746
|
+
@aaaaa.ancestors
|
747
|
+
@aaaaa.associations[:parent].should == @aaaa
|
748
|
+
@aaaaa.associations[:parent].associations[:parent].should == @aaa
|
749
|
+
@aaaaa.associations[:parent].associations[:parent].associations[:parent].should == @aa
|
750
|
+
@aaaaa.associations[:parent].associations[:parent].associations[:parent].associations[:parent].should == @a
|
751
|
+
@aaaaa.associations[:parent].associations[:parent].associations[:parent].associations[:parent].associations.fetch(:parent, 1).should == nil
|
752
|
+
end
|
753
|
+
end
|
754
|
+
end
|