Created
December 16, 2019 11:05
-
-
Save sfcgeorge/6b28e4f038e50a2e642fc14eacbc07c9 to your computer and use it in GitHub Desktop.
Patch 1 method in Scenic's schema dumper so that views are sorted first alphabetically, and then have any dependencies resolved.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
require 'rails' | |
require 'scenic' | |
module Scenic | |
# @api private | |
module SchemaDumper | |
private | |
MAX_SORT_ATTEMPTS = 500 | |
SCENIC_CHANGED = "Scenic's code has changed, update this patch!" | |
# Some checks to make sure Scenic's relevant code hasn't changed | |
raise SCENIC_CHANGED unless private_method_defined?( | |
:dumpable_views_in_database | |
) | |
raise SCENIC_CHANGED unless instance_method( | |
:dumpable_views_in_database | |
).source_location.first.split('/').last == 'schema_dumper.rb' | |
raise SCENIC_CHANGED unless instance_method( | |
:dumpable_views_in_database | |
).source_location.last == 24 | |
def dumpable_views_in_database | |
@dumpable_views_in_database ||= begin | |
views = Scenic.database.views.reject { |view| ignored?(view.name) } | |
# First alphabetize so the initial order is deterministic | |
alphabetical_views = views.sort_by(&:name) | |
# We're gonna do a really dumb sort | |
n = 0 | |
same = false | |
previously_sorted_views = alphabetical_views | |
# Keep looping until the order stops changing, AKA they're all sorted | |
until same || n > MAX_SORT_ATTEMPTS | |
n += 1 | |
sorted_views = previously_sorted_views.dup | |
previously_sorted_views.each_with_index do |view, i| | |
next if i.zero? | |
prev_views = previously_sorted_views[0...i] | |
# Match the view name on its own, not a column name | |
# boundary character, view name, space or close parenthesis | |
matcher = Regexp.new("\\b#{view.name}\(\s|\\)\)", true) | |
next unless prev_views.any? do |prev_view| | |
prev_view.definition.match?(matcher) | |
end | |
prior_view = previously_sorted_views[i - 1] | |
# Swap | |
sorted_views[i - 1] = view | |
sorted_views[i] = prior_view | |
# Let's not get smart with the sort algo perf, we'll just repeat | |
break | |
end | |
same = sorted_views == previously_sorted_views | |
previously_sorted_views = sorted_views | |
end | |
# If there's a cyclic dependency, AKA 2 views reference each other | |
# (or the above Regexp is faulty) then the sort will keep swapping views | |
# so we fall back to whatever order the database spits out. | |
if n > MAX_SORT_ATTEMPTS | |
puts 'View sort failed, order non-deterministic. Using default order' | |
views | |
else | |
previously_sorted_views | |
end | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment