spinach 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.yardopts +6 -0
- data/Gemfile +1 -0
- data/Guardfile +7 -0
- data/Rakefile +1 -0
- data/Readme.md +147 -10
- data/features/{generate_features.feature → automatic_feature_generation.feature} +0 -0
- data/features/steps/automatic_feature_generation.rb +5 -2
- data/features/steps/exit_status.rb +8 -3
- data/features/steps/feature_name_guessing.rb +4 -1
- data/features/steps/reporting/display_run_summary.rb +7 -4
- data/features/steps/reporting/error_reporting.rb +7 -2
- data/features/steps/reporting/show_step_source_location.rb +9 -5
- data/features/steps/reporting/undefined_feature_reporting.rb +4 -1
- data/features/steps/rspec_compatibility.rb +6 -2
- data/features/support/spinach_runner.rb +2 -9
- data/lib/spinach/capybara.rb +3 -4
- data/lib/spinach/cli.rb +0 -1
- data/lib/spinach/config.rb +0 -8
- data/lib/spinach/dsl.rb +4 -13
- data/lib/spinach/exceptions.rb +1 -1
- data/lib/spinach/feature_steps.rb +1 -9
- data/lib/spinach/generators/feature_generator.rb +3 -2
- data/lib/spinach/generators.rb +1 -1
- data/lib/spinach/hookable.rb +81 -0
- data/lib/spinach/hooks.rb +132 -0
- data/lib/spinach/reporter/stdout/error_reporting.rb +163 -0
- data/lib/spinach/reporter/stdout.rb +3 -151
- data/lib/spinach/reporter.rb +39 -25
- data/lib/spinach/runner/{feature.rb → feature_runner.rb} +5 -15
- data/lib/spinach/runner/scenario_runner.rb +65 -0
- data/lib/spinach/runner.rb +5 -11
- data/lib/spinach/version.rb +1 -1
- data/lib/spinach.rb +20 -8
- data/spinach.gemspec +2 -3
- data/test/spinach/capybara_test.rb +4 -3
- data/test/spinach/cli_test.rb +0 -1
- data/test/spinach/feature_steps_test.rb +6 -23
- data/test/spinach/generators/feature_generator_test.rb +2 -2
- data/test/spinach/generators_test.rb +2 -2
- data/test/spinach/hookable_test.rb +59 -0
- data/test/spinach/hooks_test.rb +28 -0
- data/test/spinach/reporter/stdout/error_reporting_test.rb +265 -0
- data/test/spinach/reporter/stdout_test.rb +1 -238
- data/test/spinach/reporter_test.rb +58 -103
- data/test/spinach/runner/{feature_test.rb → feature_runner_test.rb} +21 -23
- data/test/spinach/runner/scenario_runner_test.rb +111 -0
- data/test/spinach/runner_test.rb +1 -1
- data/test/spinach_test.rb +19 -18
- data/test/test_helper.rb +1 -1
- metadata +60 -61
- data/lib/spinach/runner/scenario.rb +0 -77
- data/test/spinach/runner/scenario_test.rb +0 -120
data/.gitignore
CHANGED
data/.yardopts
ADDED
data/Gemfile
CHANGED
data/Guardfile
CHANGED
@@ -3,3 +3,10 @@ guard 'minitest' do
|
|
3
3
|
watch(%r|^lib/(.*)([^/]+)\.rb|) { |m| "test/#{m[1]}#{m[2]}_test.rb" }
|
4
4
|
watch(%r|^test/test_helper\.rb|) { "test" }
|
5
5
|
end
|
6
|
+
|
7
|
+
guard 'spinach' do
|
8
|
+
watch(%r|^features/(.*)\.feature|)
|
9
|
+
watch(%r|^features/steps/(.*)([^/]+)\.rb|) do |m|
|
10
|
+
"features/#{m[1]}#{m[2]}.feature"
|
11
|
+
end
|
12
|
+
end
|
data/Rakefile
CHANGED
data/Readme.md
CHANGED
@@ -1,15 +1,152 @@
|
|
1
|
-
#
|
2
|
-
Spinach is a BDD framework on top of gherkin
|
1
|
+
# Spinach - BDD framework on top of Gherkin [![Build Status](https://secure.travis-ci.org/codegram/spinach.png)](http://travis-ci.org/codegram/spinach)
|
3
2
|
|
4
|
-
|
3
|
+
Spinach is a high-level BDD framework that leverages the expressive
|
4
|
+
[Gherkin language][gherkin] (used by [Cucumber][cucumber]) to help you define
|
5
|
+
executable specifications of your application or library's acceptance criteria.
|
5
6
|
|
6
|
-
|
7
|
-
[Spinach documentation at rubydoc.info](http://rubydoc.info/github/codegram/spinach/master/frames)
|
7
|
+
Conceived as an alternative to Cucumber, here are some of its design goals:
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
* Step maintanability: since features map to their own classes, their steps are
|
10
|
+
just methods of that class. This encourages step encapsulation.
|
11
11
|
|
12
|
-
*
|
12
|
+
* Step reusability: In case you want to reuse steps across features, you can
|
13
|
+
always wrap those in plain ol' Ruby modules.
|
13
14
|
|
14
|
-
|
15
|
-
|
15
|
+
Spinach is tested against MRI 1.9.2, 1.9.3. Rubinius 2.0 support is on the
|
16
|
+
works.
|
17
|
+
|
18
|
+
We are not planning to make it compatible with MRI 1.8.7 since, you know, this
|
19
|
+
would be irresponsible :)
|
20
|
+
|
21
|
+
## Getting started
|
22
|
+
|
23
|
+
Start by adding spinach to your Gemfile:
|
24
|
+
|
25
|
+
group :test do
|
26
|
+
gem 'spinach'
|
27
|
+
# along with gem 'minitest' or gem 'rspec'
|
28
|
+
end
|
29
|
+
|
30
|
+
Spinach works with your favorite test suite, you just have to tell it which
|
31
|
+
one are you going to use in `features/support/env.rb`:
|
32
|
+
|
33
|
+
# If you want to use minitest:
|
34
|
+
require 'minitest/spec'
|
35
|
+
|
36
|
+
# If you want to use rspec:
|
37
|
+
require 'rspec'
|
38
|
+
|
39
|
+
Now create a `features` folder in your app or library and write your first
|
40
|
+
feature:
|
41
|
+
|
42
|
+
## features/test_how_spinach_works.feature
|
43
|
+
|
44
|
+
Feature: Test how spinach works
|
45
|
+
In order to know what the heck is spinach
|
46
|
+
As a developer
|
47
|
+
I want it to behave in an expected way
|
48
|
+
|
49
|
+
Scenario: Formal greeting
|
50
|
+
Given I have an empty array
|
51
|
+
And I append my first name and my last name to it
|
52
|
+
When I pass it to my super-duper method
|
53
|
+
Then the output should contain a formal greeting
|
54
|
+
|
55
|
+
Scenario: Informal greeting
|
56
|
+
Given I have an empty array
|
57
|
+
And I append only my first name to it
|
58
|
+
When I pass it to my super-duper method
|
59
|
+
Then the output should contain a casual greeting
|
60
|
+
|
61
|
+
Now for the steps file. Remember that in Spinach steps are just Ruby classes,
|
62
|
+
following a camelcase naming convention. Spinach generator will do some
|
63
|
+
scaffolding for you:
|
64
|
+
|
65
|
+
$ spinach --generate
|
66
|
+
|
67
|
+
Spinach will detect your features and generate the following class:
|
68
|
+
|
69
|
+
## features/steps/test_how_spinach_works.rb
|
70
|
+
|
71
|
+
class TestHowSpinachWorks < Spinach::FeatureSteps
|
72
|
+
Given 'I have an empty array' do
|
73
|
+
end
|
74
|
+
|
75
|
+
And 'I append my first name and my last name to it' do
|
76
|
+
end
|
77
|
+
|
78
|
+
When 'I pass it to my super-duper method' do
|
79
|
+
end
|
80
|
+
|
81
|
+
Then 'the output should contain a formal greeting' do
|
82
|
+
end
|
83
|
+
|
84
|
+
And 'I append only my first name to it' do
|
85
|
+
end
|
86
|
+
|
87
|
+
Then 'the output should contain a casual greeting' do
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
Then, you can fill it in with your logic - remember, it's just a class, you can
|
92
|
+
use private methods, mix in modules or whatever!
|
93
|
+
|
94
|
+
class TestHowSpinachWorks < Spinach::FeatureSteps
|
95
|
+
Given 'I have an empty array' do
|
96
|
+
@array = Array.new
|
97
|
+
end
|
98
|
+
|
99
|
+
And 'I append my first name and my last name to it' do
|
100
|
+
@array += ["John", "Doe"]
|
101
|
+
end
|
102
|
+
|
103
|
+
When 'I pass it to my super-duper method' do
|
104
|
+
@output = capture_output do
|
105
|
+
Greeter.greet(@array)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
Then 'the output should contain a formal salutation' do
|
110
|
+
@output.must_include "Hello, mr. John Doe"
|
111
|
+
end
|
112
|
+
|
113
|
+
And 'I append only my first name to it' do
|
114
|
+
@array += ["John"]
|
115
|
+
end
|
116
|
+
|
117
|
+
Then 'the output should contain a casual salutation' do
|
118
|
+
@output.must_include "Yo, John! Whassup?"
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def capture_output
|
124
|
+
out = StreamIO.new
|
125
|
+
$stdout = out
|
126
|
+
$stderr = out
|
127
|
+
yield
|
128
|
+
$stdout = STDOUT
|
129
|
+
$stderr = STDERR
|
130
|
+
out.string
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
Then run your feature again running `spinach` and watch it all turn green! :)
|
135
|
+
|
136
|
+
## Contributing
|
137
|
+
|
138
|
+
You can easily contribute to Spinach. Its codebase is simple and
|
139
|
+
[extensively documented][documentation].
|
140
|
+
|
141
|
+
* Fork the project.
|
142
|
+
* Make your feature addition or bug fix.
|
143
|
+
* Add specs for it. This is important so we don't break it in a future
|
144
|
+
version unintentionally.
|
145
|
+
* Commit, do not mess with rakefile, version, or history.
|
146
|
+
If you want to have your own version, that is fine but bump version
|
147
|
+
in a commit by itself I can ignore when I pull.
|
148
|
+
* Send me a pull request. Bonus points for topic branches.
|
149
|
+
|
150
|
+
[gherkin]: http://github.com/cucumber/gherkin
|
151
|
+
[cucumber]: http://github.com/cucumber/cucumber
|
152
|
+
[documentation]: http://rubydoc.info/github/codegram/spinach/master/frames
|
File without changes
|
@@ -1,4 +1,7 @@
|
|
1
|
-
|
1
|
+
class AutomaticFeatureGeneration < Spinach::FeatureSteps
|
2
|
+
|
3
|
+
feature 'Automatic feature generation'
|
4
|
+
|
2
5
|
include Integration::SpinachRunner
|
3
6
|
Given 'I have defined a "Cheezburger can I has" feature' do
|
4
7
|
write_file('features/cheezburger_can_i_has.feature',
|
@@ -20,7 +23,7 @@ Feature 'Automatic feature generation' do
|
|
20
23
|
File.exists?(@file).must_equal true
|
21
24
|
end
|
22
25
|
end
|
23
|
-
|
26
|
+
|
24
27
|
And "that feature should have the example feature steps" do
|
25
28
|
in_current_dir do
|
26
29
|
content = File.read(@file)
|
@@ -1,4 +1,7 @@
|
|
1
|
-
|
1
|
+
class ExitStatus < Spinach::FeatureSteps
|
2
|
+
|
3
|
+
feature "Exit status"
|
4
|
+
|
2
5
|
include Integration::SpinachRunner
|
3
6
|
|
4
7
|
Given "I have a feature that has no error or failure" do
|
@@ -9,7 +12,8 @@ Feature "Exit status" do
|
|
9
12
|
Then I succeed
|
10
13
|
')
|
11
14
|
write_file('features/steps/success_feature.rb',
|
12
|
-
'
|
15
|
+
'class ASuccessFeature < Spinach::FeatureSteps
|
16
|
+
feature "A success feature"
|
13
17
|
Then "I succeed" do
|
14
18
|
end
|
15
19
|
end')
|
@@ -24,7 +28,8 @@ Feature "Exit status" do
|
|
24
28
|
Then I fail
|
25
29
|
')
|
26
30
|
write_file('features/steps/failure_feature.rb',
|
27
|
-
'
|
31
|
+
'class AFailureFeature < Spinach::FeatureSteps
|
32
|
+
feature "A failure feature"
|
28
33
|
Then "I fail" do
|
29
34
|
true.must_equal false
|
30
35
|
end
|
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'aruba/api'
|
2
2
|
|
3
|
-
|
3
|
+
class DisplayRunSummary < Spinach::FeatureSteps
|
4
|
+
|
5
|
+
feature 'automatic'
|
6
|
+
|
4
7
|
include Integration::SpinachRunner
|
5
8
|
|
6
9
|
Given "I have a feature that has some successful, undefined, failed and error steps" do
|
@@ -24,7 +27,9 @@ Feature "Display run summary" do
|
|
24
27
|
Then I must succeed
|
25
28
|
')
|
26
29
|
write_file('features/steps/test_feature.rb',
|
27
|
-
'
|
30
|
+
'class ATestFeature < Spinach::FeatureSteps
|
31
|
+
feature "A test feature"
|
32
|
+
|
28
33
|
Given "I am a fool" do
|
29
34
|
end
|
30
35
|
|
@@ -65,5 +70,3 @@ Feature "Display run summary" do
|
|
65
70
|
)
|
66
71
|
end
|
67
72
|
end
|
68
|
-
|
69
|
-
|
@@ -1,4 +1,7 @@
|
|
1
|
-
|
1
|
+
class ErrorReporting < Spinach::FeatureSteps
|
2
|
+
|
3
|
+
feature "Error reporting"
|
4
|
+
|
2
5
|
include Integration::SpinachRunner
|
3
6
|
include Integration::ErrorReporting
|
4
7
|
|
@@ -12,7 +15,9 @@ Feature "Error reporting" do
|
|
12
15
|
')
|
13
16
|
|
14
17
|
write_file('features/steps/failure_feature.rb',
|
15
|
-
'
|
18
|
+
'class FeatureWithFailures < Spinach::FeatureSteps
|
19
|
+
feature "Feature with failures"
|
20
|
+
|
16
21
|
Given "true is false" do
|
17
22
|
true.must_equal false
|
18
23
|
end
|
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'aruba/api'
|
2
2
|
|
3
|
-
|
3
|
+
class ShowStepSourceLocation < Spinach::FeatureSteps
|
4
|
+
|
5
|
+
feature "Show step source location"
|
6
|
+
|
4
7
|
include Integration::SpinachRunner
|
5
8
|
|
6
9
|
Given "I have a feature that has no error or failure" do
|
@@ -11,7 +14,8 @@ Feature "Show step source location" do
|
|
11
14
|
Then I succeed
|
12
15
|
')
|
13
16
|
write_file('features/steps/success_feature.rb',
|
14
|
-
'
|
17
|
+
'class ASuccessFeature < Spinach::FeatureSteps
|
18
|
+
feature "A success feature"
|
15
19
|
Then "I succeed" do
|
16
20
|
end
|
17
21
|
end')
|
@@ -24,7 +28,7 @@ Feature "Show step source location" do
|
|
24
28
|
|
25
29
|
Then "I should see the source location of each step of every scenario" do
|
26
30
|
all_stdout.must_match(
|
27
|
-
/I succeed.*features\/steps\/success_feature\.rb.*
|
31
|
+
/I succeed.*features\/steps\/success_feature\.rb.*3/
|
28
32
|
)
|
29
33
|
end
|
30
34
|
|
@@ -36,7 +40,8 @@ Feature "Show step source location" do
|
|
36
40
|
Given this is a external step
|
37
41
|
')
|
38
42
|
write_file('features/steps/success_feature.rb',
|
39
|
-
'
|
43
|
+
'class AFeatureThatUsesExternalSteps < Spinach::FeatureSteps
|
44
|
+
feature "A feature that uses external steps"
|
40
45
|
include ExternalSteps
|
41
46
|
end')
|
42
47
|
write_file('features/support/external_steps.rb',
|
@@ -54,4 +59,3 @@ Feature "Show step source location" do
|
|
54
59
|
)
|
55
60
|
end
|
56
61
|
end
|
57
|
-
|
@@ -1,4 +1,7 @@
|
|
1
|
-
|
1
|
+
class RSpecCompatibility < Spinach::FeatureSteps
|
2
|
+
|
3
|
+
feature "RSpec compatibility"
|
4
|
+
|
2
5
|
include Integration::SpinachRunner
|
3
6
|
include Integration::ErrorReporting
|
4
7
|
|
@@ -12,7 +15,8 @@ Feature "RSpec compatibility" do
|
|
12
15
|
')
|
13
16
|
|
14
17
|
write_file('features/steps/failure_feature.rb',
|
15
|
-
'
|
18
|
+
'class FeatureWithFailures < Spinach::FeatureSteps
|
19
|
+
feature "Feature with failures"
|
16
20
|
Given "true is false" do
|
17
21
|
true.should == false
|
18
22
|
end
|
@@ -5,15 +5,8 @@ module Integration
|
|
5
5
|
include Aruba::Api
|
6
6
|
|
7
7
|
def self.included(base)
|
8
|
-
|
9
|
-
|
10
|
-
in_current_dir do
|
11
|
-
FileUtils.rm_rf("features")
|
12
|
-
end
|
13
|
-
end
|
14
|
-
before_scenario do
|
15
|
-
@aruba_timeout_seconds = 6
|
16
|
-
end
|
8
|
+
Spinach.hooks.before_scenario do
|
9
|
+
@aruba_timeout_seconds = 6
|
17
10
|
end
|
18
11
|
end
|
19
12
|
|
data/lib/spinach/capybara.rb
CHANGED
@@ -24,10 +24,9 @@ module Spinach
|
|
24
24
|
def self.included(base)
|
25
25
|
base.class_eval do
|
26
26
|
include ::Capybara::DSL
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
27
|
+
end
|
28
|
+
Spinach.hooks.before_scenario do
|
29
|
+
::Capybara.current_session.reset! if ::Capybara.app
|
31
30
|
end
|
32
31
|
end
|
33
32
|
end
|
data/lib/spinach/cli.rb
CHANGED
data/lib/spinach/config.rb
CHANGED
@@ -45,14 +45,6 @@ module Spinach
|
|
45
45
|
@support_path || 'features/support'
|
46
46
|
end
|
47
47
|
|
48
|
-
# The default reporter is the reporter spinach will use if there's no other
|
49
|
-
# specified. Defaults to Spinach::Reporter::Stdout, which will print all
|
50
|
-
# output to the standard output
|
51
|
-
#
|
52
|
-
def default_reporter
|
53
|
-
@default_reporter || Spinach::Reporter::Stdout.new
|
54
|
-
end
|
55
|
-
|
56
48
|
# Allows you to read the config object using a hash-like syntax.
|
57
49
|
#
|
58
50
|
# @param [String] attribute
|
data/lib/spinach/dsl.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'hooks'
|
2
|
-
|
3
1
|
module Spinach
|
4
2
|
# Spinach DSL aims to provide an easy way to define steps and hooks into your
|
5
3
|
# feature classes.
|
@@ -13,13 +11,6 @@ module Spinach
|
|
13
11
|
base.class_eval do
|
14
12
|
include InstanceMethods
|
15
13
|
extend ClassMethods
|
16
|
-
include Hooks
|
17
|
-
|
18
|
-
define_hook :before_scenario
|
19
|
-
define_hook :after_scenario
|
20
|
-
define_hook :before_step
|
21
|
-
define_hook :after_step
|
22
|
-
|
23
14
|
end
|
24
15
|
end
|
25
16
|
|
@@ -82,11 +73,11 @@ module Spinach
|
|
82
73
|
#
|
83
74
|
# @api public
|
84
75
|
def execute_step(step)
|
85
|
-
|
76
|
+
underscored_step = Spinach::Support.underscore(step)
|
86
77
|
location = nil
|
87
|
-
if self.respond_to?(
|
88
|
-
location = method(
|
89
|
-
self.send(
|
78
|
+
if self.respond_to?(underscored_step)
|
79
|
+
location = method(underscored_step).source_location
|
80
|
+
self.send(underscored_step)
|
90
81
|
else
|
91
82
|
raise Spinach::StepNotDefinedException.new(step)
|
92
83
|
end
|
data/lib/spinach/exceptions.rb
CHANGED
@@ -11,15 +11,7 @@ module Spinach
|
|
11
11
|
#
|
12
12
|
# @api public
|
13
13
|
def self.inherited(base)
|
14
|
-
Spinach.
|
14
|
+
Spinach.feature_steps << base
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
18
|
-
|
19
|
-
# Syntactic sugar. Define the "Feature do" syntax.
|
20
|
-
Object.send(:define_method, :Feature) do |name, &block|
|
21
|
-
Class.new(Spinach::FeatureSteps) do
|
22
|
-
feature name
|
23
|
-
class_eval &block
|
24
|
-
end
|
25
|
-
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Spinach
|
2
2
|
module Generators
|
3
|
-
# A feature generator generates and/or writes an example feature steps class
|
3
|
+
# A feature generator generates and/or writes an example feature steps class
|
4
4
|
# given the parsed feture data
|
5
5
|
class FeatureGenerator
|
6
6
|
|
@@ -43,7 +43,8 @@ module Spinach
|
|
43
43
|
# an example feature steps definition
|
44
44
|
def generate
|
45
45
|
result = StringIO.new
|
46
|
-
result.puts "
|
46
|
+
result.puts "class #{Spinach::Support.camelize name} < Spinach::FeatureSteps"
|
47
|
+
result.puts " feature \'#{Spinach::Support.escape_single_commas name}\'\n"
|
47
48
|
generated_steps = steps.map do |step|
|
48
49
|
step_generator = Generators::StepGenerator.new(step)
|
49
50
|
step_generator.generate.split("\n").map do |line|
|
data/lib/spinach/generators.rb
CHANGED
@@ -5,7 +5,7 @@ module Spinach
|
|
5
5
|
module Generators
|
6
6
|
# Binds the feature generator to the "feature not found" hook
|
7
7
|
def self.bind
|
8
|
-
Spinach
|
8
|
+
Spinach.hooks.on_undefined_feature do |data|
|
9
9
|
Spinach::Generators.generate_feature(data)
|
10
10
|
end
|
11
11
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Spinach
|
2
|
+
# The hookable module includes subscription capabilities to the class in which
|
3
|
+
# it is included.
|
4
|
+
#
|
5
|
+
# Take in account that while most subscription/notification mechanism work
|
6
|
+
# at the class level, Hookable defines hooks at the instance level - so they
|
7
|
+
# are not the same in all the class instances.
|
8
|
+
module Hookable
|
9
|
+
|
10
|
+
def self.included(base)
|
11
|
+
base.class_eval do
|
12
|
+
extend ClassMethods
|
13
|
+
include InstanceMethods
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
# Adds a new hook to this class. Every hook defines two methods used to
|
19
|
+
# add new callbacks and to run them passing a bunch of parameters.
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# class
|
23
|
+
def hook(hook)
|
24
|
+
define_method hook do |&block|
|
25
|
+
add_hook(hook, &block)
|
26
|
+
end
|
27
|
+
define_method "run_#{hook}" do |*args|
|
28
|
+
run_hook(hook, *args)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module InstanceMethods
|
34
|
+
attr_writer :hooks
|
35
|
+
|
36
|
+
# @return [Hash]
|
37
|
+
# hash in which the key is the hook name and the value an array of any
|
38
|
+
# defined callbacks, or nil.
|
39
|
+
def hooks
|
40
|
+
@hooks ||= {}
|
41
|
+
end
|
42
|
+
|
43
|
+
# Resets all this class' hooks to a pristine state
|
44
|
+
def reset
|
45
|
+
self.hooks = {}
|
46
|
+
end
|
47
|
+
|
48
|
+
# Runs a particular hook given a set of arguments
|
49
|
+
#
|
50
|
+
# @param [String] name
|
51
|
+
# the hook's name
|
52
|
+
#
|
53
|
+
def run_hook(name, *args)
|
54
|
+
if callbacks = hooks[name.to_sym]
|
55
|
+
callbacks.each{ |c| c.call(*args) }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# @param [String] name
|
60
|
+
# the hook's identifier
|
61
|
+
#
|
62
|
+
# @return [Array]
|
63
|
+
# array of hooks for that particular identifier
|
64
|
+
def hooks_for(name)
|
65
|
+
hooks[name.to_sym] || []
|
66
|
+
end
|
67
|
+
|
68
|
+
# Adds a hook to the queue
|
69
|
+
#
|
70
|
+
# @param [String] name
|
71
|
+
# the hook's identifier
|
72
|
+
#
|
73
|
+
# @param [Proc] block
|
74
|
+
# an action to perform once that hook is executed
|
75
|
+
def add_hook(name, &block)
|
76
|
+
hooks[name.to_sym] ||= []
|
77
|
+
hooks[name.to_sym] << block
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|