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,
lib/strict_lazy/preloader.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, Preloader, 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.4.0"

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.preload(records, *spec) ⇒ Object

Prepare lazy values for a group of records.

The spec is a Rails-style list (mirroring ActiveRecord’s preload): each element is either a reader name (Symbol) prepared on the given records, or a Hash whose keys are associations to traverse and whose values are the spec to apply recursively to the associated records. A Hash value may itself be a Symbol, a Hash, or an array mixing both — so a single level can prepare its own readers and descend into nested associations at once:

StrictLazy.preload(posts,
  :comments_count,                              # reader on posts
  comments: [:score, { replies: :like_count }] # reader on comments + nested
)

With no spec at all, every declared loader on the records is prepared. sync: true loaders resolve immediately; others on first read.

Records may mix classes (e.g. STI subtrees, or children gathered across associations): they are grouped by their STI base class so each loader’s resolver runs once per declaring class.



155
156
157
# File 'lib/strict_lazy.rb', line 155

def self.preload(records, *spec)
  Preloader.call(records, spec)
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.



98
99
100
# File 'lib/strict_lazy.rb', line 98

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.



105
106
107
# File 'lib/strict_lazy.rb', line 105

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


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

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).



91
92
93
# File 'lib/strict_lazy.rb', line 91

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