cheeky-dreams 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,7 +1,6 @@
1
1
  source "http://rubygems.org"
2
2
  # Add dependencies required to use your gem here.
3
3
  # Example:
4
- gem "flt", ">= 1.3.0"
5
4
 
6
5
  # Add dependencies to develop your gem here.
7
6
  # Include everything needed to run rake, tests, features, etc.
data/Gemfile.lock CHANGED
@@ -2,7 +2,6 @@ GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
4
  diff-lcs (1.1.3)
5
- flt (1.3.0)
6
5
  git (1.2.5)
7
6
  jeweler (1.6.4)
8
7
  bundler (~> 1.0)
@@ -24,7 +23,6 @@ PLATFORMS
24
23
 
25
24
  DEPENDENCIES
26
25
  bundler (~> 1.0.0)
27
- flt (>= 1.3.0)
28
26
  jeweler (~> 1.6.4)
29
27
  rcov
30
28
  rspec (>= 2.3.0)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.2
@@ -0,0 +1,65 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "cheeky-dreams"
8
+ s.version = "0.0.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["simon"]
12
+ s.date = "2011-11-02"
13
+ s.description = "For controlling dream cheeky usb light"
14
+ s.email = "simojenki@gmail.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE.txt",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "cheeky-dreams.gemspec",
29
+ "lib/cheeky-dreams.rb",
30
+ "spec/cheeky-dreams_spec.rb",
31
+ "spec/spec_helper.rb",
32
+ "spec/support/collecting_auditor.rb",
33
+ "spec/support/stub_driver.rb",
34
+ "spec/support/stub_effect.rb",
35
+ "spec/support/within.rb",
36
+ "spec/test.rb"
37
+ ]
38
+ s.homepage = "http://github.com/simojenki/cheeky-dreams"
39
+ s.licenses = ["MIT"]
40
+ s.require_paths = ["lib"]
41
+ s.rubygems_version = "1.8.10"
42
+ s.summary = "For controlling dream cheeky usb light"
43
+
44
+ if s.respond_to? :specification_version then
45
+ s.specification_version = 3
46
+
47
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
48
+ s.add_development_dependency(%q<rspec>, [">= 2.3.0"])
49
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
50
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
51
+ s.add_development_dependency(%q<rcov>, [">= 0"])
52
+ else
53
+ s.add_dependency(%q<rspec>, [">= 2.3.0"])
54
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
55
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
56
+ s.add_dependency(%q<rcov>, [">= 0"])
57
+ end
58
+ else
59
+ s.add_dependency(%q<rspec>, [">= 2.3.0"])
60
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
61
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
62
+ s.add_dependency(%q<rcov>, [">= 0"])
63
+ end
64
+ end
65
+
data/lib/cheeky-dreams.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'thread'
2
- require 'flt'
3
2
 
4
3
  module CheekyDreams
5
4
 
@@ -18,13 +17,20 @@ module CheekyDreams
18
17
  position_between(a[2], b[2], ratio),
19
18
  ]
20
19
  end
20
+ def rgb_between a, b, ratio
21
+ CheekyDreams::rgb_between a, b, ratio
22
+ end
21
23
 
22
24
  def self.position_between a, b, ratio
23
25
  return b if ratio >= 1.0
24
- ((b - a) * ratio) + a
26
+ (((b - a) * ratio) + a).floor
27
+ end
28
+ def position_between a, b, ratio
29
+ CheekyDreams::position_between a, b, ratio
25
30
  end
26
31
 
