experiment 0.0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +5 -1
- data/README.rdoc +12 -3
- data/Rakefile +1 -1
- data/bin/experiment +62 -186
- data/bin/growl.sh +241 -0
- data/lib/experiment.rb +1 -1
- data/lib/experiment/base.rb +51 -13
- data/lib/experiment/config.rb +32 -3
- data/lib/experiment/distributed.rb +67 -0
- data/lib/experiment/notify.rb +177 -3
- data/lib/experiment/runner.rb +208 -0
- data/lib/experiment/work_server.rb +58 -0
- data/test/test_base.rb +5 -0
- data/test/test_config.rb +11 -0
- data/test/test_experiment.rb +1 -1
- data/test/test_stats.rb +34 -0
- metadata +14 -3
data/Manifest.txt
CHANGED
@@ -5,11 +5,15 @@ Rakefile
|
|
5
5
|
lib/experiment.rb
|
6
6
|
lib/experiment/config.rb
|
7
7
|
lib/experiment/stats.rb
|
8
|
+
lib/experiment/runner.rb
|
8
9
|
lib/experiment/generator/readme_template.txt
|
9
10
|
lib/experiment/generator/experiment_template.rb
|
10
11
|
lib/experiment/generator/Rakefile
|
11
12
|
lib/experiment/base.rb
|
12
13
|
lib/experiment/notify.rb
|
14
|
+
lib/experiment/work_server.rb
|
15
|
+
lib/experiment/distributed.rb
|
13
16
|
test/test_experiment.rb
|
14
17
|
test/test_helper.rb
|
15
|
-
bin/experiment
|
18
|
+
bin/experiment
|
19
|
+
bin/growl.sh
|
data/README.rdoc
CHANGED
@@ -27,7 +27,7 @@ Experiments are set up in the experiments directory. The first thing you need to
|
|
27
27
|
|
28
28
|
For a typical experiment you will need to do some setup work (eg. initialize your classes, calculate parametres, etc.), run the experiment and maybe do cleanup (remove temp. files).
|
29
29
|
|
30
|
-
You do all this work in the `run_the_experiment` method.
|
30
|
+
You do all this work in the `run_the_experiment` method. Use the `measure` method to wrap your measurements. These will be autmatically benchmarked and their ouput will be automatically saved to the results directory for further analysis.
|
31
31
|
|
32
32
|
The `test_data` method lets you specify an array of data points that you want split for cross-validation (see below). This will be passed to in `run_the_experiment` in the input variable.
|
33
33
|
|
@@ -102,7 +102,7 @@ Then your final config will look like this:
|
|
102
102
|
:master_dir => "/Users/kubowo/Desktop/points-vals/s015",
|
103
103
|
:alpha => 0.6 }
|
104
104
|
|
105
|
-
Flexible, eh?
|
105
|
+
Flexible, eh? **NEW** Check out the *get* method. It has features like interpolation and defaults.
|
106
106
|
|
107
107
|
== Cross Validation
|
108
108
|
|
@@ -116,8 +116,17 @@ Surprise, surprise. This will create two files in your `report` directory (BTW,
|
|
116
116
|
|
117
117
|
The second file created is the `data.csv` file which contains the data from all your experiments. It should be importable to Numbers, Excel even Matlab for further analysis and charting.
|
118
118
|
|
119
|
+
|
120
|
+
== Distributed computing support
|
121
|
+
|
122
|
+
Newly this library supports a simple distributed model of running experiments. Setup worker computers with the
|
123
|
+
|
124
|
+
$ experiment worker --address IP_OF_COMPUTER_WHERE_YOU_RUN_EXPERIMENTS
|
125
|
+
|
126
|
+
and then run experiments with --distributed flag.
|
127
|
+
|
119
128
|
== Misc
|
120
129
|
|
121
|
-
So that's pretty much the gist of experiment. There's a few other features (and a few soon to come to a gem near you ;-)
|
130
|
+
So that's pretty much the gist of experiment. There's a few other features (and a few soon to come to a gem near you ;-) Growl notifications are now supported. Turn them of by setting growl_notifications to false in your config file.
|
122
131
|
|
123
132
|
Also check out the RDocs: http://rdoc.info/github/gampleman/Experiment/master/frames
|
data/Rakefile
CHANGED
@@ -14,7 +14,7 @@ $hoe = Hoe.spec 'experiment' do
|
|
14
14
|
self.developer 'Jakub Hampl', 'honitom@seznam.cz'
|
15
15
|
#self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
|
16
16
|
self.rubyforge_name = self.name # TODO this is default value
|
17
|
-
#
|
17
|
+
#self.extra_deps = [['ruby-growl','>= 1.0']]
|
18
18
|
|
19
19
|
end
|
20
20
|
|
data/bin/experiment
CHANGED
@@ -1,47 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
# == Synopsis
|
4
|
-
# This program will run an experimental batch or generate files
|
5
|
-
# for a new experiment
|
6
|
-
#
|
7
|
-
# == Examples
|
8
|
-
# Running an experiment
|
9
|
-
# experiment --env dice experiment1 experiment2 ...
|
10
|
-
#
|
11
|
-
# Generating a new experiment with 2 cross validations
|
12
|
-
# experiment new experiment_name --cv 2
|
13
|
-
#
|
14
|
-
# List all available experiments
|
15
|
-
# experiment list
|
16
|
-
#
|
17
|
-
#
|
18
|
-
# == Usage
|
19
|
-
# experiment command [options]
|
20
|
-
#
|
21
|
-
# For help use: experiment -h
|
22
|
-
#
|
23
|
-
# == Options
|
24
|
-
# -h, --help Displays help message
|
25
|
-
# -v, --version Display the version, then exit
|
26
|
-
# -q, --quiet Output as little as possible, overrides verbose
|
27
|
-
# -V, --verbose Verbose output
|
28
|
-
# -e, --env Sets the environment to run in
|
29
|
-
# Defaults to development
|
30
|
-
# -c, --cv Number of Cross validations to run
|
31
|
-
# -m, --description A description of the current experiment
|
32
|
-
#
|
33
|
-
# == Author
|
34
|
-
# Jakub Hampl
|
35
|
-
#
|
36
|
-
|
37
|
-
#require "rubygems"
|
38
3
|
require 'optparse'
|
39
|
-
#require 'rdoc/usage'
|
40
4
|
require 'ostruct'
|
41
|
-
|
5
|
+
require "rdoc"
|
6
|
+
require "rdoc/rdoc"
|
7
|
+
|
8
|
+
require File.dirname(__FILE__) + "/../lib/experiment/runner"
|
42
9
|
|
43
10
|
class App
|
44
|
-
VERSION = '
|
11
|
+
VERSION = '0.2.0'
|
45
12
|
|
46
13
|
attr_reader :options
|
47
14
|
|
@@ -55,11 +22,10 @@ class App
|
|
55
22
|
@options.quiet = false
|
56
23
|
@options.env = :development
|
57
24
|
@options.cv = 5
|
58
|
-
@options.n_classes = 10
|
59
|
-
@options.kind = "d"
|
60
25
|
@options.description = ""
|
61
|
-
@options.opts =
|
62
|
-
|
26
|
+
@options.opts = ""
|
27
|
+
@options.distributed = false
|
28
|
+
@options.master = "localhost"
|
63
29
|
end
|
64
30
|
|
65
31
|
# Parse options, check arguments, then process the command
|
@@ -69,10 +35,7 @@ class App
|
|
69
35
|
|
70
36
|
puts "Start at #{DateTime.now}\n\n" if @options.verbose
|
71
37
|
|
72
|
-
output_options if @options.verbose # [Optional]
|
73
|
-
require File.dirname(__FILE__) + "/vendor/backports/backports" if @options.env == :dice
|
74
|
-
|
75
|
-
process_arguments
|
38
|
+
output_options if @options.verbose # [Optional]
|
76
39
|
process_command
|
77
40
|
|
78
41
|
puts "\nFinished at #{DateTime.now}" if @options.verbose
|
@@ -88,20 +51,20 @@ class App
|
|
88
51
|
def parsed_options?
|
89
52
|
|
90
53
|
# Specify options
|
91
|
-
opts = OptionParser.new
|
92
|
-
opts.on('-v', '--version') { output_version ; exit 0 }
|
93
|
-
opts.on('-h', '--help') { output_help }
|
94
|
-
opts.on('-V', '--verbose') { @options.verbose = true }
|
95
|
-
opts.on('-q', '--quiet') { @options.quiet = true }
|
96
|
-
opts.on('-e', '--env [ENV]', [:development, :
|
97
|
-
opts.on('-c', '--cv CV', Integer) { |v| @options.cv = v }
|
98
|
-
opts.on('-
|
99
|
-
opts.on('-
|
100
|
-
opts.on('-m', '--description M', String) { |v| @options.description = v }
|
101
|
-
opts.on('-o', '--options OPTSTRING', String) do |v|
|
54
|
+
@opts = OptionParser.new
|
55
|
+
@opts.on('-v', '--version') { output_version ; exit 0 }
|
56
|
+
@opts.on('-h', '--help') { output_help }
|
57
|
+
@opts.on('-V', '--verbose') { @options.verbose = true }
|
58
|
+
@opts.on('-q', '--quiet') { @options.quiet = true }
|
59
|
+
@opts.on('-e', '--env [ENV]', [:development, :compute], "Sets the environment to run in.") { |v| @options.env = v }
|
60
|
+
@opts.on('-c', '--cv CV', Integer, "The number of cross validations to run.") { |v| @options.cv = v }
|
61
|
+
@opts.on('-m', '--description M', String, "Description or hypothesis for the condition being generated.") { |v| @options.description = v }
|
62
|
+
@opts.on('-o', '--options OPTSTRING', String, "Options to override config with. key1:val1,key2:val2") do |v|
|
102
63
|
@options.opts = v
|
103
64
|
end
|
104
|
-
opts.
|
65
|
+
@opts.on('-D', '--distributed', "Run with a distributed computing mode. This will be the master server/work cue.") { @options.distributed = true }
|
66
|
+
@opts.on('-a', '--address MODE', String, "Address to the master machine.") { |v| @options.master = v }
|
67
|
+
@opts.parse!(@arguments) #rescue return false
|
105
68
|
|
106
69
|
process_options
|
107
70
|
true
|
@@ -122,21 +85,45 @@ class App
|
|
122
85
|
|
123
86
|
# True if required arguments were provided
|
124
87
|
def arguments_valid?
|
125
|
-
true if @arguments.length > 0
|
126
|
-
end
|
127
|
-
|
128
|
-
# Setup the arguments
|
129
|
-
def process_arguments
|
130
|
-
# TO DO - place in local vars, etc
|
88
|
+
true #if @arguments.length > 0
|
131
89
|
end
|
132
90
|
|
133
91
|
def output_help
|
134
92
|
output_version
|
135
|
-
|
93
|
+
puts "= Synopsis
|
94
|
+
This program will run an experimental batch or generate files
|
95
|
+
for a new experiment"
|
96
|
+
puts
|
97
|
+
output_usage
|
98
|
+
puts
|
99
|
+
puts "= Options"
|
100
|
+
puts @opts.help
|
101
|
+
puts
|
102
|
+
puts "= Commands"
|
103
|
+
# Bizzare hax to make RDoc parse the files
|
104
|
+
top_level = RDoc::TopLevel.new File.dirname(__FILE__) + "/../lib/experiment/runner.rb"
|
105
|
+
opts = RDoc::Options.new
|
106
|
+
stats = RDoc::Stats.new 1
|
107
|
+
parser = RDoc::Parser.for top_level, File.dirname(__FILE__) + "/../lib/experiment/runner.rb", File.read(File.dirname(__FILE__) + "/../lib/experiment/runner.rb"), opts, stats
|
108
|
+
d = parser.scan
|
109
|
+
d.modules.first.classes.first.method_list.each do |m|
|
110
|
+
if m.comment != ""
|
111
|
+
puts "== #{m.name == 'new_project' ? 'new' : m.name}"
|
112
|
+
puts m.comment
|
113
|
+
puts
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
puts "----"
|
118
|
+
exit 0
|
136
119
|
end
|
137
120
|
|
138
121
|
def output_usage
|
139
|
-
|
122
|
+
puts "= Usage
|
123
|
+
experiment command [options]
|
124
|
+
|
125
|
+
For help use: experiment -h"
|
126
|
+
|
140
127
|
end
|
141
128
|
|
142
129
|
def output_version
|
@@ -144,130 +131,19 @@ class App
|
|
144
131
|
end
|
145
132
|
|
146
133
|
def process_command
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
req_file.puts "# This includes the reference implementation."
|
157
|
-
req_file.puts "# Override any desired files in this directory."
|
158
|
-
Dir["./app/**/*.rb"].each do |f|
|
159
|
-
p = f.split("/") - File.expand_path(".").split("/")
|
160
|
-
req_file.puts "require File.dirname(__FILE__) + \"/../../#{p.join("/")}\""
|
161
|
-
end
|
162
|
-
req_file.puts "\nclass #{as_class_name @arguments[1]} < MyExperiment\n\t\nend"
|
163
|
-
end
|
164
|
-
File.open(dir + "/config.yaml", "w") do |f|
|
165
|
-
f << "---\nexperiment:\n development:\n compute:\n"
|
166
|
-
end
|
167
|
-
|
168
|
-
elsif @arguments.first == "new" # generate a new project
|
169
|
-
require 'fileutils'
|
170
|
-
dir = "./" + @arguments[1]
|
171
|
-
Dir.mkdir(dir)
|
172
|
-
%w[app config experiments report results test tmp vendor].each do |d|
|
173
|
-
Dir.mkdir(dir + "/" + d)
|
174
|
-
end
|
175
|
-
basedir = File.dirname(__FILE__) + "/.."
|
176
|
-
File.open(File.join(dir, "config", "config.yaml"), "w") do |f|
|
177
|
-
f << "---\nenvironments:\n development:\n compute:\n"
|
178
|
-
end
|
179
|
-
File.open(File.join(dir, ".gitignore"), "w") do |f|
|
180
|
-
f << "tmp/*"
|
181
|
-
end
|
182
|
-
FileUtils::cp File.join(basedir, "lib/experiment/generator/readme_template.txt"), File.join(dir, "README")
|
183
|
-
FileUtils::cp File.join(basedir, "lib/experiment/generator/Rakefile"), File.join(dir, "Rakefile")
|
184
|
-
FileUtils::cp File.join(basedir, "lib/experiment/generator/experiment_template.rb"), File.join(dir, "experiments", "experiment.rb")
|
185
|
-
elsif @arguments.first == "list"
|
186
|
-
puts "Available experiments:"
|
187
|
-
puts " " + Dir["./experiments/*"].map{|a| File.basename(a) }.join(", ")
|
188
|
-
elsif @arguments.first == "report"
|
189
|
-
dir = "./report/"
|
190
|
-
File.open(dir + "method.mmd", "w") do |f|
|
191
|
-
f.puts "# Methods #"
|
192
|
-
Dir["./experiments/*/*.rb"].each do |desc|
|
193
|
-
if File.basename(desc) == File.basename(File.dirname(desc)) + ".rb"
|
194
|
-
File.read(desc).split("\n").each do |line|
|
195
|
-
if m = line.match(/^\# (.+)/)
|
196
|
-
f.puts m[1]
|
197
|
-
else
|
198
|
-
break
|
199
|
-
end
|
200
|
-
end
|
201
|
-
f.puts
|
202
|
-
f.puts
|
203
|
-
end
|
204
|
-
end
|
205
|
-
end
|
206
|
-
require 'csv'
|
207
|
-
require "yaml"
|
208
|
-
require File.dirname(__FILE__)+"/../lib/experiment/stats"
|
209
|
-
CSV.open(dir + "/data.csv", "w") do |csv|
|
210
|
-
data = {}
|
211
|
-
Dir["./results/*/results.yaml"].each do |res|
|
212
|
-
d = YAML::load_file(res)
|
213
|
-
da = {}
|
214
|
-
d.each do |k, vals|
|
215
|
-
da[k.to_s + " mean"], da[k.to_s + " sd"] = Stats::mean(vals), Stats::standard_deviation(vals)
|
216
|
-
vals.each_with_index do |v, i|
|
217
|
-
da[k.to_s + " cv:" + i.to_s] = v
|
218
|
-
end
|
219
|
-
end
|
220
|
-
array_merge(data, da)
|
221
|
-
end
|
222
|
-
data.keys.map do |key|
|
223
|
-
# calculate stats
|
224
|
-
a = data[key]
|
225
|
-
[key] + a
|
226
|
-
end.transpose.each do |row|
|
227
|
-
csv << row
|
228
|
-
end
|
229
|
-
end
|
230
|
-
elsif @arguments.shift == "run"
|
231
|
-
require File.dirname(__FILE__) + "/../lib/experiment/base"
|
232
|
-
require "experiments/experiment"
|
233
|
-
@arguments.each do |exp|
|
234
|
-
require "./experiments/#{exp}/#{exp}"
|
235
|
-
cla = eval(as_class_name(exp))
|
236
|
-
experiment = cla.new exp, @options.opts, @options.env
|
237
|
-
experiment.run! @options.cv
|
238
|
-
end
|
239
|
-
else
|
240
|
-
output_usage
|
241
|
-
end
|
242
|
-
|
243
|
-
end
|
244
|
-
|
245
|
-
|
246
|
-
private
|
247
|
-
def array_merge(h1, h2)
|
248
|
-
h2.each do |key, value|
|
249
|
-
h1[key] ||= []
|
250
|
-
h1[key] << value
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
def as_class_name(str)
|
255
|
-
str.split(/[\_\-]+/).map(&:capitalize).join
|
256
|
-
end
|
257
|
-
|
258
|
-
def as_human_name(str)
|
259
|
-
str.split(/[\_\-]+/).map(&:capitalize).join(" ")
|
134
|
+
command = @arguments.shift
|
135
|
+
runner = Experiment::Runner.new @arguments, options
|
136
|
+
command = "new_project" if command == 'new'
|
137
|
+
#begin
|
138
|
+
runner.send command.to_sym
|
139
|
+
#rescue NoMethodError => e
|
140
|
+
# puts "Wrong input #{e.inspect}"
|
141
|
+
# output_usage
|
142
|
+
#end
|
260
143
|
end
|
261
144
|
|
262
145
|
def process_standard_input
|
263
146
|
input = @stdin.read
|
264
|
-
# TO DO - process input
|
265
|
-
|
266
|
-
# [Optional]
|
267
|
-
# @stdin.each do |line|
|
268
|
-
# # TO DO - process each line
|
269
|
-
#end
|
270
|
-
|
271
147
|
end
|
272
148
|
end
|
273
149
|
|
data/bin/growl.sh
ADDED
@@ -0,0 +1,241 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
#-----------------------------------------------------------------------------
|
3
|
+
# growl - Send the a message to Mac OS X Growl <http://growl.info> via a
|
4
|
+
# Unix shell script.
|
5
|
+
#
|
6
|
+
# And if this is a Linux or Windows_NT box, use ssh to forward the
|
7
|
+
# growl notice to my Mac OS X workstation (taking advantage of
|
8
|
+
# previously exchanged ssh so no additional authentication is
|
9
|
+
# needed).
|
10
|
+
#
|
11
|
+
# This script should be somewhere in your PATH. I like to use
|
12
|
+
# $HOME/bin for personal scripts, and /usr/local/bin for system
|
13
|
+
# wide scripts, with appropriate entries added to PATH. The script
|
14
|
+
# needs to be executable:
|
15
|
+
#
|
16
|
+
# chmod +x $HOME/bin/growl
|
17
|
+
#
|
18
|
+
# The basics for the Growl Applescript came from the Growl
|
19
|
+
# documentation at:
|
20
|
+
# <http://growl.info/documentation/applescript-support.php>
|
21
|
+
#-----------------------------------------------------------------------------
|
22
|
+
# Bob Harris - 2-Jun-2007
|
23
|
+
#-----------------------------------------------------------------------------
|
24
|
+
|
25
|
+
|
26
|
+
#--- usage -------------------------------------------------------------------
|
27
|
+
usage()
|
28
|
+
{
|
29
|
+
echo 1>&2 ""
|
30
|
+
echo 1>&2 "Usage: growl [options] \"message to display\""
|
31
|
+
echo 1>&2 ""
|
32
|
+
echo 1>&2 " -sticky - Stays on screen until dismissed [Default]."
|
33
|
+
echo 1>&2 " -nosticky - Goes away after several seconds."
|
34
|
+
echo 1>&2 " -priority n - Priority -2,-1,0,1,2 [Default: 0]"
|
35
|
+
echo 1>&2 " -verylow - priority [-2]"
|
36
|
+
echo 1>&2 " -moderate - priority [-1]"
|
37
|
+
echo 1>&2 " -normal - priority [0] [Default]"
|
38
|
+
echo 1>&2 " -high - priority [1]"
|
39
|
+
echo 1>&2 " -emergency - priority [2]"
|
40
|
+
echo 1>&2 ""
|
41
|
+
echo 1>&2 " Interesting environment variables:"
|
42
|
+
echo 1>&2 " G_TITLE - Used as the Growl message title."
|
43
|
+
echo 1>&2 " G_APPLICATION_NAME - Used by Growl to manage a set of"
|
44
|
+
echo 1>&2 " G_WITH_NAME message configurations."
|
45
|
+
echo 1>&2 " See System Preferences -> Growl"
|
46
|
+
echo 1>&2 " G_ALL_NAMES - Used to specify 'ALL' the possible"
|
47
|
+
echo 1>&2 " G_WITH_NAME values this"
|
48
|
+
echo 1>&2 " G_APPLICATION_NAME will ever use."
|
49
|
+
echo 1>&2 " Specify something like:"
|
50
|
+
echo 1>&2 " G_ALL_NAMES='\"class1\",\"class2\"'"
|
51
|
+
echo 1>&2 " export G_ALL_NAMES"
|
52
|
+
echo 1>&2 " G_WITH_NAME - Used by Growl to associate a"
|
53
|
+
echo 1>&2 " message with a set of Growl"
|
54
|
+
echo 1>&2 " notification settings, such as"
|
55
|
+
echo 1>&2 " message style and colors associated"
|
56
|
+
echo 1>&2 " with different priorities. See"
|
57
|
+
echo 1>&2 " System preferences -> Growl"
|
58
|
+
echo 1>&2 " Specify something like:"
|
59
|
+
echo 1>&2 " G_WITH_NAME='class2'"
|
60
|
+
echo 1>&2 " export G_WITH_NAME"
|
61
|
+
echo 1>&2 " If G_WITH_NAME is not in"
|
62
|
+
echo 1>&2 " G_ALL_NAMES, nothing will be"
|
63
|
+
echo 1>&2 " displayed"
|
64
|
+
echo 1>&2 " G_APPLICATION_ICON - Display this application's icon in"
|
65
|
+
echo 1>&2 " the Growl message (default is"
|
66
|
+
echo 1>&2 " Terminal.app, as this is a shell"
|
67
|
+
echo 1>&2 " script generated Growl notice)."
|
68
|
+
echo 1>&2 ""
|
69
|
+
exit 1
|
70
|
+
}
|
71
|
+
|
72
|
+
|
73
|
+
#--- defaults ----------------------------------------------------------------
|
74
|
+
defaults()
|
75
|
+
{
|
76
|
+
#
|
77
|
+
# These 2 variables are used when this script notices that it is on a Linux
|
78
|
+
# or Windows_NT system, then this script uses ssh to forward the request to
|
79
|
+
# my Mac OS X workstation. This assumes that ssh keys have been exchanged
|
80
|
+
# between the systems so that no passwords are needed. If you don't know
|
81
|
+
# what this means, then do a Google search for something like
|
82
|
+
# "ssh nopassword key" and you should turn up a number of guides for
|
83
|
+
# configuring ssh.
|
84
|
+
#
|
85
|
+
G_REMOTE="${G_REMOTE:-juggle-mac.us.oracle.com}" # assumes ssh exchanged keys
|
86
|
+
G_REMOTE_GROWL="${G_REMOTE_GROWL:-bin/growl}" # growl script remote location
|
87
|
+
|
88
|
+
#
|
89
|
+
# G_APPLICATION_NAME is the name Growl will use in the Growl System
|
90
|
+
# Preferences to provide defaults for Display, and to collect each of your
|
91
|
+
# notification classes (See G_ALL_NAMES below).
|
92
|
+
#
|
93
|
+
G_APPLICATION_NAME="${G_APPLICATION_NAME:-Shell Script Growl Message}"
|
94
|
+
#
|
95
|
+
# G_ALL_NAMES contains a list of all the notification classes to be
|
96
|
+
# associated with the G_APPLICATION_NAME. Each notification class can have
|
97
|
+
# its own default Display, Priority, and Stickness settings in the Growl
|
98
|
+
# System Preferences.
|
99
|
+
#
|
100
|
+
G_ALL_NAMES="${G_ALL_NAMES:-\"Shell Script Growl Message\",\"Growl Message\"}"
|
101
|
+
#
|
102
|
+
# The default notification class this message should use, must be in the
|
103
|
+
# G_ALL_NAMES list above.
|
104
|
+
#
|
105
|
+
G_WITH_NAME="${G_WITH_NAME:-Shell Script Growl Message}" # default notification
|
106
|
+
#
|
107
|
+
G_TITLE="${G_TITLE:-Shell Script Growl Message}" # default title
|
108
|
+
G_APPLICATION_ICON="${G_APPLICATION_ICON:-Terminal.app}" # default icon to use
|
109
|
+
G_STICKY="${G_STICKY:-yes}" # default sticky setting
|
110
|
+
G_PRIORITY="${G_PRIORITY:-0}" # default priority (normal)
|
111
|
+
}
|
112
|
+
|
113
|
+
|
114
|
+
#--- growl -------------------------------------------------------------------
|
115
|
+
# notify v : Post a notification to be displayed via Growl
|
116
|
+
# notify
|
117
|
+
# with name string : name of the notification to display
|
118
|
+
# title string : title of the notification to display
|
119
|
+
# description string : full text of the notification to display
|
120
|
+
# application name string : name of the application posting the
|
121
|
+
# notification.
|
122
|
+
# [image from location location_reference]
|
123
|
+
# : Location of the image file to use for this
|
124
|
+
# notification. Accepts aliases, paths and
|
125
|
+
# file:/// URLs.
|
126
|
+
# [icon of file location_reference]
|
127
|
+
# : Location of the file whose icon should be
|
128
|
+
# used as the image for this notification.
|
129
|
+
# Accepts aliases, paths and file:/// URLs.
|
130
|
+
# e.g. 'file:///Applications'.
|
131
|
+
# [icon of application string]
|
132
|
+
# : Name of the application whose icon should
|
133
|
+
# be used for this notification. For
|
134
|
+
# example, 'Mail.app'.
|
135
|
+
# [image Image] : TIFF Image to be used for the
|
136
|
+
# notification.
|
137
|
+
# [pictImage Picture] : PICT Image to be used for the
|
138
|
+
# notification.
|
139
|
+
# [sticky boolean] : whether or not the notification displayed
|
140
|
+
# should time out. Defaults to 'no'.
|
141
|
+
# [priority integer] : The priority of the notification, from -2
|
142
|
+
# (low) to 0 (normal) to 2 (emergency).
|
143
|
+
#
|
144
|
+
growl()
|
145
|
+
{
|
146
|
+
typeset description="$*"
|
147
|
+
|
148
|
+
osascript <<EOD
|
149
|
+
-- From <http://growl.info/documentation/applescript-support.php>
|
150
|
+
--
|
151
|
+
tell application "GrowlHelperApp"
|
152
|
+
-- Make a list of all the notification types that this script will ever send:
|
153
|
+
set the allNotificationsList to {${G_ALL_NAMES}}
|
154
|
+
|
155
|
+
-- Make a list of the notifications that will be enabled by default.
|
156
|
+
-- Those not enabled by default can be enabled later in the 'Applications'
|
157
|
+
-- tab of the growl prefpane.
|
158
|
+
set the enabledNotificationsList to {"${G_WITH_NAME}"}
|
159
|
+
|
160
|
+
-- Register our script with growl. You can optionally (as here) set a
|
161
|
+
-- default icon for this script's notifications.
|
162
|
+
register as application "${G_APPLICATION_NAME}" all notifications allNotificationsList default notifications enabledNotificationsList icon of application "${G_APPLICATION_ICON}"
|
163
|
+
|
164
|
+
-- Send a Notification...
|
165
|
+
notify with name "${G_WITH_NAME}" title "${G_TITLE}" description "${description}" application name "${G_APPLICATION_NAME}" sticky ${G_STICKY} priority ${G_PRIORITY}
|
166
|
+
|
167
|
+
end tell
|
168
|
+
EOD
|
169
|
+
}
|
170
|
+
|
171
|
+
|
172
|
+
#--- main --------------------------------------------------------------------
|
173
|
+
#{
|
174
|
+
if [[ $# = 0 ]]; then
|
175
|
+
#
|
176
|
+
# No arguments, so give the usage message.
|
177
|
+
#
|
178
|
+
usage
|
179
|
+
exit 1
|
180
|
+
fi
|
181
|
+
while [[ "X$1" = X-* ]]
|
182
|
+
do
|
183
|
+
if [[ "X$1" = X-nos* ]]; then
|
184
|
+
G_STICKY=no
|
185
|
+
elif [[ "X$1" = X-s* ]]; then
|
186
|
+
G_STICKY=yes
|
187
|
+
elif [[ "X$1" = X-p* ]]; then
|
188
|
+
G_PRIORITY="$2"
|
189
|
+
G_TITLE="${G_TITLE:-Priority $2}"
|
190
|
+
OPTIONS="$OPTIONS $1"
|
191
|
+
shift
|
192
|
+
elif [[ "X$1" = X-v* ]]; then
|
193
|
+
G_PRIORITY="-2"
|
194
|
+
G_TITLE="${G_TITLE:-Very Low Priority}"
|
195
|
+
elif [[ "X$1" = X-m* ]]; then
|
196
|
+
G_PRIORITY="-1"
|
197
|
+
G_TITLE="${G_TITLE:-Moderate Priority}"
|
198
|
+
elif [[ "X$1" = X-n* ]]; then
|
199
|
+
G_PRIORITY="0"
|
200
|
+
G_TITLE="${G_TITLE:-Normal Priority}"
|
201
|
+
elif [[ "X$1" = X-h* ]]; then
|
202
|
+
G_PRIORITY="1"
|
203
|
+
G_TITLE="${G_TITLE:-High Priority}"
|
204
|
+
elif [[ "X$1" = X-e* ]]; then
|
205
|
+
G_PRIORITY="2"
|
206
|
+
G_TITLE="${G_TITLE:-Emergency Priority}"
|
207
|
+
else
|
208
|
+
break;
|
209
|
+
fi
|
210
|
+
OPTIONS="$OPTIONS $1"
|
211
|
+
shift
|
212
|
+
done
|
213
|
+
|
214
|
+
#
|
215
|
+
# If any of the option variables have not been set yet, then apply the
|
216
|
+
# default values now.
|
217
|
+
#
|
218
|
+
defaults
|
219
|
+
|
220
|
+
UNAME=$(uname)
|
221
|
+
if [[ "$UNAME" = Darwin ]]; then
|
222
|
+
#
|
223
|
+
# I'm assuming this is one of my systems where I have Growl
|
224
|
+
# installed.
|
225
|
+
#
|
226
|
+
growl "$*"
|
227
|
+
elif [[ "$UNAME" = *Linux* || "$UNAME" = *Windows_NT* ]]; then
|
228
|
+
#
|
229
|
+
# Must be one of the development systems I work on, so lets ship
|
230
|
+
# this request to my Mac OS X workstation.
|
231
|
+
#
|
232
|
+
ssh ${G_REMOTE} ${G_REMOTE_GROWL} $OPTIONS "$*"
|
233
|
+
else
|
234
|
+
#
|
235
|
+
# I don't know what this is, so I'll just try to Growl anyway. It
|
236
|
+
# will most likely fail (no osascript would be my guess), but what
|
237
|
+
# do I have to loose at this point!
|
238
|
+
#
|
239
|
+
growl "$*"
|
240
|
+
fi
|
241
|
+
#}
|