ruck 0.2.0 → 0.3.0
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/LICENSE +21 -0
- data/README.markdown +146 -0
- data/Rakefile +11 -0
- data/VERSION +1 -1
- data/examples/ex01.rb +27 -0
- data/examples/ex02.rb +31 -0
- data/examples/ex03.rb +75 -0
- data/examples/ex04.rb +33 -0
- data/examples/ex05.rb +33 -0
- data/examples/ex06.rb +110 -0
- data/examples/space/media/Beep.wav +0 -0
- data/examples/space/media/Space.png +0 -0
- data/examples/space/media/Star.png +0 -0
- data/examples/space/media/Starfighter.bmp +0 -0
- data/examples/space/space.rb +307 -0
- data/lib/ruck.rb +4 -4
- data/lib/ruck/clock.rb +137 -0
- data/lib/ruck/event_clock.rb +39 -0
- data/lib/ruck/shred.rb +135 -0
- data/lib/ruck/shreduler.rb +149 -0
- data/ruck.gemspec +45 -8
- data/spec/clock_spec.rb +218 -0
- data/spec/shred_spec.rb +116 -0
- data/spec/shreduler_spec.rb +200 -0
- metadata +58 -12
- data/README +0 -48
- data/lib/ruck/shreduling.rb +0 -135
data/spec/shred_spec.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
|
2
|
+
require "ruck"
|
3
|
+
|
4
|
+
include Ruck
|
5
|
+
|
6
|
+
describe Shred do
|
7
|
+
context "when calling call" do
|
8
|
+
it "should execute the given block" do
|
9
|
+
$ran = false
|
10
|
+
@shred = Shred.new { $ran = true }
|
11
|
+
@shred.call
|
12
|
+
$ran.should == true
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should resume execution" do
|
16
|
+
$ran = 0
|
17
|
+
|
18
|
+
@shred = Shred.new do
|
19
|
+
$ran = 1
|
20
|
+
Shred.current.pause
|
21
|
+
$ran = 2
|
22
|
+
end
|
23
|
+
|
24
|
+
@shred.call
|
25
|
+
$ran.should == 1
|
26
|
+
@shred.call
|
27
|
+
$ran.should == 2
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should not mind if you run it too many times" do
|
31
|
+
@shred = Shred.new { }
|
32
|
+
@shred.call
|
33
|
+
@shred.call
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should not let you pause the wrong shred" do
|
37
|
+
@shred1 = Shred.new do
|
38
|
+
@shred2.call
|
39
|
+
end
|
40
|
+
@shred2 = Shred.new do
|
41
|
+
@shred1.pause
|
42
|
+
$shred2_ran = true
|
43
|
+
end
|
44
|
+
|
45
|
+
$shred2_ran = false
|
46
|
+
@shred1.call
|
47
|
+
$shred2_ran.should be_true
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should let you use [] instead of #call" do
|
51
|
+
$ran = false
|
52
|
+
@shred = Shred.new { $ran = true }
|
53
|
+
@shred[]
|
54
|
+
$ran.should == true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "when killing" do
|
59
|
+
it "should not resume the next time you call it" do
|
60
|
+
$ran = 0
|
61
|
+
|
62
|
+
@shred = Shred.new do
|
63
|
+
$ran = 1
|
64
|
+
Shred.current.pause
|
65
|
+
$ran = 2
|
66
|
+
end
|
67
|
+
|
68
|
+
@shred.call
|
69
|
+
$ran.should == 1
|
70
|
+
@shred.kill
|
71
|
+
@shred.call
|
72
|
+
$ran.should == 1
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "when checking finished? and running?" do
|
77
|
+
it "finished? should be false just after creation" do
|
78
|
+
@shred = Shred.new { }
|
79
|
+
@shred.finished?.should be_false
|
80
|
+
@shred.running?.should be_true
|
81
|
+
end
|
82
|
+
|
83
|
+
it "finished? should be true just after executing the shred for the last time" do
|
84
|
+
pending "not yet supported in Ruby 1.9 because Fiber#alive? is missing"
|
85
|
+
@shred = Shred.new { }
|
86
|
+
@shred.call
|
87
|
+
@shred.finished?.should be_true
|
88
|
+
@shred.running?.should be_false
|
89
|
+
end
|
90
|
+
|
91
|
+
it "finished? should be true just after killing the shred" do
|
92
|
+
@shred = Shred.new { }
|
93
|
+
@shred.kill
|
94
|
+
@shred.finished?.should be_true
|
95
|
+
@shred.running?.should be_false
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context "when calling a shred from a shred" do
|
100
|
+
it "should update Shred.current appropriately when the inner shred returns" do
|
101
|
+
@shred1 = Shred.new do
|
102
|
+
Shred.current.should == @shred1
|
103
|
+
@shred2.call
|
104
|
+
Shred.current.should == @shred1
|
105
|
+
end
|
106
|
+
@shred2 = Shred.new do
|
107
|
+
Shred.current.should == @shred2
|
108
|
+
$shred2_ran = true
|
109
|
+
end
|
110
|
+
|
111
|
+
$shred2_ran = false
|
112
|
+
@shred1.call
|
113
|
+
$shred2_ran.should be_true
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
|
2
|
+
require "ruck"
|
3
|
+
|
4
|
+
include Ruck
|
5
|
+
|
6
|
+
class MockShred
|
7
|
+
def self.next_name
|
8
|
+
@@next_name ||= "a"
|
9
|
+
name = @@next_name
|
10
|
+
@@next_name = @@next_name.succ
|
11
|
+
name
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(runs_until_finished = 1, shreduler = nil)
|
15
|
+
@name = MockShred.next_name
|
16
|
+
@finished = false
|
17
|
+
@runs_until_finished = runs_until_finished
|
18
|
+
@shreduler = shreduler
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
"MockShred<#{@name}>"
|
23
|
+
end
|
24
|
+
|
25
|
+
def call
|
26
|
+
$runs << self
|
27
|
+
@runs_until_finished -= 1
|
28
|
+
@finished = (@runs_until_finished == 0)
|
29
|
+
@shreduler.shredule(self) unless @finished || @shreduler == nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def finished?
|
33
|
+
@finished
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe Shreduler do
|
38
|
+
before(:each) do
|
39
|
+
@shreduler = Shreduler.new
|
40
|
+
$runs = []
|
41
|
+
end
|
42
|
+
|
43
|
+
context "when calling run" do
|
44
|
+
# this is internal behavior, but should be tested as run_one is an important override point
|
45
|
+
it "should run the shred with run_one" do
|
46
|
+
@shreduler.should_receive(:run_one)
|
47
|
+
@shreduler.run
|
48
|
+
end
|
49
|
+
|
50
|
+
context "with one shred" do
|
51
|
+
it "should run it" do
|
52
|
+
@shred = MockShred.new
|
53
|
+
@shreduler.shredule(@shred)
|
54
|
+
@shreduler.run
|
55
|
+
$runs.should == [@shred]
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should end up at the shred's shreduled time" do
|
59
|
+
@shred = MockShred.new
|
60
|
+
@shreduler.shredule(@shred, 3)
|
61
|
+
@shreduler.run
|
62
|
+
@shreduler.now.should == 3
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "with multiple shreds" do
|
67
|
+
it "should run them in order if shreduled in order" do
|
68
|
+
@shreds = [MockShred.new, MockShred.new]
|
69
|
+
@shreduler.shredule(@shreds[0], 0)
|
70
|
+
@shreduler.shredule(@shreds[1], 1)
|
71
|
+
@shreduler.run
|
72
|
+
|
73
|
+
$runs.should == [@shreds[0], @shreds[1]]
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should run them in order if shreduled out of order" do
|
77
|
+
@shreds = [MockShred.new, MockShred.new]
|
78
|
+
@shreduler.shredule(@shreds[1], 1)
|
79
|
+
@shreduler.shredule(@shreds[0], 0)
|
80
|
+
@shreduler.run
|
81
|
+
|
82
|
+
$runs.should == [@shreds[0], @shreds[1]]
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should run them until they are finished" do
|
86
|
+
@shred = MockShred.new(5, @shreduler)
|
87
|
+
@shreduler.shredule(@shred, 0)
|
88
|
+
@shreduler.run
|
89
|
+
|
90
|
+
$runs.should == (1..5).map { @shred }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context "when calling run_one" do
|
96
|
+
it "should only run one shred" do
|
97
|
+
@shreds = [MockShred.new, MockShred.new]
|
98
|
+
@shreduler.shredule(@shreds[1], 1)
|
99
|
+
@shreduler.shredule(@shreds[0], 0)
|
100
|
+
@shreduler.run_one
|
101
|
+
|
102
|
+
$runs.should == [@shreds[0]]
|
103
|
+
end
|
104
|
+
|
105
|
+
# fast_forward is protected, but a crucial override point, so should be tested
|
106
|
+
it "should call fast_forward before executing the shred" do
|
107
|
+
$runs_when_fast_forward_triggered = nil
|
108
|
+
|
109
|
+
@shreds = [MockShred.new]
|
110
|
+
@shreduler.shredule(@shreds[0], 1)
|
111
|
+
@shreduler.should_receive(:fast_forward).with(1).and_return { $runs_when_fast_forward_triggered = $runs.dup; nil }
|
112
|
+
@shreduler.run_one
|
113
|
+
|
114
|
+
$runs_when_fast_forward_triggered.should == []
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "when unshreduling" do
|
119
|
+
it "should work" do
|
120
|
+
@shred = MockShred.new
|
121
|
+
@shreduler.shredule(@shred)
|
122
|
+
@shreduler.unshredule(@shred)
|
123
|
+
@shreduler.run
|
124
|
+
$runs.should == []
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context "when convenient" do
|
129
|
+
before(:each) do
|
130
|
+
@shreduler = Shreduler.new
|
131
|
+
@shreduler.make_convenient
|
132
|
+
end
|
133
|
+
|
134
|
+
context "when scheduling with spork" do
|
135
|
+
it "should let you schedule with spork" do
|
136
|
+
$ran = false
|
137
|
+
spork { $ran = true }
|
138
|
+
@shreduler.run
|
139
|
+
$ran.should be_true
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context "when scheduling with spork_loop" do
|
144
|
+
it "should let you schedule a looping shred with spork_loop" do
|
145
|
+
$ran = 0
|
146
|
+
spork_loop do
|
147
|
+
$ran += 1
|
148
|
+
if $ran == 3
|
149
|
+
Shred.current.kill
|
150
|
+
else
|
151
|
+
Shred.yield(1)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
@shreduler.run
|
156
|
+
$ran.should == 3
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should let you specify an amount to automatically yield after before run" do
|
160
|
+
$ran = 0
|
161
|
+
spork_loop(1) do
|
162
|
+
$ran += 1
|
163
|
+
Shred.current.kill if $ran == 3
|
164
|
+
end
|
165
|
+
|
166
|
+
@shreduler.run
|
167
|
+
$ran.should == 3
|
168
|
+
@shreduler.now.should == 3
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context "when waiting on events" do
|
173
|
+
it "should let you wait with Shred.wait_on" do
|
174
|
+
$ran = false
|
175
|
+
spork do
|
176
|
+
Shred.wait_on(:booger)
|
177
|
+
$ran = true
|
178
|
+
end
|
179
|
+
@shreduler.run
|
180
|
+
$ran.should be_false
|
181
|
+
@shreduler.raise_all(:booger)
|
182
|
+
@shreduler.run
|
183
|
+
$ran.should be_true
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should let you raise an event with raise_event" do
|
187
|
+
$ran = false
|
188
|
+
spork do
|
189
|
+
Shred.wait_on(:booger)
|
190
|
+
$ran = true
|
191
|
+
end
|
192
|
+
@shreduler.run
|
193
|
+
$ran.should be_false
|
194
|
+
raise_event(:booger)
|
195
|
+
@shreduler.run
|
196
|
+
$ran.should be_true
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruck
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 19
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
|
-
-
|
8
|
+
- 3
|
8
9
|
- 0
|
9
|
-
version: 0.
|
10
|
+
version: 0.3.0
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Tom Lieber
|
@@ -14,26 +15,58 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-
|
18
|
+
date: 2010-08-15 00:00:00 -07:00
|
18
19
|
default_executable:
|
19
|
-
dependencies:
|
20
|
-
|
21
|
-
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: PriorityQueue
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description: " Ruck uses continuations and a simple scheduler to ensure \"shreds\"\n (Ruck threads) are woken at precisely the right time according\n to its virtual clock. Schedulers can map virtual time to samples\n in a WAV file, real time, time in a MIDI file, or anything else\n by overriding \"sim_to\" in the Shreduler class.\n \n A small library of useful unit generators and plenty of examples\n are provided. See the README or the web page for details.\n"
|
22
36
|
email: tom@alltom.com
|
23
37
|
executables: []
|
24
38
|
|
25
39
|
extensions: []
|
26
40
|
|
27
41
|
extra_rdoc_files:
|
28
|
-
-
|
42
|
+
- LICENSE
|
43
|
+
- README.markdown
|
29
44
|
files:
|
30
45
|
- .gitignore
|
31
|
-
-
|
46
|
+
- LICENSE
|
47
|
+
- README.markdown
|
32
48
|
- Rakefile
|
33
49
|
- VERSION
|
50
|
+
- examples/ex01.rb
|
51
|
+
- examples/ex02.rb
|
52
|
+
- examples/ex03.rb
|
53
|
+
- examples/ex04.rb
|
54
|
+
- examples/ex05.rb
|
55
|
+
- examples/ex06.rb
|
56
|
+
- examples/space/media/Beep.wav
|
57
|
+
- examples/space/media/Space.png
|
58
|
+
- examples/space/media/Star.png
|
59
|
+
- examples/space/media/Starfighter.bmp
|
60
|
+
- examples/space/space.rb
|
34
61
|
- lib/ruck.rb
|
35
|
-
- lib/ruck/
|
62
|
+
- lib/ruck/clock.rb
|
63
|
+
- lib/ruck/event_clock.rb
|
64
|
+
- lib/ruck/shred.rb
|
65
|
+
- lib/ruck/shreduler.rb
|
36
66
|
- ruck.gemspec
|
67
|
+
- spec/clock_spec.rb
|
68
|
+
- spec/shred_spec.rb
|
69
|
+
- spec/shreduler_spec.rb
|
37
70
|
has_rdoc: true
|
38
71
|
homepage: http://github.com/alltom/ruck
|
39
72
|
licenses: []
|
@@ -44,25 +77,38 @@ rdoc_options:
|
|
44
77
|
require_paths:
|
45
78
|
- lib
|
46
79
|
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
47
81
|
requirements:
|
48
82
|
- - ">="
|
49
83
|
- !ruby/object:Gem::Version
|
84
|
+
hash: 3
|
50
85
|
segments:
|
51
86
|
- 0
|
52
87
|
version: "0"
|
53
88
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
54
90
|
requirements:
|
55
91
|
- - ">="
|
56
92
|
- !ruby/object:Gem::Version
|
93
|
+
hash: 3
|
57
94
|
segments:
|
58
95
|
- 0
|
59
96
|
version: "0"
|
60
97
|
requirements: []
|
61
98
|
|
62
99
|
rubyforge_project:
|
63
|
-
rubygems_version: 1.3.
|
100
|
+
rubygems_version: 1.3.7
|
64
101
|
signing_key:
|
65
102
|
specification_version: 3
|
66
103
|
summary: "strong timing for Ruby: cooperative threads on a virtual clock"
|
67
|
-
test_files:
|
68
|
-
|
104
|
+
test_files:
|
105
|
+
- spec/clock_spec.rb
|
106
|
+
- spec/shred_spec.rb
|
107
|
+
- spec/shreduler_spec.rb
|
108
|
+
- examples/ex01.rb
|
109
|
+
- examples/ex02.rb
|
110
|
+
- examples/ex03.rb
|
111
|
+
- examples/ex04.rb
|
112
|
+
- examples/ex05.rb
|
113
|
+
- examples/ex06.rb
|
114
|
+
- examples/space/space.rb
|
data/README
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
A port of ChucK's strong timing to Ruby!
|
2
|
-
|
3
|
-
ruck lets you easily create a virtual clock against
|
4
|
-
which precise timing of execution can be guaranteed. The
|
5
|
-
virtual clock can be kept roughly in sync with real time
|
6
|
-
if you want it to, but you don't have to. For example, in
|
7
|
-
ChucK, even if your scripts take too long to generate
|
8
|
-
samples in real time, the audio stream isn't compromised
|
9
|
-
because it's timed against the virtual clock.
|
10
|
-
|
11
|
-
ruck provides an easily extended scheduler for non-preemptive
|
12
|
-
shreds (threads) implemented using continuations.
|
13
|
-
Cooperatively scheduled threads usually don't require
|
14
|
-
synchronization since execution is never interrupted.
|
15
|
-
|
16
|
-
A few example shredulers (schedulers) are provided:
|
17
|
-
|
18
|
-
UGenShreduler:
|
19
|
-
|
20
|
-
Recently moved to its own gem: ruck-ugen
|
21
|
-
|
22
|
-
RealTimeShreduler
|
23
|
-
|
24
|
-
Recently moved to its own gem: ruck-realtime
|
25
|
-
|
26
|
-
MIDIShreduler
|
27
|
-
|
28
|
-
Recently moved to its own gem: ruck-midi
|
29
|
-
|
30
|
-
GLAppShreduler
|
31
|
-
|
32
|
-
You don't want to know. But see ruck-glapp if you do.
|
33
|
-
|
34
|
-
USAGE
|
35
|
-
=====
|
36
|
-
|
37
|
-
This project has tons of examples because even though the library
|
38
|
-
is tiny, there's a lot to test, and a lot of ways to use it.
|
39
|
-
|
40
|
-
For examples of how to implement your own Shreduler, see the bottom
|
41
|
-
of lib/ruck/shreduling.rb and all the scripts in bin/.
|
42
|
-
|
43
|
-
For example of how to use the provided Shredulers, check the
|
44
|
-
examples/ directory. Those scripts contain no boilerplate includes
|
45
|
-
because they are written to be invoked on the command line by
|
46
|
-
one of the bin/ scripts. For example:
|
47
|
-
|
48
|
-
$ ruck_ugen examples/ugen/ex01.rb
|