27
32
  COLOURS = {
33
+ :off => rgb(0, 0, 0),
28
34
  :red => rgb(255, 0, 0),
29
35
  :green => rgb(0, 255, 0),
30
36
  :blue => rgb(0, 0, 255),
@@ -41,7 +47,8 @@ module CheekyDreams
41
47
  raise "Unknown colour '#{colour}'" unless COLOURS.has_key?(colour)
42
48
  COLOURS[colour]
43
49
  when Array
44
- colour
50
+ raise "Invalid rgb #{colour}" unless colour.length == 3 && colour.all? { |c| c.is_a? Fixnum }
51
+ rgb(colour[0], colour[1], colour[2])
45
52
  else
46
53
  raise "Unsupported colour type #{colour}"
47
54
  end
@@ -50,6 +57,15 @@ module CheekyDreams
50
57
  CheekyDreams::rgb_for colour
51
58
  end
52
59
 
60
+ def stderr_auditor
61
+ Class.new do
62
+ def unhandled_error e
63
+ STDERR.puts e.message
64
+ STDERR.puts e.backtrace.join("\n")
65
+ end
66
+ end.new
67
+ end
68
+
53
69
  def stdout_driver
54
70
  Class.new do
55
71
  def go rgb
@@ -68,81 +84,157 @@ module CheekyDreams
68
84
  end.new
69
85
  end
70
86
 
71
- def cycle colours, freq
72
- Effect::Cycle.new colours, freq
73
- end
74
-
75
- def solid colour
76
- Effect::Solid.new colour
77
- end
78
-
79
- def fade from, to, over_how_long
80
- Effect::Fade.new from, to, over_how_long
87
+ def find_dream_cheeky_usb_device
88
+ Device::DreamCheeky.new(File.dirname(Dir.glob('/sys/devices/**/red').first))
81
89
  end
82
90
 
83
- def func freq, &block
84
- Effect::Func.new freq, block
91
+ module Device
92
+ class DreamCheeky
93
+ attr_reader :path
94
+ def initialize path, max_threshold = 50
95
+ @path, @max_threshold = path, max_threshold
96
+ end
97
+
98
+ def go rgb
99
+ system "echo #{using_threshold(rgb[0])} > #{path}/red"
100
+ system "echo #{using_threshold(rgb[1])} > #{path}/green"
101
+ system "echo #{using_threshold(rgb[2])} > #{path}/blue"
102
+ end
103
+
104
+ private
105
+ def using_threshold colour
106
+ colour if @threshold == 255
107
+ ((colour / 255.to_f) * @max_threshold).floor
108
+ end
109
+ end
85
110
  end
86
111
 
87
112
  module Effect
88
113
  class Effect
89
- include Flt
90
114
  include CheekyDreams
115
+ include Math
116
+
117
+ attr_reader :freq
118
+
119
+ def initialize freq
120
+ @freq = freq
121
+ end
91
122
  end
92
123
 
124
+ class Throb2 < Effect
125
+ def initialize freq, amplitude, centre
126
+ super freq
127
+ @amplitude, @centre, @count = amplitude, centre, 1
128
+ end
129
+
130
+ def next current_colour
131
+ x = freq * (@count += 1)
132
+ v = sin(x) * @amplitude + @centre
133
+ [v, 0, 0]
134
+ end
135
+ end
136
+
137
+ class Throb < Effect
138
+ attr_reader :r_amp, :r_centre, :g_amp, :g_centre, :b_amp, :b_centre
139
+
140
+ def initialize freq, from, to
141
+ super freq
142
+ @r_centre, @r_amp = centre_and_amp from[0], to[0]
143
+ @g_centre, @g_amp = centre_and_amp from[1], to[1]
144
+ @b_centre, @b_amp = centre_and_amp from[2], to[2]
145
+ @count = 1
146
+ end
147
+
148
+ def next current_colour
149
+ x = sin(freq * @count)
150
+ r = x * r_amp + r_centre
151
+ g = x * g_amp + g_centre
152
+ b = x * b_amp + b_centre
153
+ # x = freq * (@count += 1)
154
+ # v = sin(x) * @amplitude + @centre
155
+ # [v, 0, 0]
156
+
157
+ @count += 1
158
+ [r, g, b]
159
+ end
160
+
161
+ private
162
+ def centre_and_amp from, to
163
+ amp = ((from - to).abs / 2.0).floor
164
+ centre = max(from, to) - amp
165
+ [centre, amp]
166
+ end
167
+
168
+ def max a, b
169
+ a > b ? a : b
170
+ end
171
+ end
172
+
173
+
93
174
  class Func < Effect
94
- def initialize freq, block
95
- @freq, @block, @last_change = freq, block, Time.at(0)
175
+ def initialize freq, &block
176
+ super freq
177
+ @block = block
96
178
  end
97
179
 
98
- def next
99
- now = Time.now
100
- if (now - @last_change) >= (DecNum(1)/DecNum(@freq))
101
- @last_change = now
102
- @current = rgb_for(@block.yield)
103
- end
104
- @current
180
+ def next current_colour = nil
181
+ rgb_for(@block.yield(current_colour))
105
182
  end
106
183
  end
107
184
 
108
185
  class Solid < Effect
109
186
  def initialize colour
110
- @rgb = CheekyDreams::rgb_for(colour)
187
+ super 1
188
+ @rgb = rgb_for(colour)
111
189
  end
112
- def next
190
+
191
+ def next current_colour = nil
113
192
  @rgb
114
193
  end
115
194
  end
116
195
 
117
196
  class Cycle < Effect
118
197
  def initialize colours, freq
119
- @colours, @freq, @last_change = colours.cycle, freq, Time.at(0)
198
+ super freq
199
+ @cycle = colours.cycle
120
200
  end
121
201
 
122
- def next
123
- now = Time.now
124
- if (now - @last_change) >= (DecNum(1)/DecNum(@freq))
125
- @last_change = now
126
- @current = rgb_for(@colours.next)
127
- end
128
- @current
202
+ def next current_colour = nil
203
+ rgb_for(@cycle.next)
129
204
  end
130
205
  end
131
206
 
132
207
  class Fade < Effect
133
- def initialize from, to, over_how_long
134
- @from, @to, @over_how_long = from, to, over_how_long
208
+ def initialize from, to, steps, freq
209
+ super freq
210
+ @rgb_from, @rgb_to = rgb_for(from), rgb_for(to)
211
+ @fade = [@rgb_from]
212
+ (1..(steps-1)).each { |i| @fade << rgb_between(@rgb_from, @rgb_to, i / steps.to_f) }
213
+ @fade << @rgb_to
214
+ @index = 0
135
215
  end
136
216
 
137
- def next
138
- now = Time.now
139
- if @started_at == nil
140
- @started_at = now
141
- @from
142
- else
143
- ratio_done = (now - @started_at) / @over_how_long
144
- CheekyDreams.rgb_between(rgb_for(@from), rgb_for(@to), ratio_done)
217
+ def next current_colour = nil
218
+ return @rgb_to if @index >= @fade.length
219
+ next_colour = @fade[@index]
220
+ @index += 1
221
+ next_colour
222
+ end
223
+ end
224
+
225
+ class FadeTo < Effect
226
+ def initialize to, steps, freq
227
+ super freq
228
+ @to, @steps = to, steps
229
+ @fade = nil
230
+ end
231
+
232
+ def next current_colour
233
+ unless @fade
234
+ @fade = Fade.new(current_colour, @to, @steps, freq)
235
+ @fade.next current_colour
145
236
  end
237
+ @fade.next current_colour
146
238
  end
147
239
  end
148
240
  end
@@ -151,53 +243,84 @@ end
151
243
  class Light
152
244
 
153
245
  include CheekyDreams
154
- include Flt
155
246
 
156
- def initialize driver, freq = 5
157
- @driver = driver
247
+ attr_accessor :freq, :auditor
248
+
249
+ def initialize driver
250
+ @driver, @freq, @auditor = driver, 100, stderr_auditor
158
251
  @lock = Mutex.new
159
252
  @effect = nil
160
253
  @on = false
161
- @freq = freq
162
254
  end
163
255
 
164
- def go colour
256
+ def cycle colours, freq = 1
257
+ go(Effect::Cycle.new(colours, freq))
258
+ end
259
+
260
+ def solid colour
261
+ go(Effect::Solid.new(colour))
262
+ end
263
+
264
+ def fade from, to, steps = 10, freq = 1
265
+ go Effect::Fade.new from, to, steps, freq
266
+ end
267
+
268
+ def fade_to to, steps = 10, freq = 1
269
+ go Effect::FadeTo.new to, steps, freq
270
+ end
271
+
272
+ def func freq = 1, &block
273
+ go Effect::Func.new freq, &block
274
+ end
275
+
276
+ def throb freq, amplitude, centre
277
+ go Effect::Throb.new freq, amplitude, centre
278
+ end
279
+
280
+ def go effect
165
281
  @lock.synchronize {
166
- case colour
282
+ case effect
167
283
  when Symbol
168
- @effect = solid(colour)
284
+ @effect = Effect::Solid.new(effect)
169
285
  when Array
170
- @effect = solid(colour)
286
+ @effect = Effect::Solid.new(effect)
171
287
  when Effect::Effect
172
- @effect = colour
288
+ @effect = effect
173
289
  else
174
- raise "Im sorry dave, I'm afraid I can't do that. #{colour}"
290
+ raise "Im sorry dave, I'm afraid I can't do that. #{effect}"
175
291
  end
176
292
  }
293
+ turn_on unless @on
177
294
  end
178
295
 
179
- def on
296
+ private
297
+ def turn_on
180
298
  @on = true
181
- t = Thread.new do
299
+ Thread.new do
300
+ current_effect = nil
182
301
  last_colour = nil
302
+ next_colour_time = nil
183
303
  while @on
184
304
  begin
185
305
  @lock.synchronize {
186
- if @effect
187
- new_colour = @effect.next
188
- if new_colour != last_colour
189
- @driver.go new_colour
190
- last_colour = new_colour
191
- end
306
+ if @effect && current_effect != @effect
307
+ current_effect = @effect
308
+ next_colour_time = Time.at(0)
192
309
  end
193
310
  }
311
+ if current_effect
312
+ if Time.now > next_colour_time
313
+ new_colour = current_effect.next(last_colour)
314
+ @driver.go new_colour
315
+ last_colour = new_colour
316
+ next_colour_time = Time.now + (1 / current_effect.freq.to_f)
317
+ end
318
+ end
194
319
  rescue => e
195
- puts e.message
196
- puts e.backtrace.join("\n")
320
+ auditor.unhandled_error e
197
321
  end
198
- sleep (DecNum(1)/DecNum(@freq))
322
+ sleep (1 / freq.to_f)
199
323
  end
200
324
  end
201
325
  end
202
-
203
326
  end
@@ -1,64 +1,401 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  describe CheekyDreams do
4
+
5
+ include CheekyDreams
6
+
7
+ describe "find_dream_cheeky_usb_device" do
8
+ it "should locate the rgb files and return the driver" do
9
+ Dir.should_receive(:glob).with('/sys/devices/**/red').and_return(["/sys/devices/some-crazy-pci-bus-stuff/red"])
10
+ driver = find_dream_cheeky_usb_device
11
+ driver.path.should == "/sys/devices/some-crazy-pci-bus-stuff"
12
+ end
13
+ end
14
+
4
15
  describe "position_between" do
16
+ it "should calculate the position as hole numbers" do
17
+ position_between(100, 102, 0.25).should == 100
18
+ position_between(100, 102, 0.5).should == 101
19
+ position_between(100, 102, 0.75).should == 101
20
+ position_between(100, 102, 1).should == 102
21
+ end
22
+
5
23
  it "should calculate the position" do
6
- CheekyDreams.position_between(100, 110, 0.5).should == 105
7
- CheekyDreams.position_between(110, 100, 0.5).should == 105
8
- CheekyDreams.position_between(100, 0, 0.2).should == 80
9
- CheekyDreams.position_between(100, 0, 0.25).should == 75
10
- CheekyDreams.position_between(100, 0, 1).should == 0
24
+ position_between(100, 110, 0.5).should == 105
25
+ position_between(110, 100, 0.5).should == 105
26
+ position_between(100, 0, 0.2).should == 80
27
+ position_between(100, 0, 0.25).should == 75
28
+ position_between(100, 0, 1).should == 0
11
29
  end
12
30
 
13
31
  it "should return the end point when the ration goes above 1" do
14
- CheekyDreams.position_between(100, 1, 1.01).should == 1
15
- CheekyDreams.position_between(96, 99, 999).should == 99
32
+ position_between(100, 1, 1.01).should == 1
33
+ position_between(96, 99, 999).should == 99
16
34
  end
17
35
  end
18
36
 
19
37
  describe "rgb_between" do
20
38
  it "should calculate the position" do
21
- CheekyDreams.rgb_between([100, 50, 0], [110, 20, 0], 0.1).should == [101, 47, 0]
39
+ rgb_between([100, 50, 0], [110, 20, 0], 0.1).should == [101, 47, 0]
40
+ end
41
+ end
42
+
43
+ describe "rgb_for" do
44
+ it "should return rgb array for simple colours" do
45
+ rgb_for(:off).should == [0, 0, 0]
46
+ rgb_for(:red).should == [255, 0, 0]
47
+ rgb_for(:green).should == [0, 255, 0]
48
+ rgb_for(:blue).should == [0, 0, 255]
49
+ end
50
+
51
+ it "should return an rgb array when given one" do
52
+ rgb_for([11, 34, 111]).should == [11, 34, 111]
53
+ end
54
+
55
+ it "should blow up when given meaningless symbol" do
56
+ lambda { rgb_for(:purple_patch) }.should raise_error "Unknown colour 'purple_patch'"
57
+ end
58
+
59
+ it "should blow up when given array that isnt rgb" do
60
+ lambda { rgb_for([1]) }.should raise_error "Invalid rgb [1]"
61
+ lambda { rgb_for([1, 2, 3, 4]) }.should raise_error "Invalid rgb [1, 2, 3, 4]"
62
+ lambda { rgb_for(["a", "b", "c"]) }.should raise_error 'Invalid rgb ["a", "b", "c"]'
63
+ end
64
+
65
+ it "should blow up when given invalid rgb values" do
66
+ lambda { rgb_for([0, 256, 0]) }.should raise_error "Invalid rgb value 0, 256, 0"
67
+ lambda { rgb_for([-1, 0, 0]) }.should raise_error "Invalid rgb value -1, 0, 0"
68
+ lambda { rgb_for([0, 0, 256]) }.should raise_error "Invalid rgb value 0, 0, 256"
22
69
  end
23
70
  end
24
71
  end
25
72
 
26
- describe Light do
73
+ module CheekyDreams::Device
74
+
75
+ include CheekyDreams
76
+
77
+ describe DreamCheeky do
78
+ before :each do
79
+ @device_path = "/device"
80
+ end
81
+
82
+ describe "when the max threshold is 100" do
83
+ before :each do
84
+ @device = DreamCheeky.new @device_path, 100
85
+ end
86
+
87
+ describe "making it go 123,34,255" do
88
+ before :each do
89
+ @device.should_receive(:system).with("echo 48 > /device/red").and_return(true)
90
+ @device.should_receive(:system).with("echo 13 > /device/green").and_return(true)
91
+ @device.should_receive(:system).with("echo 100 > /device/blue").and_return(true)
92
+ end
93
+
94
+ it "should turn the light the correct colours" do
95
+ @device.go [123, 34, 255]
96
+ end
97
+ end
98
+
99
+ describe "making it go 0,1,100" do
100
+ before :each do
101
+ @device.should_receive(:system).with("echo 0 > /device/red").and_return(true)
102
+ @device.should_receive(:system).with("echo 0 > /device/green").and_return(true)
103
+ @device.should_receive(:system).with("echo 39 > /device/blue").and_return(true)
104
+ end
105
+
106
+ it "should turn the light the correct colours" do
107
+ @device.go [0, 1, 100]
108
+ end
109
+ end
110
+ end
111
+
112
+ describe "when the max threshold is 255" do
113
+ before :each do
114
+ @device = DreamCheeky.new @device_path, 255
115
+ end
116
+
117
+ describe "making it go 123,34,255" do
118
+ before :each do
119
+ @device.should_receive(:system).with("echo 123 > /device/red").and_return(true)
120
+ @device.should_receive(:system).with("echo 34 > /device/green").and_return(true)
121
+ @device.should_receive(:system).with("echo 255 > /device/blue").and_return(true)
122
+ end
123
+
124
+ it "should turn the light the correct colours" do
125
+ @device.go [123, 34, 255]
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ module CheekyDreams::Effect
27
133
 
28
134
  include CheekyDreams
29
135
 
30
- class StubDriver
136
+ describe Solid do
137
+ describe "when is symbols" do
138
+ before :each do
139
+ @solid = Solid.new :purple
140
+ end
31
141
 
32
- def initialize
33
- @lock = Mutex.new
142
+ it "should return rgb every time called" do
143
+ @solid.next.should == COLOURS[:purple]
144
+ @solid.next.should == COLOURS[:purple]
145
+ @solid.next.should == COLOURS[:purple]
146
+ end
147
+ end
148
+
149
+ describe "when is random rgb value" do
150
+ before :each do
151
+ @solid = Solid.new [123, 123, 123]
152
+ end
153
+
154
+ it "should return rgb every time called" do
155
+ @solid.next.should == [123, 123, 123]
156
+ @solid.next.should == [123, 123, 123]
157
+ @solid.next.should == [123, 123, 123]
158
+ end
159
+ end
160
+ end
161
+
162
+ describe Cycle do
163
+ before :each do
164
+ @cycle = Cycle.new [:red, :blue, [211, 192, 101]], 22
34
165
  end
166
+
167
+ it "should have a frequency" do
168
+ @cycle.freq.should == 22
169
+ end
170
+
171
+ it "should cycle through the colours as rgb" do
172
+ @cycle.next.should == [255, 0, 0]
173
+ @cycle.next.should == [ 0, 0,255]
174
+ @cycle.next.should == [211, 192, 101]
175
+
176
+ @cycle.next.should == [255, 0, 0]
177
+ @cycle.next.should == [ 0, 0,255]
178
+ @cycle.next.should == [211, 192, 101]
179
+
180
+ @cycle.next.should == [255, 0, 0]
181
+ @cycle.next.should == [ 0, 0,255]
182
+ @cycle.next.should == [211, 192, 101]
183
+ end
184
+ end
185
+
186
+ describe Fade do
187
+ describe "fading between two symbols" do
188
+ before :each do
189
+ @fade = Fade.new :blue, :green, 1, 5
190
+ end
35
191
 
36
- def go colour
37
- @lock.synchronize {
38
- @colour = colour
39
- }
192
+ it "should have a freq of 2" do
193
+ @fade.freq.should == 5
194
+ end
195
+
196
+ it "should be able to provide the steps when asked" do
197
+ @fade.next.should == [ 0, 0, 255]
198
+ @fade.next.should == [ 0, 255, 0]
199
+ @fade.next.should == [ 0, 255, 0]
200
+ @fade.next.should == [ 0, 255, 0]
201
+ end
40
202
  end
41
203
 
42
- def should_become expected_colour
43
- rgb = expected_colour.is_a?(Symbol) ? Light::COLOURS[expected_colour] : expected_colour
44
- start, match = Time.now, false
45
- while ((Time.now - start < 1) && !match) do
46
- @lock.synchronize {
47
- match = rgb == @colour
48
- }
49
- sleep 0.01
204
+ describe "fading between two arbitary rgb values" do
205
+ before :each do
206
+ @fade = Fade.new [100, 100, 100], [110, 90, 0], 10, 2
207
+ end
208
+
209
+ it "should have a freq of 2" do
210
+ @fade.freq.should == 2
50
211
  end
51
- raise "Expected driver to become #{rgb}, and didn't, instead is #{@colour}" unless match
212
+
213
+ it "should be able to provide the steps when asked" do
214
+ @fade.next.should == [100, 100, 100]
215
+ @fade.next.should == [101, 99, 90]
216
+ @fade.next.should == [102, 98, 80]
217
+ @fade.next.should == [103, 97, 70]
218
+ @fade.next.should == [104, 96, 60]
219
+ @fade.next.should == [105, 95, 50]
220
+ @fade.next.should == [106, 94, 40]
221
+ @fade.next.should == [107, 93, 30]
222
+ @fade.next.should == [108, 92, 20]
223
+ @fade.next.should == [109, 91, 10]
224
+ @fade.next.should == [110, 90, 0]
225
+ # and then continue to provide the same colour
226
+ @fade.next.should == [110, 90, 0]
227
+ @fade.next.should == [110, 90, 0]
228
+ end
52
229
  end
53
230
  end
54
231
 
55
- describe "changing colour" do
232
+ describe FadeTo do
233
+ describe "fading to a symbol" do
234
+ before :each do
235
+ @fade_to = FadeTo.new :green, 11, 2
236
+ end
237
+
238
+ it "should have a freq of 2" do
239
+ @fade_to.freq.should == 2
240
+ end
241
+
242
+ it "should be able to gradually go to colour when asked" do
243
+ @fade_to.next([0, 145, 0]).should == [0, 155, 0]
244
+ @fade_to.next([0, 155, 0]).should == [0, 165, 0]
245
+ @fade_to.next([0, 165, 0]).should == [0, 175, 0]
246
+ @fade_to.next([0, 175, 0]).should == [0, 185, 0]
247
+ @fade_to.next([0, 185, 0]).should == [0, 195, 0]
248
+ @fade_to.next([0, 195, 0]).should == [0, 205, 0]
249
+ @fade_to.next([0, 205, 0]).should == [0, 215, 0]
250
+ @fade_to.next([0, 215, 0]).should == [0, 225, 0]
251
+ @fade_to.next([0, 225, 0]).should == [0, 235, 0]
252
+ @fade_to.next([0, 235, 0]).should == [0, 245, 0]
253
+ @fade_to.next([0, 245, 0]).should == [0, 255, 0]
254
+ end
255
+ end
256
+
257
+ describe "fading to a random rgb" do
258
+ before :each do
259
+ @fade_to = FadeTo.new [130, 80, 170], 3, 20
260
+ end
261
+
262
+ it "should have a freq of 20" do
263
+ @fade_to.freq.should == 20
264
+ end
265
+
266
+ it "should be able to gradually go to colour when asked" do
267
+ @fade_to.next([190, 77, 140]).should == [170, 78, 150]
268
+ @fade_to.next([170, 78, 150]).should == [150, 79, 160]
269
+ @fade_to.next([150, 79, 160]).should == [130, 80, 170]
270
+ end
271
+ end
272
+ end
273
+
274
+ describe Func do
275
+ describe "when the block return rgb values" do
276
+ before :each do
277
+ @func = Func.new(22) { |current_colour| [current_colour[0] + 1, current_colour[1] + 1, current_colour[2] + 1] }
278
+ end
279
+
280
+ it "should have a freq of 22" do
281
+ @func.freq.should == 22
282
+ end
283
+
284
+ it "should return the given rgb plus 1 to each of r, g, and b" do
285
+ @func.next([2, 5, 6]).should == [3, 6, 7]
286
+ end
287
+ end
288
+
289
+ describe "when the block return symbol" do
290
+ before :each do
291
+ @func = Func.new(22) { |current_colour| :purple }
292
+ end
293
+
294
+ it "should return the rgb for the symbol" do
295
+ @func.next([2, 5, 6]).should == COLOURS[:purple]
296
+ end
297
+ end
298
+ end
299
+
300
+ describe Throb do
301
+ before :each do
302
+ @throb = Throb.new 10, [100, 100, 100], [250, 50, 0]
303
+ end
304
+
305
+ it "should have r_amp" do
306
+ @throb.r_amp.should == 75
307
+ end
308
+
309
+ it "should have r_centre" do
310
+ @throb.r_centre.should == 175
311
+ end
312
+
313
+ it "should have g_amp" do
314
+ @throb.g_amp.should == 25
315
+ end
316
+
317
+ it "should have g_centre" do
318
+ @throb.g_centre.should == 75
319
+ end
320
+
321
+ it "should have b_amp" do
322
+ @throb.b_amp.should == 50
323
+ end
324
+
325
+ it "should have b_centre" do
326
+ @throb.b_centre.should == 50
327
+ end
328
+ end
329
+ end
330
+
331
+ describe Light do
332
+
333
+ include CheekyDreams
334
+ include Within
335
+
336
+ before :each do
337
+ @driver = StubDriver.new
338
+ @light = Light.new @driver
339
+ end
340
+
341
+ describe "unhandled errors" do
56
342
  before :each do
57
- @driver = StubDriver.new
58
- @light = Light.new @driver
59
- @light.on
343
+ @error = RuntimeError.new "On purpose error"
344
+ @effect = StubEffect.new(20) { raise @error }
345
+ @auditor = CollectingAuditor.new
346
+ @light.auditor = @auditor
347
+ end
348
+
349
+ it 'should notify the auditor' do
350
+ @light.go @effect
351
+ within(1, "auditor should have received '#{@error}'") { [@auditor.has_received?(@error), @auditor.errors] }
352
+ end
353
+ end
354
+
355
+ describe "frequency of effect" do
356
+ describe 'when frequency is 1' do
357
+ before :each do
358
+ @effect = StubEffect.new 1
359
+ end
360
+
361
+ it 'should call the effect almost immediately, and then about 1 second later' do
362
+ @light.go @effect
363
+ sleep 0.1
364
+ @effect.asked_for_colour_count.should be == 1
365
+ sleep 1
366
+ @effect.asked_for_colour_count.should be == 2
367
+ end
368
+ end
369
+
370
+ describe 'when frequency is 10' do
371
+ before :each do
372
+ @effect = StubEffect.new 10
373
+ end
374
+
375
+ it 'should call the effect between 9 and 11 times in the next second' do
376
+ @light.go @effect
377
+ sleep 1
378
+ count = @effect.asked_for_colour_count
379
+ count.should be <= 11
380
+ count.should be >= 9
381
+ end
60
382
  end
61
383
 
384
+ describe 'when frequency is 5' do
385
+ before :each do
386
+ @effect = StubEffect.new 5
387
+ end
388
+
389
+ it 'should call the effect 5 times in the next second' do
390
+ @light.go @effect
391
+ sleep 1
392
+ count = @effect.asked_for_colour_count
393
+ count.should be == 5
394
+ end
395
+ end
396
+ end
397
+
398
+ describe "changing colour" do
62
399
  it "should go red" do
63
400
  @light.go :red
64
401
  @driver.should_become [255,0,0]
@@ -89,7 +426,7 @@ describe Light do
89
426
  end
90
427
 
91
428
  it "should be able to cycle between colours when specified as rgb" do
92
- @light.go cycle([[255, 255, 255], [200, 200, 200], [100, 100, 100]], 10)
429
+ @light.cycle([[255, 255, 255], [200, 200, 200], [100, 100, 100]], 10)
93
430
  @driver.should_become [255, 255, 255]
94
431
  @driver.should_become [200, 200, 200]
95
432
  @driver.should_become [100, 100, 100]
@@ -98,7 +435,7 @@ describe Light do
98
435
  end
99
436
 
100
437
  it "should be able to cycle between colours when specified as symbols" do
101
- @light.go cycle([:red, :green, :blue], 10)
438
+ @light.cycle([:red, :green, :blue], 10)
102
439
  @driver.should_become :red
103
440
  @driver.should_become :green
104
441
  @driver.should_become :blue
@@ -106,6 +443,37 @@ describe Light do
106
443
  @driver.should_become :green
107
444
  end
108
445
 
446
+ it "should be able to fade from one colour to another" do
447
+ @light.fade([100, 100, 0], [105, 95, 0], 5, 2)
448
+ @driver.should_become [101, 99, 0]
449
+ @driver.should_become [102, 98, 0]
450
+ @driver.should_become [103, 97, 0]
451
+ @driver.should_become [104, 96, 0]
452
+ @driver.should_become [105, 95, 0]
453
+ end
454
+
455
+ it "should be able to fade from current colour to a new colour" do
456
+ @light.go [100, 100, 0]
457
+ @driver.should_become [100, 100, 0]
458
+
459
+ @light.fade_to([105, 95, 0], 5, 2)
460
+ @driver.should_become [101, 99, 0]
461
+ @driver.should_become [102, 98, 0]
462
+ @driver.should_become [103, 97, 0]
463
+ @driver.should_become [104, 96, 0]
464
+ @driver.should_become [105, 95, 0]
465
+ end
466
+
467
+ it "should be able to go different colours based on a function" do
468
+ cycle = [:blue, :red, :green, :purple, :grey, :aqua].cycle
469
+ @light.func(10) { cycle.next }
470
+ @driver.should_become :blue
471
+ @driver.should_become :red
472
+ @driver.should_become :green
473
+ @driver.should_become :purple
474
+ @driver.should_become :grey
475
+ @driver.should_become :aqua
476
+ end
109
477
  end
110
478
  end
111
479
 
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
2
  $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'support'))
