Class: Exwiw::SchemaCheck

Inherits:
Object
  • Object
show all
Defined in:
lib/exwiw/schema_check.rb

Overview

Reports how the committed schema config differs from what the application would generate now, plus the columns still waiting for a masking decision. Regenerating into a copy rather than over the config directory lets it run on a working tree it must not modify (CI).

The diff/report side is source-agnostic (it reads JSON config files); what varies per schema source is only how to regenerate, injected as regenerator — a callable that receives a directory pre-seeded with a copy of the committed config and rewrites it the way generate! + tidy! would. models: remains as the ActiveRecord shorthand for it.

Constant Summary collapse

CATEGORIES =
%w[
  added_tables added_columns removed_tables removed_columns changed_tables needs_mask_decision
].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(schema_dir:, models: nil, regenerator: nil) ⇒ SchemaCheck

Returns a new instance of SchemaCheck.



55
56
57
58
59
60
61
62
# File 'lib/exwiw/schema_check.rb', line 55

def initialize(schema_dir:, models: nil, regenerator: nil)
  if models.nil? && regenerator.nil?
    raise ArgumentError, "SchemaCheck requires either models: or regenerator:"
  end

  @schema_dir = schema_dir
  @regenerator = regenerator || active_record_regenerator(models)
end

Class Method Details

.clean?(report) ⇒ Boolean

Whether the report requires someone to act: the config is out of date, or a column's masking has not been decided yet.

Returns:

  • (Boolean)


80
81
82
# File 'lib/exwiw/schema_check.rb', line 80

def self.clean?(report)
  CATEGORIES.all? { |category| report.fetch(category, []).empty? }
end

.from_mongoid_application(schema_dir:) ⇒ Object

The Mongoid counterpart. Mongoid.models registers only the base class of an inheritance hierarchy, which is exactly what MongoidSchemaGenerator expects (it expands descendants itself), so the same source the generate task introspects is used here.



33
34
35
36
# File 'lib/exwiw/schema_check.rb', line 33

def self.from_mongoid_application(schema_dir:)
  Rails.application.eager_load!
  new(schema_dir: schema_dir, regenerator: mongoid_regenerator(::Mongoid.models))
end

.from_rails_application(schema_dir:) ⇒ Object



24
25
26
27
# File 'lib/exwiw/schema_check.rb', line 24

def self.from_rails_application(schema_dir:)
  Rails.application.eager_load!
  new(models: ActiveRecord::Base.descendants, schema_dir: schema_dir)
end

.mongoid_regenerator(models) ⇒ Object

The regeneration step from_mongoid_application injects, exposed so a caller (and the specs) can run the check against an explicit model list without a Rails application — and so what they exercise is the very lambda production uses.

skip_unsupported is deliberately left off: this runs as a CI gate, and a newly added construct exwiw cannot represent has to fail the task with the generator's actionable message rather than quietly become an ignore: true config that reports clean while the collection stops being dumped.



47
48
49
50
51
52
53
# File 'lib/exwiw/schema_check.rb', line 47

def self.mongoid_regenerator(models)
  lambda do |tmp_dir|
    # Explicit: safe mode is not optional here, whatever the library default is.
    MongoidSchemaGenerator.new(models: models, output_dir: tmp_dir, safe_new_columns: true).generate!
    MongoidSchemaGenerator.new(models: models, output_dir: tmp_dir).tidy!
  end
end

Instance Method Details

#runObject

The report as a plain Hash. Every list is sorted, so a given state always produces the same output (it ends up in a CI comment).



66
67
68
69
70
71
72
73
74
75
76
# File 'lib/exwiw/schema_check.rb', line 66

def run
  committed = read_configs(@schema_dir)
  regenerated = Dir.mktmpdir do |tmp_dir|
    regenerate_into(tmp_dir)
    read_configs(tmp_dir)
  end

  report = diff(committed, regenerated)
  report["needs_mask_decision"] = flagged_columns(committed)
  report
end