em-scenario 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 +65 -0
- data/lib/scenario/core.rb +12 -0
- data/lib/scenario/iterator.rb +23 -0
- data/lib/scenario/latin.rb +197 -0
- data/lib/scenario/multi.rb +25 -0
- data/lib/scenario/sequence.rb +58 -0
- data/lib/scenario/timer.rb +24 -0
- data/lib/scenario.rb +5 -193
- metadata +14 -8
data/README.md
CHANGED
@@ -7,9 +7,74 @@ Names use kitchen's latin, because it's more leet then japanese words.
|
|
7
7
|
|
8
8
|
Ruby 1.9.2 is used, it may work with ruby 1.8.x
|
9
9
|
|
10
|
+
Scenario use the bleeding edge version of event machine, the 1.0.0.beta3, with few informations from Google, checkout the source and build the doc yourself.
|
11
|
+
Some of this patterns are now in Event Machine, with a verbose syntax and without chainability. I'll try to don't rebuild the wheel and use it.
|
12
|
+
|
10
13
|
Tools
|
11
14
|
-----
|
12
15
|
|
16
|
+
### Multi
|
17
|
+
|
18
|
+
Just like the Multi tool in _em-http-request_ and _em-synchrony_.
|
19
|
+
You can launch any deferrable.
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
EM.run do
|
23
|
+
m = EM::Scenario::Multi.new
|
24
|
+
stack = []
|
25
|
+
m.add(EM::Scenario::Timer.new(Random.rand(0.1)) do
|
26
|
+
stack << 1
|
27
|
+
end)
|
28
|
+
m.add(EM::Scenario::Timer.new(Random.rand(0.1)) do
|
29
|
+
stack << 2
|
30
|
+
end)
|
31
|
+
m.add(EM::Scenario::Timer.new(Random.rand(0.1)) do
|
32
|
+
stack << 3
|
33
|
+
end)
|
34
|
+
m.callback do
|
35
|
+
assert [1,2,3] == stack.sort
|
36
|
+
EM.stop
|
37
|
+
end
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
### Sequence
|
42
|
+
|
43
|
+
No stairs, just a sequence of deferrables.
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
EM.run do
|
47
|
+
stack = []
|
48
|
+
EM::Scenario::Sequence.new do
|
49
|
+
EM::Scenario::Timer.new(0.4) do
|
50
|
+
stack << 1
|
51
|
+
end
|
52
|
+
end.then do
|
53
|
+
EM::Scenario::Timer.new(0.3) do
|
54
|
+
stack << 2
|
55
|
+
end
|
56
|
+
end.then do |iter|
|
57
|
+
EM::Scenario::Timer.new(0.2) do
|
58
|
+
stack << 3
|
59
|
+
iter.return 42 #you can return values for the next step
|
60
|
+
end
|
61
|
+
end.then do |iter, n|
|
62
|
+
assert n == 42 # and retrieve it
|
63
|
+
EM::Scenario::Timer.new(0.1) do
|
64
|
+
stack << 4
|
65
|
+
end
|
66
|
+
end.then do
|
67
|
+
assert (1..4).to_a == stack
|
68
|
+
EM.stop
|
69
|
+
end
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
Experimentations
|
74
|
+
----------------
|
75
|
+
|
76
|
+
Strange and experimental tools with strange names. Most are specific and redundant iterator. Some guinea pigs could die soon.
|
77
|
+
|
13
78
|
### Quorum
|
14
79
|
|
15
80
|
Do something when n actions are done.
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "eventmachine"
|
2
|
+
|
3
|
+
module EventMachine
|
4
|
+
module Scenario
|
5
|
+
|
6
|
+
class Iterator
|
7
|
+
include EM::Deferrable
|
8
|
+
|
9
|
+
def initialize array, workers=10, &block
|
10
|
+
@datas = array
|
11
|
+
@action = block
|
12
|
+
@workers = workers
|
13
|
+
end
|
14
|
+
|
15
|
+
def finally &block
|
16
|
+
EM::Iterator.new(@datas, @workers).map(
|
17
|
+
@action, block
|
18
|
+
)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require "eventmachine"
|
2
|
+
require "scenario/core"
|
3
|
+
|
4
|
+
# @see http://en.wikipedia.org/wiki/List_of_Latin_phrases
|
5
|
+
|
6
|
+
module EventMachine
|
7
|
+
module Scenario
|
8
|
+
|
9
|
+
# from the start
|
10
|
+
# Sequences of actions.
|
11
|
+
class AbInitio
|
12
|
+
include EM::Deferrable
|
13
|
+
|
14
|
+
def initialize &block
|
15
|
+
@actions = AbInitioActions.new
|
16
|
+
block.call @actions
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def nextStep
|
21
|
+
if @actions.actions.length > 0
|
22
|
+
@actions.actions.pop.succeed(Proc.new { nextStep })
|
23
|
+
else
|
24
|
+
self.succeed
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def finally &block
|
29
|
+
self.callback &block
|
30
|
+
@actions.actions.reverse!
|
31
|
+
self.nextStep
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class AbInitioActions
|
36
|
+
attr_accessor :actions
|
37
|
+
def initialize
|
38
|
+
@actions = []
|
39
|
+
end
|
40
|
+
|
41
|
+
def then &block
|
42
|
+
d = EM::DefaultDeferrable.new
|
43
|
+
d.callback(&block)
|
44
|
+
@actions << d
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
#Trigger when a quota of actions is done
|
51
|
+
class Quorum < Scenario
|
52
|
+
include EM::Deferrable
|
53
|
+
|
54
|
+
def initialize times, &block
|
55
|
+
@times = times
|
56
|
+
@loop = block
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def finally &block
|
61
|
+
self.callback(&block)
|
62
|
+
@loop.call( Proc.new {nextStep} )
|
63
|
+
end
|
64
|
+
|
65
|
+
protected
|
66
|
+
def nextStep
|
67
|
+
@times -= 1
|
68
|
+
self.succeed(self) if @times == 0
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# As much as enough.
|
73
|
+
# You wont lots of parralel workers, but not too much.
|
74
|
+
class QuantumSatis
|
75
|
+
include EM::Deferrable
|
76
|
+
|
77
|
+
def initialize times, throttle=nil, &block
|
78
|
+
@opened = 0
|
79
|
+
@finished = 0
|
80
|
+
@worker = 0
|
81
|
+
@times = times
|
82
|
+
@throttle = throttle
|
83
|
+
@loop = block
|
84
|
+
@debug = false
|
85
|
+
end
|
86
|
+
|
87
|
+
def finally &block
|
88
|
+
self.callback &block
|
89
|
+
if @throttle
|
90
|
+
@throttle.times{ call }
|
91
|
+
else
|
92
|
+
@times.times{ call }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
protected
|
97
|
+
def call
|
98
|
+
@worker += 1
|
99
|
+
@loop.call Proc.new{nextStep}, @opened, @worker
|
100
|
+
@opened += 1
|
101
|
+
if @debug
|
102
|
+
puts "worker: #{@worker} opened: #{@opened} finished: #{@finished}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def nextStep
|
107
|
+
puts "ending" if @debug
|
108
|
+
@finished += 1
|
109
|
+
@worker -= 1
|
110
|
+
if @finished == @times
|
111
|
+
self.succeed
|
112
|
+
else
|
113
|
+
call if @opened < @times
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
# Repeat sequentially an action
|
120
|
+
class AdLib
|
121
|
+
include EM::Deferrable
|
122
|
+
|
123
|
+
def initialize times, &block
|
124
|
+
@cpt = 0
|
125
|
+
@times = times
|
126
|
+
@loop = block
|
127
|
+
self
|
128
|
+
end
|
129
|
+
|
130
|
+
def finally &block
|
131
|
+
self.callback(&block)
|
132
|
+
self.nextStep
|
133
|
+
end
|
134
|
+
|
135
|
+
def nextStep
|
136
|
+
if @cpt == @times
|
137
|
+
self.succeed
|
138
|
+
else
|
139
|
+
@loop.call( Proc.new {nextStep}, @cpt)
|
140
|
+
@cpt += 1
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Until sick. Act again and again, until criteria
|
146
|
+
class AdNauseum
|
147
|
+
include EM::Deferrable
|
148
|
+
|
149
|
+
def initialize &block
|
150
|
+
@loop = block
|
151
|
+
self
|
152
|
+
end
|
153
|
+
|
154
|
+
def until &block
|
155
|
+
@criteria = block
|
156
|
+
self
|
157
|
+
end
|
158
|
+
|
159
|
+
def finally &block
|
160
|
+
self.callback &block
|
161
|
+
@loop.call( Proc.new { nextStep })
|
162
|
+
self
|
163
|
+
end
|
164
|
+
|
165
|
+
def nextStep
|
166
|
+
if @criteria.call
|
167
|
+
self.succeed
|
168
|
+
else
|
169
|
+
@loop.call( Proc.new { nextStep })
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def quorum(times, &block)
|
178
|
+
EventMachine::Scenario::Quorum.new times, &block
|
179
|
+
end
|
180
|
+
|
181
|
+
def adlib(times, &block)
|
182
|
+
EventMachine::Scenario::AdLib.new times, &block
|
183
|
+
end
|
184
|
+
|
185
|
+
def abinitio(&block)
|
186
|
+
EventMachine::Scenario::AbInitio.new &block
|
187
|
+
end
|
188
|
+
|
189
|
+
alias sequence abinitio
|
190
|
+
|
191
|
+
def adnauseum(&block)
|
192
|
+
EventMachine::Scenario::AdNauseum.new &block
|
193
|
+
end
|
194
|
+
|
195
|
+
def quantumsatis(times, throttle=nil, &block)
|
196
|
+
EventMachine::Scenario::QuantumSatis.new times, throttle, &block
|
197
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "eventmachine"
|
2
|
+
|
3
|
+
module EventMachine
|
4
|
+
module Scenario
|
5
|
+
|
6
|
+
# Just like with em-http-request
|
7
|
+
class Multi
|
8
|
+
include EM::Deferrable
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@actions = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def add deferable
|
15
|
+
@actions += 1
|
16
|
+
deferable.callback do
|
17
|
+
@actions -= 1
|
18
|
+
self.succeed if @actions == 0
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "eventmachine"
|
2
|
+
|
3
|
+
module EventMachine
|
4
|
+
module Scenario
|
5
|
+
|
6
|
+
class Sequence
|
7
|
+
include EM::Deferrable
|
8
|
+
|
9
|
+
# block must return a deferrable
|
10
|
+
def initialize &block
|
11
|
+
@action = []
|
12
|
+
@bag = Bag.new
|
13
|
+
block.call(@bag).callback do
|
14
|
+
@action[0].call
|
15
|
+
end
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def then &block
|
20
|
+
size = @action.size + 1
|
21
|
+
@bag.incr
|
22
|
+
@action << proc {
|
23
|
+
defer = block.call(@bag, *@bag[size+1])
|
24
|
+
if size < @action.length
|
25
|
+
defer.callback do
|
26
|
+
@action[size].call
|
27
|
+
end
|
28
|
+
end
|
29
|
+
}
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
class Bag
|
37
|
+
|
38
|
+
def initialize
|
39
|
+
@datas = []
|
40
|
+
@poz = 0
|
41
|
+
end
|
42
|
+
|
43
|
+
def incr
|
44
|
+
@poz +=1
|
45
|
+
end
|
46
|
+
|
47
|
+
def return *data
|
48
|
+
@datas[@poz] = data
|
49
|
+
end
|
50
|
+
|
51
|
+
def [] poz
|
52
|
+
@datas[poz]
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "eventmachine"
|
2
|
+
|
3
|
+
module EventMachine
|
4
|
+
module Scenario
|
5
|
+
|
6
|
+
class Timer
|
7
|
+
|
8
|
+
include EM::Deferrable
|
9
|
+
|
10
|
+
def initialize timer, &block
|
11
|
+
self.callback &block
|
12
|
+
@id = EM.add_timer(timer) do
|
13
|
+
self.succeed
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def cancel
|
18
|
+
EM.cancel_timer @id
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
data/lib/scenario.rb
CHANGED
@@ -1,195 +1,7 @@
|
|
1
1
|
require "eventmachine"
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
10
|
-
|
11
|
-
# from the start
|
12
|
-
class AbInitio
|
13
|
-
include EM::Deferrable
|
14
|
-
|
15
|
-
def initialize &block
|
16
|
-
@actions = AbInitioActions.new
|
17
|
-
block.call @actions
|
18
|
-
self
|
19
|
-
end
|
20
|
-
|
21
|
-
def nextStep
|
22
|
-
if @actions.actions.length > 0
|
23
|
-
@actions.actions.pop.succeed(Proc.new { nextStep })
|
24
|
-
else
|
25
|
-
self.succeed
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def finally &block
|
30
|
-
self.callback &block
|
31
|
-
@actions.actions.reverse!
|
32
|
-
self.nextStep
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
class AbInitioActions
|
37
|
-
attr_accessor :actions
|
38
|
-
def initialize
|
39
|
-
@actions = []
|
40
|
-
end
|
41
|
-
|
42
|
-
def then &block
|
43
|
-
d = EM::DefaultDeferrable.new
|
44
|
-
d.callback(&block)
|
45
|
-
@actions << d
|
46
|
-
self
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
#Trigger when a quota of actions is done
|
52
|
-
class Quorum < Scenario
|
53
|
-
include EM::Deferrable
|
54
|
-
|
55
|
-
def initialize times, &block
|
56
|
-
@times = times
|
57
|
-
@loop = block
|
58
|
-
self
|
59
|
-
end
|
60
|
-
|
61
|
-
def finally &block
|
62
|
-
self.callback(&block)
|
63
|
-
@loop.call( Proc.new {nextStep} )
|
64
|
-
end
|
65
|
-
|
66
|
-
protected
|
67
|
-
def nextStep
|
68
|
-
@times -= 1
|
69
|
-
self.succeed(self) if @times == 0
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
# As much as enough.
|
74
|
-
# You wont lots of parralel workers, but not too much.
|
75
|
-
class QuantumSatis
|
76
|
-
include EM::Deferrable
|
77
|
-
|
78
|
-
def initialize times, throttle=nil, &block
|
79
|
-
@opened = 0
|
80
|
-
@finished = 0
|
81
|
-
@worker = 0
|
82
|
-
@times = times
|
83
|
-
@throttle = throttle
|
84
|
-
@loop = block
|
85
|
-
@debug = false
|
86
|
-
end
|
87
|
-
|
88
|
-
def finally &block
|
89
|
-
self.callback &block
|
90
|
-
if @throttle
|
91
|
-
@throttle.times{ call }
|
92
|
-
else
|
93
|
-
@times.times{ call }
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
protected
|
98
|
-
def call
|
99
|
-
@worker += 1
|
100
|
-
@loop.call Proc.new{nextStep}, @opened, @worker
|
101
|
-
@opened += 1
|
102
|
-
if @debug
|
103
|
-
puts "worker: #{@worker} opened: #{@opened} finished: #{@finished}"
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def nextStep
|
108
|
-
puts "ending" if @debug
|
109
|
-
@finished += 1
|
110
|
-
@worker -= 1
|
111
|
-
if @finished == @times
|
112
|
-
self.succeed
|
113
|
-
else
|
114
|
-
call if @opened < @times
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
end
|
119
|
-
|
120
|
-
# Repeat sequentially an action
|
121
|
-
class AdLib
|
122
|
-
include EM::Deferrable
|
123
|
-
|
124
|
-
def initialize times, &block
|
125
|
-
@cpt = 0
|
126
|
-
@times = times
|
127
|
-
@loop = block
|
128
|
-
self
|
129
|
-
end
|
130
|
-
|
131
|
-
def finally &block
|
132
|
-
self.callback(&block)
|
133
|
-
self.nextStep
|
134
|
-
end
|
135
|
-
|
136
|
-
def nextStep
|
137
|
-
if @cpt == @times
|
138
|
-
self.succeed
|
139
|
-
else
|
140
|
-
@loop.call( Proc.new {nextStep}, @cpt)
|
141
|
-
@cpt += 1
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
# Until sick. Act again and again, until criteria
|
147
|
-
class AdNauseum
|
148
|
-
include EM::Deferrable
|
149
|
-
|
150
|
-
def initialize &block
|
151
|
-
@loop = block
|
152
|
-
self
|
153
|
-
end
|
154
|
-
|
155
|
-
def until &block
|
156
|
-
@criteria = block
|
157
|
-
self
|
158
|
-
end
|
159
|
-
|
160
|
-
def finally &block
|
161
|
-
self.callback &block
|
162
|
-
@loop.call( Proc.new { nextStep })
|
163
|
-
self
|
164
|
-
end
|
165
|
-
|
166
|
-
def nextStep
|
167
|
-
if @criteria.call
|
168
|
-
self.succeed
|
169
|
-
else
|
170
|
-
@loop.call( Proc.new { nextStep })
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
def quorum(times, &block)
|
178
|
-
EventMachine::Scenario::Quorum.new times, &block
|
179
|
-
end
|
180
|
-
|
181
|
-
def adlib(times, &block)
|
182
|
-
EventMachine::Scenario::AdLib.new times, &block
|
183
|
-
end
|
184
|
-
|
185
|
-
def abinitio(&block)
|
186
|
-
EventMachine::Scenario::AbInitio.new &block
|
187
|
-
end
|
188
|
-
|
189
|
-
def adnauseum(&block)
|
190
|
-
EventMachine::Scenario::AdNauseum.new &block
|
191
|
-
end
|
192
|
-
|
193
|
-
def quantumsatis(times, throttle=nil, &block)
|
194
|
-
EventMachine::Scenario::QuantumSatis.new times, throttle, &block
|
195
|
-
end
|
3
|
+
require "scenario/iterator"
|
4
|
+
require "scenario/multi"
|
5
|
+
require "scenario/timer"
|
6
|
+
require "scenario/sequence"
|
7
|
+
require "scenario/latin"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: em-scenario
|
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,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-09-
|
12
|
+
date: 2011-09-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine
|
16
|
-
requirement: &
|
16
|
+
requirement: &2152036200 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - =
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 1.0.0.beta3
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2152036200
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: minitest
|
27
|
-
requirement: &
|
27
|
+
requirement: &2160363040 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '2.0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *2160363040
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rake
|
38
|
-
requirement: &
|
38
|
+
requirement: &2160375720 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *2160375720
|
47
47
|
description: Handling simpler story with event machine's callback
|
48
48
|
email: mathieu@garambrogne.net
|
49
49
|
executables: []
|
@@ -53,6 +53,12 @@ extra_rdoc_files:
|
|
53
53
|
files:
|
54
54
|
- README.md
|
55
55
|
- Gemfile
|
56
|
+
- lib/scenario/core.rb
|
57
|
+
- lib/scenario/iterator.rb
|
58
|
+
- lib/scenario/latin.rb
|
59
|
+
- lib/scenario/multi.rb
|
60
|
+
- lib/scenario/sequence.rb
|
61
|
+
- lib/scenario/timer.rb
|
56
62
|
- lib/scenario.rb
|
57
63
|
homepage: http://github.com/athoune/em-scenario
|
58
64
|
licenses: []
|