Мне удалось сделать это немного по-другому, но это может не сработать в вашей ситуации. Я выполнял рефакторинг существующего кода и не хотел выполнять какую-либо серьезную миграцию базы данных.
Я хотел отдельные листовые и нодовые классы. Оба наследуют от класса дерева.
Я добавил две функции в ClassMethods в vendor/plugins/acts_as_tree/lib/active_record/acts/tree.rb
:
# Configuration options are:
#
# * <tt>foreign_key</tt> - specifies the column name to use for tracking of the tree (default: +parent_id+)
# * <tt>order</tt> - makes it possible to sort the children according to this SQL snippet.
# * <tt>counter_cache</tt> - keeps a count in a +children_count+ column if set to +true+ (default: +false+).
# * <tt>leaf_class_name</tt> - leaf class subtype of base tree class
# * <tt>node_class_name</tt> - node class subtype of base tree class
def acts_as_tree_node(options = {})
configuration = { :foreign_key => "parent_id", :order => nil, :counter_cache => nil, :node_class_name => 'Node', :leaf_class_name => 'Leaf' }
configuration.update(options) if options.is_a?(Hash)
belongs_to :parent, :class_name => configuration[:node_class_name], :foreign_key => configuration[:foreign_key], :counter_cache => configuration[:counter_cache]
#has_many :children, :foreign_key => configuration[:foreign_key], :order => configuration[:order], :dependent => :destroy
class_eval <<-EOV
has_many :child_nodes, :class_name => '#{configuration[:node_class_name]}', :foreign_key => "#{configuration[:foreign_key]}", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}}, :dependent => :destroy
has_many :child_leaves, :class_name => '#{configuration[:leaf_class_name]}', :foreign_key => "#{configuration[:foreign_key]}", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}}, :dependent => :destroy
include ActiveRecord::Acts::Tree::InstanceMethods
def self.roots
find(:all, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
end
def self.root
find(:first, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
end
EOV
end
# Configuration options are:
#
# * <tt>foreign_key</tt> - specifies the column name to use for tracking of the tree (default: +parent_id+)
# * <tt>order</tt> - makes it possible to sort the children according to this SQL snippet.
# * <tt>counter_cache</tt> - keeps a count in a +children_count+ column if set to +true+ (default: +false+).
# * <tt>node_class_name</tt> - the class name of the node (subclass of the tree base)
def acts_as_tree_leaf(options = {})
configuration = { :foreign_key => "parent_id", :order => nil, :counter_cache => nil, :node_class_name => 'Node' }
configuration.update(options) if options.is_a?(Hash)
belongs_to :parent, :class_name => configuration[:node_class_name], :foreign_key => configuration[:foreign_key], :counter_cache => configuration[:counter_cache]
class_eval <<-EOV
include ActiveRecord::Acts::Tree::InstanceMethods
def self.roots
find(:all, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
end
def self.root
find(:first, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
end
EOV
end
Затем в InstanceMethods я просто добавил одну функцию:
# Returns list of children, whether nodes or leaves.
#
# NOTE: Will not return both, because that would take two queries and
# order will not be preserved.
def children
child_leaves.count == 0 ? child_nodes : child_leaves
end
Это что-то вроде хака, но это работает для меня, так как у каждого узла есть все подводные лодки одного типа. Вы можете поиграть с функцией children
, чтобы получить другое поведение, например:
def children
child_nodes | child_leaves
end
Но все равно требуется дополнительный запрос, и вы теряете свой заказ, границы и прочее.
Наконец, в моем классе Node у меня есть
acts_as_tree_node :node_class_name => 'NodeMatrix', :leaf_class_name => 'LeafMatrix'
и в моем классе Leaf, следующее:
acts_as_tree_leaf :node_class_name => 'NodeMatrix'
Оба они наследуются от TreeMatrix, который является чисто виртуальным (на самом деле ничего не создается как TreeMatrix, это просто базовый класс).
Опять же, это очень зависит от приложения. Но это дает вам представление о том, как вы можете изменить acts_as_tree.