3
4
  require 'rspec'
4
5
  require 'cheeky-dreams'
5
6
 
@@ -0,0 +1,18 @@
1
+
2
+
3
+ class CollectingAuditor
4
+
5
+ attr_reader :errors
6
+
7
+ def initialize
8
+ @errors = []
9
+ end
10
+
11
+ def unhandled_error e
12
+ @errors << e
13
+ end
14
+
15
+ def has_received? e
16
+ @errors.include? e
17
+ end
18
+ end
@@ -0,0 +1,27 @@
1
+ require 'within'
2
+
3
+ class StubDriver
4
+
5
+ include Within
6
+
7
+ def initialize
8
+ @lock = Mutex.new
9
+ @colours = []
10
+ end
11
+
12
+ def go colour
13
+ @lock.synchronize {
14
+ @colour = colour
15
+ @colours << colour
16
+ }
17
+ end
18
+
19
+ def should_become expected_colour
20
+ rgb = expected_colour.is_a?(Symbol) ? CheekyDreams::COLOURS[expected_colour] : expected_colour
21
+ within 1, "driver to become #{rgb}" do
22
+ @lock.synchronize {
23
+ [rgb == @colour, @colour]
24
+ }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+
2
+ class StubEffect < CheekyDreams::Effect::Effect
3
+
4
+ attr_reader :freq, :asked_for_colour_count
5
+
6
+ def initialize freq, &block
7
+ @freq, @asked_for_colour_count = freq, 0
8
+ if block
9
+ @block = block
10
+ else
11
+ @block = lambda { [0,0,0] }
12
+ end
13
+ end
14
+
15
+ def next last_colour
16
+ @asked_for_colour_count += 1
17
+ @block.yield
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ module Within
2
+ def within seconds, what
3
+ raise 'errm, was expected a block to perform condition on' unless block_given?
4
+ start, match, hint = Time.now, false, "[you didnt specify]"
5
+ while (((Time.now - start) < seconds) && !match) do
6
+ match, hint = yield
7
+ end
8
+ raise "Expected #{what}, within #{seconds} second, instead was #{hint}" unless match
9
+ end
10
+ end
data/spec/test.rb CHANGED
@@ -7,8 +7,15 @@ require 'rainbow'
7
7
 
