diff --git a/src/spectator/dsl/values.cr b/src/spectator/dsl/values.cr index 0e0aa7f..210a9c1 100644 --- a/src/spectator/dsl/values.cr +++ b/src/spectator/dsl/values.cr @@ -3,8 +3,13 @@ require "../lazy_wrapper" module Spectator::DSL # DSL methods for defining test values (subjects). module Values + # Defines a memoized getter. + # The *name* is the name of the getter method. + # The block is evaluated only on the first time the getter is used + # and the return value is saved for subsequent calls. macro let(name, &block) {% raise "Block required for 'let'" unless block %} + {% raise "Cannot use 'let' inside of a test block" if @def %} @%value = ::Spectator::LazyWrapper.new @@ -12,49 +17,69 @@ module Spectator::DSL @%value.get {{block}} end end - end - macro let!(name, &block) - @%wrapper : ::Spectator::ValueWrapper? + # Defines a memoized getter. + # The *name* is the name of the getter method. + # The block is evaluated once before the example runs + # and the return value is saved. + macro let!(name, &block) + {% raise "Block required for 'let!'" unless block %} + {% raise "Cannot use 'let!' inside of a test block" if @def %} - def %wrapper - {{block.body}} - end - - before_each do - @%wrapper = ::Spectator::TypedValueWrapper.new(%wrapper) - end - - def {{name.id}} - @%wrapper.as(::Spectator::TypedValueWrapper(typeof(%wrapper))).value - end + let({{name}}) {{block}} + before_each { {{name.id}} } end - macro subject(&block) - {% if block.is_a?(Nop) %} - self.subject - {% else %} - let(:subject) {{block}} + # Explicitly defines the subject of the tests. + # Creates a memoized getter for the subject. + # The block is evaluated only the first time the subject is referenced + # and the return value is saved for subsequent calls. + macro subject(&block) + {% raise "Block required for 'subject'" unless block %} + {% raise "Cannot use 'subject' inside of a test block" if @def %} + + let(subject) {{block}} + end + + # Explicitly defines the subject of the tests. + # Creates a memoized getter for the subject. + # The subject can be referenced by using `subject` or *name*. + # The block is evaluated only the first time the subject is referenced + # and the return value is saved for subsequent calls. + macro subject(name, &block) + subject {{block}} + + {% if name.id != :subject.id %} + def {{name.id}} + subject + end {% end %} end - macro subject(name, &block) - let({{name.id}}) {{block}} + # Explicitly defines the subject of the tests. + # Creates a memoized getter for the subject. + # The block is evaluated once before the example runs + # and the return value is saved for subsequent calls. + macro subject!(&block) + {% raise "Block required for 'subject!'" unless block %} + {% raise "Cannot use 'subject!' inside of a test block" if @def %} - def subject - {{name.id}} - end + let!(subject) {{block}} end - macro subject!(&block) - let!(:subject) {{block}} - end + # Explicitly defines the subject of the tests. + # Creates a memoized getter for the subject. + # The subject can be referenced by using `subject` or *name*. + # The block is evaluated once before the example runs + # and the return value is saved for subsequent calls. + macro subject!(name, &block) + subject! {{block}} - macro subject!(name, &block) - let!({{name.id}}) {{block}} - - def subject - {{name.id}} - end + {% if name.id != :subject.id %} + def {{name.id}} + subject + end + {% end %} end + end end