spec/integration/plugin_test.rb in sequel-4.2.0 vs spec/integration/plugin_test.rb in sequel-4.3.0

- old
+ new

@@ -703,47 +703,12 @@ @e2.month.should == 10 @e2.day.should == 2 end end -# DB2's implemention of CTE is too limited to use this plugin -if DB.dataset.supports_cte? and !Sequel.guarded?(:db2) - describe "RcteTree Plugin" do - before(:all) do - @db = DB - @db.create_table!(:nodes) do - primary_key :id - Integer :parent_id - String :name - end - class ::Node < Sequel::Model(@db) - plugin :rcte_tree, :order=>:name - end - - @nodes = [] - @nodes << @a = Node.create(:name=>'a') - @nodes << @b = Node.create(:name=>'b') - @nodes << @aa = Node.create(:name=>'aa', :parent=>@a) - @nodes << @ab = Node.create(:name=>'ab', :parent=>@a) - @nodes << @ba = Node.create(:name=>'ba', :parent=>@b) - @nodes << @bb = Node.create(:name=>'bb', :parent=>@b) - @nodes << @aaa = Node.create(:name=>'aaa', :parent=>@aa) - @nodes << @aab = Node.create(:name=>'aab', :parent=>@aa) - @nodes << @aba = Node.create(:name=>'aba', :parent=>@ab) - @nodes << @abb = Node.create(:name=>'abb', :parent=>@ab) - @nodes << @aaaa = Node.create(:name=>'aaaa', :parent=>@aaa) - @nodes << @aaab = Node.create(:name=>'aaab', :parent=>@aaa) - @nodes << @aaaaa = Node.create(:name=>'aaaaa', :parent=>@aaaa) - end - before do - @nodes.each{|n| n.associations.clear} - end - after(:all) do - @db.drop_table? :nodes - Object.send(:remove_const, :Node) - end - +describe "RcteTree Plugin" do + shared_examples_for "rcte tree plugin" do specify "should load all standard (not-CTE) methods correctly" do @a.children.should == [@aa, @ab] @b.children.should == [@ba, @bb] @aa.children.should == [@aaa, @aab] @ab.children.should == [@aba, @abb] @@ -801,48 +766,36 @@ @aaab.ancestors.should == [@a, @aa, @aaa] @aaaaa.ancestors.should == [@a, @aa, @aaa, @aaaa] end specify "should eagerly load all ancestors and descendants for a dataset" do - nodes = Node.filter(:id=>[@a.id, @b.id, @aaa.id]).order(:name).eager(:ancestors, :descendants).all + nodes = @Node.filter(@Node.primary_key=>[@a.pk, @b.pk, @aaa.pk]).order(:name).eager(:ancestors, :descendants).all nodes.should == [@a, @aaa, @b] nodes[0].descendants.should == [@aa, @aaa, @aaaa, @aaaaa, @aaab, @aab, @ab, @aba, @abb] nodes[1].descendants.should == [@aaaa, @aaaaa, @aaab] nodes[2].descendants.should == [@ba, @bb] nodes[0].ancestors.should == [] nodes[1].ancestors.should == [@a, @aa] nodes[2].ancestors.should == [] end - specify "should work correctly if not all columns are selected" do - c = Class.new(Sequel::Model(@db[:nodes])) - c.plugin :rcte_tree, :order=>:name - c.plugin :lazy_attributes, :name - c[:name=>'aaaa'].descendants.should == [c.load(:parent_id=>11, :id=>13)] - c[:name=>'aa'].ancestors.should == [c.load(:parent_id=>nil, :id=>1)] - nodes = c.filter(:id=>[@a.id, @b.id, @aaa.id]).order(:name).eager(:ancestors, :descendants).all - nodes.should == [{:parent_id=>nil, :id=>1}, {:parent_id=>3, :id=>7}, {:parent_id=>nil, :id=>2}].map{|x| c.load(x)} - nodes[2].descendants.should == [{:parent_id=>2, :id=>5}, {:parent_id=>2, :id=>6}].map{|x| c.load(x)} - nodes[1].ancestors.should == [{:parent_id=>nil, :id=>1}, {:parent_id=>1, :id=>3}].map{|x| c.load(x)} - end - specify "should eagerly load descendants to a given level" do - nodes = Node.filter(:id=>[@a.id, @b.id, @aaa.id]).order(:name).eager(:descendants=>1).all + nodes = @Node.filter(@Node.primary_key=>[@a.pk, @b.pk, @aaa.pk]).order(:name).eager(:descendants=>1).all nodes.should == [@a, @aaa, @b] nodes[0].descendants.should == [@aa, @ab] nodes[1].descendants.should == [@aaaa, @aaab] nodes[2].descendants.should == [@ba, @bb] - nodes = Node.filter(:id=>[@a.id, @b.id, @aaa.id]).order(:name).eager(:descendants=>2).all + nodes = @Node.filter(@Node.primary_key=>[@a.pk, @b.pk, @aaa.pk]).order(:name).eager(:descendants=>2).all nodes.should == [@a, @aaa, @b] nodes[0].descendants.should == [@aa, @aaa, @aab, @ab, @aba, @abb] nodes[1].descendants.should == [@aaaa, @aaaaa, @aaab] nodes[2].descendants.should == [@ba, @bb] end specify "should populate all :children associations when eagerly loading descendants for a dataset" do - nodes = Node.filter(:id=>[@a.id, @b.id, @aaa.id]).order(:name).eager(:descendants).all + nodes = @Node.filter(@Node.primary_key=>[@a.pk, @b.pk, @aaa.pk]).order(:name).eager(:descendants).all nodes[0].associations[:children].should == [@aa, @ab] nodes[1].associations[:children].should == [@aaaa, @aaab] nodes[2].associations[:children].should == [@ba, @bb] nodes[0].associations[:children].map{|c1| c1.associations[:children]}.should == [[@aaa, @aab], [@aba, @abb]] nodes[1].associations[:children].map{|c1| c1.associations[:children]}.should == [[@aaaaa], []] @@ -852,11 +805,11 @@ nodes[0].associations[:children].map{|c1| c1.associations[:children].map{|c2| c2.associations[:children].map{|c3| c3.associations[:children]}}}.should == [[[[@aaaaa], []], []], [[], []]] 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 == [[[[[]], []], []], [[], []]] end specify "should not populate :children associations for final level when loading descendants to a given level" do - nodes = Node.filter(:id=>[@a.id, @b.id, @aaa.id]).order(:name).eager(:descendants=>1).all + nodes = @Node.filter(@Node.primary_key=>[@a.pk, @b.pk, @aaa.pk]).order(:name).eager(:descendants=>1).all nodes[0].associations[:children].should == [@aa, @ab] nodes[0].associations[:children].map{|c1| c1.associations[:children]}.should == [nil, nil] nodes[1].associations[:children].should == [@aaaa, @aaab] nodes[1].associations[:children].map{|c1| c1.associations[:children]}.should == [nil, nil] nodes[2].associations[:children].should == [@ba, @bb] @@ -864,11 +817,11 @@ nodes[0].associations[:children].map{|c1| c1.children}.should == [[@aaa, @aab], [@aba, @abb]] nodes[1].associations[:children].map{|c1| c1.children}.should == [[@aaaaa], []] nodes[2].associations[:children].map{|c1| c1.children}.should == [[], []] - nodes = Node.filter(:id=>[@a.id, @b.id, @aaa.id]).order(:name).eager(:descendants=>2).all + nodes = @Node.filter(@Node.primary_key=>[@a.pk, @b.pk, @aaa.pk]).order(:name).eager(:descendants=>2).all nodes[0].associations[:children].should == [@aa, @ab] nodes[0].associations[:children].map{|c1| c1.associations[:children]}.should == [[@aaa, @aab], [@aba, @abb]] nodes[0].associations[:children].map{|c1| c1.associations[:children].map{|c2| c2.associations[:children]}}.should == [[[@aaaa, @aaab], nil], [nil, nil]] 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]] 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]] @@ -902,11 +855,11 @@ @aaa.associations[:children].map{|c1| c1.associations[:children]}.should == [[@aaaaa], []] @aaa.associations[:children].map{|c1| c1.associations[:children].map{|c2| c2.associations[:children]}}.should == [[[]], []] end specify "should populate all :parent associations when eagerly loading ancestors for a dataset" do - nodes = Node.filter(:id=>[@a.id, @ba.id, @aaa.id, @aaaaa.id]).order(:name).eager(:ancestors).all + nodes = @Node.filter(@Node.primary_key=>[@a.pk, @ba.pk, @aaa.pk, @aaaaa.pk]).order(:name).eager(:ancestors).all nodes[0].associations.fetch(:parent, 1).should == nil nodes[1].associations[:parent].should == @aa nodes[1].associations[:parent].associations[:parent].should == @a nodes[1].associations[:parent].associations[:parent].associations.fetch(:parent, 1).should == nil nodes[2].associations[:parent].should == @aaaa @@ -935,12 +888,96 @@ @aaaaa.associations[:parent].associations[:parent].associations[:parent].should == @aa @aaaaa.associations[:parent].associations[:parent].associations[:parent].associations[:parent].should == @a @aaaaa.associations[:parent].associations[:parent].associations[:parent].associations[:parent].associations.fetch(:parent, 1).should == nil end end -end + before do + @nodes.each{|n| n.associations.clear} + end + + describe "with single key" do + before(:all) do + @db = DB + @db.create_table!(:nodes) do + primary_key :id + Integer :parent_id + String :name + end + @Node = Class.new(Sequel::Model(@db[:nodes])) + @Node.plugin :rcte_tree, :order=>:name + @nodes = [] + @nodes << @a = @Node.create(:name=>'a') + @nodes << @b = @Node.create(:name=>'b') + @nodes << @aa = @Node.create(:name=>'aa', :parent=>@a) + @nodes << @ab = @Node.create(:name=>'ab', :parent=>@a) + @nodes << @ba = @Node.create(:name=>'ba', :parent=>@b) + @nodes << @bb = @Node.create(:name=>'bb', :parent=>@b) + @nodes << @aaa = @Node.create(:name=>'aaa', :parent=>@aa) + @nodes << @aab = @Node.create(:name=>'aab', :parent=>@aa) + @nodes << @aba = @Node.create(:name=>'aba', :parent=>@ab) + @nodes << @abb = @Node.create(:name=>'abb', :parent=>@ab) + @nodes << @aaaa = @Node.create(:name=>'aaaa', :parent=>@aaa) + @nodes << @aaab = @Node.create(:name=>'aaab', :parent=>@aaa) + @nodes << @aaaaa = @Node.create(:name=>'aaaaa', :parent=>@aaaa) + end + after(:all) do + @db.drop_table? :nodes + end + + it_should_behave_like "rcte tree plugin" + + specify "should work correctly if not all columns are selected" do + c = Class.new(Sequel::Model(@db[:nodes])) + c.plugin :rcte_tree, :order=>:name + c.plugin :lazy_attributes, :name + c[:name=>'aaaa'].descendants.should == [c.load(:parent_id=>11, :id=>13)] + c[:name=>'aa'].ancestors.should == [c.load(:parent_id=>nil, :id=>1)] + nodes = c.filter(:id=>[@a.id, @b.id, @aaa.id]).order(:name).eager(:ancestors, :descendants).all + nodes.should == [{:parent_id=>nil, :id=>1}, {:parent_id=>3, :id=>7}, {:parent_id=>nil, :id=>2}].map{|x| c.load(x)} + nodes[2].descendants.should == [{:parent_id=>2, :id=>5}, {:parent_id=>2, :id=>6}].map{|x| c.load(x)} + nodes[1].ancestors.should == [{:parent_id=>nil, :id=>1}, {:parent_id=>1, :id=>3}].map{|x| c.load(x)} + end + end + + describe "with composite keys" do + before(:all) do + @db = DB + @db.create_table!(:nodes) do + Integer :id + Integer :id2 + Integer :parent_id + Integer :parent_id2 + String :name + primary_key [:id, :id2] + end + @Node = Class.new(Sequel::Model(@db[:nodes])) + @Node.plugin :rcte_tree, :order=>:name, :key=>[:parent_id, :parent_id2] + @Node.unrestrict_primary_key + @nodes = [] + @nodes << @a = @Node.create(:id=>1, :id2=>1, :name=>'a') + @nodes << @b = @Node.create(:id=>1, :id2=>2, :name=>'b') + @nodes << @aa = @Node.create(:id=>2, :id2=>1, :name=>'aa', :parent=>@a) + @nodes << @ab = @Node.create(:id=>2, :id2=>2, :name=>'ab', :parent=>@a) + @nodes << @ba = @Node.create(:id=>3, :id2=>1, :name=>'ba', :parent=>@b) + @nodes << @bb = @Node.create(:id=>3, :id2=>2, :name=>'bb', :parent=>@b) + @nodes << @aaa = @Node.create(:id=>3, :id2=>3, :name=>'aaa', :parent=>@aa) + @nodes << @aab = @Node.create(:id=>1, :id2=>3, :name=>'aab', :parent=>@aa) + @nodes << @aba = @Node.create(:id=>2, :id2=>3, :name=>'aba', :parent=>@ab) + @nodes << @abb = @Node.create(:id=>4, :id2=>1, :name=>'abb', :parent=>@ab) + @nodes << @aaaa = @Node.create(:id=>1, :id2=>4, :name=>'aaaa', :parent=>@aaa) + @nodes << @aaab = @Node.create(:id=>2, :id2=>4, :name=>'aaab', :parent=>@aaa) + @nodes << @aaaaa = @Node.create(:id=>3, :id2=>4, :name=>'aaaaa', :parent=>@aaaa) + end + after(:all) do + @db.drop_table? :nodes + end + + it_should_behave_like "rcte tree plugin" + end +end if DB.dataset.supports_cte? and !Sequel.guarded?(:db2) + describe "Instance Filters plugin" do before(:all) do @db = DB @db.create_table!(:items) do primary_key :id @@ -1536,170 +1573,119 @@ proc { @c[:name => "P1"].move_down(10) }.should raise_error(Sequel::Error) end end describe "Sequel::Plugins::Tree" do - before(:all) do - @db = DB - end - - describe "with natural database order" do - before(:all) do - @db.create_table!(:nodes) do - Integer :id, :primary_key=>true - String :name - Integer :parent_id - Integer :position - end - - @nodes = [{:id => 1, :name => 'one', :parent_id => nil, :position => 1}, - {:id => 2, :name => 'two', :parent_id => nil, :position => 2}, - {:id => 3, :name => 'three', :parent_id => nil, :position => 3}, - {:id => 4, :name => "two.one", :parent_id => 2, :position => 1}, - {:id => 5, :name => "two.two", :parent_id => 2, :position => 2}, - {:id => 6, :name => "two.two.one", :parent_id => 5, :position => 1}, - {:id => 7, :name => "one.two", :parent_id => 1, :position => 2}, - {:id => 8, :name => "one.one", :parent_id => 1, :position => 1}, - {:id => 9, :name => "five", :parent_id => nil, :position => 5}, - {:id => 10, :name => "four", :parent_id => nil, :position => 4}, - {:id => 11, :name => "five.one", :parent_id => 9, :position => 1}, - {:id => 12, :name => "two.three", :parent_id => 2, :position => 3}] - @nodes.each{|node| @db[:nodes].insert(node)} - - class ::Node < Sequel::Model - plugin :tree - end - end - after(:all) do - @db.drop_table?(:nodes) - Object.send(:remove_const, :Node) - end - + shared_examples_for "tree plugin" do it "should instantiate" do - Node.all.size.should == 12 + @Node.all.size.should == 12 end - it "should find top level nodes" do - Node.roots_dataset.count.should == 5 - end - it "should find all descendants of a node" do - two = Node.find(:id => 2) - two.name.should == "two" - two.descendants.map{|m| m[:id]}.should == [4, 5, 12, 6] + @Node.find(:name => 'two').descendants.map{|m| m.name}.should == %w'two.one two.two two.three two.two.one' end it "should find all ancestors of a node" do - twotwoone = Node.find(:id => 6) - twotwoone.name.should == "two.two.one" - twotwoone.ancestors.map{|m| m[:id]}.should == [5, 2] + @Node.find(:name => "two.two.one").ancestors.map{|m| m.name}.should == %w'two.two two' end it "should find all siblings of a node, excepting self" do - twoone = Node.find(:id => 4) - twoone.name.should == "two.one" - twoone.siblings.map{|m| m[:id]}.should == [5, 12] + @Node.find(:name=>"two.one").siblings.map{|m| m.name}.should == %w'two.two two.three' end it "should find all siblings of a node, including self" do - twoone = Node.find(:id => 4) - twoone.name.should == "two.one" - twoone.self_and_siblings.map{|m| m[:id]}.should == [4, 5, 12] + @Node.find(:name=>"two.one").self_and_siblings.map{|m| m.name}.should == %w'two.one two.two two.three' end it "should find siblings for root nodes" do - three = Node.find(:id => 3) - three.name.should == "three" - three.self_and_siblings.map{|m| m[:id]}.should == [1, 2, 3, 9, 10] + @Node.find(:name=>'three').self_and_siblings.map{|m| m.name}.should == %w'one two three four five' end it "should find correct root for a node" do - twotwoone = Node.find(:id => 6) - twotwoone.name.should == "two.two.one" - twotwoone.root[:id].should == 2 - - three = Node.find(:id => 3) - three.name.should == "three" - three.root[:id].should == 3 - - fiveone = Node.find(:id => 11) - fiveone.name.should == "five.one" - fiveone.root[:id].should == 9 + @Node.find(:name=>"two.two.one").root.name.should == 'two' + @Node.find(:name=>"three").root.name.should == 'three' + @Node.find(:name=>"five.one").root.name.should == 'five' end - it "iterate top-level nodes in natural database order" do - Node.roots_dataset.count.should == 5 - Node.roots.inject([]){|ids, p| ids << p.position}.should == [1, 2, 3, 5, 4] + it "iterate top-level nodes in order" do + @Node.roots_dataset.count.should == 5 + @Node.roots.map{|p| p.name}.should == %w'one two three four five' end it "should have children" do - one = Node.find(:id => 1) - one.name.should == "one" - one.children.size.should == 2 + @Node.find(:name=>'one').children.map{|m| m.name}.should == %w'one.one one.two' end - - it "children should be natural database order" do - one = Node.find(:id => 1) - one.name.should == "one" - one.children.map{|m| m[:position]}.should == [2, 1] - end + end - describe "Nodes in specified order" do - before(:all) do - class ::OrderedNode < Sequel::Model(:nodes) - plugin :tree, :order => :position - end + describe "with simple key" do + before(:all) do + @db = DB + @db.create_table!(:nodes) do + Integer :id, :primary_key=>true + String :name + Integer :parent_id + Integer :position end - after(:all) do - Object.send(:remove_const, :OrderedNode) - end - it "iterate top-level nodes in order by position" do - OrderedNode.roots_dataset.count.should == 5 - OrderedNode.roots.inject([]){|ids, p| ids << p.position}.should == [1, 2, 3, 4, 5] - end + @nodes = [{:id => 1, :name => 'one', :parent_id => nil, :position => 1}, + {:id => 2, :name => 'two', :parent_id => nil, :position => 2}, + {:id => 3, :name => 'three', :parent_id => nil, :position => 3}, + {:id => 4, :name => "two.one", :parent_id => 2, :position => 1}, + {:id => 5, :name => "two.two", :parent_id => 2, :position => 2}, + {:id => 6, :name => "two.two.one", :parent_id => 5, :position => 1}, + {:id => 7, :name => "one.two", :parent_id => 1, :position => 2}, + {:id => 8, :name => "one.one", :parent_id => 1, :position => 1}, + {:id => 9, :name => "five", :parent_id => nil, :position => 5}, + {:id => 10, :name => "four", :parent_id => nil, :position => 4}, + {:id => 11, :name => "five.one", :parent_id => 9, :position => 1}, + {:id => 12, :name => "two.three", :parent_id => 2, :position => 3}] + @nodes.each{|node| @db[:nodes].insert(node)} - it "children should be in specified order" do - one = OrderedNode.find(:id => 1) - one.name.should == "one" - one.children.map{|m| m[:position]}.should == [1, 2] - end + @Node = Class.new(Sequel::Model(:nodes)) + @Node.plugin :tree, :order=>:position end + after(:all) do + @db.drop_table?(:nodes) + end + + it_should_behave_like "tree plugin" end - describe "Lorems in specified order" do + describe "with composite key" do before(:all) do - @db.create_table!(:lorems) do - Integer :id, :primary_key=>true + @db = DB + @db.create_table!(:nodes) do + Integer :id + Integer :id2 String :name - Integer :ipsum_id - Integer :neque + Integer :parent_id + Integer :parent_id2 + Integer :position + primary_key [:id, :id2] end - @lorems = [{:id => 1, :name => 'Lorem', :ipsum_id => nil, :neque => 4}, - {:id => 2, :name => 'Ipsum', :ipsum_id => nil, :neque => 3}, - {:id => 4, :name => "Neque", :ipsum_id => 2, :neque => 2}, - {:id => 5, :name => "Porro", :ipsum_id => 2, :neque => 1}] - @lorems.each{|lorem| @db[:lorems].insert(lorem)} + @nodes = [{:id => 1, :id2=> 1, :name => 'one', :parent_id => nil, :parent_id2 => nil, :position => 1}, + {:id => 2, :id2=> 1, :name => 'two', :parent_id => nil, :parent_id2 => nil, :position => 2}, + {:id => 1, :id2=> 2, :name => 'three', :parent_id => nil, :parent_id2 => nil, :position => 3}, + {:id => 2, :id2=> 2, :name => "two.one", :parent_id => 2, :parent_id2 => 1, :position => 1}, + {:id => 3, :id2=> 1, :name => "two.two", :parent_id => 2, :parent_id2 => 1, :position => 2}, + {:id => 3, :id2=> 2, :name => "two.two.one", :parent_id => 3, :parent_id2 => 1, :position => 1}, + {:id => 3, :id2=> 3, :name => "one.two", :parent_id => 1, :parent_id2 => 1, :position => 2}, + {:id => 1, :id2=> 3, :name => "one.one", :parent_id => 1, :parent_id2 => 1, :position => 1}, + {:id => 2, :id2=> 3, :name => "five", :parent_id => nil, :parent_id2 => nil, :position => 5}, + {:id => 4, :id2=> 1, :name => "four", :parent_id => nil, :parent_id2 => nil, :position => 4}, + {:id => 1, :id2=> 4, :name => "five.one", :parent_id => 2, :parent_id2 => 3, :position => 1}, + {:id => 2, :id2=> 4, :name => "two.three", :parent_id => 2, :parent_id2 => 1, :position => 3}] + @nodes.each{|node| @db[:nodes].insert(node)} - class ::Lorem < Sequel::Model - plugin :tree, :key => :ipsum_id, :order => :neque - end + @Node = Class.new(Sequel::Model(:nodes)) + @Node.plugin :tree, :order=>:position, :key=>[:parent_id, :parent_id2] end after(:all) do - @db.drop_table?(:lorems) - Object.send(:remove_const, :Lorem) + @db.drop_table?(:nodes) end - it "iterate top-level nodes in order by position" do - Lorem.roots_dataset.count.should == 2 - Lorem.roots.inject([]){|ids, p| ids << p.neque}.should == [3, 4] - end - - it "children should be specified order" do - one = Lorem.find(:id => 2) - one.children.map{|m| m[:neque]}.should == [1, 2] - end + it_should_behave_like "tree plugin" end end describe "Sequel::Plugins::PreparedStatements" do before(:all) do