Module: StrictLazy

Extended by:
ActiveSupport::Concern
Defined in:
lib/strict_lazy.rb,
lib/strict_lazy/batch.rb,
lib/strict_lazy/errors.rb,
lib/strict_lazy/facade.rb,
lib/strict_lazy/loader.rb,
lib/strict_lazy/railtie.rb,
lib/strict_lazy/version.rb

Overview

strict_lazy applies the spirit of Rails’ strict_loading to computed values. Include it in a model, declare values with lazy_load, prepare them in the controller with StrictLazy.preload, and read them via record.lazy.x. Reading without a preceding preload raises in development/test.

Defined Under Namespace

Classes: Batch, Error, Facade, Loader, Railtie, UnloadedError

Constant Summary collapse

VALID_VIOLATIONS =

The accepted violation policies.

%i[raise log ignore].freeze
READER_FORMAT =

A valid reader is a bare name, optionally a ‘?` predicate. Setter (`=`), bang (`!`), and operator readers are rejected: the `.lazy` namespace is read-only, and any other form has no valid ivar to back it.

/\A[A-Za-z_][A-Za-z0-9_]*\??\z/
VERSION =
"0.3.0"

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.preload(records, *readers) ⇒ Object

Prepare lazy values for a group of records. With no readers, prepares every declared loader. sync: true loaders resolve immediately; others on first read.



135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/strict_lazy.rb', line 135

def self.preload(records, *readers)
  records = Array(records)
  return records if records.empty?

  model = records.first.class
  loaders_for(model, readers).each do |loader|
    batch = Batch.new(model, records, loader)
    records.each { |record| record.instance_variable_set(loader.batch_ivar, batch) }
    batch.resolve! if loader.sync?
  end

  records
end

.violationObject

The effective policy for the current execution context: the innermost with_violation override if any, otherwise the global baseline. The facade consults this — never read default_violation directly.



96
97
98
# File 'lib/strict_lazy.rb', line 96

def self.violation
  (violation_overrides || []).last || default_violation
end

.violation=(mode) ⇒ Object

Backward-compatible global setter. Sets the baseline only; it does not touch any active with_violation override. Existing callers (and the Railtie) keep working unchanged.



103
104
105
# File 'lib/strict_lazy.rb', line 103

def self.violation=(mode)
  self.default_violation = validate_violation!(mode)
end

.with_violation(mode) ⇒ Object

Run the block with mode as the effective policy, restoring the previous state afterward (exception-safe). Overrides nest; an inner call shadows an outer one and unwinds cleanly. Scoped to the current Fiber/Thread, so parallel test processes never interfere.

StrictLazy.with_violation(:ignore) { ... }   # never raises inside


113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/strict_lazy.rb', line 113

def self.with_violation(mode)
  # Validate first: an invalid mode raises here, before the stack is touched,
  # so the begin/ensure only ever runs against a successfully pushed frame.
  validated = validate_violation!(mode)
  begin
    pushed = (violation_overrides || []) + [validated]
    self.violation_overrides = pushed
    yield
  ensure
    self.violation_overrides = pushed[0...-1]
  end
end

Instance Method Details

#lazyObject

The .lazy namespace facade (memoized per record).



89
90
91
# File 'lib/strict_lazy.rb', line 89

def lazy
  @_lazy_facade ||= Facade.new(self)
end