Module: ClosureTree::Finders::ClassMethods

Defined in:
lib/closure_tree/finders.rb

Instance Method Summary collapse

Instance Method Details

#ct_scoped_attributes(scope, attributes, target_table = table_name) ⇒ Object



109
110
111
112
113
# File 'lib/closure_tree/finders.rb', line 109

def ct_scoped_attributes(scope, attributes, target_table = table_name)
  attributes.inject(scope) do |scope, pair|
    scope.where("#{target_table}.#{pair.first}" => pair.last)
  end
end

#find_all_by_generation(generation_level) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/closure_tree/finders.rb', line 89

def find_all_by_generation(generation_level)
  s = joins(<<-SQL)
    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



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

def find_by_path(path, attributes = {}, parent_id = nil)
  path = path.is_a?(Enumerable) ? path.dup : [path]
  scope = where(_ct.name_sym => path.pop).readonly(false)
  scope = ct_scoped_attributes(scope, attributes)
  last_joined_table = _ct.table_name
  path.reverse.each_with_index do |ea, idx|
    next_joined_table = "p#{idx}"
    scope = scope.joins(<<-SQL)
      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 = scope.where("#{next_joined_table}.#{_ct.name_column}" => ea)
    scope = ct_scoped_attributes(scope, attributes, next_joined_table)
    last_joined_table = next_joined_table
  end
  scope = scope.where("#{last_joined_table}.#{_ct.parent_column_name}" => parent_id)
  scope.first
end

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

Find or create nodes such that the ancestry_path is path



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

def find_or_create_by_path(path, attributes = {})
  find_by_path(path, attributes) || begin
    subpath = path.dup
    root_name = subpath.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):
      attrs = attributes.merge(_ct.name_sym => root_name)
      root = roots.where(attrs).first || roots.create!(attrs)
      root.find_or_create_by_path(subpath, attributes)
    end
  end
end

#leavesObject



68
69
70
71
72
73
74
75
76
77
78
# File 'lib/closure_tree/finders.rb', line 68

def leaves
  s = joins(<<-SQL)
    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.



64
65
66
# File 'lib/closure_tree/finders.rb', line 64

def root
  roots.first
end

#rootsObject



59
60
61
# File 'lib/closure_tree/finders.rb', line 59

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

#with_ancestor(*ancestors) ⇒ Object



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

def with_ancestor(*ancestors)
  ancestor_ids = ancestors.map { |ea| ea.is_a?(ActiveRecord::Base) ? ea._ct_id : ea }
  scope = ancestor_ids.blank? ? scoped : 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



51
52
53
54
55
56
57
# File 'lib/closure_tree/finders.rb', line 51

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