hero 0.0.3 → 0.0.4
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/README.md +15 -149
- data/lib/hero.rb +6 -0
- data/lib/hero/formula.rb +31 -7
- data/lib/hero/observer.rb +20 -2
- data/spec/formula_spec.rb +138 -0
- data/spec/observer_spec.rb +39 -0
- data/spec/spec_helper.rb +5 -0
- metadata +5 -3
- data/spec/hero_spec.rb +0 -70
data/README.md
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
# Hero
|
2
2
|
|
3
|
+

|
4
|
+
|
3
5
|
## Its a bird, its a plane, its... its... my Hero
|
4
6
|
|
5
|
-
|
7
|
+
---
|
6
8
|
|
7
|
-
|
9
|
+
*Controlling complexity is the essence of computer programming. -- [Brian Kernighan](http://en.wikipedia.org/wiki/Brian_Kernighan)*
|
8
10
|
|
9
|
-
|
11
|
+
---
|
10
12
|
|
11
13
|
I've seen my share of poor app structure.
|
12
14
|
Hell, I wrote most of it.
|
@@ -17,161 +19,25 @@ The question remains. **Where do I put my business logic?**
|
|
17
19
|
Finally... an answer that might even make DHH proud.
|
18
20
|
One that evolved from the real world with concrete use cases and actual production code.
|
19
21
|
|
20
|
-
##
|
22
|
+
## Why Hero?
|
21
23
|
|
22
|
-
|
24
|
+
* App structure matches the mental map of your business
|
25
|
+
* Testable coponents
|
26
|
+
* Faster ramp up time for new team members
|
27
|
+
* Easily handles changing requirements
|
28
|
+
|
29
|
+
### Process Modeling
|
30
|
+
|
31
|
+
The problem has always been: **How do you effectively model a business process within your app?**
|
23
32
|
|
24
33
|
Things start simply enough but eventually edge cases force *gotchas* into
|
25
34
|
various libs, modules, and classes. Before you know you it,
|
26
35
|
you have a lump of spaghetti that's difficult to maintain and even harder to improve.
|
27
36
|
|
28
|
-
### Enter Hero
|
29
|
-
|
30
37
|
Hero provides a simple pattern that encourages you to
|
31
38
|
<a href="http://en.wikipedia.org/wiki/Decomposition_(computer_science)">decompose</a>
|
32
|
-
|
33
|
-
|
34
|
-
And... the best part is, they are easily tested.
|
39
|
+
these processes into managable chunks. And the best part... the components can be easily tested.
|
35
40
|
|
36
41
|
---
|
37
42
|
|
38
|
-
Here's an example.
|
39
|
-
Assume we have a Rails app that needs to support logins.
|
40
|
-
|
41
|
-
Our implementation might look something like this.
|
42
|
-
|
43
|
-
```ruby
|
44
|
-
# app/controllers/logins_controller.rb
|
45
|
-
class LoginsController < ApplicationController
|
46
|
-
|
47
|
-
def create
|
48
|
-
if user = User.authenticate(params[:username], params[:password])
|
49
|
-
session[:current_user_id] = user.id
|
50
|
-
redirect_to root_url
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def destroy
|
55
|
-
@_current_user = session[:current_user_id] = nil
|
56
|
-
redirect_to root_url
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
60
|
-
```
|
61
|
-
|
62
|
-
```ruby
|
63
|
-
# app/controllers/application_controller.rb
|
64
|
-
class ApplicationController < ActionController::Base
|
65
|
-
before_filter :require_login
|
66
|
-
|
67
|
-
private
|
68
|
-
|
69
|
-
def require_login
|
70
|
-
unless logged_in?
|
71
|
-
flash[:error] = "You must be logged in to access this section"
|
72
|
-
redirect_to new_login_url # halts request cycle
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def logged_in?
|
77
|
-
!!current_user
|
78
|
-
end
|
79
|
-
|
80
|
-
def current_user
|
81
|
-
@_current_user ||= session[:current_user_id] &&
|
82
|
-
User.find_by_id(session[:current_user_id])
|
83
|
-
end
|
84
|
-
|
85
|
-
end
|
86
|
-
```
|
87
|
-
|
88
|
-
Hero approaches this problem differently.
|
89
|
-
It asks us to <a href="http://en.wikipedia.org/wiki/Decomposition_(computer_science)">decompose</a>
|
90
|
-
the login requirement into a business processes looks something like this.
|
91
|
-
|
92
|
-
#### Login
|
93
|
-
|
94
|
-
1. Authenticate the user
|
95
|
-
1. Save user info to session
|
96
|
-
1. Send user to home page
|
97
|
-
|
98
|
-
#### Logout
|
99
|
-
|
100
|
-
1. Remove user session
|
101
|
-
1. Send user to home page
|
102
|
-
|
103
|
-
#### Protect Page
|
104
|
-
|
105
|
-
1. Verify the user is logged in
|
106
|
-
|
107
|
-
Note that we just defined an [ontology](http://en.wikipedia.org/wiki/Process_ontology)
|
108
|
-
that can be used to discuss the requirement and its implementation with non developers.
|
109
|
-
|
110
|
-
Here's an example of an implementation with Hero.
|
111
|
-
|
112
|
-
```ruby
|
113
|
-
# lib/errors.rb
|
114
|
-
class AuthenticationError < StandardError; end
|
115
|
-
class AuthorizationError < StandardError; end
|
116
|
-
```
|
117
|
-
|
118
|
-
```ruby
|
119
|
-
# config/initializers/login.rb
|
120
|
-
Hero::Formula[:login].add_step do |context|
|
121
|
-
user = User.authenticate(context.params[:username], context.params[:password])
|
122
|
-
raise AuthenticationError unless user
|
123
|
-
context.session[:current_user_id] = user.id
|
124
|
-
end
|
125
|
-
|
126
|
-
Hero::Formula[:logout].add_step do |context|
|
127
|
-
context.session[:current_user_id] = nil
|
128
|
-
end
|
129
|
-
|
130
|
-
Hero::Formula[:protect_page].add_step do |context|
|
131
|
-
raise AuthorizationError if context.session[:current_user_id].nil?
|
132
|
-
end
|
133
|
-
```
|
134
|
-
|
135
|
-
```ruby
|
136
|
-
# app/controllers/logins_controller.rb
|
137
|
-
class LoginsController < ApplicationController
|
138
|
-
rescue_from AuthenticationError { render "new" }
|
139
|
-
|
140
|
-
def create
|
141
|
-
Hero::Formula[:login].run(self)
|
142
|
-
redirect_to root_url
|
143
|
-
end
|
144
|
-
|
145
|
-
def destroy
|
146
|
-
Hero::Formula[:logout].run(self)
|
147
|
-
redirect_to root_url
|
148
|
-
end
|
149
|
-
|
150
|
-
end
|
151
|
-
```
|
152
|
-
|
153
|
-
```ruby
|
154
|
-
# app/controllers/application_controller.rb
|
155
|
-
class ApplicationController < ActionController::Base
|
156
|
-
before_filter :protect
|
157
|
-
rescue_from AuthorizationError, :with => :force_login
|
158
|
-
|
159
|
-
private
|
160
|
-
|
161
|
-
def protect
|
162
|
-
Hero::Formula[:protect_page].run(self)
|
163
|
-
end
|
164
|
-
|
165
|
-
def force_login
|
166
|
-
flash[:error] = "You must be logged in to access this section"
|
167
|
-
redirect_to new_login_url
|
168
|
-
end
|
169
|
-
|
170
|
-
end
|
171
|
-
```
|
172
|
-
|
173
|
-
I know what you're thinking, and you're right.
|
174
|
-
This doesn't pass DHH's before/after test,
|
175
|
-
but lets start throwing edge cases at it and see what happens.
|
176
|
-
|
177
43
|
More soon. Stay tuned...
|
data/lib/hero.rb
CHANGED
data/lib/hero/formula.rb
CHANGED
@@ -1,17 +1,26 @@
|
|
1
1
|
require 'observer'
|
2
2
|
require 'singleton'
|
3
3
|
require 'forwardable'
|
4
|
+
require File.join(File.dirname(__FILE__), "observer")
|
4
5
|
|
5
6
|
module Hero
|
7
|
+
|
6
8
|
class Formula
|
7
|
-
include Observable
|
8
|
-
include Singleton
|
9
9
|
|
10
|
+
# Class attributes & methods ==============================================
|
10
11
|
class << self
|
11
12
|
extend Forwardable
|
12
13
|
def_delegator :formulas, :each, :each
|
13
14
|
def_delegator :formulas, :length, :count
|
14
15
|
|
16
|
+
def publish
|
17
|
+
value = []
|
18
|
+
each do |name, formula|
|
19
|
+
value << formula.publish
|
20
|
+
end
|
21
|
+
value.join("\n\n")
|
22
|
+
end
|
23
|
+
|
15
24
|
def reset
|
16
25
|
formulas.values.each { |f| f.delete_observers }
|
17
26
|
@formulas = {}
|
@@ -22,10 +31,13 @@ module Hero
|
|
22
31
|
end
|
23
32
|
|
24
33
|
def register(name)
|
25
|
-
observer = Hero::Observer.new
|
34
|
+
observer = Hero::Observer.new(name)
|
26
35
|
formula = Class.new(Hero::Formula).instance
|
27
36
|
formula.add_observer(observer)
|
28
|
-
formula.instance_eval
|
37
|
+
formula.instance_eval do
|
38
|
+
@name = name
|
39
|
+
@observer = observer
|
40
|
+
end
|
29
41
|
formulas[name] = formula
|
30
42
|
end
|
31
43
|
|
@@ -36,9 +48,21 @@ module Hero
|
|
36
48
|
end
|
37
49
|
end
|
38
50
|
|
39
|
-
|
40
|
-
|
41
|
-
|
51
|
+
# Instance attributes & methods ===========================================
|
52
|
+
extend Forwardable
|
53
|
+
include Observable
|
54
|
+
include Singleton
|
55
|
+
|
56
|
+
attr_reader :name, :observer
|
57
|
+
def_delegator :observer, :steps, :steps
|
58
|
+
def_delegator :observer, :add_step, :add_step
|
59
|
+
|
60
|
+
def publish
|
61
|
+
value = [name]
|
62
|
+
steps.each_with_index do |step, index|
|
63
|
+
value << "#{(index + 1).to_s.rjust(3)}. #{step.keys.first}"
|
64
|
+
end
|
65
|
+
value.join("\n")
|
42
66
|
end
|
43
67
|
|
44
68
|
def notify(context=nil, options={})
|
data/lib/hero/observer.rb
CHANGED
@@ -1,13 +1,31 @@
|
|
1
1
|
module Hero
|
2
2
|
class Observer
|
3
|
+
attr_reader :formula_name
|
4
|
+
|
5
|
+
def initialize(formula_name)
|
6
|
+
@formula_name = formula_name
|
7
|
+
end
|
3
8
|
|
4
9
|
def steps
|
5
|
-
@steps ||=
|
10
|
+
@steps ||= {}
|
11
|
+
@steps.sort{ |a, b| a.last[:index] <=> b.last[:index] }.map{ |k, v| { k => v[:step] } }
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_step(name, step=nil, &block)
|
15
|
+
@steps ||= {}
|
16
|
+
step ||= block if block_given?
|
17
|
+
@steps[name] = { :step => step, :index => @steps.length }
|
6
18
|
end
|
7
19
|
|
8
20
|
def update(context, options={})
|
9
|
-
steps.each
|
21
|
+
steps.each do |step|
|
22
|
+
if Hero.logger
|
23
|
+
Hero.logger.info "HERO Formula: #{formula_name}, Step: #{step.keys.first}, Context: #{context.inspect}, Options: #{options.inspect}"
|
24
|
+
end
|
25
|
+
step.values.first.call(context, options)
|
26
|
+
end
|
10
27
|
end
|
11
28
|
|
12
29
|
end
|
13
30
|
end
|
31
|
+
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Hero::Formula do
|
4
|
+
include GrumpyOldMan
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
Hero.logger = nil
|
8
|
+
Hero::Formula.reset
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should support reset" do
|
12
|
+
Hero::Formula.register(:test_formula)
|
13
|
+
assert_equal Hero::Formula.count, 1
|
14
|
+
Hero::Formula.reset
|
15
|
+
assert_equal Hero::Formula.count, 0
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should support registering a formula" do
|
19
|
+
Hero::Formula.register(:test_formula)
|
20
|
+
assert_equal Hero::Formula.count, 1
|
21
|
+
assert Hero::Formula[:test_formula].is_a? Hero::Formula
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should auto register formulas" do
|
25
|
+
Hero::Formula[:test_formula]
|
26
|
+
assert_equal Hero::Formula.count, 1
|
27
|
+
assert Hero::Formula[:test_formula].is_a? Hero::Formula
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should support registering N number of formulas" do
|
31
|
+
10.times { |i| Hero::Formula.register("example_#{i}") }
|
32
|
+
assert_equal Hero::Formula.count, 10
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should unregister formula observers on reset" do
|
36
|
+
formula = Hero::Formula[:test_formula]
|
37
|
+
assert_equal Hero::Formula.count, 1
|
38
|
+
formula.add_step(:one) {}
|
39
|
+
assert_equal formula.count_observers, 1
|
40
|
+
Hero::Formula.reset
|
41
|
+
assert_equal Hero::Formula.count, 0
|
42
|
+
assert_equal formula.count_observers, 0
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should publish all formulas" do
|
46
|
+
Hero::Formula[:first].add_step(:one) {}
|
47
|
+
Hero::Formula[:first].add_step(:two) {}
|
48
|
+
Hero::Formula[:first].add_step(:three) {}
|
49
|
+
Hero::Formula[:first].add_step(:four) {}
|
50
|
+
|
51
|
+
Hero::Formula[:second].add_step(:one) {}
|
52
|
+
Hero::Formula[:second].add_step(:two) {}
|
53
|
+
Hero::Formula[:second].add_step(:three) {}
|
54
|
+
Hero::Formula[:second].add_step(:four) {}
|
55
|
+
|
56
|
+
expected = "first 1. one 2. two 3. three 4. foursecond 1. one 2. two 3. three 4. four"
|
57
|
+
assert_equal Hero::Formula.publish.gsub(/\n/, ""), expected
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "a registered formula" do
|
61
|
+
it "should support adding steps" do
|
62
|
+
Hero::Formula[:test_formula].add_step(:one) { }
|
63
|
+
assert_equal Hero::Formula.count, 1
|
64
|
+
assert_equal Hero::Formula[:test_formula].steps.length, 1
|
65
|
+
end
|
66
|
+
|
67
|
+
def invoke_notify_method(name)
|
68
|
+
step_ran = false
|
69
|
+
target = Object.new
|
70
|
+
Hero::Formula[:test_formula].add_step(:one) do |t, opts|
|
71
|
+
assert_equal t, target
|
72
|
+
assert_equal opts[:foo], :bar
|
73
|
+
step_ran = true
|
74
|
+
end
|
75
|
+
Hero::Formula[:test_formula].notify(target, :foo => :bar)
|
76
|
+
assert step_ran
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should support notify" do
|
80
|
+
invoke_notify_method(:notify)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should support run" do
|
84
|
+
invoke_notify_method(:run)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should support running step defined in a class" do
|
88
|
+
class Step
|
89
|
+
def call(context, options)
|
90
|
+
options[:context] = context
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
opts = {}
|
95
|
+
Hero::Formula[:test_formula].add_step(:one, Step.new)
|
96
|
+
Hero::Formula[:test_formula].run(:foo, opts)
|
97
|
+
assert_equal opts[:context], :foo
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should support running multiple tests" do
|
101
|
+
log = {}
|
102
|
+
Hero::Formula[:test_formula].add_step(:one) { |o, l| l[:one] = true }
|
103
|
+
Hero::Formula[:test_formula].add_step(:two) { |o, l| l[:two] = true }
|
104
|
+
Hero::Formula[:test_formula].run(self, log)
|
105
|
+
assert log[:one]
|
106
|
+
assert log[:two]
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should publish all steps in the formula" do
|
110
|
+
Hero::Formula[:test_formula].add_step(:one) {}
|
111
|
+
Hero::Formula[:test_formula].add_step(:two) {}
|
112
|
+
Hero::Formula[:test_formula].add_step(:three) {}
|
113
|
+
Hero::Formula[:test_formula].add_step(:four) {}
|
114
|
+
expected = "test_formula 1. one 2. two 3. three 4. four"
|
115
|
+
assert_equal Hero::Formula[:test_formula].publish.gsub(/\n/, ""), expected
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should support logging" do
|
119
|
+
class TestLogger
|
120
|
+
attr_reader :buffer
|
121
|
+
def info(value)
|
122
|
+
@buffer ||= []
|
123
|
+
@buffer << value
|
124
|
+
end
|
125
|
+
end
|
126
|
+
Hero.logger = TestLogger.new
|
127
|
+
Hero::Formula[:test_formula].add_step(:one) {}
|
128
|
+
Hero::Formula[:test_formula].add_step(:two) {}
|
129
|
+
Hero::Formula[:test_formula].run(:example, :logging => true)
|
130
|
+
assert_equal Hero.logger.buffer.length, 2
|
131
|
+
assert_equal "HERO Formula: test_formula, Step: one, Context: :example, Options: {:logging=>true}", Hero.logger.buffer.first
|
132
|
+
assert_equal "HERO Formula: test_formula, Step: two, Context: :example, Options: {:logging=>true}", Hero.logger.buffer.last
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Hero::Observer instance" do
|
4
|
+
include GrumpyOldMan
|
5
|
+
|
6
|
+
it "should support add_step" do
|
7
|
+
step = lambda {}
|
8
|
+
o = Hero::Observer.new(:example)
|
9
|
+
o.add_step(:one, step)
|
10
|
+
assert_equal o.steps.length, 1
|
11
|
+
assert_equal o.steps[0].keys.first, :one
|
12
|
+
assert_equal o.steps[0].values.first, step
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should support properly handle a double add" do
|
16
|
+
step1 = lambda {}
|
17
|
+
step2 = lambda {}
|
18
|
+
o = Hero::Observer.new(:example)
|
19
|
+
o.add_step(:one, step1)
|
20
|
+
o.add_step(:one, step2)
|
21
|
+
assert_equal o.steps.length, 1
|
22
|
+
assert_equal o.steps[0].keys.first, :one
|
23
|
+
assert_equal o.steps[0].values.first, step2
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should properly sort steps based on the order they were added" do
|
27
|
+
o = Hero::Observer.new(:example)
|
28
|
+
o.add_step(:one) {}
|
29
|
+
o.add_step(:two) {}
|
30
|
+
o.add_step(:three) {}
|
31
|
+
o.add_step(:four) {}
|
32
|
+
o.add_step(:one) {}
|
33
|
+
assert_equal o.steps.length, 4
|
34
|
+
assert_equal o.steps[0].keys.first, :two
|
35
|
+
assert_equal o.steps[1].keys.first, :three
|
36
|
+
assert_equal o.steps[2].keys.first, :four
|
37
|
+
assert_equal o.steps[3].keys.first, :one
|
38
|
+
end
|
39
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hero
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-08-
|
12
|
+
date: 2012-08-24 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! ' Simplify your apps with Hero.
|
15
15
|
|
@@ -26,7 +26,9 @@ files:
|
|
26
26
|
- Gemfile
|
27
27
|
- Gemfile.lock
|
28
28
|
- README.md
|
29
|
-
- spec/
|
29
|
+
- spec/formula_spec.rb
|
30
|
+
- spec/observer_spec.rb
|
31
|
+
- spec/spec_helper.rb
|
30
32
|
homepage: http://hopsoft.github.com/hero/
|
31
33
|
licenses:
|
32
34
|
- MIT
|
data/spec/hero_spec.rb
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
require "pry"
|
2
|
-
require "grumpy_old_man"
|
3
|
-
Dir[File.join(File.dirname(__FILE__), "..", "lib", "*.rb")].each do |file|
|
4
|
-
require file
|
5
|
-
end
|
6
|
-
|
7
|
-
describe Hero::Formula do
|
8
|
-
include GrumpyOldMan
|
9
|
-
|
10
|
-
before :each do
|
11
|
-
Hero::Formula.reset
|
12
|
-
end
|
13
|
-
|
14
|
-
it "should support reset" do
|
15
|
-
Hero::Formula.register(:test_formula)
|
16
|
-
assert_equal Hero::Formula.count, 1
|
17
|
-
Hero::Formula.reset
|
18
|
-
assert_equal Hero::Formula.count, 0
|
19
|
-
end
|
20
|
-
|
21
|
-
it "should support registering a formula" do
|
22
|
-
Hero::Formula.register(:test_formula)
|
23
|
-
assert_equal Hero::Formula.count, 1
|
24
|
-
assert Hero::Formula[:test_formula].is_a? Hero::Formula
|
25
|
-
end
|
26
|
-
|
27
|
-
it "should auto register formulas" do
|
28
|
-
Hero::Formula[:test_formula]
|
29
|
-
assert_equal Hero::Formula.count, 1
|
30
|
-
assert Hero::Formula[:test_formula].is_a? Hero::Formula
|
31
|
-
end
|
32
|
-
|
33
|
-
it "should support registering N number of formulas" do
|
34
|
-
10.times { |i| Hero::Formula.register("example_#{i}") }
|
35
|
-
assert_equal Hero::Formula.count, 10
|
36
|
-
end
|
37
|
-
|
38
|
-
it "should unregister formula observers on reset" do
|
39
|
-
formula = Hero::Formula[:test_formula]
|
40
|
-
assert_equal Hero::Formula.count, 1
|
41
|
-
formula.add_step {}
|
42
|
-
assert_equal formula.count_observers, 1
|
43
|
-
Hero::Formula.reset
|
44
|
-
assert_equal Hero::Formula.count, 0
|
45
|
-
assert_equal formula.count_observers, 0
|
46
|
-
end
|
47
|
-
|
48
|
-
describe "a registered formula" do
|
49
|
-
it "should support adding steps" do
|
50
|
-
Hero::Formula.register(:test_formula)
|
51
|
-
Hero::Formula[:test_formula].add_step { }
|
52
|
-
end
|
53
|
-
|
54
|
-
it "should support notify" do
|
55
|
-
Hero::Formula.register(:test_formula)
|
56
|
-
step_ran = false
|
57
|
-
target = Object.new
|
58
|
-
Hero::Formula[:test_formula].add_step do |t, opts|
|
59
|
-
assert_equal t, target
|
60
|
-
assert_equal opts[:foo], :bar
|
61
|
-
step_ran = true
|
62
|
-
end
|
63
|
-
Hero::Formula[:test_formula].notify(target, :foo => :bar)
|
64
|
-
assert step_ran
|
65
|
-
end
|
66
|
-
|
67
|
-
end
|
68
|
-
|
69
|
-
end
|
70
|
-
|