Introduce abstract generic value type

Sits between AbstractExpression and Value and Block.
This commit is contained in:
Michael Miller 2021-01-15 22:32:02 -07:00
parent 4ed8c4a573
commit a74957204b
No known key found for this signature in database
GPG Key ID: FB9F12F7C646A4AD
5 changed files with 46 additions and 22 deletions

View File

@ -6,25 +6,30 @@ module Spectator
# It consists of a label and the value of the expression. # It consists of a label and the value of the expression.
# The label should be a string recognizable by the user, # The label should be a string recognizable by the user,
# or nil if one isn't available. # or nil if one isn't available.
#
# This base class is provided so that all generic sub-classes can be stored as this one type. # This base class is provided so that all generic sub-classes can be stored as this one type.
# The value of the expression can be retrieved by downcasting to the expected type with `#cast`. # The value of the expression can be retrieved by downcasting to the expected type with `#cast`.
#
# NOTE: This is intentionally a class and not a struct.
# If it were a struct, changes made to the value held by an instance may not be kept when passing it around.
# See commit ca564619ad2ae45f832a058d514298c868fdf699.
abstract class AbstractExpression abstract class AbstractExpression
# User recognizable string for the expression. # User recognizable string for the expression.
# This can be something like a variable name or a snippet of Crystal code. # This can be something like a variable name or a snippet of Crystal code.
getter label : Label getter label : Label
# Creates the expression. # Creates the expression.
# The *label* is usually the Crystal code evaluating to the `#value`. # The *label* is usually the Crystal code evaluating to the `#raw_value`.
# It can be nil if it isn't available. # It can be nil if it isn't available.
def initialize(@label : Label) def initialize(@label : Label)
end end
# Retrieves the real value of the expression. # Retrieves the evaluated value of the expression.
abstract def value abstract def raw_value
# Attempts to cast `#value` to the type *T* and return it. # Attempts to cast `#raw_value` to the type *T* and return it.
def cast(type : T.class) : T forall T def cast(type : T.class) : T forall T
value.as(T) raw_value.as(T)
end end
# Produces a string representation of the expression. # Produces a string representation of the expression.
@ -35,7 +40,7 @@ module Spectator
io << ':' io << ':'
io << ' ' io << ' '
end end
io << value raw_value.to_s(io)
end end
# Produces a detailed string representation of the expression. # Produces a detailed string representation of the expression.
@ -46,7 +51,7 @@ module Spectator
io << ':' io << ':'
io << ' ' io << ' '
end end
value.inspect(io) raw_value.inspect(io)
end end
end end
end end

View File

@ -1,4 +1,4 @@
require "./abstract_expression" require "./expression"
require "./label" require "./label"
require "./lazy" require "./lazy"
@ -8,7 +8,7 @@ module Spectator
# It consists of a label and parameterless block. # It consists of a label and parameterless block.
# The label should be a string recognizable by the user, # The label should be a string recognizable by the user,
# or nil if one isn't available. # or nil if one isn't available.
class Block(T) < AbstractExpression class Block(T) < Expression(T)
# Cached value returned from the block. # Cached value returned from the block.
@value = Lazy(T).new @value = Lazy(T).new

View File

@ -1,7 +1,8 @@
require "../assertion" require "../assertion"
require "../assertion_failed" require "../assertion_failed"
require "../expression" require "../block"
require "../source" require "../source"
require "../value"
module Spectator::DSL module Spectator::DSL
# Methods and macros for asserting that conditions are met. # Methods and macros for asserting that conditions are met.
@ -50,7 +51,7 @@ module Spectator::DSL
{{actual}} {{actual}}
end end
%expression = ::Spectator::Expression.new(%actual, {{actual.stringify}}) %expression = ::Spectator::Value.new(%actual, {{actual.stringify}})
%source = ::Spectator::Source.new({{actual.filename}}, {{actual.line_number}}) %source = ::Spectator::Source.new({{actual.filename}}, {{actual.line_number}})
::Spectator::Assertion::Target.new(%expression, %source) ::Spectator::Assertion::Target.new(%expression, %source)
end end

View File

@ -1,22 +1,18 @@
require "./abstract_expression" require "./abstract_expression"
require "./label"
module Spectator module Spectator
# Represents an expression from a test. # Represents an expression from a test.
# This is typically captured by an `expect` macro. # This is typically captured by an `expect` macro.
# It consists of a label and the value of the expression. # It consists of a label and a typed expression.
# The label should be a string recognizable by the user, # The label should be a string recognizable by the user,
# or nil if one isn't available. # or nil if one isn't available.
class Expression(T) < AbstractExpression abstract class Expression(T) < AbstractExpression
# Raw value of the expression. # Retrieves the underlying value of the expression.
getter value abstract def value : T
# Creates the expression. # Retrieves the evaluated value of the expression.
# Expects the *value* of the expression and a *label* describing it. def raw_value
# The *label* is usually the Crystal code evaluating to the *value*. value
# It can be nil if it isn't available.
def initialize(@value : T, label : Label)
super(label)
end end
end end
end end

22
src/spectator/value.cr Normal file
View File

@ -0,0 +1,22 @@
require "./expression"
require "./label"
module Spectator
# Represents a value from a test.
# This is typically captured by an `expect` macro.
# It consists of a label and the value of the expression.
# The label should be a string recognizable by the user,
# or nil if one isn't available.
class Value(T) < Expression(T)
# Raw value of the expression.
getter value : T
# Creates the value.
# Expects the *value* of the expression and a *label* describing it.
# The *label* is usually the Crystal code evaluating to the *value*.
# It can be nil if it isn't available.
def initialize(@value : T, label : Label)
super(label)
end
end
end