Module: Exwiw::DetermineTableProcessingOrder
- Defined in:
- lib/exwiw/determine_table_processing_order.rb
Class Method Summary collapse
-
.compute_table_dependencies(table) ⇒ Object
The belongs_to target table names of ‘table`.
-
.run(tables, logger: nil) ⇒ Array<String>
Sorted table names.
Class Method Details
.compute_table_dependencies(table) ⇒ Object
The belongs_to target table names of ‘table`. A polymorphic belongs_to is expanded into one entry per concrete target by schema generation, so each entry is a plain table name here.
66 67 68 |
# File 'lib/exwiw/determine_table_processing_order.rb', line 66 def compute_table_dependencies(table) table.belongs_tos.map(&:table_name) end |
.run(tables, logger: nil) ⇒ Array<String>
Returns sorted table names.
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 |
# File 'lib/exwiw/determine_table_processing_order.rb', line 12 def run(tables, logger: nil) return tables.map(&:name) if tables.size < 2 ordered_table_names = [] ordered = Set.new table_by_name = tables.each_with_object({}) do |table, acc| acc[table.name] = table end # Only belongs_to relations whose target is also in this run constrain the # order. A belongs_to pointing at a table that is not being processed here # — e.g. an embedded MongoDB collection (masked through its parent, never # dumped on its own) or any table excluded from the run — is not something # we can or need to order against, so it must never block resolution. # Without this, such a dependency would stay unresolved forever and # masquerade as a circular dependency, freezing every table that # (transitively) references it. present_names = table_by_name.keys.to_set loop do break if table_by_name.empty? resolvable = table_by_name.values.select do |table| unresolved_dependencies(table, present_names, ordered).empty? end if resolvable.empty? # No table has all its (in-run) dependencies satisfied, yet tables # remain: the belongs_to graph has a genuine cycle and no strict # topological order exists. Rather than aborting the whole export, break # the cycle by emitting one cycle member; see pick_cycle_victim for how # the member is chosen. Warn so the dropped constraint is visible. victim = pick_cycle_victim(table_by_name.values, present_names, ordered) warn_cycle_break(logger, victim, unresolved_dependencies(victim, present_names, ordered)) resolvable = [victim] end # In the normal (acyclic) path, emit every currently-resolvable table in # insertion order — preserving the historical ordering the snapshot specs # depend on. The cycle-break path emits exactly its single chosen victim. resolvable.each do |table| ordered_table_names << table.name ordered << table.name table_by_name.delete(table.name) end end ordered_table_names end |