Skip to content

Commit

Permalink
Scope for leaves
Browse files Browse the repository at this point in the history
  • Loading branch information
antstorm committed Dec 5, 2015
1 parent 2fa4381 commit 15aef88
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 2 deletions.
30 changes: 30 additions & 0 deletions lib/ancestry/class_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ def scope_depth depth_options, depth
end
end

# Scope that returns all the leaves
def leaves
id_column = "#{table_name}.id"
parent_ancestry = sql_concat("#{table_name}.#{ancestry_column}", "'/'", id_column)

joins("LEFT JOIN #{table_name} AS c ON c.#{ancestry_column} = #{sql_cast_as_text(id_column)} OR c.#{ancestry_column} = #{parent_ancestry}").
group(id_column).
having('COUNT(c.id) = 0')
end

# Orphan strategy writer
def orphan_strategy= orphan_strategy
# Check value of orphan strategy, only rootify, adopt, restrict or destroy is allowed
Expand Down Expand Up @@ -197,5 +207,25 @@ def rebuild_depth_cache!
end
end
end

private

def sql_concat *parts
if ActiveRecord::Base.connection.adapter_name.downcase == 'sqlite'
parts.join(' || ')
else
"CONCAT(#{parts.join(', ')})"
end
end

def sql_cast_as_text column
text_type = if ActiveRecord::Base.connection.adapter_name.downcase == 'mysql'
'CHAR'
else
'TEXT'
end

"CAST(#{column} AS #{text_type})"
end
end
end
2 changes: 1 addition & 1 deletion lib/ancestry/has_ancestry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,4 @@ class << ActiveRecord::Base
alias_method :acts_as_tree, :has_ancestry
end
end
end
end
33 changes: 33 additions & 0 deletions test/concerns/class_methods_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require_relative '../environment'

class ClassMethodsTest < ActiveSupport::TestCase
def test_sql_concat
AncestryTestDatabase.with_model do |model|
result = model.send(:sql_concat, 'table_name.id', "'/'")

case ActiveRecord::Base.connection.adapter_name.downcase.to_sym
when :sqlite
assert_equal result, "table_name.id || '/'"
when :mysql
assert_equal result, "CONCAT(table_name.id, '/')"
when :postgresql
assert_equal result, "CONCAT(table_name.id, '/')"
end
end
end

def text_sql_cast_as_text
AncestryTestDatabase.with_model do |model|
result = model.send(:sql_cast_as_text, 'table_name.id')

case ActiveRecord::Base.connection.adapter_name.downcase.to_sym
when :sqlite
assert_equal result, 'CAST(table_name.id AS TEXT)'
when :mysql
assert_equal result, 'CAST(table_name.id AS CHAR)'
when :postgresql
assert_equal result, 'CAST(table_name.id AS TEXT)'
end
end
end
end
5 changes: 4 additions & 1 deletion test/concerns/scopes_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ def test_scopes
# Roots assertion
assert_equal roots.map(&:first), model.roots.to_a

# Leaves assertion
assert_equal model.all.select(&:is_childless?), model.leaves.order(:id).to_a

model.all.each do |test_node|
# Assertions for ancestors_of named scope
assert_equal test_node.ancestors.to_a, model.ancestors_of(test_node).to_a
Expand Down Expand Up @@ -66,4 +69,4 @@ def test_scoping_in_callbacks
assert child = parent.children.create
end
end
end
end

0 comments on commit 15aef88

Please sign in to comment.