Class: Karst::Access::PrincipalSource
- Inherits:
-
Object
- Object
- Karst::Access::PrincipalSource
- Defined in:
- lib/karst/access/principal_source.rb
Overview
One allowed principal population Karst may sample from or resolve into -- "which records may Karst consider at all," a different question from Karst::Access::CandidatePopulation ("which application-authored relation, within this source, is worth trying first").
records is a callable Karst evaluates lazily, exactly like a bare
config.principals -- never enumerated, sampled, or materialized just
by building a PrincipalSource. A single config.principals (plus any
config.principal_populations) is normalized into one implicit
:default PrincipalSource internally (see
Karst::Configuration#principal_sources), so every downstream consumer
(PrincipalSelection, Identity.resolve, the panel) only ever has to
handle "one or more sources," never a separate single-source case.
Instance Attribute Summary collapse
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#populations ⇒ Object
readonly
Returns the value of attribute populations.
-
#records ⇒ Object
readonly
Returns the value of attribute records.
Class Method Summary collapse
- .from_spec(name, spec) ⇒ Object
-
.normalize(sources) ⇒ Object
Accepts a raw Hash of name => (callable, or populations:) -- the shape config.principal_sources= receives.
-
.normalize_populations(source_name, populations) ⇒ Object
A configured population is a Hash of name => zero-argument callable expected to return an ActiveRecord::Relation scoped to this same source -- see Karst::Access::CandidatePopulation.
Instance Method Summary collapse
-
#evaluate ⇒ Object
Evaluates the configured records callable.
-
#initialize(name:, records:, populations: {}) ⇒ PrincipalSource
constructor
A new instance of PrincipalSource.
-
#record_klass ⇒ Object
The Active Record class this source's records ultimately belong to, or nil for a source whose evaluated records are not an ActiveRecord::Relation/Class (a plain Array/Enumerable source, or a callable that raises).
-
#with_populations(extra) ⇒ Object
A copy of this source with
extrapopulations appended after its own configured ones.
Constructor Details
#initialize(name:, records:, populations: {}) ⇒ PrincipalSource
Returns a new instance of PrincipalSource.
22 23 24 25 26 27 28 |
# File 'lib/karst/access/principal_source.rb', line 22 def initialize(name:, records:, populations: {}) raise ArgumentError, "principal source #{name.inspect} must be callable" unless records.respond_to?(:call) @name = name.to_sym @records = records @populations = self.class.normalize_populations(@name, populations) end |
Instance Attribute Details
#name ⇒ Object (readonly)
Returns the value of attribute name.
20 21 22 |
# File 'lib/karst/access/principal_source.rb', line 20 def name @name end |
#populations ⇒ Object (readonly)
Returns the value of attribute populations.
20 21 22 |
# File 'lib/karst/access/principal_source.rb', line 20 def populations @populations end |
#records ⇒ Object (readonly)
Returns the value of attribute records.
20 21 22 |
# File 'lib/karst/access/principal_source.rb', line 20 def records @records end |
Class Method Details
.from_spec(name, spec) ⇒ Object
88 89 90 91 92 93 94 95 96 97 |
# File 'lib/karst/access/principal_source.rb', line 88 def self.from_spec(name, spec) return new(name: name, records: spec) if spec.respond_to?(:call) unless spec.is_a?(Hash) raise ArgumentError, "principal source #{name.inspect} must be callable or a Hash with :records" end reject_unknown_keys!(name, spec) new(name: name, records: fetch_any(spec, :records), populations: fetch_any(spec, :populations) || {}) end |
.normalize(sources) ⇒ Object
Accepts a raw Hash of name => (callable, or populations:) -- the shape config.principal_sources= receives.
74 75 76 77 78 79 80 81 82 83 |
# File 'lib/karst/access/principal_source.rb', line 74 def self.normalize(sources) return nil if sources.nil? raise ArgumentError, "principal_sources must be a Hash of name => records/{records:, populations:}" unless sources.is_a?(Hash) sources.each_with_object({}) do |(name, spec), normalized| source = spec.is_a?(PrincipalSource) ? spec : from_spec(name, spec) normalized[source.name] = source end end |
.normalize_populations(source_name, populations) ⇒ Object
A configured population is a Hash of name => zero-argument callable expected to return an ActiveRecord::Relation scoped to this same source -- see Karst::Access::CandidatePopulation. Deliberately kept as raw callables here, not wrapped into CandidatePopulation instances: a CandidatePopulation represents one already-resolved (queried) population, which only happens once PrincipalSampler actually runs, never at configuration time.
125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/karst/access/principal_source.rb', line 125 def self.normalize_populations(source_name, populations) return {} if populations.nil? valid = populations.is_a?(Hash) && populations.all? { |n, c| n.is_a?(Symbol) && c.respond_to?(:call) } unless valid raise ArgumentError, "principal source #{source_name.inspect} populations must be a Hash of Symbol => callable" end populations end |
Instance Method Details
#evaluate ⇒ Object
Evaluates the configured records callable. Never enumerates or queries on its own -- for an Active Record source this only builds a Relation, exactly like Karst::Identity.principals already did for the single-source case.
34 35 36 |
# File 'lib/karst/access/principal_source.rb', line 34 def evaluate records.call end |
#record_klass ⇒ Object
The Active Record class this source's records ultimately belong to, or nil for a source whose evaluated records are not an ActiveRecord::Relation/Class (a plain Array/Enumerable source, or a callable that raises). Only ever builds a Relation to read its #klass -- never queries a row. Used to match a Karst::Access::PopulationDiscovery-discovered model against an already-configured principal source, and by the panel's guided population retry.
46 47 48 49 50 51 52 53 54 |
# File 'lib/karst/access/principal_source.rb', line 46 def record_klass evaluated = evaluate return evaluated if defined?(ActiveRecord::Base) && evaluated.is_a?(Class) && evaluated < ActiveRecord::Base return evaluated.klass if defined?(ActiveRecord::Relation) && evaluated.is_a?(ActiveRecord::Relation) nil rescue StandardError nil end |
#with_populations(extra) ⇒ Object
A copy of this source with extra populations appended after its own
configured ones. Used by Karst::Access::ApprovedPopulations to fold
locally approved discovered scopes into the effective configuration,
so nothing downstream has to know an approval workflow exists.
Explicit configuration wins outright: a name this source already
configures keeps its configured callable, and configured populations
keep their position ahead of appended ones.
63 64 65 66 67 68 69 70 |
# File 'lib/karst/access/principal_source.rb', line 63 def with_populations(extra) return self if extra.nil? || extra.empty? merged = self.class.normalize_populations(@name, extra).reject { |name, _| @populations.key?(name) } return self if merged.empty? self.class.new(name: @name, records: @records, populations: @populations.merge(merged)) end |