Class: LazyInit::LazyValue

Inherits:
Object
  • Object
show all
Defined in:
lib/lazy_init/lazy_value.rb

Overview

Thread-safe container for lazy-initialized values with performance-optimized access.

LazyValue provides a thread-safe wrapper around expensive computations that should only be executed once. It uses a double-checked locking pattern with an ultra-fast hot path that avoids synchronization overhead after initial computation.

The implementation separates the fast path (simple instance variable access) from the slow path (computation with full synchronization) for optimal performance in the common case where values are accessed repeatedly after computation.

Examples:

Basic usage

lazy_value = LazyValue.new do
  expensive_database_query
end

result = lazy_value.value # computes once
result = lazy_value.value # returns cached value

With timeout protection

lazy_value = LazyValue.new(timeout: 5) do
  slow_external_api_call
end

begin
  result = lazy_value.value
rescue LazyInit::TimeoutError
  puts "API call timed out"
end

Since:

  • 0.1.0

Instance Method Summary collapse

Constructor Details

#initialize(timeout: nil, &block) ⇒ LazyValue

Create a new lazy value container.

The computation block will be executed at most once when value() is first called. Subsequent calls to value() return the cached result without re-executing the block.

Parameters:

  • timeout (Numeric, nil) (defaults to: nil)

    optional timeout in seconds for the computation

  • block (Proc)

    the computation to execute lazily

Raises:

  • (ArgumentError)

    if no block is provided

Since:

  • 0.1.0



45
46
47
48
49
50
51
52
53
54
# File 'lib/lazy_init/lazy_value.rb', line 45

def initialize(timeout: nil, &block)
  raise ArgumentError, 'Block is required' unless block

  @block = block
  @timeout = timeout
  @mutex = Mutex.new
  @computed = false
  @value = nil
  @exception = nil
end

Instance Method Details

#computed?Boolean

Check if the value has been successfully computed.

Returns false if computation hasn’t started, failed with an exception, or was reset. Only returns true for successful computations.

Returns:

  • (Boolean)

    true if value has been computed without errors

Since:

  • 0.1.0



83
84
85
# File 'lib/lazy_init/lazy_value.rb', line 83

def computed?
  @computed && @exception.nil?
end

#exceptionException?

Access the cached exception if one exists.

Returns the actual exception object that was raised during computation, or nil if no exception occurred or computation hasn’t been attempted.

Returns:

  • (Exception, nil)

    the cached exception or nil

Since:

  • 0.1.0



117
118
119
# File 'lib/lazy_init/lazy_value.rb', line 117

def exception
  @mutex.synchronize { @exception }
end

#exception?Boolean

Check if the computation resulted in a cached exception.

This method is thread-safe and can be used to determine if value() will raise an exception without actually triggering it.

Returns:

  • (Boolean)

    true if an exception is cached

Since:

  • 0.1.0



107
108
109
# File 'lib/lazy_init/lazy_value.rb', line 107

def exception?
  @mutex.synchronize { !@exception.nil? }
end

#reset!void

This method returns an undefined value.

Reset the lazy value to its initial uncomputed state.

Clears the cached value and any cached exceptions, allowing the computation to be re-executed on the next call to value(). This method is thread-safe.

Since:

  • 0.1.0



93
94
95
96
97
98
99
# File 'lib/lazy_init/lazy_value.rb', line 93

def reset!
  @mutex.synchronize do
    @computed = false
    @value = nil
    @exception = nil
  end
end

#valueObject

Get the computed value, executing the block if necessary.

Uses an optimized double-checked locking pattern: the hot path (after computation) requires only a single instance variable check and direct return. The cold path (during computation) handles full synchronization and error management.

If the computation raises an exception, that exception is cached and re-raised on subsequent calls to maintain consistent behavior.

Returns:

  • (Object)

    the computed value

Raises:

  • (TimeoutError)

    if computation exceeds the configured timeout

  • (StandardError)

    any exception raised by the computation block

Since:

  • 0.1.0



68
69
70
71
72
73
74
75
# File 'lib/lazy_init/lazy_value.rb', line 68

def value
  # hot path: optimized for repeated access after computation
  # this should be nearly as fast as direct instance variable access
  return @value if @computed

  # cold path: handle computation and synchronization
  compute_or_raise_exception
end