Module: DagMe::TaskHelpers
- Defined in:
- lib/dag_me/task_helpers.rb
Overview
Backs the dag_me:status rake task: a doctor-style report of every
dag_me model's database objects and closure health.
Constant Summary collapse
- COLORS =
{ green: "\e[32m", red: "\e[31m", yellow: "\e[33m", cyan: "\e[36m", reset: "\e[0m" }.freeze
Class Method Summary collapse
- .bad(text) ⇒ Object
- .closure_check(model, config) ⇒ Object
- .colorize(text, color) ⇒ Object
- .config_report(model, config) ⇒ Object
- .function_checks(model, config) ⇒ Object
- .model_report(model) ⇒ Object
-
.namespace_filter(conn, config) ⇒ Object
Objects for schema-qualified node tables live in that schema regardless of search_path; unqualified ones resolve through it.
- .ok(text) ⇒ Object
- .status(models, io: $stdout) ⇒ Object
- .table_checks(model, config) ⇒ Object
- .trigger_checks(model, config) ⇒ Object
Class Method Details
.bad(text) ⇒ Object
25 26 27 |
# File 'lib/dag_me/task_helpers.rb', line 25 def bad(text) "#{colorize('✗', :red)} #{text}" end |
.closure_check(model, config) ⇒ Object
114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/dag_me/task_helpers.rb', line 114 def closure_check(model, config) return ok('closure: not materialized (recursive_cte)') unless config.closure? return nil unless model.connection.table_exists?(config.paths_table) graph = model.dag(config.name) discrepancies = graph.validate if discrepancies.empty? ok("closure valid (#{graph.edges.count} edges)") else facade = config.default? ? 'Model.dag' : "Model.dag(:#{config.name})" bad("closure diverged: #{discrepancies.length} rows - run #{facade}.rebuild!") end end |
.colorize(text, color) ⇒ Object
17 18 19 |
# File 'lib/dag_me/task_helpers.rb', line 17 def colorize(text, color) "#{COLORS[color]}#{text}#{COLORS[:reset]}" end |
.config_report(model, config) ⇒ Object
42 43 44 45 46 47 48 49 50 51 |
# File 'lib/dag_me/task_helpers.rb', line 42 def config_report(model, config) label = config.default? ? model.name : "#{model.name} [#{config.name}]" lines = ["#{colorize(label, :cyan)} (maintain: #{config.maintain}" \ "#{", scope: #{config.scope_columns.join(', ')}" if config.scope_columns.any?})"] lines.concat(table_checks(model, config)) lines.concat(trigger_checks(model, config)) lines.concat(function_checks(model, config)) lines << closure_check(model, config) "#{lines.compact.join("\n ")}\n" end |
.function_checks(model, config) ⇒ Object
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/dag_me/task_helpers.rb', line 86 def function_checks(model, config) expected = ["#{config.prefix}_lock", "#{config.prefix}_edge_insert_check"] if config.closure? expected.push("#{config.prefix}_edge_insert_apply", "#{config.prefix}_edge_delete_apply", "#{config.prefix}_node_insert", "#{config.prefix}_node_delete", "#{config.prefix}_rebuild_paths", "#{config.prefix}_validate_paths") end expected << "#{config.prefix}_node_update" if config.scope_columns.any? conn = model.connection installed = conn.select_values(<<~SQL) SELECT p.proname FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace WHERE #{namespace_filter(conn, config)} AND p.proname LIKE #{conn.quote("#{config.prefix}\\_%")} SQL (expected - installed).map { |name| bad("function #{name} missing") } .presence || [ok("functions (#{expected.length})")] end |
.model_report(model) ⇒ Object
38 39 40 |
# File 'lib/dag_me/task_helpers.rb', line 38 def model_report(model) model.dag_configs.each_value.map { |config| config_report(model, config) }.join end |
.namespace_filter(conn, config) ⇒ Object
Objects for schema-qualified node tables live in that schema regardless of search_path; unqualified ones resolve through it.
108 109 110 111 112 |
# File 'lib/dag_me/task_helpers.rb', line 108 def namespace_filter(conn, config) return "n.nspname = #{conn.quote(config.schema)}" if config.schema 'n.nspname = ANY (current_schemas(false))' end |
.ok(text) ⇒ Object
21 22 23 |
# File 'lib/dag_me/task_helpers.rb', line 21 def ok(text) "#{colorize('✓', :green)} #{text}" end |
.status(models, io: $stdout) ⇒ Object
29 30 31 32 33 34 35 36 |
# File 'lib/dag_me/task_helpers.rb', line 29 def status(models, io: $stdout) if models.empty? io.puts colorize('No dag_me models found.', :yellow) return end models.each { |model| io.puts model_report(model) } end |
.table_checks(model, config) ⇒ Object
53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/dag_me/task_helpers.rb', line 53 def table_checks(model, config) tables = [config.edge_table] tables << config.paths_table if config.closure? tables.map do |table| if model.connection.table_exists?(table) ok("table #{table}") else bad("table #{table} missing - run the dag_me migration") end end end |
.trigger_checks(model, config) ⇒ Object
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/dag_me/task_helpers.rb', line 65 def trigger_checks(model, config) expected = ["#{config.prefix}_edge_insert_check"] if config.closure? expected.push("#{config.prefix}_edge_insert_apply", "#{config.prefix}_edge_delete_apply", "#{config.prefix}_node_insert", "#{config.prefix}_node_delete") end expected << "#{config.prefix}_node_update" if config.scope_columns.any? conn = model.connection installed = conn.select_values(<<~SQL) SELECT t.tgname FROM pg_trigger t JOIN pg_class c ON c.oid = t.tgrelid JOIN pg_namespace n ON n.oid = c.relnamespace WHERE NOT t.tgisinternal AND #{namespace_filter(conn, config)} AND t.tgname LIKE #{conn.quote("#{config.prefix}\\_%")} SQL (expected - installed).map { |name| bad("trigger #{name} missing") } .presence || [ok("triggers (#{expected.length})")] end |