Module: ActsAsDAG::ActMethod

Defined in:
lib/acts_as_dag/acts_as_dag.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.descendant_table_entriesObject

Returns a relation scoping to only descendant table entries table entries that match the link conditions


51
52
53
# File 'lib/acts_as_dag/acts_as_dag.rb', line 51

def self.descendant_table_entries
  descendant_class.where(acts_as_dag_options[:link_conditions])
end

Returns a relation scoping to only link table entries that match the link conditions


46
47
48
# File 'lib/acts_as_dag/acts_as_dag.rb', line 46

def self.link_table_entries
  link_class.where(acts_as_dag_options[:link_conditions])
end

Instance Method Details

#acts_as_dag(options = {}) ⇒ Object


4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/acts_as_dag/acts_as_dag.rb', line 4

def acts_as_dag(options = {})
  class_attribute :acts_as_dag_options
  options.assert_valid_keys :link_class, :descendant_class, :link_table, :descendant_table, :link_conditions
  options.reverse_merge!(
    :link_class => "#{self.name}Link",
    :link_table => "acts_as_dag_links",
    :descendant_class => "#{self.name}Descendant",
    :descendant_table => "acts_as_dag_descendants",
    :link_conditions => {:category_type => self.name})
  self.acts_as_dag_options = options

  # Create Link and Descendant Classes
  class_eval <<-EOV
    class ::#{options[:link_class]} < ActsAsDAG::AbstractLink
      self.table_name = '#{options[:link_table]}'
      belongs_to :parent,     :class_name => '#{self.name}', :foreign_key => :parent_id, :inverse_of => :child_links
      belongs_to :child,      :class_name => '#{self.name}', :foreign_key => :child_id, :inverse_of => :parent_links

      after_save Proc.new {|link| HelperMethods.update_transitive_closure_for_new_link(link) }
      after_destroy Proc.new {|link| HelperMethods.update_transitive_closure_for_destroyed_link(link) }

      def node_class; #{self.name} end
    end

    class ::#{options[:descendant_class]} < ActsAsDAG::AbstractDescendant
      self.table_name = '#{options[:descendant_table]}'
      belongs_to :ancestor,   :class_name => '#{self.name}', :foreign_key => :ancestor_id
      belongs_to :descendant, :class_name => '#{self.name}', :foreign_key => :descendant_id

      def node_class; #{self.name} end
    end

    def self.link_class
      ::#{options[:link_class]}
    end

    def self.descendant_class
      ::#{options[:descendant_class]}
    end
  EOV

  # Returns a relation scoping to only link table entries that match the link conditions
  def self.link_table_entries
    link_class.where(acts_as_dag_options[:link_conditions])
  end

  # Returns a relation scoping to only descendant table entries table entries that match the link conditions
  def self.descendant_table_entries
    descendant_class.where(acts_as_dag_options[:link_conditions])
  end

  # Ancestors and descendants returned *include* self, e.g. A's descendants are [A,B,C,D]
  # Ancestors must always be returned in order of most distant to least
  # Descendants must always be returned in order of least distant to most
  # NOTE: Rails 4.0.0 currently ignores the order clause when eager loading, so results may not be returned in the correct order
  # NOTE: multiple instances of the same descendant/ancestor may be returned if there are multiple paths from ancestor to descendant
  #   A
  #  / \
  # B   C
  #  \ /
  #   D
  #
  has_many :ancestors,        :through => :ancestor_links, :source => :ancestor
  has_many :descendants,      :through => :descendant_links, :source => :descendant

  has_many :path,             :through => :path_links, :source => :ancestor
  has_many :subtree,          :through => :subtree_links, :source => :descendant

  has_many :ancestor_links,   lambda { where(options[:link_conditions]).where("ancestor_id != descendant_id").order("distance DESC") }, :class_name => descendant_class, :foreign_key => 'descendant_id'
  has_many :descendant_links, lambda { where(options[:link_conditions]).where("descendant_id != ancestor_id").order("distance ASC") }, :class_name => descendant_class, :foreign_key => 'ancestor_id'

  has_many :path_links,       lambda { where(options[:link_conditions]).order("distance DESC") }, :class_name => descendant_class, :foreign_key => 'descendant_id', :dependent => :delete_all
  has_many :subtree_links,    lambda { where(options[:link_conditions]).order("distance ASC") }, :class_name => descendant_class, :foreign_key => 'ancestor_id', :dependent => :delete_all

  has_many :parents,          :through => :parent_links, :source => :parent
  has_many :children,         :through => :child_links, :source => :child

  has_many :parent_links,     lambda { where options[:link_conditions] }, :class_name => link_class, :foreign_key => 'child_id', :dependent => :delete_all, :inverse_of => :child
  has_many :child_links,      lambda { where options[:link_conditions] }, :class_name => link_class, :foreign_key => 'parent_id', :dependent => :delete_all, :inverse_of => :parent

  # NOTE: Use select to prevent ActiveRecord::ReadOnlyRecord if the returned records are modified
  scope :roots,               lambda { joins(:parent_links).where(link_class.table_name => {:parent_id => nil}) }
  scope :leaves,               lambda { joins("LEFT OUTER JOIN #{link_class.table_name} ON #{table_name}.id = parent_id").where(link_class.table_name => {:child_id => nil}).uniq }
  scope :children,            lambda { joins(:parent_links).where.not(link_class.table_name => {:parent_id => nil}).uniq }
  scope :parent_records,      lambda { joins(:child_links).where.not(link_class.table_name => {:child_id => nil}).uniq }

  scope :ancestors_of,        lambda {|record| joins(:descendant_links).where("descendant_id = ?", record) }
  scope :descendants_of,      lambda {|record| joins(:ancestor_links).where("ancestor_id = ?", record) }
  scope :path_of,             lambda {|record| joins(:subtree_links).where("descendant_id = ?", record) }
  scope :subtree_of,          lambda {|record| joins(:path_links).where("ancestor_id = ?", record) }

  after_create :initialize_dag

  extend ActsAsDAG::ClassMethods
  include ActsAsDAG::InstanceMethods
  extend ActsAsDAG::Deprecated::ClassMethods
  include ActsAsDAG::Deprecated::InstanceMethods
end