text-to-noise 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/.gitignore +8 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +4 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE +21 -0
  6. data/README.md +39 -0
  7. data/Rakefile +35 -0
  8. data/bin/play +12 -0
  9. data/bin/text_to_noise +26 -0
  10. data/cucumber.yml +2 -0
  11. data/features/configuration.feature +64 -0
  12. data/features/step_definitions/debugger_steps.rb +4 -0
  13. data/features/support/env.rb +5 -0
  14. data/lib/text_to_noise/command_line.rb +27 -0
  15. data/lib/text_to_noise/log_reader.rb +15 -0
  16. data/lib/text_to_noise/logging.rb +9 -0
  17. data/lib/text_to_noise/mapper.rb +40 -0
  18. data/lib/text_to_noise/mapping.rb +58 -0
  19. data/lib/text_to_noise/mute_player.rb +10 -0
  20. data/lib/text_to_noise/player.rb +45 -0
  21. data/lib/text_to_noise/version.rb +3 -0
  22. data/lib/text_to_noise.rb +28 -0
  23. data/sample.sounds.rb +6 -0
  24. data/sounds/birds/australian_frogmouth.wav +0 -0
  25. data/sounds/birds/blue_amazon_macaw.wav +0 -0
  26. data/sounds/birds/canary.wav +0 -0
  27. data/sounds/birds/canary2.wav +0 -0
  28. data/sounds/birds/cardinal.wav +0 -0
  29. data/sounds/birds/chicken.wav +0 -0
  30. data/sounds/birds/crow-1.wav +0 -0
  31. data/sounds/birds/crow.wav +0 -0
  32. data/sounds/birds/finch.wav +0 -0
  33. data/sounds/birds/geese.wav +0 -0
  34. data/sounds/birds/hawk.wav +0 -0
  35. data/sounds/birds/lapwing.wav +0 -0
  36. data/sounds/birds/meadow_lark_long.wav +0 -0
  37. data/sounds/birds/meadow_lark_short.wav +0 -0
  38. data/sounds/birds/mexican_red_parrot.wav +0 -0
  39. data/sounds/birds/mockingbird.wav +0 -0
  40. data/sounds/birds/nightingale.wav +0 -0
  41. data/sounds/birds/owl.wav +0 -0
  42. data/sounds/birds/peacock.wav +0 -0
  43. data/sounds/birds/pigeons.wav +0 -0
  44. data/sounds/birds/red_lories.wav +0 -0
  45. data/sounds/birds/rooster.wav +0 -0
  46. data/sounds/birds/vulture.wav +0 -0
  47. data/sounds/birds/whipperwhill.wav +0 -0
  48. data/sounds/crickets.wav +0 -0
  49. data/spec/spec.opts +1 -0
  50. data/spec/spec_helper.rb +11 -0
  51. data/spec/text_to_noise/command_line_spec.rb +70 -0
  52. data/spec/text_to_noise/log_reader_spec.rb +23 -0
  53. data/spec/text_to_noise/mapper_spec.rb +100 -0
  54. data/spec/text_to_noise/mapping_spec.rb +91 -0
  55. data/spec/text_to_noise/sound_map.rb +2 -0
  56. data/ssh.sounds.rb +1 -0
  57. data/test.rb +26 -0
  58. data/text_to_noise.gemspec +30 -0
  59. data/ts.gems +2 -0
  60. data/watchr.rb +66 -0
  61. metadata +169 -0
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ tmp/*
6
+
7
+ # osx noise
8
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.rvmrc ADDED
@@ -0,0 +1,4 @@
1
+ rvm --create 1.9.2@sounds
2
+
3
+ [[ -s "ts.gems" ]] && rvm gemset import ts.gems
4
+ bundle install
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2011 Toby Tripp
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ TailSounds
2
+ ==========
3
+ Ambient monitoring.
4
+
5
+ Imagine if your servers created ambient noise instead of silent log messages. What if your
6
+ application infrastructure sounded like a jungle? Page views are the chittering of crickets,
7
+ logins are the cackle of monkeys, and errors are the roar of a lion.
8
+
9
+
10
+ Installation
11
+ ------------
12
+
13
+ Stuff that I had to do to get this running:
14
+
15
+ 1. Install the SDL Mixer library. I used [Homebrew][homebrew]:
16
+ brew install sdl sdl_mixer
17
+
18
+ 2. Install text_to_noise via [Rubygems][rubygems]:
19
+ gem install rsdl text_to_noise
20
+
21
+
22
+ Usage
23
+ -----
24
+
25
+ Pipe a log into the +tail_sound+ script and it will start chirping:
26
+
27
+ tail -f /var/log/mylog | text_to_noise
28
+
29
+
30
+ TODO
31
+ ----
32
+
33
+ * Automatically refresh configurations
34
+ * Add generators to create stub configurations
35
+ * Support sound themes?
36
+
37
+
38
+ [Homebrew]:http://mxcl.github.com/homebrew
39
+ [Rubygems]:http://rubygems.org
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
2
+ require "text_to_noise/version"
3
+
4
+ require 'bundler'
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ namespace :gem do
8
+ task :build do
9
+ system "gem build text_to_noise.gemspec"
10
+ end
11
+
12
+ task :release => :build do
13
+ system "gem push text_to_noise-#{TailSounds::VERSION}"
14
+ end
15
+ end
16
+
17
+ require 'rspec/core/rake_task'
18
+ RSpec::Core::RakeTask.new(:spec)
19
+
20
+ require 'cucumber/rake/task'
21
+ namespace :cucumber do
22
+ Cucumber::Rake::Task.new( :ok, 'Run features that should pass' ) do |t|
23
+ t.fork = true
24
+ t.profile = 'default'
25
+ end
26
+
27
+ Cucumber::Rake::Task.new( :wip, 'Run features that are being worked on' ) do |t|
28
+ t.fork = true # You may get faster startup if you set this to false
29
+ t.profile = 'wip'
30
+ end
31
+ end
32
+ desc 'Alias for cucumber:ok'
33
+ task :cucumber => 'cucumber:ok'
34
+
35
+ task :default => [:spec, :cucumber]
data/bin/play ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require './lib/text_to_noise'
4
+
5
+ player = TailSounds::Player.new
6
+
7
+ ARGV.each do |sound|
8
+ player.play sound
9
+ sleep 1
10
+ end
11
+
12
+ sleep 1 while player.playing?
data/bin/text_to_noise ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+
4
+ lib = File.expand_path( '../../lib/', __FILE__ )
5
+ $:.unshift lib unless $:.include?( lib )
6
+
7
+ require 'text_to_noise'
8
+
9
+ options = {
10
+ :input => $stdin,
11
+ :mute => false,
12
+ :config => "sample.sounds.rb"
13
+ }
14
+
15
+ OptionParser.new do |opts|
16
+ opts.banner = "Usage: #{$0} [options]"
17
+
18
+ opts.on( "-f", "--file FILE",
19
+ "Read input from FILE. Defaults to stdin" ) { |f| options[:input] = File.open( f, "r" ) }
20
+ opts.on( "-c", "--config MAPPING_CONFIG",
21
+ "Read input to sound mapping configuration from MAPPING_CONFIG" ) { |c| options[:config] = c }
22
+ opts.on( "-m", "--mute",
23
+ "Don't play any sounds, just print what matched" ) { options[:mute] = true }
24
+ end.parse!
25
+
26
+ TextToNoise::CommandLine.new( options ).run
data/cucumber.yml ADDED
@@ -0,0 +1,2 @@
1
+ default: --require features --strict --format pretty --tags ~@wip features
2
+ wip: --require features --tags @wip:3 --wip features
@@ -0,0 +1,64 @@
1
+ Feature: Mapping input lines to sounds for playback
2
+
3
+ Use the mapping configuration file to specify which sounds are played for
4
+ each line in the input stream.
5
+
6
+ Scenario: Mapping a regular expression to a single sound
7
+ Given a file named "sound_mapping.rb" with:
8
+ """
9
+ map( /caw/ ).to "crow"
10
+
11
+ """
12
+ And a file named "stuff.log" with:
13
+ """
14
+ caw
15
+
16
+ """
17
+
18
+ When I run `text_to_noise --config sound_mapping.rb --file stuff.log --mute`
19
+
20
+ Then the output should contain:
21
+ """
22
+ Playing crow.wav
23
+ """
24
+
25
+ Scenario: Using a Hash to specify mappings
26
+ Given a file named "sound_mapping.rb" with:
27
+ """
28
+ map /caw/ => "crow", /bakawk/ => "chicken"
29
+ """
30
+
31
+ And a file named "input.log" with:
32
+ """
33
+ caw
34
+ bakawk!
35
+ """
36
+
37
+ When I run `text_to_noise -c sound_mapping.rb -f input.log -m`
38
+
39
+ Then the output should contain "Playing crow.wav"
40
+ And the output should contain "Playing chicken.wav"
41
+
42
+ Scenario: Configuring a dynamic matcher with a block
43
+ Given a file named "sound_mapping.rb" with:
44
+ """
45
+ map( /Completed in (\d+)ms/ ) { |match_data|
46
+ match_data[1].to_i > 500
47
+ }.to "slow_request"
48
+
49
+ map( /Load \(([\d.]+)ms\)/ ) { |match_data|
50
+ match_data[1].to_i > 5
51
+ }.to "slow_query"
52
+
53
+ """
54
+
55
+ And a file named "input.log" with:
56
+ """
57
+ Completed in 250ms
58
+ User Load (7.1ms)
59
+ """
60
+
61
+ When I run `text_to_noise -c sound_mapping.rb -f input.log -m`
62
+
63
+ Then the output should contain "Playing slow_query.wav"
64
+ And the output should not contain "Playing slow_request.wav"
@@ -0,0 +1,4 @@
1
+ When /^I am debugging$/ do
2
+ require 'ruby-debug'
3
+ debugger
4
+ end
@@ -0,0 +1,5 @@
1
+ require 'aruba/cucumber'
2
+ lib = File.expand_path( '../../../lib/', __FILE__ )
3
+ $:.unshift lib unless $:.include?( lib )
4
+
5
+ require 'text_to_noise'
@@ -0,0 +1,27 @@
1
+ module TextToNoise
2
+ class CommandLine
3
+ attr_reader :options, :mapping
4
+
5
+ def initialize( options={} )
6
+ @options = {
7
+ :input => $stdin
8
+ }.merge options
9
+ @mapping = Mapper.parse File.read( @options[:config] )
10
+ TextToNoise.player = self.player
11
+ rescue Errno::ENOENT => e
12
+ raise ArgumentError, "Could not locate configuration file: '#{@options[:config]}'"
13
+ end
14
+
15
+ def run
16
+ LogReader.new( options[:input], mapping ).call
17
+ puts "Input processing complete. Waiting for playback to finish..."
18
+ while TextToNoise.player.playing?
19
+ sleep 1
20
+ end
21
+ end
22
+
23
+ def player
24
+ @options[:mute] ? MutePlayer.new : Player.new
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ module TextToNoise
2
+ class LogReader
3
+ attr_reader :io
4
+ def initialize( io, mapper )
5
+ @io, @mapper = io, mapper
6
+ end
7
+
8
+ def call()
9
+ while line = io.gets
10
+ @mapper.dispatch line
11
+ sleep 0.200 # FIXME: Think of a better way to throttle playback.
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ require 'forwardable'
2
+
3
+ module TextToNoise
4
+ module Logging
5
+ extend Forwardable
6
+ def_delegators :logger, :warn, :debug, :error, :info
7
+ def logger() TextToNoise.logger; end
8
+ end
9
+ end
@@ -0,0 +1,40 @@
1
+ module TextToNoise
2
+ class Mapper
3
+ include Logging
4
+ attr_accessor :mappings
5
+
6
+ def initialize()
7
+ @mappings = []
8
+ end
9
+
10
+ def self.parse( config )
11
+ raise ArgumentError, "No configuration given" if config.nil? or config.strip.empty?
12
+ new.tap { |m|
13
+ m.instance_eval config
14
+ m.info "Configured #{m.mappings.size} mappings."
15
+ }
16
+ end
17
+
18
+ def dispatch( line )
19
+ debug "Dispatching: #{line.rstrip}"
20
+ matches = mappings.select { |m| m === line }
21
+ debug "#{matches.size} matched mappings"
22
+ matches.map { |m| m.call }
23
+ end
24
+
25
+ protected
26
+
27
+ def match( expression, &block )
28
+ if expression.kind_of?( Hash ) && expression.size > 1
29
+ expression.each do |k,v|
30
+ match k => v
31
+ end
32
+ else
33
+ debug "Creating map for #{expression.inspect}"
34
+ mappings << mapping = Mapping.new( expression, &block )
35
+ mapping
36
+ end
37
+ end
38
+ alias_method :map, :match
39
+ end
40
+ end
@@ -0,0 +1,58 @@
1
+ module TextToNoise
2
+ class Mapping
3
+ include Logging
4
+ attr_accessor :targets, :matcher_proc
5
+
6
+ def initialize( expression_or_map, &block )
7
+ case expression_or_map
8
+ when Regexp
9
+ @regex = expression_or_map
10
+ when Hash
11
+ @regex = expression_or_map.keys.first
12
+ self.to expression_or_map[@regex]
13
+ else
14
+ raise ArgumentError, "Unrecognized Mapping configuration: #{expression_or_map.inspect}"
15
+ end
16
+
17
+ @matcher_proc = block if block_given?
18
+ end
19
+
20
+ def ===( other )
21
+ match_data = @regex.match( other )
22
+ if matcher_proc
23
+ match_data && matcher_proc.call( match_data )
24
+ else
25
+ not match_data.nil?
26
+ end
27
+ end
28
+
29
+ def to( sound_or_sounds )
30
+ if sound_or_sounds.is_a? Array
31
+ sounds = sound_or_sounds
32
+ else
33
+ sounds = [sound_or_sounds]
34
+ end
35
+
36
+ sounds.each do |sound|
37
+ s = sound
38
+ s += ".wav" unless sound =~ /.wav$/
39
+ self.targets << Proc.new {
40
+ info "#{@regex.inspect} -> #{sound}"
41
+ TextToNoise.player.play s
42
+ }
43
+ end
44
+ end
45
+
46
+ def call()
47
+ debug "Calling '#{@regex.inspect}' target..."
48
+ target.call
49
+ end
50
+
51
+ def targets() @targets ||= []; end
52
+ def target()
53
+ @i = -1 unless @i
54
+ @i = (@i + 1) % targets.size
55
+ self.targets[@i]
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,10 @@
1
+ module TextToNoise
2
+ class MutePlayer
3
+ include Logging
4
+ def play( sound_name )
5
+ info "Playing #{sound_name}"
6
+ end
7
+
8
+ def playing?() false; end
9
+ end
10
+ end
@@ -0,0 +1,45 @@
1
+ module TextToNoise
2
+ class Player
3
+ include Logging
4
+
5
+ SOUND_DIR = File.expand_path File.join( APP_ROOT, 'sounds' )
6
+ class SoundNotFound < StandardError
7
+ def initialize( sound_name )
8
+ super "Could not locate '#{sound_name}' in #{Rubygame::Sound.autoload_dirs}"
9
+ end
10
+ end
11
+
12
+ def initialize()
13
+ begin
14
+ require 'rubygame'
15
+ rescue LoadError
16
+ require 'rubygems'
17
+ require 'rubygame'
18
+ end
19
+
20
+ raise "No Mixer found! Make sure sdl_mixer is installed." unless defined? Rubygame::Sound
21
+
22
+ Rubygame::Sound.autoload_dirs << SOUND_DIR
23
+ themes = Dir.new(SOUND_DIR).entries.reject { |e| e =~ /[.]/ }
24
+ Rubygame::Sound.autoload_dirs.concat themes.map { |t| File.join SOUND_DIR, t }
25
+
26
+ @sounds = []
27
+ end
28
+
29
+ def play( sound_name )
30
+ sound = Rubygame::Sound[sound_name]
31
+ raise SoundNotFound, sound_name if sound.nil?
32
+
33
+ debug "Playing #{sound_name}"
34
+ sound.play :fade_out => 2
35
+ @sounds << sound
36
+
37
+ debug "#{@sounds.size} sounds in queue" if playing?
38
+ end
39
+
40
+ def playing?
41
+ @sounds = @sounds.select &:playing?
42
+ not @sounds.empty?
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,3 @@
1
+ module TextToNoise
2
+ VERSION = "0.1.2"
3
+ end
@@ -0,0 +1,28 @@
1
+ LIB_DIR = File.dirname File.expand_path( __FILE__ )
2
+ APP_ROOT = File.join LIB_DIR, '..'
3
+ $LOAD_PATH << LIB_DIR
4
+
5
+ Dir["#{LIB_DIR}/text_to_noise/*.rb"].each { |lib|
6
+ lib =~ %r<lib/(.*)\.rb$>
7
+ require $1
8
+ }
9
+ require 'logger'
10
+
11
+ module TextToNoise
12
+ def self.player
13
+ @player ||= Player.new
14
+ end
15
+
16
+ def self.player=( player )
17
+ @player = player
18
+ end
19
+
20
+ def self.logger
21
+ @logger ||= Logger.new( STDOUT ).tap { |l| l.level = Logger::INFO }
22
+ end
23
+
24
+ def self.logger=( logger )
25
+ @logger = logger
26
+ @logger
27
+ end
28
+ end
data/sample.sounds.rb ADDED
@@ -0,0 +1,6 @@
1
+ match /Rendered/ => %w(crickets canary)
2
+ match /Rendering/ => "cardinal"
3
+ match /User Load/ => "nightingale"
4
+ match /Processing/ => "finch"
5
+ match /SessionsController#new/ => "owl"
6
+ match /404 Not Found/ => "hawk"
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --format documentation
@@ -0,0 +1,11 @@
1
+ require 'rspec'
2
+
3
+ lib = File.expand_path( '../lib/', __FILE__ )
4
+ $:.unshift lib unless $:.include?( lib )
5
+
6
+ require 'text_to_noise'
7
+ TextToNoise.logger.level = Logger::WARN
8
+
9
+ Rspec.configure do |c|
10
+ c.mock_with :rspec
11
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ module TextToNoise
4
+ describe TextToNoise::CommandLine do
5
+ let( :mapping ) { double( Mapping ) }
6
+ let( :reader ) { double( LogReader, :call => nil ) }
7
+
8
+ before :each do
9
+ Mapper.stub!( :parse ).and_return mapping
10
+ LogReader.stub!( :new ).and_return reader
11
+ File.stub!( :read ).with( "sound_map.rb" ).and_return "config"
12
+ end
13
+
14
+ describe "#initialize" do
15
+ it "accepts an options object" do
16
+ CommandLine.new( {} )
17
+ end
18
+
19
+ it "raises an error if the config file cannot be found" do
20
+ File.stub!( :read ).and_raise Errno::ENOENT
21
+ lambda {
22
+ CommandLine.new :config => "not_found"
23
+ }.should raise_error( ArgumentError )
24
+ end
25
+
26
+ context "when given a 'config' option" do
27
+ it "instantiates a Mapping object with the specified configuration" do
28
+ File.should_receive( :read ).with( "sound_map.rb" ).and_return "config"
29
+ Mapper.should_receive( :parse ).with( "config" )
30
+
31
+ CommandLine.new :config => "sound_map.rb"
32
+ end
33
+ end
34
+
35
+ context "when given the 'mute' option" do
36
+ it "sets the global Player to an instance of MutePlayer" do
37
+ TextToNoise.should_receive( :player= ).with instance_of( MutePlayer )
38
+ CommandLine.new :mute => true
39
+ end
40
+ end
41
+ end
42
+
43
+ describe "#run" do
44
+ let( :options ) { Hash[:input, :io, :config, "sound_map.rb"] }
45
+
46
+ subject { CommandLine.new( options ) }
47
+
48
+
49
+ it "creates a new LogReader object" do
50
+ LogReader.should_receive( :new )
51
+ subject.run
52
+ end
53
+
54
+ it "passes the contents of the file in :input option to the reader" do
55
+ LogReader.should_receive( :new ).with( :io, anything )
56
+ subject.run
57
+ end
58
+
59
+ it "passes an instance of a Mapping to the LogReader" do
60
+ LogReader.should_receive( :new ).with( anything, mapping )
61
+ subject.run
62
+ end
63
+
64
+ it "calls #call on the LogReader" do
65
+ reader.should_receive :call
66
+ subject.run
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+ require 'stringio'
3
+
4
+ module TextToNoise
5
+ describe LogReader do
6
+ let( :io ) { StringIO.new "phantasm" }
7
+ let( :mapper ) { double( "LineToSoundMapper" ) }
8
+
9
+ subject { LogReader.new io, mapper }
10
+
11
+ describe "#call" do
12
+ it "reads lines from the provided IO object using #gets" do
13
+ io.should_receive( :gets )
14
+ subject.call
15
+ end
16
+
17
+ it "calls #dispatch on an instance of LineToSoundMapper with each line" do
18
+ mapper.should_receive( :dispatch ).with "phantasm"
19
+ subject.call
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,100 @@
1
+ require 'spec_helper'
2
+ module TextToNoise
3
+ describe Mapper do
4
+ it "reloads its configuration if it has changed"
5
+
6
+ describe ".parse" do
7
+ let( :mapping ) { double( Mapping, :to => nil ) }
8
+
9
+ context "given a nil configuration" do
10
+ it "raises an ArgumentError" do
11
+ lambda {
12
+ Mapper.parse nil
13
+ }.should raise_error( ArgumentError )
14
+ end
15
+ end
16
+
17
+ context "given a blank configuration" do
18
+ it "raises an ArgumentError" do
19
+ lambda {
20
+ Mapper.parse " \t \n"
21
+ }.should raise_error( ArgumentError )
22
+ end
23
+ end
24
+
25
+ context "given a single line configuration" do
26
+ let( :config ) { "match( /brown/ ).to \"blue\"" }
27
+
28
+ it "creates a Mapping object for the given configuration line" do
29
+ Mapping.should_receive( :new ).with( /brown/ ).and_return mapping
30
+ Mapper.parse config
31
+ end
32
+
33
+ it "configures the mapping" do
34
+ Mapping.stub!( :new ).with( /brown/ ).and_return mapping
35
+ mapping.should_receive( :to ).with( "blue" )
36
+
37
+ Mapper.parse config
38
+ end
39
+ end
40
+
41
+ context "given a multiple line configuration" do
42
+ let( :config ) { 'match( /brown/ ).to "blue"; map( /green/ ).to "orange"' }
43
+ before { Mapping.stub!( :new ).and_return mapping }
44
+
45
+ it "configures the second mapping in addition to the first" do
46
+ mapping.should_receive( :to ).with "blue"
47
+ mapping.should_receive( :to ).with "orange"
48
+ Mapper.parse config
49
+ end
50
+
51
+ it "stores the mappings" do
52
+ Mapper.parse( config ).should have( 2 ).mappings
53
+ end
54
+ end
55
+
56
+ context "given a hash-style configuration" do
57
+ let( :config ) { "match /brown/ => \"blue\"\n" }
58
+
59
+ it "configures the mapping" do
60
+ Mapping.should_receive( :new ).with( /brown/ => "blue" ).and_return mapping
61
+ Mapper.parse config
62
+ end
63
+ end
64
+
65
+ context "given a hash-style configuration with multiple mappings" do
66
+ let( :config ) { "match /brown/ => \"blue\", /green/ => \"red\"\n" }
67
+
68
+ it "configures both mappings" do
69
+ Mapping.should_receive( :new ).with( /brown/ => "blue" ).and_return mapping
70
+ Mapping.should_receive( :new ).with( /green/ => "red" ).and_return mapping
71
+
72
+ Mapper.parse config
73
+ end
74
+ end
75
+ end
76
+
77
+ describe "#dispatch" do
78
+ let( :blue ) { /blue/ }
79
+ let( :green ) { /green/ }
80
+ let( :blue2 ) { /blu/ }
81
+
82
+ subject { Mapper.new.tap { |m| m.mappings = [blue, green, blue2] } }
83
+
84
+ it "iterates over its mappings comparing them to the input with #===" do
85
+ blue.should_receive( :=== ).with( anything )
86
+ green.should_receive( :=== ).with( anything )
87
+
88
+ subject.dispatch "orange"
89
+ end
90
+
91
+ it "calls #call on all mappings that matched" do
92
+ blue.should_receive :call
93
+ blue2.should_receive :call
94
+ green.should_not_receive :call
95
+
96
+ subject.dispatch( "blue" )
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ describe TextToNoise::Mapping do
4
+ describe "#initialize" do
5
+ it "can accept a Hash" do
6
+ mapping = described_class.new /exp/ => "target"
7
+ mapping.target.should be_a( Proc )
8
+ end
9
+
10
+ it "stores a passed block for use in later matching" do
11
+ mapping = described_class.new( /exp/ => "target" ) { |md| true }
12
+ mapping.matcher_proc.should be_a( Proc )
13
+ end
14
+ end
15
+
16
+ describe "#===" do
17
+ context "when matching against a regex" do
18
+ subject { described_class.new /green/ }
19
+
20
+ it "returns true when the input matches its regex" do
21
+ subject.should === "green"
22
+ end
23
+
24
+ it "returns false when the input does not match its regex" do
25
+ subject.should_not === "blue"
26
+ end
27
+ end
28
+
29
+ context "when matching against a Proc" do
30
+ subject do
31
+ described_class.new( /green (\d+)/ ) { |match_data| match_data[1].to_i > 5 }
32
+ end
33
+
34
+ it "matches if the block returns true" do
35
+ should === "green 6"
36
+ end
37
+
38
+ it "does not match if the block returns false" do
39
+ should_not === "green 1"
40
+ end
41
+ end
42
+ end
43
+
44
+ describe "#to" do
45
+ subject { described_class.new /green/ }
46
+
47
+ before { TextToNoise.player = double( TextToNoise::Player ) }
48
+
49
+ it "sets the target of the mapping to a Proc" do
50
+ subject.to( "bar" )
51
+ subject.target.should be_a( Proc )
52
+ end
53
+
54
+ it "sets the target to call Player#play on #call" do
55
+ subject.to( "bar.wav" )
56
+ TextToNoise.player.should_receive( :play ).with "bar.wav"
57
+
58
+ subject.target.call
59
+ end
60
+
61
+ it "automatically appends .wav, if it's not present" do
62
+ subject.to( "bar" )
63
+ TextToNoise.player.should_receive( :play ).with "bar.wav"
64
+
65
+ subject.target.call
66
+ end
67
+
68
+ it "can accept a list of target files" do
69
+ subject.to ["foo", "bar", "baz"]
70
+ TextToNoise.player.should_receive( :play ).with "foo.wav"
71
+ TextToNoise.player.should_receive( :play ).with "bar.wav"
72
+ TextToNoise.player.should_receive( :play ).with "baz.wav"
73
+
74
+ subject.target.call
75
+ subject.target.call
76
+ subject.target.call
77
+ end
78
+ end
79
+
80
+ describe "#call" do
81
+ subject do
82
+ described_class.new( /green/ ).tap do |mapping|
83
+ mapping.targets = [Proc.new { "called" }]
84
+ end
85
+ end
86
+
87
+ it "calls #call on its target" do
88
+ subject.call.should == "called"
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,2 @@
1
+ match /macaw/ => "blue_amazon_macaw.wav"
2
+ match /canary/ => "canary.wav"
data/ssh.sounds.rb ADDED
@@ -0,0 +1 @@
1
+ match /sshd.*Accepted/ => %w[rooster hawk chicken crow]
data/test.rb ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygame'
3
+ include Rubygame
4
+
5
+ # Stuff that I had to do to get this running:
6
+ #
7
+ # brew install sdl sdl_mixer
8
+ # gem install rubygame rsdl
9
+
10
+ # Let's start by trying to play a random mp3 passed as an argument
11
+ mp3 = ARGV.pop
12
+ raise "No Mixer found! Make sure sdl_mixer is installed." unless defined? Sound
13
+ APP_ROOT = File.dirname File.expand_path( __FILE__ )
14
+
15
+ Sound.autoload_dirs << File.join( APP_ROOT, "sounds" )
16
+
17
+ # http://rubygame.org/wiki/NamedResources_tutorial#Autoloading_Resources
18
+ sound = Sound[mp3]
19
+ # sound = Sound.load mp3
20
+ sound.play :stop_after => 4, :fade_out => 2
21
+
22
+ while sound.playing?
23
+ sleep 1
24
+ end
25
+
26
+ # Kick Ass!
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path( '../lib/', __FILE__ )
3
+ $:.unshift lib unless $:.include?( lib )
4
+
5
+ require 'text_to_noise/version'
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "text-to-noise"
8
+ spec.version = TextToNoise::VERSION
9
+ spec.platform = Gem::Platform::RUBY
10
+
11
+ spec.summary = "Play sounds based on string matches."
12
+ spec.description = "Pipe a file, like a log file, into text_to_noise and it will play sound effects triggered by regex matches in the input."
13
+ spec.authors = ["Toby Tripp", "Lydia Tripp"]
14
+ spec.email = "toby.tripp+tailsounds@gmail.com"
15
+ spec.homepage = "https://github.com/tobytripp/text_to_noise"
16
+
17
+ spec.required_rubygems_version = ">= 1.3.6"
18
+
19
+ spec.add_dependency "rubygame", "~> 2.6.4"
20
+
21
+ spec.add_development_dependency "cucumber"
22
+ spec.add_development_dependency "aruba"
23
+ spec.add_development_dependency "rspec"
24
+
25
+ spec.files = `git ls-files`.split("\n")
26
+ spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
27
+ spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
28
+
29
+ spec.require_path = 'lib'
30
+ end
data/ts.gems ADDED
@@ -0,0 +1,2 @@
1
+ bundler -v1.0.12
2
+ rspec -v2.5.0
data/watchr.rb ADDED
@@ -0,0 +1,66 @@
1
+ # Run me with:
2
+ # watchr watchr.rb
3
+ #
4
+ # I will automatically run the relevant specs when you change things.
5
+ # Aren't I convenient?
6
+ #
7
+ begin
8
+ require "g"
9
+ rescue LoadError
10
+ end
11
+
12
+ def announce( message )
13
+ puts message
14
+ g message if Kernel.respond_to? :g
15
+ end
16
+
17
+ def spec( *files )
18
+ announce "Running specs: #{files}"
19
+ execute "rspec #{files.join " "} --options spec/spec.opts"
20
+ end
21
+
22
+ def specs_matching( type, name )
23
+ puts "looking for specs of #{name}"
24
+ matching_specs = Dir["spec/**/*#{name}*_spec.rb"]
25
+ if matching_specs.empty?
26
+ $stderr.puts "No matching specs found! Did you NOT TEST THIS?!"
27
+ else
28
+ spec *matching_specs
29
+ end
30
+ end
31
+
32
+ def run_all_tests
33
+ announce "Running full test suite"
34
+ execute "rake"
35
+ end
36
+
37
+ def execute( cmd )
38
+ puts "> #{cmd}"
39
+ system cmd
40
+ end
41
+
42
+ watch( '^spec/[^/]*/(.*)_spec\.rb' ) { |m| spec m[0] }
43
+ watch( '^spec/spec_helper\.rb' ) { |m| execute "rake spec" }
44
+ watch( '^spec/support/.*' ) { |m| execute "rake spec" }
45
+ watch( '^lib/([^/]+)/(.*)\.rb' ) { |m| specs_matching m[1], m[2] }
46
+ watch( '^features/step_definitions/.*' ) { |m| execute "cucumber features" }
47
+ watch( '^features/([^/]+\.feature)' ) { |m|
48
+ execute "cucumber #{m[0]}" unless m[1].include? "step_definitions"
49
+ }
50
+
51
+ Signal.trap 'INT' do
52
+ if @sent_an_int then
53
+ puts " A second INT? Ok, I get the message. Shutting down now."
54
+ exit
55
+ else
56
+ puts " Did you just send me an INT? Ugh. I'll quit for real if you do it again."
57
+ @sent_an_int = true
58
+ Kernel.sleep 1.5
59
+ run_all_tests
60
+ end
61
+ end
62
+
63
+ # Ctrl-\
64
+ Signal.trap 'QUIT' do
65
+ run_all_tests
66
+ end
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: text-to-noise
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.2
6
+ platform: ruby
7
+ authors:
8
+ - Toby Tripp
9
+ - Lydia Tripp
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2011-04-30 00:00:00 -05:00
15
+ default_executable:
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: rubygame
19
+ prerelease: false
20
+ requirement: &id001 !ruby/object:Gem::Requirement
21
+ none: false
22
+ requirements:
23
+ - - ~>
24
+ - !ruby/object:Gem::Version
25
+ version: 2.6.4
26
+ type: :runtime
27
+ version_requirements: *id001
28
+ - !ruby/object:Gem::Dependency
29
+ name: cucumber
30
+ prerelease: false
31
+ requirement: &id002 !ruby/object:Gem::Requirement
32
+ none: false
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: "0"
37
+ type: :development
38
+ version_requirements: *id002
39
+ - !ruby/object:Gem::Dependency
40
+ name: aruba
41
+ prerelease: false
42
+ requirement: &id003 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ type: :development
49
+ version_requirements: *id003
50
+ - !ruby/object:Gem::Dependency
51
+ name: rspec
52
+ prerelease: false
53
+ requirement: &id004 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ type: :development
60
+ version_requirements: *id004
61
+ description: Pipe a file, like a log file, into text_to_noise and it will play sound effects triggered by regex matches in the input.
62
+ email: toby.tripp+tailsounds@gmail.com
63
+ executables:
64
+ - play
65
+ - text_to_noise
66
+ extensions: []
67
+
68
+ extra_rdoc_files: []
69
+
70
+ files:
71
+ - .gitignore
72
+ - .rspec
73
+ - .rvmrc
74
+ - Gemfile
75
+ - LICENSE
76
+ - README.md
77
+ - Rakefile
78
+ - bin/play
79
+ - bin/text_to_noise
80
+ - cucumber.yml
81
+ - features/configuration.feature
82
+ - features/step_definitions/debugger_steps.rb
83
+ - features/support/env.rb
84
+ - lib/text_to_noise.rb
85
+ - lib/text_to_noise/command_line.rb
86
+ - lib/text_to_noise/log_reader.rb
87
+ - lib/text_to_noise/logging.rb
88
+ - lib/text_to_noise/mapper.rb
89
+ - lib/text_to_noise/mapping.rb
90
+ - lib/text_to_noise/mute_player.rb
91
+ - lib/text_to_noise/player.rb
92
+ - lib/text_to_noise/version.rb
93
+ - sample.sounds.rb
94
+ - sounds/birds/australian_frogmouth.wav
95
+ - sounds/birds/blue_amazon_macaw.wav
96
+ - sounds/birds/canary.wav
97
+ - sounds/birds/canary2.wav
98
+ - sounds/birds/cardinal.wav
99
+ - sounds/birds/chicken.wav
100
+ - sounds/birds/crow-1.wav
101
+ - sounds/birds/crow.wav
102
+ - sounds/birds/finch.wav
103
+ - sounds/birds/geese.wav
104
+ - sounds/birds/hawk.wav
105
+ - sounds/birds/lapwing.wav
106
+ - sounds/birds/meadow_lark_long.wav
107
+ - sounds/birds/meadow_lark_short.wav
108
+ - sounds/birds/mexican_red_parrot.wav
109
+ - sounds/birds/mockingbird.wav
110
+ - sounds/birds/nightingale.wav
111
+ - sounds/birds/owl.wav
112
+ - sounds/birds/peacock.wav
113
+ - sounds/birds/pigeons.wav
114
+ - sounds/birds/red_lories.wav
115
+ - sounds/birds/rooster.wav
116
+ - sounds/birds/vulture.wav
117
+ - sounds/birds/whipperwhill.wav
118
+ - sounds/crickets.wav
119
+ - spec/spec.opts
120
+ - spec/spec_helper.rb
121
+ - spec/text_to_noise/command_line_spec.rb
122
+ - spec/text_to_noise/log_reader_spec.rb
123
+ - spec/text_to_noise/mapper_spec.rb
124
+ - spec/text_to_noise/mapping_spec.rb
125
+ - spec/text_to_noise/sound_map.rb
126
+ - ssh.sounds.rb
127
+ - test.rb
128
+ - text_to_noise.gemspec
129
+ - ts.gems
130
+ - watchr.rb
131
+ has_rdoc: true
132
+ homepage: https://github.com/tobytripp/text_to_noise
133
+ licenses: []
134
+
135
+ post_install_message:
136
+ rdoc_options: []
137
+
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: "0"
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ none: false
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: 1.3.6
152
+ requirements: []
153
+
154
+ rubyforge_project:
155
+ rubygems_version: 1.6.2
156
+ signing_key:
157
+ specification_version: 3
158
+ summary: Play sounds based on string matches.
159
+ test_files:
160
+ - features/configuration.feature
161
+ - features/step_definitions/debugger_steps.rb
162
+ - features/support/env.rb
163
+ - spec/spec.opts
164
+ - spec/spec_helper.rb
165
+ - spec/text_to_noise/command_line_spec.rb
166
+ - spec/text_to_noise/log_reader_spec.rb
167
+ - spec/text_to_noise/mapper_spec.rb
168
+ - spec/text_to_noise/mapping_spec.rb
169
+ - spec/text_to_noise/sound_map.rb