Implement #let functionality

This commit is contained in:
Michael Miller 2018-08-28 16:33:23 -06:00
parent 623033623a
commit 709b226f8e
4 changed files with 23 additions and 37 deletions

View file

@ -46,7 +46,19 @@ module Spectator
macro let(name, &block)
module Context
@_%proxy : ValueProxy?
def {{name.id}}
if (proxy = @_%proxy)
proxy.as(TypedValueProxy(typeof({{name.id}}!))).value
else
{{name.id}}!.tap do |value|
@_%proxy = TypedValueProxy(typeof({{name.id}}!)).new(value)
end
end
end
def {{name.id}}!
{{block.body}}
end
end

View file

@ -1,36 +0,0 @@
require "./value_proxy"
module Spectator
# Lazy initialization of a value.
# Constructs a value only once by calling a `Proc`.
# The value is then stored and reused - the `Proc` is only called once.
class LazyValueProxy(T) < ValueProxy
@value_or_block : Proc(T) | T
# Creates a lazy instance.
# The block provided to this method will be called
# when `#value` is invoked.
# The block will only be called once,
# and the result of the block will be cached.
def initialize(&block : -> T)
@value_or_block = block
end
# Retrieves the lazy initialized value.
# The first call to this method will create the value.
# Subsequent calls will return the same value.
def value
if value = @value_or_block.as?(T)
return value
else
@value_or_block = construct
end
end
# Calls the block used to construct the value.
# This method can only be called once per instance.
private def construct : T
@value_or_block.as(Proc(T)).call
end
end
end

View file

@ -0,0 +1,10 @@
require "./value_proxy"
module Spectator
class TypedValueProxy(T) < ValueProxy
getter value : T
def initialize(@value : T)
end
end
end

View file

@ -2,7 +2,7 @@ module Spectator
# Base class for proxying test values to examples.
# This abstraction is required for inferring types.
# The `DSL#let` macro makes heavy use of this.
protected abstract class ValueProxy
private abstract class ValueProxy
# Retrieves the underlying value.
abstract def value
end