diff --git a/spec/closure_tree/tag_spec.rb b/spec/closure_tree/tag_spec.rb
deleted file mode 100644
index 2e5a39b..0000000
--- a/spec/closure_tree/tag_spec.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require 'spec_helper'
-
-RSpec.describe Tag do
-  it_behaves_like Tag
-end
diff --git a/spec/closure_tree/uuid_tag_spec.rb b/spec/closure_tree/uuid_tag_spec.rb
deleted file mode 100644
index 5c32ac1..0000000
--- a/spec/closure_tree/uuid_tag_spec.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require 'spec_helper'
-
-RSpec.describe UUIDTag do
-  it_behaves_like Tag
-end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index f06a10b..5f58ad4 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -97,7 +97,6 @@ def sqlite?
 # Load test helpers
 require_relative 'support/schema'
 require_relative 'support/models'
-require_relative 'support/tag_examples'
 require_relative 'support/helpers'
 require_relative 'support/exceed_query_limit'
 require_relative 'support/query_counter'
diff --git a/spec/support/tag_examples.rb b/spec/support/tag_examples.rb
deleted file mode 100644
index 6a21ee6..0000000
--- a/spec/support/tag_examples.rb
+++ /dev/null
@@ -1,821 +0,0 @@
-require 'spec_helper'
-
-RSpec.shared_examples_for Tag do
-
-  let (:tag_class) { described_class }
-  let (:tag_hierarchy_class) { described_class.hierarchy_class }
-
-  context 'class setup' do
-
-    it 'has correct accessible_attributes' do
-      if tag_class._ct.use_attr_accessible?
-        expect(tag_class.accessible_attributes.to_a).to match_array(%w(parent name title))
-      end
-    end
-
-    it 'should build hierarchy classname correctly' do
-      expect(tag_class.hierarchy_class).to eq(tag_hierarchy_class)
-      expect(tag_class._ct.hierarchy_class_name).to eq(tag_hierarchy_class.to_s)
-      expect(tag_class._ct.short_hierarchy_class_name).to eq(tag_hierarchy_class.to_s)
-    end
-
-    it 'should have a correct parent column name' do
-      expected_parent_column_name = tag_class == UUIDTag ? 'parent_uuid' : 'parent_id'
-      expect(tag_class._ct.parent_column_name).to eq(expected_parent_column_name)
-    end
-  end
-
-  describe 'from empty db' do
-
-    context 'with no tags' do
-      it 'should return no entities' do
-        expect(tag_class.roots).to be_empty
-        expect(tag_class.leaves).to be_empty
-      end
-
-      it '#find_or_create_by_path with strings' do
-        a = tag_class.create!(name: 'a')
-        expect(a.find_or_create_by_path(%w{b c}).ancestry_path).to eq(%w{a b c})
-      end
-
-      it '#find_or_create_by_path with hashes' do
-        a = tag_class.create!(name: 'a', title: 'A')
-        subject = a.find_or_create_by_path([
-          {name: 'b', title: 'B'},
-          {name: 'c', title: 'C'}
-        ])
-        expect(subject.ancestry_path).to eq(%w{a b c})
-        expect(subject.self_and_ancestors.map(&:title)).to eq(%w{C B A})
-      end
-    end
-
-    context 'with 1 tag' do
-      before do
-        @tag = tag_class.create!(name: 'tag')
-      end
-
-      it 'should be a leaf' do
-        expect(@tag.leaf?).to be_truthy
-      end
-
-      it 'should be a root' do
-        expect(@tag.root?).to be_truthy
-      end
-
-      it 'has no parent' do
-        expect(@tag.parent).to be_nil
-      end
-
-      it 'should return the only entity as a root and leaf' do
-        expect(tag_class.all).to eq([@tag])
-        expect(tag_class.roots).to eq([@tag])
-        expect(tag_class.leaves).to eq([@tag])
-      end
-
-      it 'should not be found by passing find_by_path an array of blank strings' do
-        expect(tag_class.find_by_path([''])).to be_nil
-      end
-
-      it 'should not be found by passing find_by_path an empty array' do
-        expect(tag_class.find_by_path([])).to be_nil
-      end
-
-      it 'should not be found by passing find_by_path nil' do
-        expect(tag_class.find_by_path(nil)).to be_nil
-      end
-
-      it 'should not be found by passing find_by_path an empty string' do
-        expect(tag_class.find_by_path('')).to be_nil
-      end
-
-      it 'should not be found by passing find_by_path an array of nils' do
-        expect(tag_class.find_by_path([nil])).to be_nil
-      end
-
-      it 'should not be found by passing find_by_path an array with an additional blank string' do
-        expect(tag_class.find_by_path([@tag.name, ''])).to be_nil
-      end
-
-      it 'should not be found by passing find_by_path an array with an additional nil' do
-        expect(tag_class.find_by_path([@tag.name, nil])).to be_nil
-      end
-
-      it 'should be found by passing find_by_path an array with its name' do
-        expect(tag_class.find_by_path([@tag.name])).to eq @tag
-      end
-
-      it 'should be found by passing find_by_path its name' do
-        expect(tag_class.find_by_path(@tag.name)).to eq @tag
-      end
-
-      context 'with child' do
-        before do
-          @child = tag_class.create!(name: 'tag 2')
-        end
-
-        def assert_roots_and_leaves
-          expect(@tag.root?).to be_truthy
-          expect(@tag.leaf?).to be_falsey
-
-          expect(@child.root?).to be_falsey
-          expect(@child.leaf?).to be_truthy
-        end
-
-        def assert_parent_and_children
-          expect(@child.reload.parent).to eq(@tag)
-          expect(@tag.reload.children.to_a).to eq([@child])
-        end
-
-        it 'adds children through add_child' do
-          @tag.add_child @child
-          assert_roots_and_leaves
-          assert_parent_and_children
-        end
-
-        it 'adds children through collection' do
-          @tag.children << @child
-          assert_roots_and_leaves
-          assert_parent_and_children
-        end
-      end
-    end
-
-    context 'with 2 tags' do
-      before :each do
-        @root = tag_class.create!(name: 'root')
-        @leaf = @root.add_child(tag_class.create!(name: 'leaf'))
-      end
-      it 'should return a simple root and leaf' do
-        expect(tag_class.roots).to eq([@root])
-        expect(tag_class.leaves).to eq([@leaf])
-      end
-      it 'should return child_ids for root' do
-        expect(@root.child_ids).to eq([@leaf.id])
-      end
-
-      it 'should return an empty array for leaves' do
-        expect(@leaf.child_ids).to be_empty
-      end
-    end
-
-    context '3 tag collection.create db' do
-      before :each do
-        @root = tag_class.create! name: 'root'
-        @mid = @root.children.create! name: 'mid'
-        @leaf = @mid.children.create! name: 'leaf'
-        DestroyedTag.delete_all
-      end
-
-      it 'should create all tags' do
-        expect(tag_class.all.to_a).to match_array([@root, @mid, @leaf])
-      end
-
-      it 'should return a root and leaf without middle tag' do
-        expect(tag_class.roots).to eq([@root])
-        expect(tag_class.leaves).to eq([@leaf])
-      end
-
-      it 'should delete leaves' do
-        tag_class.leaves.destroy_all
-        expect(tag_class.roots).to eq([@root]) # untouched
-        expect(tag_class.leaves).to eq([@mid])
-      end
-
-      it 'should delete everything if you delete the roots' do
-        tag_class.roots.destroy_all
-        expect(tag_class.all).to be_empty
-        expect(tag_class.roots).to be_empty
-        expect(tag_class.leaves).to be_empty
-        expect(DestroyedTag.all.map { |t| t.name }).to match_array(%w{root mid leaf})
-      end
-
-      it 'fix self_and_ancestors properly on reparenting' do
-        t = tag_class.create! name: 'moar leaf'
-        expect(t.self_and_ancestors.to_a).to eq([t])
-        @mid.children << t
-        expect(t.self_and_ancestors.to_a).to eq([t, @mid, @root])
-      end
-
-      it 'prevents ancestor loops' do
-        @leaf.add_child @root
-        expect(@root).not_to be_valid
-        expect(@root.reload.descendants).to include(@leaf)
-      end
-
-      it 'moves non-leaves' do
-        new_root = tag_class.create! name: 'new_root'
-        new_root.children << @mid
-        expect(@root.reload.descendants).to be_empty
-        expect(new_root.descendants).to eq([@mid, @leaf])
-        expect(@leaf.reload.ancestry_path).to eq(%w{new_root mid leaf})
-      end
-
-      it 'moves leaves' do
-        new_root = tag_class.create! name: 'new_root'
-        new_root.children << @leaf
-        expect(new_root.descendants).to eq([@leaf])
-        expect(@root.reload.descendants).to eq([@mid])
-        expect(@leaf.reload.ancestry_path).to eq(%w{new_root leaf})
-      end
-    end
-
-    context '3 tag explicit_create db' do
-      before :each do
-        @root = tag_class.create!(name: 'root')
-        @mid = @root.add_child(tag_class.create!(name: 'mid'))
-        @leaf = @mid.add_child(tag_class.create!(name: 'leaf'))
-      end
-
-      it 'should create all tags' do
-        expect(tag_class.all.to_a).to match_array([@root, @mid, @leaf])
-      end
-
-      it 'should return a root and leaf without middle tag' do
-        expect(tag_class.roots).to eq([@root])
-        expect(tag_class.leaves).to eq([@leaf])
-      end
-
-      it 'should prevent parental loops from torso' do
-        @mid.children << @root
-        expect(@root.valid?).to be_falsey
-        expect(@mid.reload.children).to eq([@leaf])
-      end
-
-      it 'should prevent parental loops from toes' do
-        @leaf.children << @root
-        expect(@root.valid?).to be_falsey
-        expect(@leaf.reload.children).to be_empty
-      end
-
-      it 'should support re-parenting' do
-        @root.children << @leaf
-        expect(tag_class.leaves).to eq([@leaf, @mid])
-      end
-
-      it 'cleans up hierarchy references for leaves' do
-        @leaf.destroy
-        expect(tag_hierarchy_class.where(ancestor_id: @leaf.id)).to be_empty
-        expect(tag_hierarchy_class.where(descendant_id: @leaf.id)).to be_empty
-      end
-
-      it 'cleans up hierarchy references' do
-        @mid.destroy
-        expect(tag_hierarchy_class.where(ancestor_id: @mid.id)).to be_empty
-        expect(tag_hierarchy_class.where(descendant_id: @mid.id)).to be_empty
-        expect(@root.reload).to be_root
-        root_hiers = @root.ancestor_hierarchies.to_a
-        expect(root_hiers.size).to eq(1)
-        expect(tag_hierarchy_class.where(ancestor_id: @root.id)).to eq(root_hiers)
-        expect(tag_hierarchy_class.where(descendant_id: @root.id)).to eq(root_hiers)
-      end
-
-      it 'should have different hash codes for each hierarchy model' do
-        hashes = tag_hierarchy_class.all.map(&:hash)
-        expect(hashes).to match_array(hashes.uniq)
-      end
-
-      it 'should return the same hash code for equal hierarchy models' do
-        expect(tag_hierarchy_class.first.hash).to eq(tag_hierarchy_class.first.hash)
-      end
-    end
-
-    it 'performs as the readme says it does' do
-      grandparent = tag_class.create(name: 'Grandparent')
-      parent = grandparent.children.create(name: 'Parent')
-      child1 = tag_class.create(name: 'First Child', parent: parent)
-      child2 = tag_class.new(name: 'Second Child')
-      parent.children << child2
-      child3 = tag_class.new(name: 'Third Child')
-      parent.add_child child3
-      expect(grandparent.self_and_descendants.collect(&:name)).to eq(
-        ['Grandparent', 'Parent', 'First Child', 'Second Child', 'Third Child']
-      )
-      expect(child1.ancestry_path).to eq(
-        ['Grandparent', 'Parent', 'First Child']
-      )
-      expect(child3.ancestry_path).to eq(
-        ['Grandparent', 'Parent', 'Third Child']
-      )
-      d = tag_class.find_or_create_by_path %w(a b c d)
-      h = tag_class.find_or_create_by_path %w(e f g h)
-      e = h.root
-      d.add_child(e) # "d.children << e" would work too, of course
-      expect(h.ancestry_path).to eq(%w(a b c d e f g h))
-    end
-
-    it 'roots sort alphabetically' do
-      expected = ('a'..'z').to_a
-      expected.shuffle.each { |ea| tag_class.create!(name: ea) }
-      expect(tag_class.roots.collect { |ea| ea.name }).to eq(expected)
-    end
-
-    context 'with simple tree' do
-      before :each do
-        tag_class.find_or_create_by_path %w(a1 b1 c1a)
-        tag_class.find_or_create_by_path %w(a1 b1 c1b)
-        tag_class.find_or_create_by_path %w(a1 b1 c1c)
-        tag_class.find_or_create_by_path %w(a1 b1b)
-        tag_class.find_or_create_by_path %w(a2 b2)
-        tag_class.find_or_create_by_path %w(a3)
-
-        @a1, @a2, @a3, @b1, @b1b, @b2, @c1a, @c1b, @c1c =
-          tag_class.all.sort_by(&:name)
-        @expected_roots = [@a1, @a2, @a3]
-        @expected_leaves = [@c1a, @c1b, @c1c, @b1b, @b2, @a3]
-        @expected_siblings = [[@a1, @a2, @a3], [@b1, @b1b], [@c1a, @c1b, @c1c]]
-        @expected_only_children = tag_class.all - @expected_siblings.flatten
-      end
-
-      it 'should find global roots' do
-        expect(tag_class.roots.to_a).to match_array(@expected_roots)
-      end
-      it 'should return root? for roots' do
-        @expected_roots.each { |ea| expect(ea).to be_root }
-      end
-      it 'should not return root? for non-roots' do
-        [@b1, @b2, @c1a, @c1b].each { |ea| expect(ea).not_to be_root }
-      end
-      it 'should return the correct root' do
-        {@a1 => @a1, @a2 => @a2, @a3 => @a3,
-          @b1 => @a1, @b2 => @a2, @c1a => @a1, @c1b => @a1}.each do |node, root|
-          expect(node.root).to eq(root)
-        end
-      end
-      it 'should assemble global leaves' do
-        expect(tag_class.leaves.to_a).to match_array(@expected_leaves)
-      end
-      it 'assembles siblings properly' do
-        @expected_siblings.each do |siblings|
-          siblings.each do |ea|
-            expect(ea.self_and_siblings.to_a).to match_array(siblings)
-            expect(ea.siblings.to_a).to match_array(siblings - [ea])
-          end
-        end
-        @expected_only_children.each do |ea|
-          expect(ea.siblings).to eq([])
-        end
-      end
-      it 'assembles before_siblings' do
-        @expected_siblings.each do |siblings|
-          (siblings.size - 1).times do |i|
-            target = siblings[i]
-            expected_before = siblings.first(i)
-            expect(target.siblings_before.to_a).to eq(expected_before)
-          end
-        end
-      end
-      it 'assembles after_siblings' do
-        @expected_siblings.each do |siblings|
-          (siblings.size - 1).times do |i|
-            target = siblings[i]
-            expected_after = siblings.last(siblings.size - 1 - i)
-            expect(target.siblings_after.to_a).to eq(expected_after)
-          end
-        end
-      end
-      it 'should assemble instance leaves' do
-        {@a1 => [@b1b, @c1a, @c1b, @c1c], @b1 => [@c1a, @c1b, @c1c], @a2 => [@b2]}.each do |node, leaves|
-          expect(node.leaves.to_a).to eq(leaves)
-        end
-        @expected_leaves.each { |ea| expect(ea.leaves.to_a).to eq([ea]) }
-      end
-      it 'should return leaf? for leaves' do
-        @expected_leaves.each { |ea| expect(ea).to be_leaf }
-      end
-
-      it 'can move roots' do
-        @c1a.children << @a2
-        @b2.reload.children << @a3
-        expect(@a3.reload.ancestry_path).to eq(%w(a1 b1 c1a a2 b2 a3))
-      end
-
-      it 'cascade-deletes from roots' do
-        victim_names = @a1.self_and_descendants.map(&:name)
-        survivor_names = tag_class.all.map(&:name) - victim_names
-        @a1.destroy
-        expect(tag_class.all.map(&:name)).to eq(survivor_names)
-      end
-    end
-
-    context 'with_ancestor' do
-      it 'works with no rows' do
-        expect(tag_class.with_ancestor.to_a).to be_empty
-      end
-      it 'finds only children' do
-        c = tag_class.find_or_create_by_path %w(A B C)
-        a, b = c.parent.parent, c.parent
-        spurious_tags = tag_class.find_or_create_by_path %w(D E)
-        expect(tag_class.with_ancestor(a).to_a).to eq([b, c])
-      end
-      it 'limits subsequent where clauses' do
-        a1c = tag_class.find_or_create_by_path %w(A1 B C)
-        a2c = tag_class.find_or_create_by_path %w(A2 B C)
-        # different paths!
-        expect(a1c).not_to eq(a2c)
-        expect(tag_class.where(:name => 'C').to_a).to match_array([a1c, a2c])
-        expect(tag_class.with_ancestor(a1c.parent.parent).where(:name => 'C').to_a).to eq([a1c])
-      end
-    end
-
-    context 'with_descendant' do
-      it 'works with no rows' do
-        expect(tag_class.with_descendant.to_a).to be_empty
-      end
-
-      it 'finds only parents' do
-        c = tag_class.find_or_create_by_path %w(A B C)
-        a, b = c.parent.parent, c.parent
-        spurious_tags = tag_class.find_or_create_by_path %w(D E)
-        expect(tag_class.with_descendant(c).to_a).to eq([a, b])
-      end
-
-      it 'limits subsequent where clauses' do
-        ac1 = tag_class.create(name: 'A')
-        ac2 = tag_class.create(name: 'A')
-
-        c1 = tag_class.find_or_create_by_path %w(B C1)
-        ac1.children << c1.parent
-
-        c2 = tag_class.find_or_create_by_path %w(B C2)
-        ac2.children << c2.parent
-
-        # different paths!
-        expect(ac1).not_to eq(ac2)
-        expect(tag_class.where(:name => 'A').to_a).to match_array([ac1, ac2])
-        expect(tag_class.with_descendant(c1).where(:name => 'A').to_a).to eq([ac1])
-      end
-    end
-
-    context 'lowest_common_ancestor' do
-      let!(:t1) { tag_class.create!(name: 't1') }
-      let!(:t11) { tag_class.create!(name: 't11', parent: t1) }
-      let!(:t111) { tag_class.create!(name: 't111', parent: t11) }
-      let!(:t112) { tag_class.create!(name: 't112', parent: t11) }
-      let!(:t12) { tag_class.create!(name: 't12', parent: t1) }
-      let!(:t121) { tag_class.create!(name: 't121', parent: t12) }
-      let!(:t2) { tag_class.create!(name: 't2') }
-      let!(:t21) { tag_class.create!(name: 't21', parent: t2) }
-      let!(:t211) { tag_class.create!(name: 't211', parent: t21) }
-
-      it 'finds the parent for siblings' do
-        expect(tag_class.lowest_common_ancestor(t112, t111)).to eq t11
-        expect(tag_class.lowest_common_ancestor(t12, t11)).to eq t1
-
-        expect(tag_class.lowest_common_ancestor([t112, t111])).to eq t11
-        expect(tag_class.lowest_common_ancestor([t12, t11])).to eq t1
-
-        expect(tag_class.lowest_common_ancestor(tag_class.where(name: ['t112', 't111']))).to eq t11
-        expect(tag_class.lowest_common_ancestor(tag_class.where(name: ['t12', 't11']))).to eq t1
-      end
-
-      it 'finds the grandparent for cousins' do
-        expect(tag_class.lowest_common_ancestor(t112, t111, t121)).to eq t1
-        expect(tag_class.lowest_common_ancestor([t112, t111, t121])).to eq t1
-        expect(tag_class.lowest_common_ancestor(tag_class.where(name: ['t112', 't111', 't121']))).to eq t1
-      end
-
-      it 'finds the parent/grandparent for aunt-uncle/niece-nephew' do
-        expect(tag_class.lowest_common_ancestor(t12, t112)).to eq t1
-        expect(tag_class.lowest_common_ancestor([t12, t112])).to eq t1
-        expect(tag_class.lowest_common_ancestor(tag_class.where(name: ['t12', 't112']))).to eq t1
-      end
-
-      it 'finds the self/parent for parent/child' do
-        expect(tag_class.lowest_common_ancestor(t12, t121)).to eq t12
-        expect(tag_class.lowest_common_ancestor(t1, t12)).to eq t1
-
-        expect(tag_class.lowest_common_ancestor([t12, t121])).to eq t12
-        expect(tag_class.lowest_common_ancestor([t1, t12])).to eq t1
-
-        expect(tag_class.lowest_common_ancestor(tag_class.where(name: ['t12', 't121']))).to eq t12
-        expect(tag_class.lowest_common_ancestor(tag_class.where(name: ['t1', 't12']))).to eq t1
-      end
-
-      it 'finds the self/grandparent for grandparent/grandchild' do
-        expect(tag_class.lowest_common_ancestor(t211, t2)).to eq t2
-        expect(tag_class.lowest_common_ancestor(t111, t1)).to eq t1
-
-        expect(tag_class.lowest_common_ancestor([t211, t2])).to eq t2
-        expect(tag_class.lowest_common_ancestor([t111, t1])).to eq t1
-
-        expect(tag_class.lowest_common_ancestor(tag_class.where(name: ['t211', 't2']))).to eq t2
-        expect(tag_class.lowest_common_ancestor(tag_class.where(name: ['t111', 't1']))).to eq t1
-      end
-
-      it 'finds the grandparent for a whole extended family' do
-        expect(tag_class.lowest_common_ancestor(t1, t11, t111, t112, t12, t121)).to eq t1
-        expect(tag_class.lowest_common_ancestor(t2, t21, t211)).to eq t2
-
-        expect(tag_class.lowest_common_ancestor([t1, t11, t111, t112, t12, t121])).to eq t1
-        expect(tag_class.lowest_common_ancestor([t2, t21, t211])).to eq t2
-
-        expect(tag_class.lowest_common_ancestor(tag_class.where(name: ['t1', 't11', 't111', 't112', 't12', 't121']))).to eq t1
-        expect(tag_class.lowest_common_ancestor(tag_class.where(name: ['t2', 't21', 't211']))).to eq t2
-      end
-
-      it 'is nil for no items' do
-        expect(tag_class.lowest_common_ancestor).to be_nil
-        expect(tag_class.lowest_common_ancestor([])).to be_nil
-        expect(tag_class.lowest_common_ancestor(tag_class.none)).to be_nil
-      end
-
-      it 'is nil if there are no common ancestors' do
-        expect(tag_class.lowest_common_ancestor(t111, t211)).to be_nil
-        expect(tag_class.lowest_common_ancestor([t111, t211])).to be_nil
-        expect(tag_class.lowest_common_ancestor(tag_class.where(name: ['t111', 't211']))).to be_nil
-      end
-
-      it 'is itself for single item' do
-        expect(tag_class.lowest_common_ancestor(t111)).to eq t111
-        expect(tag_class.lowest_common_ancestor(t2)).to eq t2
-
-        expect(tag_class.lowest_common_ancestor([t111])).to eq t111
-        expect(tag_class.lowest_common_ancestor([t2])).to eq t2
-
-        expect(tag_class.lowest_common_ancestor(tag_class.where(name: 't111'))).to eq t111
-        expect(tag_class.lowest_common_ancestor(tag_class.where(name: 't2'))).to eq t2
-      end
-    end
-
-    context 'paths' do
-      context 'with grandchild' do
-        before do
-          @child = tag_class.find_or_create_by_path([
-            {name: 'grandparent', title: 'Nonnie'},
-            {name: 'parent', title: 'Mom'},
-            {name: 'child', title: 'Kid'}])
-          @parent = @child.parent
-          @grandparent = @parent.parent
-        end
-
-        it 'should build ancestry path' do
-          expect(@child.ancestry_path).to eq(%w{grandparent parent child})
-          expect(@child.ancestry_path(:name)).to eq(%w{grandparent parent child})
-          expect(@child.ancestry_path(:title)).to eq(%w{Nonnie Mom Kid})
-        end
-
-        it 'assembles ancestors' do
-          expect(@child.ancestors).to eq([@parent, @grandparent])
-          expect(@child.self_and_ancestors).to eq([@child, @parent, @grandparent])
-        end
-
-        it 'should find by path' do
-          # class method:
-          expect(tag_class.find_by_path(%w{grandparent parent child})).to eq(@child)
-          # instance method:
-          expect(@parent.find_by_path(%w{child})).to eq(@child)
-          expect(@grandparent.find_by_path(%w{parent child})).to eq(@child)
-          expect(@parent.find_by_path(%w{child larvae})).to be_nil
-        end
-
-        it 'should respect attribute hashes with both selection and creation' do
-          expected_title = 'something else'
-          attrs = {title: expected_title}
-          existing_title = @grandparent.title
-          new_grandparent = tag_class.find_or_create_by_path(%w{grandparent}, attrs)
-          expect(new_grandparent).not_to eq(@grandparent)
-          expect(new_grandparent.title).to eq(expected_title)
-          expect(@grandparent.reload.title).to eq(existing_title)
-        end
-
-        it 'should create a hierarchy with a given attribute' do
-          expected_title = 'unicorn rainbows'
-          attrs = {title: expected_title}
-          child = tag_class.find_or_create_by_path(%w{grandparent parent child}, attrs)
-          expect(child).not_to eq(@child)
-          [child, child.parent, child.parent.parent].each do |ea|
-            expect(ea.title).to eq(expected_title)
-          end
-        end
-      end
-
-      it 'finds correctly rooted paths' do
-        decoy = tag_class.find_or_create_by_path %w(a b c d)
-        b_d = tag_class.find_or_create_by_path %w(b c d)
-        expect(tag_class.find_by_path(%w(b c d))).to eq(b_d)
-        expect(tag_class.find_by_path(%w(c d))).to be_nil
-      end
-
-      it 'find_by_path for 1 node' do
-        b = tag_class.find_or_create_by_path %w(a b)
-        b2 = b.root.find_by_path(%w(b))
-        expect(b2).to eq(b)
-      end
-
-      it 'find_by_path for 2 nodes' do
-        path = %w(a b c)
-        c = tag_class.find_or_create_by_path path
-        permutations = path.permutation.to_a
-        correct = %w(b c)
-        expect(c.root.find_by_path(correct)).to eq(c)
-        (permutations - correct).each do |bad_path|
-          expect(c.root.find_by_path(bad_path)).to be_nil
-        end
-      end
-
-      it 'find_by_path for 3 nodes' do
-        d = tag_class.find_or_create_by_path %w(a b c d)
-        expect(d.root.find_by_path(%w(b c d))).to eq(d)
-        expect(tag_class.find_by_path(%w(a b c d))).to eq(d)
-        expect(tag_class.find_by_path(%w(d))).to be_nil
-      end
-
-      it 'should return nil for missing nodes' do
-        expect(tag_class.find_by_path(%w{missing})).to be_nil
-        expect(tag_class.find_by_path(%w{grandparent missing})).to be_nil
-        expect(tag_class.find_by_path(%w{grandparent parent missing})).to be_nil
-        expect(tag_class.find_by_path(%w{grandparent parent missing child})).to be_nil
-      end
-
-      describe '.find_or_create_by_path' do
-        it 'uses existing records' do
-          grandparent = tag_class.find_or_create_by_path(%w{grandparent})
-          expect(grandparent).to eq(grandparent)
-          child = tag_class.find_or_create_by_path(%w{grandparent parent child})
-          expect(child).to eq(child)
-        end
-
-        it 'creates 2-deep trees with strings' do
-          subject = tag_class.find_or_create_by_path(%w{events anniversary})
-          expect(subject.ancestry_path).to eq(%w{events anniversary})
-        end
-
-        it 'creates 2-deep trees with hashes' do
-          subject = tag_class.find_or_create_by_path([
-            {name: 'test1', title: 'TEST1'},
-            {name: 'test2', title: 'TEST2'}
-          ])
-          expect(subject.ancestry_path).to eq(%w{test1 test2})
-          # `self_and_ancestors` and `ancestors` is ordered parent-first. (!!)
-          expect(subject.self_and_ancestors.map(&:title)).to eq(%w{TEST2 TEST1})
-        end
-
-      end
-    end
-
-    context 'hash_tree' do
-      before :each do
-        @d1 = tag_class.find_or_create_by_path %w(a b c1 d1)
-        @c1 = @d1.parent
-        @b = @c1.parent
-        @a = @b.parent
-        @a2 = tag_class.create(name: 'a2')
-        @b2 = tag_class.find_or_create_by_path %w(a b2)
-        @c3 = tag_class.find_or_create_by_path %w(a3 b3 c3)
-        @b3 = @c3.parent
-        @a3 = @b3.parent
-        @tree2 = {
-          @a => {@b => {}, @b2 => {}}, @a2 => {}, @a3 => {@b3 => {}}
-        }
-
-        @one_tree = {
-          @a => {},
-          @a2 => {},
-          @a3 => {}
-        }
-        @two_tree = {
-          @a => {
-            @b => {},
-            @b2 => {}
-          },
-          @a2 => {},
-          @a3 => {
-            @b3 => {}
-          }
-        }
-        @three_tree = {
-          @a => {
-            @b => {
-              @c1 => {},
-            },
-            @b2 => {}
-          },
-          @a2 => {},
-          @a3 => {
-            @b3 => {
-              @c3 => {}
-            }
-          }
-        }
-        @full_tree = {
-          @a => {
-            @b => {
-              @c1 => {
-                @d1 => {}
-              },
-            },
-            @b2 => {}
-          },
-          @a2 => {},
-          @a3 => {
-            @b3 => {
-              @c3 => {}
-            }
-          }
-        }
-        #File.open("example.dot", "w") { |f| f.write(tag_class.root.to_dot_digraph) }
-      end
-
-      context '#hash_tree' do
-        it 'returns {} for depth 0' do
-          expect(tag_class.hash_tree(limit_depth: 0)).to eq({})
-        end
-        it 'limit_depth 1' do
-          expect(tag_class.hash_tree(limit_depth: 1)).to eq(@one_tree)
-        end
-        it 'limit_depth 2' do
-          expect(tag_class.hash_tree(limit_depth: 2)).to eq(@two_tree)
-        end
-        it 'limit_depth 3' do
-          expect(tag_class.hash_tree(limit_depth: 3)).to eq(@three_tree)
-        end
-        it 'limit_depth 4' do
-          expect(tag_class.hash_tree(limit_depth: 4)).to eq(@full_tree)
-        end
-        it 'no limit' do
-          expect(tag_class.hash_tree).to eq(@full_tree)
-        end
-      end
-
-      context '.hash_tree' do
-        it 'returns {} for depth 0' do
-          expect(@b.hash_tree(limit_depth: 0)).to eq({})
-        end
-        it 'limit_depth 1' do
-          expect(@b.hash_tree(limit_depth: 1)).to eq(@two_tree[@a].slice(@b))
-        end
-        it 'limit_depth 2' do
-          expect(@b.hash_tree(limit_depth: 2)).to eq(@three_tree[@a].slice(@b))
-        end
-        it 'limit_depth 3' do
-          expect(@b.hash_tree(limit_depth: 3)).to eq(@full_tree[@a].slice(@b))
-        end
-        it 'no limit from subsubroot' do
-          expect(@c1.hash_tree).to eq(@full_tree[@a][@b].slice(@c1))
-        end
-        it 'no limit from subroot' do
-          expect(@b.hash_tree).to eq(@full_tree[@a].slice(@b))
-        end
-        it 'no limit from root' do
-          expect(@a.hash_tree.merge(@a2.hash_tree)).to eq(@full_tree.slice(@a, @a2))
-        end
-      end
-
-      context '.hash_tree from relations' do
-        it 'limit_depth 2 from chained activerecord association subroots' do
-          expect(@a.children.hash_tree(limit_depth: 2)).to eq(@three_tree[@a])
-        end
-        it 'no limit from chained activerecord association subroots' do
-          expect(@a.children.hash_tree).to eq(@full_tree[@a])
-        end
-        it 'limit_depth 3 from b.parent' do
-          expect(@b.parent.hash_tree(limit_depth: 3)).to eq(@three_tree.slice(@a))
-        end
-        it 'no limit_depth from b.parent' do
-          expect(@b.parent.hash_tree).to eq(@full_tree.slice(@a))
-        end
-        it 'no limit_depth from c.parent' do
-          expect(@c1.parent.hash_tree).to eq(@full_tree[@a].slice(@b))
-        end
-      end
-    end
-
-    it 'finds_by_path for very deep trees' do
-      expect(tag_class._ct).to receive(:max_join_tables).at_least(1).and_return(3)
-      path = (1..20).to_a.map { |ea| ea.to_s }
-      subject = tag_class.find_or_create_by_path(path)
-      expect(subject.ancestry_path).to eq(path)
-      expect(tag_class.find_by_path(path)).to eq(subject)
-      root = subject.root
-      expect(root.find_by_path(path[1..-1])).to eq(subject)
-    end
-
-    describe 'DOT rendering' do
-      it 'should render for an empty scope' do
-        expect(tag_class.to_dot_digraph(tag_class.where('0=1'))).to eq("digraph G {\n}\n")
-      end
-      it 'should render for an empty scope' do
-        tag_class.find_or_create_by_path(%w(a b1 c1))
-        tag_class.find_or_create_by_path(%w(a b2 c2))
-        tag_class.find_or_create_by_path(%w(a b2 c3))
-        a, b1, b2, c1, c2, c3 = %w(a b1 b2 c1 c2 c3).map { |ea| tag_class.where(name: ea).first.id }
-        dot = tag_class.roots.first.to_dot_digraph
-        expect(dot).to eq <<-DOT
-digraph G {
-  "#{a}" [label="a"]
-  "#{a}" -> "#{b1}"
-  "#{b1}" [label="b1"]
-  "#{a}" -> "#{b2}"
-  "#{b2}" [label="b2"]
-  "#{b1}" -> "#{c1}"
-  "#{c1}" [label="c1"]
-  "#{b2}" -> "#{c2}"
-  "#{c2}" [label="c2"]
-  "#{b2}" -> "#{c3}"
-  "#{c3}" [label="c3"]
-}
-        DOT
-      end
-    end
-  end
-end
diff --git a/test/closure_tree/tag_test.rb b/test/closure_tree/tag_test.rb
new file mode 100644
index 0000000..db99bbd
--- /dev/null
+++ b/test/closure_tree/tag_test.rb
@@ -0,0 +1,5 @@
+require "test_helper"
+
+describe Tag do
+  include TagExamples
+end
diff --git a/test/closure_tree/uuid_tag_test.rb b/test/closure_tree/uuid_tag_test.rb
new file mode 100644
index 0000000..1a2f797
--- /dev/null
+++ b/test/closure_tree/uuid_tag_test.rb
@@ -0,0 +1,5 @@
+require "test_helper"
+
+describe UUIDTag do
+  include TagExamples
+end
diff --git a/test/support/tag_examples.rb b/test/support/tag_examples.rb
new file mode 100644
index 0000000..3986baa
--- /dev/null
+++ b/test/support/tag_examples.rb
@@ -0,0 +1,862 @@
+module TagExamples
+  def self.included(mod)
+    @@described_class = mod.name.safe_constantize
+  end
+
+  describe "TagExamples" do
+    before do
+      @tag_class = @@described_class
+      @tag_hierarchy_class = @@described_class.hierarchy_class
+    end
+
+    describe "class setup" do
+      it "has correct accessible_attributes" do
+        if @tag_class._ct.use_attr_accessible?
+          assert_equal(%w[parent name title].sort, @tag_class.accessible_attributes.to_a.sort)
+        end
+      end
+
+      it "should build hierarchy classname correctly" do
+        assert_equal @tag_hierarchy_class, @tag_class.hierarchy_class
+        assert_equal @tag_hierarchy_class.to_s, @tag_class._ct.hierarchy_class_name
+        assert_equal @tag_hierarchy_class.to_s, @tag_class._ct.short_hierarchy_class_name
+      end
+
+      it "should have a correct parent column name" do
+        expected_parent_column_name = @tag_class == UUIDTag ? "parent_uuid" : "parent_id"
+        assert_equal expected_parent_column_name, @tag_class._ct.parent_column_name
+      end
+    end
+
+    describe "from empty db" do
+      describe "with no tags" do
+        it "should return no entities" do
+          assert_empty @tag_class.roots
+          assert_empty @tag_class.leaves
+        end
+
+        it "#find_or_create_by_path with strings" do
+          a = @tag_class.create!(name: "a")
+          assert_equal(%w[a b c], a.find_or_create_by_path(%w[b c]).ancestry_path)
+        end
+
+        it "#find_or_create_by_path with hashes" do
+          a = @tag_class.create!(name: "a", title: "A")
+          subject = a.find_or_create_by_path([
+            {name: "b", title: "B"},
+            {name: "c", title: "C"}
+          ])
+          assert_equal(%w[a b c], subject.ancestry_path)
+          assert_equal(%w[C B A], subject.self_and_ancestors.map(&:title))
+        end
+      end
+
+      describe "with 1 tag" do
+        before do
+          @tag = @tag_class.create!(name: "tag")
+        end
+
+        it "should be a leaf" do
+          assert @tag.leaf?
+        end
+
+        it "should be a root" do
+          assert @tag.root?
+        end
+
+        it "has no parent" do
+          assert_nil @tag.parent
+        end
+
+        it "should return the only entity as a root and leaf" do
+          assert_equal [@tag], @tag_class.all
+          assert_equal [@tag], @tag_class.roots
+          assert_equal [@tag], @tag_class.leaves
+        end
+
+        it "should not be found by passing find_by_path an array of blank strings" do
+          assert_nil @tag_class.find_by_path([""])
+        end
+
+        it "should not be found by passing find_by_path an empty array" do
+          assert_nil @tag_class.find_by_path([])
+        end
+
+        it "should not be found by passing find_by_path nil" do
+          assert_nil @tag_class.find_by_path(nil)
+        end
+
+        it "should not be found by passing find_by_path an empty string" do
+          assert_nil @tag_class.find_by_path("")
+        end
+
+        it "should not be found by passing find_by_path an array of nils" do
+          assert_nil @tag_class.find_by_path([nil])
+        end
+
+        it "should not be found by passing find_by_path an array with an additional blank string" do
+          assert_nil @tag_class.find_by_path([@tag.name, ""])
+        end
+
+        it "should not be found by passing find_by_path an array with an additional nil" do
+          assert_nil @tag_class.find_by_path([@tag.name, nil])
+        end
+
+        it "should be found by passing find_by_path an array with its name" do
+          assert_equal @tag, @tag_class.find_by_path([@tag.name])
+        end
+
+        it "should be found by passing find_by_path its name" do
+          assert_equal @tag, @tag_class.find_by_path(@tag.name)
+        end
+
+        describe "with child" do
+          before do
+            @child = @tag_class.create!(name: "tag 2")
+          end
+
+          def assert_roots_and_leaves
+            assert @tag.root?
+            refute @tag.leaf?
+
+            refute @child.root?
+            assert @child.leaf?
+          end
+
+          def assert_parent_and_children
+            assert_equal @tag, @child.reload.parent
+            assert_equal [@child], @tag.reload.children.to_a
+          end
+
+          it "adds children through add_child" do
+            @tag.add_child @child
+            assert_roots_and_leaves
+            assert_parent_and_children
+          end
+
+          it "adds children through collection" do
+            @tag.children << @child
+            assert_roots_and_leaves
+            assert_parent_and_children
+          end
+        end
+      end
+
+      describe "with 2 tags" do
+        before do
+          @root = @tag_class.create!(name: "root")
+          @leaf = @root.add_child(@tag_class.create!(name: "leaf"))
+        end
+
+        it "should return a simple root and leaf" do
+          assert_equal [@root], @tag_class.roots
+          assert_equal [@leaf], @tag_class.leaves
+        end
+
+        it "should return child_ids for root" do
+          assert_equal [@leaf.id], @root.child_ids
+        end
+
+        it "should return an empty array for leaves" do
+          assert_empty @leaf.child_ids
+        end
+      end
+
+      describe "3 tag collection.create db" do
+        before do
+          @root = @tag_class.create! name: "root"
+          @mid = @root.children.create! name: "mid"
+          @leaf = @mid.children.create! name: "leaf"
+          DestroyedTag.delete_all
+        end
+
+        it "should create all tags" do
+          assert_equal [@root, @mid, @leaf].sort, @tag_class.all.to_a.sort
+        end
+
+        it "should return a root and leaf without middle tag" do
+          assert_equal [@root], @tag_class.roots
+          assert_equal [@leaf], @tag_class.leaves
+        end
+
+        it "should delete leaves" do
+          @tag_class.leaves.destroy_all
+          assert_equal [@root], @tag_class.roots # untouched
+          assert_equal [@mid], @tag_class.leaves
+        end
+
+        it "should delete everything if you delete the roots" do
+          @tag_class.roots.destroy_all
+          assert_empty @tag_class.all
+          assert_empty @tag_class.roots
+          assert_empty @tag_class.leaves
+          assert_equal %w[root mid leaf].sort, DestroyedTag.all.map { |t| t.name }.sort
+        end
+
+        it "fix self_and_ancestors properly on reparenting" do
+          t = @tag_class.create! name: "moar leaf"
+          assert_equal [t], t.self_and_ancestors.to_a
+          @mid.children << t
+          assert_equal [t, @mid, @root], t.self_and_ancestors.to_a
+        end
+
+        it "prevents ancestor loops" do
+          @leaf.add_child @root
+          refute @root.valid?
+          assert_includes @root.reload.descendants, @leaf
+        end
+
+        it "moves non-leaves" do
+          new_root = @tag_class.create! name: "new_root"
+          new_root.children << @mid
+          assert_empty @root.reload.descendants
+          assert_equal [@mid, @leaf], new_root.descendants
+          assert_equal %w[new_root mid leaf], @leaf.reload.ancestry_path
+        end
+
+        it "moves leaves" do
+          new_root = @tag_class.create! name: "new_root"
+          new_root.children << @leaf
+          assert_equal [@leaf], new_root.descendants
+          assert_equal [@mid], @root.reload.descendants
+          assert_equal %w[new_root leaf], @leaf.reload.ancestry_path
+        end
+      end
+
+      describe "3 tag explicit_create db" do
+        before do
+          @root = @tag_class.create!(name: "root")
+          @mid = @root.add_child(@tag_class.create!(name: "mid"))
+          @leaf = @mid.add_child(@tag_class.create!(name: "leaf"))
+        end
+
+        it "should create all tags" do
+          assert_equal [@root, @mid, @leaf].sort, @tag_class.all.to_a.sort
+        end
+
+        it "should return a root and leaf without middle tag" do
+          assert_equal [@root], @tag_class.roots
+          assert_equal [@leaf], @tag_class.leaves
+        end
+
+        it "should prevent parental loops from torso" do
+          @mid.children << @root
+          refute @root.valid?
+          assert_equal [@leaf], @mid.reload.children
+        end
+
+        it "should prevent parental loops from toes" do
+          @leaf.children << @root
+          refute @root.valid?
+          assert_empty @leaf.reload.children
+        end
+
+        it "should support re-parenting" do
+          @root.children << @leaf
+          assert_equal [@leaf, @mid], @tag_class.leaves
+        end
+
+        it "cleans up hierarchy references for leaves" do
+          @leaf.destroy
+          assert_empty @tag_hierarchy_class.where(ancestor_id: @leaf.id)
+          assert_empty @tag_hierarchy_class.where(descendant_id: @leaf.id)
+        end
+
+        it "cleans up hierarchy references" do
+          @mid.destroy
+          assert_empty @tag_hierarchy_class.where(ancestor_id: @mid.id)
+          assert_empty @tag_hierarchy_class.where(descendant_id: @mid.id)
+          assert @root.reload.root?
+          root_hiers = @root.ancestor_hierarchies.to_a
+          assert_equal 1, root_hiers.size
+          assert_equal root_hiers, @tag_hierarchy_class.where(ancestor_id: @root.id)
+          assert_equal root_hiers, @tag_hierarchy_class.where(descendant_id: @root.id)
+        end
+
+        it "should have different hash codes for each hierarchy model" do
+          hashes = @tag_hierarchy_class.all.map(&:hash)
+          assert_equal hashes.uniq.sort, hashes.sort
+        end
+
+        it "should return the same hash code for equal hierarchy models" do
+          assert_equal @tag_hierarchy_class.first.hash, @tag_hierarchy_class.first.hash
+        end
+      end
+
+      it "performs as the readme says it does" do
+        grandparent = @tag_class.create(name: "Grandparent")
+        parent = grandparent.children.create(name: "Parent")
+        child1 = @tag_class.create(name: "First Child", parent: parent)
+        child2 = @tag_class.new(name: "Second Child")
+        parent.children << child2
+        child3 = @tag_class.new(name: "Third Child")
+        parent.add_child child3
+        assert_equal(
+          ["Grandparent", "Parent", "First Child", "Second Child", "Third Child"],
+          grandparent.self_and_descendants.collect(&:name)
+        )
+        assert_equal(["Grandparent", "Parent", "First Child"], child1.ancestry_path)
+        assert_equal(["Grandparent", "Parent", "Third Child"], child3.ancestry_path)
+        d = @tag_class.find_or_create_by_path %w[a b c d]
+        h = @tag_class.find_or_create_by_path %w[e f g h]
+        e = h.root
+        d.add_child(e) # "d.children << e" would work too, of course
+        assert_equal %w[a b c d e f g h], h.ancestry_path
+      end
+
+      it "roots sort alphabetically" do
+        expected = ("a".."z").to_a
+        expected.shuffle.each { |ea| @tag_class.create!(name: ea) }
+        assert_equal expected, @tag_class.roots.collect { |ea| ea.name }
+      end
+
+      describe "with simple tree" do
+        before do
+          @tag_class.find_or_create_by_path %w[a1 b1 c1a]
+          @tag_class.find_or_create_by_path %w[a1 b1 c1b]
+          @tag_class.find_or_create_by_path %w[a1 b1 c1c]
+          @tag_class.find_or_create_by_path %w[a1 b1b]
+          @tag_class.find_or_create_by_path %w[a2 b2]
+          @tag_class.find_or_create_by_path %w[a3]
+
+          @a1, @a2, @a3, @b1, @b1b, @b2, @c1a, @c1b, @c1c = @tag_class.all.sort_by(&:name)
+          @expected_roots = [@a1, @a2, @a3]
+          @expected_leaves = [@c1a, @c1b, @c1c, @b1b, @b2, @a3]
+          @expected_siblings = [[@a1, @a2, @a3], [@b1, @b1b], [@c1a, @c1b, @c1c]]
+          @expected_only_children = @tag_class.all - @expected_siblings.flatten
+        end
+
+        it "should find global roots" do
+          assert_equal @expected_roots.sort, @tag_class.roots.to_a.sort
+        end
+
+        it "should return root? for roots" do
+          @expected_roots.each { |ea| assert(ea.root?) }
+        end
+
+        it "should not return root? for non-roots" do
+          [@b1, @b2, @c1a, @c1b].each { |ea| refute(ea.root?) }
+        end
+
+        it "should return the correct root" do
+          {@a1 => @a1, @a2 => @a2, @a3 => @a3,
+           @b1 => @a1, @b2 => @a2, @c1a => @a1, @c1b => @a1}.each do |node, root|
+            assert_equal(root, node.root)
+          end
+        end
+
+        it "should assemble global leaves" do
+          assert_equal @expected_leaves.sort, @tag_class.leaves.to_a.sort
+        end
+
+        it "assembles siblings properly" do
+          @expected_siblings.each do |siblings|
+            siblings.each do |ea|
+              assert_equal siblings.sort, ea.self_and_siblings.to_a.sort
+              assert_equal((siblings - [ea]).sort, ea.siblings.to_a.sort)
+            end
+          end
+
+          @expected_only_children.each do |ea|
+            assert_equal [], ea.siblings
+          end
+        end
+
+        it "assembles before_siblings" do
+          @expected_siblings.each do |siblings|
+            (siblings.size - 1).times do |i|
+              target = siblings[i]
+              expected_before = siblings.first(i)
+              assert_equal expected_before, target.siblings_before.to_a
+            end
+          end
+        end
+
+        it "assembles after_siblings" do
+          @expected_siblings.each do |siblings|
+            (siblings.size - 1).times do |i|
+              target = siblings[i]
+              expected_after = siblings.last(siblings.size - 1 - i)
+              assert_equal expected_after, target.siblings_after.to_a
+            end
+          end
+        end
+
+        it "should assemble instance leaves" do
+          {@a1 => [@b1b, @c1a, @c1b, @c1c], @b1 => [@c1a, @c1b, @c1c], @a2 => [@b2]}.each do |node, leaves|
+            assert_equal leaves, node.leaves.to_a
+          end
+
+          @expected_leaves.each { |ea| assert_equal [ea], ea.leaves.to_a }
+        end
+
+        it "should return leaf? for leaves" do
+          @expected_leaves.each { |ea| assert ea.leaf? }
+        end
+
+        it "can move roots" do
+          @c1a.children << @a2
+          @b2.reload.children << @a3
+          assert_equal %w[a1 b1 c1a a2 b2 a3], @a3.reload.ancestry_path
+        end
+
+        it "cascade-deletes from roots" do
+          victim_names = @a1.self_and_descendants.map(&:name)
+          survivor_names = @tag_class.all.map(&:name) - victim_names
+          @a1.destroy
+          assert_equal survivor_names, @tag_class.all.map(&:name)
+        end
+      end
+
+      describe "with_ancestor" do
+        it "works with no rows" do
+          assert_empty @tag_class.with_ancestor.to_a
+        end
+
+        it "finds only children" do
+          c = @tag_class.find_or_create_by_path %w[A B C]
+          a = c.parent.parent
+          b = c.parent
+          spurious_tags = @tag_class.find_or_create_by_path %w[D E]
+          assert_equal [b, c], @tag_class.with_ancestor(a).to_a
+        end
+
+        it "limits subsequent where clauses" do
+          a1c = @tag_class.find_or_create_by_path %w[A1 B C]
+          a2c = @tag_class.find_or_create_by_path %w[A2 B C]
+          # different paths!
+          refute_equal a2c, a1c
+          assert_equal [a1c, a2c].sort, @tag_class.where(name: "C").to_a.sort
+          assert_equal [a1c], @tag_class.with_ancestor(a1c.parent.parent).where(name: "C").to_a.sort
+        end
+      end
+
+      describe "with_descendant" do
+        it "works with no rows" do
+          assert_empty @tag_class.with_descendant.to_a
+        end
+
+        it "finds only parents" do
+          c = @tag_class.find_or_create_by_path %w[A B C]
+          a = c.parent.parent
+          b = c.parent
+          spurious_tags = @tag_class.find_or_create_by_path %w[D E]
+          assert_equal [a, b], @tag_class.with_descendant(c).to_a
+        end
+
+        it "limits subsequent where clauses" do
+          ac1 = @tag_class.create(name: "A")
+          ac2 = @tag_class.create(name: "A")
+
+          c1 = @tag_class.find_or_create_by_path %w[B C1]
+          ac1.children << c1.parent
+
+          c2 = @tag_class.find_or_create_by_path %w[B C2]
+          ac2.children << c2.parent
+
+          # different paths!
+          refute_equal ac2, ac1
+          assert_equal [ac1, ac2].sort, @tag_class.where(name: "A").to_a.sort
+          assert_equal [ac1], @tag_class.with_descendant(c1).where(name: "A").to_a
+        end
+      end
+
+      describe "lowest_common_ancestor" do
+        before do
+          @t1 = @tag_class.create!(name: "t1")
+          @t11 = @tag_class.create!(name: "t11", parent: @t1)
+          @t111 = @tag_class.create!(name: "t111", parent: @t11)
+          @t112 = @tag_class.create!(name: "t112", parent: @t11)
+          @t12 = @tag_class.create!(name: "t12", parent: @t1)
+          @t121 = @tag_class.create!(name: "t121", parent: @t12)
+          @t2 = @tag_class.create!(name: "t2")
+          @t21 = @tag_class.create!(name: "t21", parent: @t2)
+          @t21 = @tag_class.create!(name: "t21", parent: @t2)
+          @t211 = @tag_class.create!(name: "t211", parent: @t21)
+        end
+
+        it "finds the parent for siblings" do
+          assert_equal @t11, @tag_class.lowest_common_ancestor(@t112, @t111)
+          assert_equal @t1, @tag_class.lowest_common_ancestor(@t12, @t11)
+
+          assert_equal @t11, @tag_class.lowest_common_ancestor([@t112, @t111])
+          assert_equal @t1, @tag_class.lowest_common_ancestor([@t12, @t11])
+
+          assert_equal @t11, @tag_class.lowest_common_ancestor(@tag_class.where(name: %w[t112 t111]))
+          assert_equal @t1, @tag_class.lowest_common_ancestor(@tag_class.where(name: %w[t12 t11]))
+        end
+
+        it "finds the grandparent for cousins" do
+          assert_equal @t1, @tag_class.lowest_common_ancestor(@t112, @t111, @t121)
+          assert_equal @t1, @tag_class.lowest_common_ancestor([@t112, @t111, @t121])
+          assert_equal @t1, @tag_class.lowest_common_ancestor(@tag_class.where(name: %w[t112 t111 t121]))
+        end
+
+        it "finds the parent/grandparent for aunt-uncle/niece-nephew" do
+          assert_equal @t1, @tag_class.lowest_common_ancestor(@t12, @t112)
+          assert_equal @t1, @tag_class.lowest_common_ancestor([@t12, @t112])
+          assert_equal @t1, @tag_class.lowest_common_ancestor(@tag_class.where(name: %w[t12 t112]))
+        end
+
+        it "finds the self/parent for parent/child" do
+          assert_equal @t12, @tag_class.lowest_common_ancestor(@t12, @t121)
+          assert_equal @t1, @tag_class.lowest_common_ancestor(@t1, @t12)
+
+          assert_equal @t12, @tag_class.lowest_common_ancestor([@t12, @t121])
+          assert_equal @t1, @tag_class.lowest_common_ancestor([@t1, @t12])
+
+          assert_equal @t12, @tag_class.lowest_common_ancestor(@tag_class.where(name: %w[t12 t121]))
+          assert_equal @t1, @tag_class.lowest_common_ancestor(@tag_class.where(name: %w[t1 t12]))
+        end
+
+        it "finds the self/grandparent for grandparent/grandchild" do
+          assert_equal @t2, @tag_class.lowest_common_ancestor(@t211, @t2)
+          assert_equal @t1, @tag_class.lowest_common_ancestor(@t111, @t1)
+
+          assert_equal @t2, @tag_class.lowest_common_ancestor([@t211, @t2])
+          assert_equal @t1, @tag_class.lowest_common_ancestor([@t111, @t1])
+
+          assert_equal @t2, @tag_class.lowest_common_ancestor(@tag_class.where(name: %w[t211 t2]))
+          assert_equal @t1, @tag_class.lowest_common_ancestor(@tag_class.where(name: %w[t111 t1]))
+        end
+
+        it "finds the grandparent for a whole extended family" do
+          assert_equal @t1, @tag_class.lowest_common_ancestor(@t1, @t11, @t111, @t112, @t12, @t121)
+          assert_equal @t2, @tag_class.lowest_common_ancestor(@t2, @t21, @t211)
+
+          assert_equal @t1, @tag_class.lowest_common_ancestor([@t1, @t11, @t111, @t112, @t12, @t121])
+          assert_equal @t2, @tag_class.lowest_common_ancestor([@t2, @t21, @t211])
+
+          assert_equal @t1,
+            @tag_class.lowest_common_ancestor(@tag_class.where(name: %w[t1 t11 t111 t112 t12 t121]))
+          assert_equal @t2, @tag_class.lowest_common_ancestor(@tag_class.where(name: %w[t2 t21 t211]))
+        end
+
+        it "is nil for no items" do
+          assert_nil @tag_class.lowest_common_ancestor
+          assert_nil @tag_class.lowest_common_ancestor([])
+          assert_nil @tag_class.lowest_common_ancestor(@tag_class.none)
+        end
+
+        it "is nil if there are no common ancestors" do
+          assert_nil @tag_class.lowest_common_ancestor(@t111, @t211)
+          assert_nil @tag_class.lowest_common_ancestor([@t111, @t211])
+          assert_nil @tag_class.lowest_common_ancestor(@tag_class.where(name: %w[t111 t211]))
+        end
+
+        it "is itself for single item" do
+          assert_equal @t111, @tag_class.lowest_common_ancestor(@t111)
+          assert_equal @t2, @tag_class.lowest_common_ancestor(@t2)
+
+          assert_equal @t111, @tag_class.lowest_common_ancestor([@t111])
+          assert_equal @t2, @tag_class.lowest_common_ancestor([@t2])
+
+          assert_equal @t111, @tag_class.lowest_common_ancestor(@tag_class.where(name: "t111"))
+          assert_equal @t2, @tag_class.lowest_common_ancestor(@tag_class.where(name: "t2"))
+        end
+      end
+
+      describe "paths" do
+        describe "with grandchild " do
+          before do
+            @child = @tag_class.find_or_create_by_path([
+              {name: "grandparent", title: "Nonnie"},
+              {name: "parent", title: "Mom"},
+              {name: "child", title: "Kid"}
+            ])
+            @parent = @child.parent
+            @grandparent = @parent.parent
+          end
+
+          it "should build ancestry path" do
+            assert_equal %w[grandparent parent child], @child.ancestry_path
+            assert_equal %w[grandparent parent child], @child.ancestry_path(:name)
+            assert_equal %w[Nonnie Mom Kid], @child.ancestry_path(:title)
+          end
+
+          it "assembles ancestors" do
+            assert_equal [@parent, @grandparent], @child.ancestors
+            assert_equal [@child, @parent, @grandparent], @child.self_and_ancestors
+          end
+
+          it "should find by path" do
+            # class method:
+            assert_equal @child, @tag_class.find_by_path(%w[grandparent parent child])
+            # instance method:
+            assert_equal @child, @parent.find_by_path(%w[child])
+            assert_equal @child, @grandparent.find_by_path(%w[parent child])
+            assert_nil @parent.find_by_path(%w[child larvae])
+          end
+
+          it "should respect attribute hashes with both selection and creation" do
+            expected_title = "something else"
+            attrs = {title: expected_title}
+            existing_title = @grandparent.title
+            new_grandparent = @tag_class.find_or_create_by_path(%w[grandparent], attrs)
+            refute_equal @grandparent, new_grandparent
+            assert_equal expected_title, new_grandparent.title
+            assert_equal existing_title, @grandparent.reload.title
+          end
+
+          it "should create a hierarchy with a given attribute" do
+            expected_title = "unicorn rainbows"
+            attrs = {title: expected_title}
+            child = @tag_class.find_or_create_by_path(%w[grandparent parent child], attrs)
+            refute_equal @child, child
+            [child, child.parent, child.parent.parent].each do |ea|
+              assert_equal expected_title, ea.title
+            end
+          end
+        end
+
+        it "finds correctly rooted paths" do
+          decoy = @tag_class.find_or_create_by_path %w[a b c d]
+          b_d = @tag_class.find_or_create_by_path %w[b c d]
+          assert_equal b_d, @tag_class.find_by_path(%w[b c d])
+          assert_nil @tag_class.find_by_path(%w[c d])
+        end
+
+        it "find_by_path for 1 node" do
+          b = @tag_class.find_or_create_by_path %w[a b]
+          b2 = b.root.find_by_path(%w[b])
+          assert_equal b, b2
+        end
+
+        it "find_by_path for 2 nodes" do
+          path = %w[a b c]
+          c = @tag_class.find_or_create_by_path path
+          permutations = path.permutation.to_a
+          correct = %w[b c]
+          assert_equal c, c.root.find_by_path(correct)
+          (permutations - correct).each do |bad_path|
+            assert_nil c.root.find_by_path(bad_path)
+          end
+        end
+
+        it "find_by_path for 3 nodes" do
+          d = @tag_class.find_or_create_by_path %w[a b c d]
+          assert_equal d, d.root.find_by_path(%w[b c d])
+          assert_equal d, @tag_class.find_by_path(%w[a b c d])
+          assert_nil @tag_class.find_by_path(%w[d])
+        end
+
+        it "should return nil for missing nodes" do
+          assert_nil @tag_class.find_by_path(%w[missing])
+          assert_nil @tag_class.find_by_path(%w[grandparent missing])
+          assert_nil @tag_class.find_by_path(%w[grandparent parent missing])
+          assert_nil @tag_class.find_by_path(%w[grandparent parent missing child])
+        end
+
+        describe ".find_or_create_by_path" do
+          it "uses existing records" do
+            grandparent = @tag_class.find_or_create_by_path(%w[grandparent])
+            assert_equal grandparent, grandparent
+            child = @tag_class.find_or_create_by_path(%w[grandparent parent child])
+            assert_equal child, child
+          end
+
+          it "creates 2-deep trees with strings" do
+            subject = @tag_class.find_or_create_by_path(%w[events anniversary])
+            assert_equal %w[events anniversary], subject.ancestry_path
+          end
+
+          it "creates 2-deep trees with hashes" do
+            subject = @tag_class.find_or_create_by_path([
+              {name: "test1", title: "TEST1"},
+              {name: "test2", title: "TEST2"}
+            ])
+            assert_equal %w[test1 test2], subject.ancestry_path
+            # `self_and_ancestors` and `ancestors` is ordered parent-first. (!!)
+            assert_equal %w[TEST2 TEST1], subject.self_and_ancestors.map(&:title)
+          end
+        end
+      end
+
+      describe "hash_tree" do
+        before do
+          @d1 = @tag_class.find_or_create_by_path %w[a b c1 d1]
+          @c1 = @d1.parent
+          @b = @c1.parent
+          @a = @b.parent
+          @a2 = @tag_class.create(name: "a2")
+          @b2 = @tag_class.find_or_create_by_path %w[a b2]
+          @c3 = @tag_class.find_or_create_by_path %w[a3 b3 c3]
+          @b3 = @c3.parent
+          @a3 = @b3.parent
+
+          @tree2 = {
+            @a => {@b => {}, @b2 => {}}, @a2 => {}, @a3 => {@b3 => {}}
+          }
+
+          @one_tree = {
+            @a => {},
+            @a2 => {},
+            @a3 => {}
+          }
+
+          @two_tree = {
+            @a => {
+              @b => {},
+              @b2 => {}
+            },
+            @a2 => {},
+            @a3 => {
+              @b3 => {}
+            }
+          }
+
+          @three_tree = {
+            @a => {
+              @b => {
+                @c1 => {}
+              },
+              @b2 => {}
+            },
+            @a2 => {},
+            @a3 => {
+              @b3 => {
+                @c3 => {}
+              }
+            }
+          }
+
+          @full_tree = {
+            @a => {
+              @b => {
+                @c1 => {
+                  @d1 => {}
+                }
+              },
+              @b2 => {}
+            },
+            @a2 => {},
+            @a3 => {
+              @b3 => {
+                @c3 => {}
+              }
+            }
+          }
+        end
+
+        describe "#hash_tree" do
+          it "returns {} for depth 0" do
+            assert_equal({}, @tag_class.hash_tree(limit_depth: 0))
+          end
+
+          it "limit_depth 1" do
+            assert_equal @one_tree, @tag_class.hash_tree(limit_depth: 1)
+          end
+
+          it "limit_depth 2" do
+            assert_equal @two_tree, @tag_class.hash_tree(limit_depth: 2)
+          end
+
+          it "limit_depth 3" do
+            assert_equal @three_tree, @tag_class.hash_tree(limit_depth: 3)
+          end
+
+          it "limit_depth 4" do
+            assert_equal @full_tree, @tag_class.hash_tree(limit_depth: 4)
+          end
+
+          it "no limit" do
+            assert_equal @full_tree, @tag_class.hash_tree
+          end
+        end
+
+        describe ".hash_tree" do
+          it "returns {} for depth 0" do
+            assert_equal({}, @b.hash_tree(limit_depth: 0))
+          end
+
+          it "limit_depth 1" do
+            assert_equal @two_tree[@a].slice(@b), @b.hash_tree(limit_depth: 1)
+          end
+
+          it "limit_depth 2" do
+            assert_equal @three_tree[@a].slice(@b), @b.hash_tree(limit_depth: 2)
+          end
+
+          it "limit_depth 3" do
+            assert_equal @full_tree[@a].slice(@b), @b.hash_tree(limit_depth: 3)
+          end
+
+          it "no limit from subsubroot" do
+            assert_equal @full_tree[@a][@b].slice(@c1), @c1.hash_tree
+          end
+
+          it "no limit from subroot" do
+            assert_equal @full_tree[@a].slice(@b), @b.hash_tree
+          end
+
+          it "no limit from root" do
+            assert_equal @full_tree.slice(@a, @a2), @a.hash_tree.merge(@a2.hash_tree)
+          end
+        end
+
+        describe ".hash_tree from relations" do
+          it "limit_depth 2 from chained activerecord association subroots" do
+            assert_equal @three_tree[@a], @a.children.hash_tree(limit_depth: 2)
+          end
+
+          it "no limit from chained activerecord association subroots" do
+            assert_equal @full_tree[@a], @a.children.hash_tree
+          end
+
+          it "limit_depth 3 from b.parent" do
+            assert_equal @three_tree.slice(@a), @b.parent.hash_tree(limit_depth: 3)
+          end
+
+          it "no limit_depth from b.parent" do
+            assert_equal @full_tree.slice(@a), @b.parent.hash_tree
+          end
+
+          it "no limit_depth from c.parent" do
+            assert_equal @full_tree[@a].slice(@b), @c1.parent.hash_tree
+          end
+        end
+      end
+
+      it "finds_by_path for very deep trees" do
+        path = (1..20).to_a.map { |ea| ea.to_s }
+        subject = @tag_class.find_or_create_by_path(path)
+        assert_equal path, subject.ancestry_path
+        assert_equal subject, @tag_class.find_by_path(path)
+        root = subject.root
+        assert_equal subject, root.find_by_path(path[1..-1])
+      end
+
+      describe "DOT rendering" do
+        it "should render for an empty scope" do
+          assert_equal "digraph G {\n}\n", @tag_class.to_dot_digraph(@tag_class.where("0=1"))
+        end
+
+        it "should render for an empty scope" do
+          @tag_class.find_or_create_by_path(%w[a b1 c1])
+          @tag_class.find_or_create_by_path(%w[a b2 c2])
+          @tag_class.find_or_create_by_path(%w[a b2 c3])
+          a, b1, b2, c1, c2, c3 = %w[a b1 b2 c1 c2 c3].map { |ea| @tag_class.where(name: ea).first.id }
+          dot = @tag_class.roots.first.to_dot_digraph
+
+          graph = <<~DOT
+            digraph G {
+              "#{a}" [label="a"]
+              "#{a}" -> "#{b1}"
+              "#{b1}" [label="b1"]
+              "#{a}" -> "#{b2}"
+              "#{b2}" [label="b2"]
+              "#{b1}" -> "#{c1}"
+              "#{c1}" [label="c1"]
+              "#{b2}" -> "#{c2}"
+              "#{c2}" [label="c2"]
+              "#{b2}" -> "#{c3}"
+              "#{c3}" [label="c3"]
+            }
+          DOT
+
+          assert_equal(graph, dot)
+        end
+      end
+    end
+  end
+end
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 7aff03c..c70dd25 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -1,53 +1,61 @@
-require 'erb'
-require 'active_record'
-require 'with_advisory_lock'
-require 'tmpdir'
-require 'securerandom'
-require 'minitest'
-require 'minitest/autorun'
+require "erb"
+require "active_record"
+require "with_advisory_lock"
+require "tmpdir"
+require "securerandom"
+require "minitest"
+require "minitest/autorun"
+require "database_cleaner"
 
 ActiveRecord::Base.configurations = {
   default_env: {
-    url: ENV.fetch('DATABASE_URL', "sqlite3://#{Dir.tmpdir}/#{SecureRandom.hex}.sqlite3"),
-    properties: { allowPublicKeyRetrieval: true } # for JRuby madness
+    url: ENV.fetch("DATABASE_URL", "sqlite3://#{Dir.tmpdir}/#{SecureRandom.hex}.sqlite3"),
+    properties: {allowPublicKeyRetrieval: true} # for JRuby madness
   }
 }
 
