From 0ee708281f188c2fb748ba751c2195a5952467a6 Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Sat, 9 Jan 2021 10:27:54 -0700 Subject: [PATCH] Cleanup hook macros --- src/spectator/events.cr | 59 ++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/src/spectator/events.cr b/src/spectator/events.cr index 86a6132..33857ac 100644 --- a/src/spectator/events.cr +++ b/src/spectator/events.cr @@ -7,61 +7,90 @@ module Spectator # Defines an event for an example group. # This event typically runs before or after an example group finishes. # No contextual information (or example) is provided to the hooks. + # # The *name* defines the name of the event. - # This must be unique across all events. - # Three methods are defined - one to add a hook and the others to trigger the event which calls every hook. + # This must be unique across all events, not just group events. + # Three public methods are defined - one to add a hook and the others to trigger the event which calls every hook. # One trigger method, prefixed with *call_* will always call the event hooks. # The other trigger method, prefixed with *call_once_* will only call the event hooks on the first invocation. + # + # A block must be provided to this macro. + # The block defines the logic for invoking all of the hooks. + # A single argument is yielded to the block - the set of hooks for the event. + # + # ``` + # group_event some_hook do |hooks| + # hooks.each(&.call) + # end + # ``` private macro group_event(name, &block) - @{{name.id}}_hooks = Deque(->).new - @{{name.id}}_called = Atomic::Flag.new + @%hooks = Deque(->).new + @%called = Atomic::Flag.new # Defines a hook for the *{{name.id}}* event. # The block of code given to this method is invoked when the event occurs. def {{name.id}}(&block) : Nil - @{{name.id}}_hooks << block + @%hooks << block end # Signals that the *{{name.id}}* event has occurred. # All hooks associated with the event will be called. def call_{{name.id}} : Nil - {{block.args.first}} = @{{name.id}}_hooks - {{yield}} + %block(@%hooks) end # Signals that the *{{name.id}}* event has occurred. # Only calls the hooks if the event hasn't been triggered before by this method. # Returns true if the hooks were called and false if they weren't (called previously). def call_once_{{name.id}} : Bool - first = @{{name.id}}_called.test_and_set + first = @%called.test_and_set call_{{name.id}} if first first end + + # Logic specific to invoking the *{{name.id}}* hook. + private def %block({{block.args.splat}}) + {{block.body}} + end end # Defines an event for an example. # This event typically runs before or after an example finishes. # The current example is provided to the hooks. + # # The *name* defines the name of the event. # This must be unique across all events. - # Three methods are defined - two to add a hook and the other to trigger the event which calls every hook. - # A hook can be added with an `ExampleContextDelegate` or a block that accepts an example (no context). + # Two public methods are defined - one to add a hook and the other to trigger the event which calls every hook. + # + # A block must be provided to this macro. + # The block defines the logic for invoking all of the hooks. + # Two arguments are yielded to the block - the set of hooks for the event, and the current example. + # + # ``` + # example_event some_hook do |hooks, example| + # hooks.each(&.call(example)) + # end + # ``` private macro example_event(name, &block) - @{{name.id}}_hooks = Deque(Example ->).new + @%hooks = Deque(Example ->).new # Defines a hook for the *{{name.id}}* event. # The block of code given to this method is invoked when the event occurs. # The current example is provided as a block argument. def {{name.id}}(&block : Example ->) : Nil - @{{name.id}}_hooks << block + @%hooks << block end # Signals that the *{{name.id}}* event has occurred. # All hooks associated with the event will be called. # The *example* should be the current example. - def call_{{name.id}}({{block.args[1]}}) : Nil - {{block.args.first}} = @{{name.id}}_hooks - {{yield}} + def call_{{name.id}}(example : Example) : Nil + %block(@%hooks, example) + end + + # Logic specific to invoking the *{{name.id}}* hook. + private def %block({{block.args.splat}}) + {{block.body}} end end end