musical_spec 0.0.1

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 ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in musical_spec.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # musical_spec
2
+
3
+ An RSpec formatter that plays higher notes as tests pass and lower notes as
4
+ tests fail. A revelatory auditory experience! It's a subclass of the progress
5
+ formatter, so you still get your pretty dots.
6
+
7
+ ## Usage
8
+
9
+ This uses [Bloopsaphone](https://github.com/mental/bloopsaphone), which requires PortAudio:
10
+
11
+ brew install portaudio
12
+
13
+ In your Gemfile:
14
+
15
+ gem 'musical_spec'
16
+
17
+ Then run your specs like this:
18
+
19
+ $ rspec --format MusicalSpec::Formatter spec/
20
+
21
+ To always run your specs with MusicalSpec, add the option to your `.rspec` file:
22
+
23
+ --format MusicalSpec::Formatter
24
+
25
+ ## Caveats
26
+
27
+ Due to Bloopsaphone wackiness, this library has to `sleep` while the sounds
28
+ are playing. This will probably slow your test suite down a LOT.
29
+ Also, sometimes the sounds can't keep up with the test suite and some of the
30
+ sounds don't play. The dots still show up though.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+ class SoundPlayer
3
+ def initialize
4
+ @bloopsaphone = Bloops.new
5
+ @sound = @bloopsaphone.sound(Bloops::SQUARE)
6
+ @sound.sustain = 1
7
+
8
+ @note = Note.new
9
+ end
10
+
11
+ def start
12
+ 200.times do
13
+ @bloopsaphone.clear
14
+ if success?
15
+ @note.next!
16
+ else
17
+ @note.next!
18
+ # @note.prev!
19
+ end
20
+
21
+ @bloopsaphone.tune(@sound, @note.note)
22
+ @bloopsaphone.play
23
+ sleep 0.1 #while !@bloopsaphone.stopped?
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def success?
30
+ rand(2) == 0
31
+ end
32
+ end
33
+
34
+
35
+ player = SoundPlayer.new
36
+ player.start
@@ -0,0 +1,54 @@
1
+ # RSpec will only require this file, so require the rest of the gem.
2
+ require 'musical_spec'
3
+ require 'rspec/core/formatters/progress_formatter'
4
+
5
+ module MusicalSpec
6
+ class Formatter < RSpec::Core::Formatters::ProgressFormatter
7
+ def initialize(output)
8
+ super(output)
9
+ @note = MusicalSpec::Note.new
10
+ @player = MusicalSpec::Player.new
11
+ end
12
+
13
+ attr_reader :player
14
+
15
+ # Plays a higher note and then calls super.
16
+ def example_passed(example)
17
+ play_higher_note
18
+ super(example)
19
+ end
20
+
21
+ # Plays a lower note and then calls super.
22
+ def example_failed(example)
23
+ play_lower_note
24
+ super(example)
25
+ end
26
+
27
+ # Plays a note without changing the pitch and then calls super.
28
+ def example_pending(example)
29
+ play_note
30
+ super(example)
31
+ end
32
+
33
+ private
34
+
35
+ def play_note
36
+ @player.play(note)
37
+ end
38
+
39
+ def play_higher_note
40
+ @note.next!
41
+ play_note
42
+ end
43
+
44
+ def play_lower_note
45
+ @note.prev!
46
+ play_note
47
+ end
48
+
49
+
50
+ def note
51
+ @note.note
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,67 @@
1
+ module MusicalSpec
2
+ # Uses scientific notation, e.g. C4 is middle C.
3
+ class Note
4
+ # The highest octave this will play. If it goes higher than this, it will be
5
+ # normalized back down to MAX_OCTAVE.
6
+ MAX_OCTAVE = 6
7
+ # The lowest octave this will play. If it goes lower than this, it will be
8
+ # normalized back up to MIN_OCTAVE.
9
+ MIN_OCTAVE = 2
10
+
11
+ def initialize
12
+ @letter = 'C'
13
+ @octave = 4
14
+ end
15
+
16
+ # A string like "C4". Octave (the number) does not go above MAX_OCTAVE or
17
+ # below MIN_OCTAVE.
18
+ def note
19
+ "#{@letter}#{normalized_octave}"
20
+ end
21
+
22
+ # Set the note to a new one, e.g. `note.note = 'A5'`
23
+ def note=(new_note)
24
+ new_letter, new_octave = new_note.split('')
25
+ @letter = new_letter
26
+ @octave = new_octave.to_i
27
+
28
+ note
29
+ end
30
+
31
+ # Increase the pitch, handling octave changes.
32
+ def next!
33
+ if @letter == 'B'
34
+ @letter = 'C'
35
+ @octave += 1
36
+ elsif @letter == 'G'
37
+ @letter = 'A'
38
+ else
39
+ @letter.next!
40
+ end
41
+ end
42
+
43
+ # Decrease the pitch, handling octave changes.
44
+ def prev!
45
+ if @letter == 'C'
46
+ @letter = 'B'
47
+ @octave -= 1
48
+ elsif @letter == 'A'
49
+ @letter = 'G'
50
+ else
51
+ @letter = (@letter.ord - 1).chr
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def normalized_octave
58
+ if @octave > MAX_OCTAVE
59
+ MAX_OCTAVE
60
+ elsif @octave < MIN_OCTAVE
61
+ MIN_OCTAVE
62
+ else
63
+ @octave
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,18 @@
1
+ module MusicalSpec
2
+ # This class plays the music using Bloopsaphone.
3
+ class Player
4
+ def initialize
5
+ @sound = ONE_TRUE_BLOOPSAPHONE.sound(Bloops::SQUARE)
6
+ @sound.sustain = 0.01
7
+ ONE_TRUE_BLOOPSAPHONE.tempo = 320
8
+ end
9
+
10
+ # Takes a note string like "C4".
11
+ def play(note)
12
+ ONE_TRUE_BLOOPSAPHONE.clear
13
+ ONE_TRUE_BLOOPSAPHONE.tune(@sound, note)
14
+ ONE_TRUE_BLOOPSAPHONE.play
15
+ sleep 0.01 while ! ONE_TRUE_BLOOPSAPHONE.stopped?
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module MusicalSpec
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,10 @@
1
+ require 'bloops'
2
+
3
+ require "musical_spec/note"
4
+ require "musical_spec/player"
5
+ require "musical_spec/formatter"
6
+ require "musical_spec/version"
7
+
8
+ module MusicalSpec
9
+ ONE_TRUE_BLOOPSAPHONE = Bloops.new
10
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/musical_spec/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Gabe Berke-Williams"]
6
+ gem.email = ["gabe@thoughtbot.com"]
7
+ gem.description = %q{A musical formatter for RSpec.}
8
+ gem.summary = %q{A musical formatter for RSpec. The sound gets lower as failures pile up, and higher when tests pass.}
9
+ gem.homepage = "" # FIXME
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "musical_spec"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = MusicalSpec::VERSION
17
+
18
+ gem.add_dependency('bloopsaphone', '~> 0.4')
19
+ gem.add_dependency('rspec-core', '~> 2.0')
20
+
21
+ gem.add_development_dependency('rspec', '~> 2.0')
22
+ gem.add_development_dependency('bourne', '~> 1.0')
23
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+ require 'stringio'
3
+
4
+ describe MusicalSpec::Formatter do
5
+ before { subject.player.stubs(:play) }
6
+
7
+ it { should be_a RSpec::Core::Formatters::ProgressFormatter }
8
+
9
+ it 'plays a higher-pitched sound when an example passes' do
10
+ subject.example_passed(example)
11
+ subject.player.should have_received(:play).with('D4')
12
+ end
13
+
14
+ it 'plays a lower-pitched sound when an example fails' do
15
+ subject.example_failed(example)
16
+ subject.player.should have_received(:play).with('B3')
17
+ end
18
+
19
+ it 'does not change the note when an example is pending' do
20
+ subject.example_pending(example)
21
+ subject.player.should have_received(:play).with('C4')
22
+ end
23
+
24
+ subject { MusicalSpec::Formatter.new(StringIO.new) }
25
+ let(:example){ RSpec::Core::ExampleGroup.describe.example }
26
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ describe MusicalSpec::Note do
4
+ it 'defaults to middle C' do
5
+ subject.note.should == 'C4'
6
+ end
7
+
8
+ it 'sets MAX_OCTAVE to 6' do
9
+ MusicalSpec::Note::MAX_OCTAVE.should == 6
10
+ end
11
+
12
+ it 'sets MIN_OCTAVE to 2' do
13
+ MusicalSpec::Note::MIN_OCTAVE.should == 2
14
+ end
15
+
16
+ it 'allows setting the note' do
17
+ subject.note = 'A4'
18
+ subject.note.should == 'A4'
19
+ end
20
+
21
+ it 'does not let the octave go above MAX_OCTAVE' do
22
+ very_high_octave = MusicalSpec::Note::MAX_OCTAVE + 1
23
+ subject.note = "A#{very_high_octave}"
24
+ subject.note.should == "A#{MusicalSpec::Note::MAX_OCTAVE}"
25
+ end
26
+
27
+ it 'does not let the octave go below MIN_OCTAVE' do
28
+ very_low_octave = MusicalSpec::Note::MIN_OCTAVE - 1
29
+ subject.note = "A#{very_low_octave}"
30
+ subject.note.should == "A#{MusicalSpec::Note::MIN_OCTAVE}"
31
+ end
32
+
33
+ context '#next!' do
34
+ it 'increases the note by 1' do
35
+ subject.note = 'C4'
36
+ subject.next!
37
+ subject.note.should == 'D4'
38
+ end
39
+
40
+ it 'changes octaves when moving from B to C' do
41
+ subject.note = 'B4'
42
+ subject.next!
43
+ subject.note.should == 'C5'
44
+ end
45
+
46
+ it 'does not change octaves when moving from G to A' do
47
+ subject.note = 'G4'
48
+ subject.next!
49
+ subject.note.should == 'A4'
50
+ end
51
+ end
52
+
53
+ context '#prev!' do
54
+ it 'decreases the note by 1' do
55
+ subject.note = 'E4'
56
+ subject.prev!
57
+ subject.note.should == 'D4'
58
+ end
59
+
60
+ it 'changes octaves when moving from C to B' do
61
+ subject.note = 'C4'
62
+ subject.prev!
63
+ subject.note.should == 'B3'
64
+ end
65
+
66
+ it 'does not change octaves when moving from A to G' do
67
+ subject.note = 'A4'
68
+ subject.prev!
69
+ subject.note.should == 'G4'
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe MusicalSpec::Player do
4
+ before do
5
+ @real_bloopsaphone = MusicalSpec::ONE_TRUE_BLOOPSAPHONE
6
+ MusicalSpec::ONE_TRUE_BLOOPSAPHONE = fake_bloopsaphone
7
+ end
8
+
9
+ after { MusicalSpec::ONE_TRUE_BLOOPSAPHONE = @real_bloopsaphone }
10
+
11
+ it 'sets the correct note' do
12
+ subject.play('C4')
13
+ fake_bloopsaphone.should have_received(:tune).with(fake_sound, 'C4')
14
+ end
15
+
16
+ it 'clears tunes before playing' do
17
+ subject.play('C4')
18
+ fake_bloopsaphone.should have_received(:clear).once
19
+ end
20
+
21
+ it 'plays the tune' do
22
+ subject.play('C4')
23
+ fake_bloopsaphone.should have_received(:play).once
24
+ end
25
+
26
+ let(:fake_sound) { stub("Bloopsaphone sound", :sustain= => nil) }
27
+ let(:fake_bloopsaphone) do
28
+ stub('Fake Bloopsaphone',
29
+ :tune => nil,
30
+ :tempo= => nil,
31
+ :play => nil,
32
+ :clear => nil,
33
+ :stopped? => true,
34
+ :sound => fake_sound)
35
+ end
36
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe MusicalSpec do
4
+ it "creates a Bloopsaphone singleton because of thread wackiness" do
5
+ MusicalSpec::ONE_TRUE_BLOOPSAPHONE.should be_a Bloops
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ $LOAD_PATH << File.expand_path('../lib', __FILE__)
2
+
3
+ require 'rspec'
4
+ require 'bourne'
5
+
6
+ require 'musical_spec'
7
+
8
+ RSpec.configure do |config|
9
+ config.mock_with :mocha
10
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: musical_spec
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Gabe Berke-Williams
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-27 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bloopsaphone
16
+ requirement: &2153435480 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '0.4'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2153435480
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec-core
27
+ requirement: &2153433840 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2153433840
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &2153433380 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: '2.0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *2153433380
47
+ - !ruby/object:Gem::Dependency
48
+ name: bourne
49
+ requirement: &2153432720 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *2153432720
58
+ description: A musical formatter for RSpec.
59
+ email:
60
+ - gabe@thoughtbot.com
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gitignore
66
+ - Gemfile
67
+ - README.md
68
+ - Rakefile
69
+ - lib/musical_spec.rb
70
+ - lib/musical_spec/bloops.rb
71
+ - lib/musical_spec/formatter.rb
72
+ - lib/musical_spec/note.rb
73
+ - lib/musical_spec/player.rb
74
+ - lib/musical_spec/version.rb
75
+ - musical_spec.gemspec
76
+ - spec/musical_spec/formatter_spec.rb
77
+ - spec/musical_spec/note_spec.rb
78
+ - spec/musical_spec/player_spec.rb
79
+ - spec/musical_spec_spec.rb
80
+ - spec/spec_helper.rb
81
+ homepage: ''
82
+ licenses: []
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 1.8.11
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: A musical formatter for RSpec. The sound gets lower as failures pile up,
105
+ and higher when tests pass.
106
+ test_files:
107
+ - spec/musical_spec/formatter_spec.rb
108
+ - spec/musical_spec/note_spec.rb
109
+ - spec/musical_spec/player_spec.rb
110
+ - spec/musical_spec_spec.rb
111
+ - spec/spec_helper.rb
112
+ has_rdoc: