Module: Edge::Forest::ClassMethods

Defined in:
lib/edge/forest.rb

Instance Method Summary collapse

Instance Method Details

#find_forestObject

Finds entire forest and preloads all associations. It can be used at the end of an ActiveRecord finder chain.

Example:

# loads all locations
Location.find_forest

# loads all nodes with matching names and all there descendants
Category.where(:name => %w{clothing books electronics}).find_forest


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
# File 'lib/edge/forest.rb', line 49

def find_forest
  manager = recursive_manager.project(Arel.star)
  manager.order(forest_order) if forest_order

  bind_values = current_scope ? current_scope.bind_values : []
  records = find_by_sql manager.to_sql, bind_values

  records_by_id = records.each_with_object({}) { |r, h| h[r.id] = r }

  # Set all children associations to an empty array
  records.each do |r|
    children_association = r.association(:children)
    children_association.target = []
  end

  top_level_records = []

  records.each do |r|
    parent = records_by_id[r[forest_foreign_key]]
    if parent
      r.association(:parent).target = parent
      parent.association(:children).target.push(r)
    else
      top_level_records.push(r)
    end
  end

  top_level_records
end

#find_tree(id_or_ids) ⇒ Object

Finds an a tree or trees by id.

If any requested ids are not found it raises ActiveRecord::RecordNotFound.



83
84
85
86
87
88
89
90
91
92
# File 'lib/edge/forest.rb', line 83

def find_tree(id_or_ids)
  trees = where(:id => id_or_ids).find_forest
  if id_or_ids.kind_of?(Array)
    raise ActiveRecord::RecordNotFound unless trees.size == id_or_ids.size
    trees
  else
    raise ActiveRecord::RecordNotFound if trees.empty?
    trees.first
  end
end

#with_descendantsObject

Returns a new scope that includes previously scoped records and their descendants by subsuming the previous scope into a subquery

Only where scopes can precede this in a scope chain



97
98
99
100
101
102
# File 'lib/edge/forest.rb', line 97

def with_descendants
  manager = recursive_manager.project(arel_table[:id])
  scope = unscoped.where(arel_table[:id].in manager)
  scope.bind_values = current_scope.bind_values if current_scope
  scope
end