8
8
  include CheekyDreams
9
9
 
10
- light = Light.new(ansi_driver, 10)
11
- light.on
10
+ light = Light.new find_dream_cheeky_usb_device
11
+
12
+ puts "throbbing"
13
+ light.throb(2, [255,0,0],[0, 255, 0])
14
+ sleep 10
15
+
16
+ puts "is it red"
17
+ light.go [255,0,0]
18
+ sleep 1
12
19
 
13
20
  puts "colours"
14
21
  [:red, :green, :blue, CheekyDreams::rgb(155, 155, 155)].each do |colour|
@@ -17,25 +24,31 @@ puts "colours"
17
24
  end
18
25
 
19
26
  puts "cycle"
20
- light.go(cycle(CheekyDreams::COLOURS.keys, 2))
27
+ light.cycle(CheekyDreams::COLOURS.keys, 2)
21
28
  sleep 5
22
29
 
23
30
  puts "fade"
24
- light.go(fade(:green, :red, 2))
31
+ light.fade(:green, :red, 20, 2)
25
32
  sleep 3
26
- light.go(fade(:red, :green, 2))
33
+ light.fade(:red, :green, 20, 2)
27
34
  sleep 3
28
35
 
29
36
  puts "fade from current to somewhere"
37
+ light.go :red
38
+ sleep 2
39
+ light.fade_to :green, 20, 2
40
+ sleep 2
41
+ light.fade_to :red, 20, 2
42
+ sleep 2
30
43
 