-ENV['WITH_ADVISORY_LOCK_PREFIX'] ||= SecureRandom.hex
+ENV["WITH_ADVISORY_LOCK_PREFIX"] ||= SecureRandom.hex
 
 ActiveRecord::Base.establish_connection
 
 def env_db
   @env_db ||= if ActiveRecord::Base.respond_to?(:connection_db_config)
-                ActiveRecord::Base.connection_db_config.adapter
-              else
-                ActiveRecord::Base.connection_config[:adapter]
-              end.to_sym
+    ActiveRecord::Base.connection_db_config.adapter
+  else
+    ActiveRecord::Base.connection_config[:adapter]
+  end.to_sym
 end
 
 ActiveRecord::Migration.verbose = false
-ActiveRecord::Base.table_name_prefix = ENV['DB_PREFIX'].to_s
-ActiveRecord::Base.table_name_suffix = ENV['DB_SUFFIX'].to_s
+ActiveRecord::Base.table_name_prefix = ENV["DB_PREFIX"].to_s
+ActiveRecord::Base.table_name_suffix = ENV["DB_SUFFIX"].to_s
 ActiveRecord::Base.establish_connection
 
-def env_db
-  @env_db ||= if ActiveRecord::Base.respond_to?(:connection_db_config)
-                ActiveRecord::Base.connection_db_config.adapter
-              else
-                ActiveRecord::Base.connection_config[:adapter]
-              end.to_sym
-end
-
 # Use in specs to skip some tests
 def sqlite?
   env_db == :sqlite3
 end
 
-ENV['WITH_ADVISORY_LOCK_PREFIX'] ||= SecureRandom.hex
+ENV["WITH_ADVISORY_LOCK_PREFIX"] ||= SecureRandom.hex
 
 ActiveRecord::Base.connection.recreate_database("closure_tree_test") unless sqlite?
 puts "Testing with #{env_db} database, ActiveRecord #{ActiveRecord.gem_version} and #{RUBY_ENGINE} #{RUBY_ENGINE_VERSION} as #{RUBY_VERSION}"
 
-require 'closure_tree'
-require_relative '../spec/support/schema'
-require_relative '../spec/support/models'
\ No newline at end of file
+DatabaseCleaner.strategy = :transaction
+
+class MiniTest::Spec
+  before :each do
+    ENV["FLOCK_DIR"] = Dir.mktmpdir
+    DatabaseCleaner.start
+  end
+
+  after :each do
+    FileUtils.remove_entry_secure ENV["FLOCK_DIR"]
+    DatabaseCleaner.clean
+  end
+end
+
+require "closure_tree"
+require_relative "../spec/support/schema"
+require_relative "../spec/support/models"
+require "support/tag_examples"