Module: DagMe::Model

Extended by:
ActiveSupport::Concern
Defined in:
lib/dag_me/model.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.attach(model, config) ⇒ Object

Builds the per-dag machinery: edge/paths constants and associations. Called by the macro once per dag_me declaration, so a model hosting several named graphs gets one full set each.



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
# File 'lib/dag_me/model.rb', line 10

def self.attach(model, config)
  parent_fk = config.composite_pk? ? config.edge_parent_columns.map(&:to_sym) : :parent_id
  child_fk = config.composite_pk? ? config.edge_child_columns.map(&:to_sym) : :child_id
  pk_opt = config.composite_pk? ? { primary_key: config.node_pk_columns.map(&:to_sym) } : {}

  child_edges = config.association_name('dag_child_edges')
  parent_edges = config.association_name('dag_parent_edges')

  edge_class = Class.new(ActiveRecord::Base)
  model.const_set(config.edge_class_name, edge_class)
  edge_class.table_name = config.edge_table
  edge_class.belongs_to :parent, class_name: model.name, foreign_key: parent_fk,
                                 inverse_of: child_edges, **pk_opt
  edge_class.belongs_to :child, class_name: model.name, foreign_key: child_fk,
                                inverse_of: parent_edges, **pk_opt

  if config.closure?
    paths_class = Class.new(ActiveRecord::Base)
    model.const_set(config.paths_class_name, paths_class)
    paths_class.table_name = config.paths_table
    paths_class.primary_key = config.paths_ancestor_columns + config.paths_descendant_columns
  end

  model.has_many child_edges, class_name: "#{model.name}::#{config.edge_class_name}",
                              foreign_key: parent_fk, inverse_of: :parent, dependent: nil, **pk_opt
  model.has_many config.association_name('children'), through: child_edges, source: :child
  model.has_many parent_edges, class_name: "#{model.name}::#{config.edge_class_name}",
                               foreign_key: child_fk, inverse_of: :child, dependent: nil, **pk_opt
  model.has_many config.association_name('parents'), through: parent_edges, source: :parent
end

Instance Method Details

#add_child(node, dag: nil) ⇒ Object



90
91
92
93
94
# File 'lib/dag_me/model.rb', line 90

def add_child(node, dag: nil)
  config = self.class.dag_config_for(dag)
  DagMe.translate_errors { config.edge_class.create!(parent: self, child: node) }
  node
end

#add_parent(node, dag: nil) ⇒ Object



96
97
98
99
100
# File 'lib/dag_me/model.rb', line 96

def add_parent(node, dag: nil)
  config = self.class.dag_config_for(dag)
  DagMe.translate_errors { config.edge_class.create!(parent: node, child: self) }
  node
end

#ancestor_of?(node, dag: nil) ⇒ Boolean

Returns:

  • (Boolean)


134
135
136
# File 'lib/dag_me/model.rb', line 134

def ancestor_of?(node, dag: nil)
  self.class.dag(dag).adapter.reachable?(self, node)
end

#ancestors(dag: nil) ⇒ Object



118
119
120
# File 'lib/dag_me/model.rb', line 118

def ancestors(dag: nil)
  self.class.dag(dag).adapter.ancestors(self)
end

#descendant_of?(node, dag: nil) ⇒ Boolean

Returns:

  • (Boolean)


138
139
140
# File 'lib/dag_me/model.rb', line 138

def descendant_of?(node, dag: nil)
  self.class.dag(dag).adapter.reachable?(node, self)
end

#descendants(dag: nil) ⇒ Object



122
123
124
# File 'lib/dag_me/model.rb', line 122

def descendants(dag: nil)
  self.class.dag(dag).adapter.descendants(self)
end

#leaf?(dag: nil) ⇒ Boolean

Returns:

  • (Boolean)


157
158
159
160
# File 'lib/dag_me/model.rb', line 157

def leaf?(dag: nil)
  config = self.class.dag_config_for(dag)
  !public_send(config.association_name('dag_child_edges')).exists?
end

#remove_child(node, dag: nil) ⇒ Object



102
103
104
105
106
107
108
# File 'lib/dag_me/model.rb', line 102

def remove_child(node, dag: nil)
  config = self.class.dag_config_for(dag)
  DagMe.translate_errors do
    config.edge_class.where(dag_edge_key(config, parent: self, child: node)).delete_all
  end
  node
end

#remove_parent(node, dag: nil) ⇒ Object



110
111
112
113
114
115
116
# File 'lib/dag_me/model.rb', line 110

def remove_parent(node, dag: nil)
  config = self.class.dag_config_for(dag)
  DagMe.translate_errors do
    config.edge_class.where(dag_edge_key(config, parent: node, child: self)).delete_all
  end
  node
end

#root?(dag: nil) ⇒ Boolean

Returns:

  • (Boolean)


152
153
154
155
# File 'lib/dag_me/model.rb', line 152

def root?(dag: nil)
  config = self.class.dag_config_for(dag)
  !public_send(config.association_name('dag_parent_edges')).exists?
end

#self_and_ancestors(dag: nil) ⇒ Object



126
127
128
# File 'lib/dag_me/model.rb', line 126

def self_and_ancestors(dag: nil)
  self.class.dag(dag).adapter.self_and_ancestors(self)
end

#self_and_descendants(dag: nil) ⇒ Object



130
131
132
# File 'lib/dag_me/model.rb', line 130

def self_and_descendants(dag: nil)
  self.class.dag(dag).adapter.self_and_descendants(self)
end

#subgraph(dag: nil) ⇒ Object

The induced subgraph rooted here: this node, its descendants, and the edges among them.



144
145
146
# File 'lib/dag_me/model.rb', line 144

def subgraph(dag: nil)
  self_and_descendants(dag:)
end

#subgraph_edges(dag: nil) ⇒ Object



148
149
150
# File 'lib/dag_me/model.rb', line 148

def subgraph_edges(dag: nil)
  self.class.dag(dag).edges_among(self_and_descendants(dag:))
end