31
44
  puts "colour on block"
32
45
  cycle = [:blue, :purple].cycle
33
- light.go(func(2) { cycle.next })
46
+ light.func(2) { cycle.next }
34
47
  sleep 3
35
48
 
36
49
  puts "fake cpu cycles"
37
50
  green = 100
38
- light.go(func(2) { [0, green+=20, 0] })
51
+ light.func(2) { [0, green+=20, 0] }
39
52
  sleep 3
40
53
 
41
54
  puts "done"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cheeky-dreams
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,22 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-28 00:00:00.000000000Z
12
+ date: 2011-11-02 00:00:00.000000000Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: flt
16
- requirement: &2158506720 !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: 1.3.0
22
- type: :runtime
23
- prerelease: false
24
- version_requirements: *2158506720
25
14
  - !ruby/object:Gem::Dependency
26
15
  name: rspec
27
- requirement: &2158503320 !ruby/object:Gem::Requirement
16
+ requirement: &2168510760 !ruby/object:Gem::Requirement
28
17
  none: false
29
18
  requirements:
30
19
  - - ! '>='
@@ -32,10 +21,10 @@ dependencies:
32
21
  version: 2.3.0
33
22
  type: :development
34
23
  prerelease: false
35
- version_requirements: *2158503320
24
+ version_requirements: *2168510760
36
25
  - !ruby/object:Gem::Dependency
