text-to-noise 0.1.2
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 +8 -0
- data/.rspec +2 -0
- data/.rvmrc +4 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +39 -0
- data/Rakefile +35 -0
- data/bin/play +12 -0
- data/bin/text_to_noise +26 -0
- data/cucumber.yml +2 -0
- data/features/configuration.feature +64 -0
- data/features/step_definitions/debugger_steps.rb +4 -0
- data/features/support/env.rb +5 -0
- data/lib/text_to_noise/command_line.rb +27 -0
- data/lib/text_to_noise/log_reader.rb +15 -0
- data/lib/text_to_noise/logging.rb +9 -0
- data/lib/text_to_noise/mapper.rb +40 -0
- data/lib/text_to_noise/mapping.rb +58 -0
- data/lib/text_to_noise/mute_player.rb +10 -0
- data/lib/text_to_noise/player.rb +45 -0
- data/lib/text_to_noise/version.rb +3 -0
- data/lib/text_to_noise.rb +28 -0
- data/sample.sounds.rb +6 -0
- data/sounds/birds/australian_frogmouth.wav +0 -0
- data/sounds/birds/blue_amazon_macaw.wav +0 -0
- data/sounds/birds/canary.wav +0 -0
- data/sounds/birds/canary2.wav +0 -0
- data/sounds/birds/cardinal.wav +0 -0
- data/sounds/birds/chicken.wav +0 -0
- data/sounds/birds/crow-1.wav +0 -0
- data/sounds/birds/crow.wav +0 -0
- data/sounds/birds/finch.wav +0 -0
- data/sounds/birds/geese.wav +0 -0
- data/sounds/birds/hawk.wav +0 -0
- data/sounds/birds/lapwing.wav +0 -0
- data/sounds/birds/meadow_lark_long.wav +0 -0
- data/sounds/birds/meadow_lark_short.wav +0 -0
- data/sounds/birds/mexican_red_parrot.wav +0 -0
- data/sounds/birds/mockingbird.wav +0 -0
- data/sounds/birds/nightingale.wav +0 -0
- data/sounds/birds/owl.wav +0 -0
- data/sounds/birds/peacock.wav +0 -0
- data/sounds/birds/pigeons.wav +0 -0
- data/sounds/birds/red_lories.wav +0 -0
- data/sounds/birds/rooster.wav +0 -0
- data/sounds/birds/vulture.wav +0 -0
- data/sounds/birds/whipperwhill.wav +0 -0
- data/sounds/crickets.wav +0 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/text_to_noise/command_line_spec.rb +70 -0
- data/spec/text_to_noise/log_reader_spec.rb +23 -0
- data/spec/text_to_noise/mapper_spec.rb +100 -0
- data/spec/text_to_noise/mapping_spec.rb +91 -0
- data/spec/text_to_noise/sound_map.rb +2 -0
- data/ssh.sounds.rb +1 -0
- data/test.rb +26 -0
- data/text_to_noise.gemspec +30 -0
- data/ts.gems +2 -0
- data/watchr.rb +66 -0
- metadata +169 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rvmrc
ADDED
data/Gemfile
ADDED
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
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,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,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,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,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,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
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/sounds/crickets.wav
ADDED
Binary file
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--format documentation
|
data/spec/spec_helper.rb
ADDED
@@ -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
|
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
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
|