Module: ClosureTree::Finders::ClassMethods

Defined in:
lib/closure_tree/finders.rb

Instance Method Summary collapse

Instance Method Details

#_ct_allObject

Fix deprecation warning:


56
57
58
# File 'lib/closure_tree/finders.rb', line 56

def _ct_all
  (ActiveRecord::VERSION::MAJOR >= 4) ? all : scoped
end

#find_all_by_generation(generation_level) ⇒ Object


98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/closure_tree/finders.rb', line 98

def find_all_by_generation(generation_level)
  s = joins(<<-SQL.strip_heredoc)
    INNER JOIN (
      SELECT #{primary_key} as root_id
      FROM #{_ct.quoted_table_name}
      WHERE #{_ct.quoted_parent_column_name} IS NULL
    ) AS roots ON (1 = 1)
    INNER JOIN (
      SELECT ancestor_id, descendant_id
      FROM #{_ct.quoted_hierarchy_table_name}
      GROUP BY 1, 2
      HAVING MAX(generations) = #{generation_level.to_i}
    ) AS descendants ON (
      #{_ct.quoted_table_name}.#{primary_key} = descendants.descendant_id
      AND roots.root_id = descendants.ancestor_id
    )
  SQL
  _ct.scope_with_order(s)
end

#find_by_path(path, attributes = {}, parent_id = nil) ⇒ Object

Find the node whose ancestry_path is path


119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/closure_tree/finders.rb', line 119

def find_by_path(path, attributes = {}, parent_id = nil)
  path = _ct.build_ancestry_attr_path(path, attributes)
  if path.size > _ct.max_join_tables
    return _ct.find_by_large_path(path, attributes, parent_id)
  end
  scope = where(path.pop)
  last_joined_table = _ct.table_name
  path.reverse.each_with_index do |ea, idx|
    next_joined_table = "p#{idx}"
    scope = scope.joins(<<-SQL.strip_heredoc)
      INNER JOIN #{_ct.quoted_table_name} AS #{next_joined_table}
        ON #{next_joined_table}.#{_ct.quoted_id_column_name} =
 #{connection.quote_table_name(last_joined_table)}.#{_ct.quoted_parent_column_name}
    SQL
    scope = _ct.scoped_attributes(scope, ea, next_joined_table)
    last_joined_table = next_joined_table
  end
  scope.where("#{last_joined_table}.#{_ct.parent_column_name}" => parent_id).readonly(false).first
end

#find_or_create_by_path(path, attributes = {}) ⇒ Object

Find or create nodes such that the ancestry_path is path


140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/closure_tree/finders.rb', line 140

def find_or_create_by_path(path, attributes = {})
  attr_path = _ct.build_ancestry_attr_path(path, attributes)
  find_by_path(attr_path) || begin
    root_attrs = attr_path.shift
    _ct.with_advisory_lock do
      # shenanigans because find_or_create can't infer that we want the same class as this:
      # Note that roots will already be constrained to this subclass (in the case of polymorphism):
      root = roots.where(root_attrs).first || _ct.create!(self, root_attrs)
      root.find_or_create_by_path(attr_path)
    end
  end
end

#leavesObject


77
78
79
80
81
82
83
84
85
86
87
# File 'lib/closure_tree/finders.rb', line 77

def leaves
  s = joins(<<-SQL.strip_heredoc)
    INNER JOIN (
      SELECT ancestor_id
      FROM #{_ct.quoted_hierarchy_table_name}
      GROUP BY 1
      HAVING MAX(#{_ct.quoted_hierarchy_table_name}.generations) = 0
    ) AS leaves ON (#{_ct.quoted_table_name}.#{primary_key} = leaves.ancestor_id)
  SQL
  _ct.scope_with_order(s.readonly(false))
end

#rootObject

Returns an arbitrary node that has no parents.


73
74
75
# File 'lib/closure_tree/finders.rb', line 73

def root
  roots.first
end

#rootsObject


68
69
70
# File 'lib/closure_tree/finders.rb', line 68

def roots
  _ct.scope_with_order(where(_ct.parent_column_name => nil))
end

#with_ancestor(*ancestors) ⇒ Object


89
90
91
92
93
94
95
96
# File 'lib/closure_tree/finders.rb', line 89

def with_ancestor(*ancestors)
  ancestor_ids = ancestors.map { |ea| ea.is_a?(ActiveRecord::Base) ? ea._ct_id : ea }
  scope = ancestor_ids.blank? ? _ct_all : joins(:ancestor_hierarchies).
    where("#{_ct.hierarchy_table_name}.ancestor_id" => ancestor_ids).
    where("#{_ct.hierarchy_table_name}.generations > 0").
    readonly(false)
  _ct.scope_with_order(scope)
end

#without(instance) ⇒ Object


60
61
62
63
64
65
66
# File 'lib/closure_tree/finders.rb', line 60

def without(instance)
  if instance.new_record?
    all
  else
    where(["#{_ct.quoted_table_name}.#{_ct.quoted_id_column_name} != ?", instance.id])
  end
end