37
26
  name: bundler
38
- requirement: &2158502820 !ruby/object:Gem::Requirement
27
+ requirement: &2168510280 !ruby/object:Gem::Requirement
39
28
  none: false
40
29
  requirements:
41
30
  - - ~>
@@ -43,10 +32,10 @@ dependencies:
43
32
  version: 1.0.0
44
33
  type: :development
45
34
  prerelease: false
46
- version_requirements: *2158502820
35
+ version_requirements: *2168510280
47
36
  - !ruby/object:Gem::Dependency
48
37
  name: jeweler
49
- requirement: &2158502260 !ruby/object:Gem::Requirement
38
+ requirement: &2168509720 !ruby/object:Gem::Requirement
50
39
  none: false
51
40
  requirements:
52
41
  - - ~>
@@ -54,10 +43,10 @@ dependencies:
54
43
  version: 1.6.4
55
44
  type: :development
56
45
  prerelease: false
57
- version_requirements: *2158502260
46
+ version_requirements: *2168509720
58
47
  - !ruby/object:Gem::Dependency
59
48
  name: rcov
60
- requirement: &2158501660 !ruby/object:Gem::Requirement
49
+ requirement: &2160044460 !ruby/object:Gem::Requirement
61
50
  none: false
62
51
  requirements:
63
52
  - - ! '>='
@@ -65,7 +54,7 @@ dependencies:
65
54
  version: '0'
66
55
  type: :development
67
56
  prerelease: false
68
- version_requirements: *2158501660
57
+ version_requirements: *2160044460
69
58
  description: For controlling dream cheeky usb light
70
59
  email: simojenki@gmail.com
71
60
  executables: []
@@ -82,9 +71,14 @@ files:
82
71
  - README.rdoc
83
72
  - Rakefile
84
73
  - VERSION
74
+ - cheeky-dreams.gemspec
85
75
  - lib/cheeky-dreams.rb
86
76
  - spec/cheeky-dreams_spec.rb
87
77
  - spec/spec_helper.rb
78
+ - spec/support/collecting_auditor.rb
79
+ - spec/support/stub_driver.rb
80
+ - spec/support/stub_effect.rb
81
+ - spec/support/within.rb
88
82
  - spec/test.rb
89
83
  homepage: http://github.com/simojenki/cheeky-dreams
90
84
  licenses:
@@ -101,7 +95,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
101
95
  version: '0'
102
96
  segments:
103
97
  - 0
104
- hash: 1122224430567929941
98
+ hash: -1596020986901995182
105
99
  required_rubygems_version: !ruby/object:Gem::Requirement
106
100
  none: false
107
101
  requirements: