forked from codegram/spinach
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor and document hooking mechanism
- Loading branch information
1 parent
2c3f1d0
commit d407be0
Showing
5 changed files
with
251 additions
and
82 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
module Spinach | ||
module Hookable | ||
|
||
def self.included(base) | ||
base.class_eval do | ||
extend ClassMethods | ||
include InstanceMethods | ||
end | ||
end | ||
|
||
module ClassMethods | ||
# Adds a new hook to this class. Every hook defines two methods used to | ||
# add new callbacks and to run them passing a bunch of parameters. | ||
# | ||
# @example | ||
# class | ||
def define_hook(hook) | ||
define_method hook do |&block| | ||
add_hook(hook, &block) | ||
end | ||
define_method "run_#{hook}" do |*args| | ||
run_hook(hook, *args) | ||
end | ||
end | ||
end | ||
|
||
module InstanceMethods | ||
attr_writer :hooks | ||
|
||
def hooks | ||
@hooks ||= {} | ||
end | ||
|
||
# Resets all this class' hooks to a pristine state | ||
def reset | ||
self.hooks = {} | ||
end | ||
|
||
# Runs a particular hook | ||
# | ||
# @param [String] name | ||
# the hook's name | ||
# | ||
# @param [ | ||
def run_hook(name, *args) | ||
if callbacks = hooks[name.to_sym] | ||
callbacks.each{ |c| c.call(*args) } | ||
end | ||
end | ||
|
||
def hooks_for(name) | ||
hooks[name.to_sym] || [] | ||
end | ||
|
||
def add_hook(name, &block) | ||
hooks[name.to_sym] ||= [] | ||
hooks[name.to_sym] << block | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,149 @@ | ||
require_relative 'hookable' | ||
|
||
module Spinach | ||
# The hooks class is a subscription/notification mechanism that allows you to | ||
# hook into signals provided by the runner and perform certain actions on | ||
# it. | ||
# | ||
# @example | ||
# Spinach.hooks.before_run do | ||
# # Runs before the entire spinach execution | ||
# end | ||
# | ||
# Spinach.hooks.before_scenario do |scenario_data| | ||
# # Runs before every scenario and passes a hash of the parsed scenario | ||
# # data to the block as argument | ||
# end | ||
# | ||
# Spinach.hooks.on_failed_step do |step_data| | ||
# # Runs before every failed stepand passes a hash of the parsed step | ||
# # data to the block as argument | ||
# end | ||
class Hooks | ||
include Hookable | ||
|
||
def self.define_hook(hook) | ||
define_method hook do |&block| | ||
add_hook(hook, &block) | ||
end | ||
define_method "run_#{hook}" do |*args| | ||
run_hook(hook, *args) | ||
end | ||
end | ||
|
||
# Runs before the entire spinach run | ||
# | ||
# @example | ||
# Spinach.before_run do | ||
# # Whatever | ||
# end | ||
define_hook :before_run | ||
|
||
# Runs after the entire spinach run | ||
# | ||
# @example | ||
# Spinach.after_run do |status| | ||
# # status is true when the run is successful, false otherwise | ||
# end | ||
define_hook :after_run | ||
|
||
# Runs before every feature, | ||
# | ||
# @example | ||
# Spinach.before_feature do |feature_data| | ||
# # feature_data is a hash of the parsed feature data | ||
# end | ||
define_hook :before_feature | ||
|
||
# Runs after every feature | ||
# | ||
# @example | ||
# Spinach.after_feature do |feature_data| | ||
# # feature_data is a hash of the parsed feature data | ||
# end | ||
define_hook :after_feature | ||
|
||
# Runs when an undefined feature is found | ||
# | ||
# @example | ||
# Spinach.on_undefined_feature do |feature_data, exception| | ||
# # feature_data is a hash of the parsed feature data | ||
# # exception contains the thrown exception | ||
# end | ||
define_hook :on_undefined_feature | ||
|
||
# Runs before every scenario | ||
# | ||
# @example | ||
# Spinach.before_scenario do |scenario_data| | ||
# # feature_data is a hash of the parsed scenario data | ||
# end | ||
define_hook :before_scenario | ||
|
||
# Runs after every scenario | ||
# | ||
# @example | ||
# Spinach.after_scenario do |scenario_data| | ||
# # feature_data is a hash of the parsed scenario data | ||
# end | ||
define_hook :after_scenario | ||
|
||
# Runs before every step execution | ||
# | ||
# @example | ||
# Spinach.before_step do |step_data| | ||
# # step_data contains a hash with this step's data | ||
# end | ||
define_hook :before_step | ||
|
||
# Runs after every step execution | ||
# | ||
# @example | ||
# Spinach.before_step do |step_data| | ||
# # step_data contains a hash with this step's data | ||
# end | ||
define_hook :after_step | ||
define_hook :on_successful_step | ||
define_hook :on_failed_step | ||
define_hook :on_error_step | ||
define_hook :on_undefined_step | ||
define_hook :on_skipped_step | ||
|
||
def initialize | ||
@hooks = {} | ||
end | ||
# Runs after every successful step execution | ||
# | ||
# @example | ||
# Spinach.on_successful_step do |step_data, location| | ||
# # step_data contains a hash with this step's data | ||
# # step_location contains a string indication this step definition's | ||
# # location | ||
# end | ||
define_hook :on_successful_step | ||
|
||
def reset | ||
@hooks = {} | ||
end | ||
# Runs after every failed step execution | ||
# | ||
# @example | ||
# Spinach.on_failed_step do |step_data, location| | ||
# # step_data contains a hash with this step's data | ||
# # step_location contains a string indication this step definition's | ||
# # location | ||
# end | ||
define_hook :on_failed_step | ||
|
||
def run_hook(name, *args) | ||
if callbacks = @hooks[name.to_sym] | ||
callbacks.each{ |c| c.call(*args) } | ||
end | ||
end | ||
# Runs after every step execution that raises an exception | ||
# | ||
# @example | ||
# Spinach.on_error_step do |step_data, location| | ||
# # step_data contains a hash with this step's data | ||
# # step_location contains a string indication this step definition's | ||
# # location | ||
# end | ||
define_hook :on_error_step | ||
|
||
def hooks_for(name) | ||
@hooks[name.to_sym] || [] | ||
end | ||
# Runs every time a step which is not defined is called | ||
# | ||
# @example | ||
# Spinach.on_undefined_step do |step_data, location| | ||
# # step_data contains a hash with this step's data | ||
# # step_location contains a string indication this step definition's | ||
# # location | ||
# end | ||
define_hook :on_undefined_step | ||
|
||
def add_hook(name, &block) | ||
@hooks[name.to_sym] ||= [] | ||
@hooks[name.to_sym] << block | ||
end | ||
# Runs every time a step is skipped because there has been an unsuccessful | ||
# one just before. | ||
# | ||
# @example | ||
# Spinach.on_undefined_step do |step_data| | ||
# # step_data contains a hash with this step's data | ||
# end | ||
define_hook :on_skipped_step | ||
|
||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
require_relative '../test_helper' | ||
|
||
describe Spinach::Hookable do | ||
subject do | ||
Class.new do | ||
include Spinach::Hookable | ||
end.new | ||
end | ||
|
||
describe ".define_hook" do | ||
it "defines a new hook" do | ||
subject.class.define_hook :before_save | ||
subject.must_respond_to :before_save | ||
end | ||
end | ||
|
||
describe "hooking mechanism" do | ||
describe "without params" do | ||
it "adds a new hook to the queue" do | ||
subject.add_hook(:before_save) do | ||
end | ||
(subject.hooks_for(:before_save).empty?).must_equal false | ||
end | ||
|
||
it "allows to run a hook" do | ||
arbitrary_variable = false | ||
subject.add_hook(:before_save) do | ||
arbitrary_variable = true | ||
end | ||
subject.run_hook(:before_save) | ||
arbitrary_variable.must_equal true | ||
end | ||
end | ||
|
||
describe "with params" do | ||
it "adds a new hook to the queue" do | ||
subject.add_hook(:before_save) do |var1, var2| | ||
end | ||
(subject.hooks_for(:before_save).empty?).must_equal false | ||
end | ||
|
||
it "allows to run a hook" do | ||
array = [] | ||
subject.add_hook(:before_save) do |var1, var2| | ||
array << var1 | ||
array << var2 | ||
end | ||
subject.run_hook(:before_save, 1, 2) | ||
array.must_equal [1, 2] | ||
end | ||
end | ||
|
||
describe "#reset_hooks" do | ||
it "resets the hooks to a pristine state" do | ||
subject.add_hook(:before_save) | ||
end | ||
end | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters