spinach 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 [](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
|