Module: Karst::Access::PrincipalSourceSelection

Defined in:
lib/karst/access/principal_source_selection.rb

Overview

The local, machine-scoped record of which ambiguous Devise model(s) a developer has explicitly told Karst to test (see Karst::Access::SelectedPrincipalSources for how this is turned back into runnable Karst::Access::PrincipalSource objects, revalidated against Karst::Identity::DeviseSupport's own current metadata on every read).

Deliberately data, never code: an entry is a bare model name and nothing else -- never a scope, a class, or any executable Ruby. Karst never constantizes a stored name; it is only ever compared, as a string, against what Devise.mappings currently reports. This is the same never-trust-the-file posture Karst::Access::PopulationApprovals uses for candidate populations, applied one layer earlier: to which models Karst may consider at all, not what it may sample from within one already-known model.

Stored under the host application's tmp/ (tmp/karst/) for the same reason approved populations are: machine-local, disposable, git-ignored, reset by deleting the file, and consulted only in development/test (see Karst::Access::ApprovedPopulations.local_environment?, reused as-is by Karst::Access::SelectedPrincipalSources -- this is the same local-preference mechanism, not a parallel one).

Every read fails closed, exactly like PopulationApprovals: a file that is unreadable, is not JSON, is not the expected document shape, carries an unknown schema version, or holds a single entry that is not a plausible constant name selects nothing at all.

Constant Summary collapse

SCHEMA_VERSION =
1
RELATIVE_PATH =
File.join("tmp", "karst", "principal_source_selection.json")
MODEL_NAME =

Matched only against Karst::Identity::DeviseSupport.mappings' own model names -- a stored name is never constantized and never used to look up an arbitrary constant.

/\A[A-Z][A-Za-z0-9_]*(?:::[A-Z][A-Za-z0-9_]*)*\z/
MAX_ENTRIES =

A generous bound no realistic application approaches -- exists only so a corrupted or maliciously grown document cannot turn every principal source resolution into unbounded work.

50
Record =
Value.define(:model_names, :error) do
  def selected?(model_name)
    model_names.include?(model_name.to_s)
  end
end

Class Method Summary collapse

Class Method Details

.display_pathObject

The path as a developer should see it: relative to the application root, since that is where they will go looking for (or delete) it.



64
65
66
# File 'lib/karst/access/principal_source_selection.rb', line 64

def display_path
  RELATIVE_PATH
end

.loadObject



68
69
70
71
72
73
74
75
76
77
# File 'lib/karst/access/principal_source_selection.rb', line 68

def load
  document = JSON.parse(File.read(path))
  parse(document)
rescue Errno::ENOENT
  empty
rescue JSON::ParserError
  failed("could not be read as JSON")
rescue StandardError => e
  failed("could not be read (#{e.class})")
end

.pathObject



58
59
60
# File 'lib/karst/access/principal_source_selection.rb', line 58

def path
  File.join(root, RELATIVE_PATH)
end

.replace(model_names) ⇒ Object

Replaces the whole selection with model_names, atomically: callers always submit the complete set they intend to keep, so deselecting a model is simply selecting a smaller set, and a partially written file can never be observed.



83
84
85
86
87
88
89
90
# File 'lib/karst/access/principal_source_selection.rb', line 83

def replace(model_names)
  normalized = normalize(model_names)
  write(normalized)
  Record.new(model_names: normalized, error: nil)
rescue StandardError => e
  Record.new(model_names: normalized || [].freeze,
             error: "selection could not be saved (#{e.class})")
end