dock_driver 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +23 -0
- data/.gemtest +0 -0
- data/.hgignore +2 -0
- data/History.txt +26 -0
- data/Ideas.txt +20 -0
- data/Manifest.txt +17 -0
- data/README.txt +74 -0
- data/Rakefile +12 -0
- data/bin/dock_driver +56 -0
- data/data/example.dock_driver.yml +80 -0
- data/lib/dock_driver/data_rate_poll.rb +141 -0
- data/lib/dock_driver/dock.rb +126 -0
- data/lib/dock_driver/laika_hashutils.rb +74 -0
- data/lib/dock_driver/poll.rb +72 -0
- data/lib/dock_driver.rb +16 -0
- data/spec/dock_driver/dock_spec.rb +40 -0
- data/spec/dock_driver/poll_spec.rb +51 -0
- data/spec/dock_driver_spec.rb +17 -0
- data.tar.gz.sig +1 -0
- metadata +161 -0
- metadata.gz.sig +0 -0
data/.autotest
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'autotest/restart'
|
4
|
+
|
5
|
+
# Autotest.add_hook :initialize do |at|
|
6
|
+
# at.extra_files << "../some/external/dependency.rb"
|
7
|
+
#
|
8
|
+
# at.libs << ":../some/external"
|
9
|
+
#
|
10
|
+
# at.add_exception 'vendor'
|
11
|
+
#
|
12
|
+
# at.add_mapping(/dependency.rb/) do |f, _|
|
13
|
+
# at.files_matching(/test_.*rb$/)
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# %w(TestA TestB).each do |klass|
|
17
|
+
# at.extra_class_map[klass] = "test/test_misc.rb"
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
|
21
|
+
# Autotest.add_hook :run_command do |at|
|
22
|
+
# system "rake build"
|
23
|
+
# end
|
data/.gemtest
ADDED
File without changes
|
data/.hgignore
ADDED
data/History.txt
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
=== 0.1.0 / 2012-05-15
|
2
|
+
|
3
|
+
* 3 minor enhancements
|
4
|
+
|
5
|
+
* bin/dock_driver completed
|
6
|
+
* RSpec tests completed & passing
|
7
|
+
* Several bugs fixed
|
8
|
+
|
9
|
+
=== 0.0.3 / 2012-05-15
|
10
|
+
|
11
|
+
* 1 minor enhancement
|
12
|
+
|
13
|
+
* Doc & Manifest Updates
|
14
|
+
|
15
|
+
=== 0.0.2 / 2012-05-15
|
16
|
+
|
17
|
+
* 1 minor enhancement
|
18
|
+
|
19
|
+
* 100% documentation coverage
|
20
|
+
|
21
|
+
=== 0.0.1 / 2012-05-15
|
22
|
+
|
23
|
+
* 1 major enhancement
|
24
|
+
|
25
|
+
* Birthday!
|
26
|
+
|
data/Ideas.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
= Ideas
|
2
|
+
|
3
|
+
== Without Dependencies
|
4
|
+
|
5
|
+
* Graphing
|
6
|
+
* Automatic Coloring (with definable syntax)
|
7
|
+
* Logging
|
8
|
+
|
9
|
+
== With Dependencies
|
10
|
+
|
11
|
+
* Maildir support
|
12
|
+
* SNMP Poller
|
13
|
+
* gateway / internet latency
|
14
|
+
|
15
|
+
== dzen2 Ideas
|
16
|
+
|
17
|
+
* buttons
|
18
|
+
* events
|
19
|
+
* text window
|
20
|
+
|
data/Manifest.txt
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
.autotest
|
2
|
+
.hgignore
|
3
|
+
History.txt
|
4
|
+
Ideas.txt
|
5
|
+
Manifest.txt
|
6
|
+
README.txt
|
7
|
+
Rakefile
|
8
|
+
bin/dock_driver
|
9
|
+
data/example.dock_driver.yml
|
10
|
+
lib/dock_driver.rb
|
11
|
+
lib/dock_driver/laika_hashutils.rb
|
12
|
+
lib/dock_driver/poll.rb
|
13
|
+
lib/dock_driver/data_rate_poll.rb
|
14
|
+
lib/dock_driver/dock.rb
|
15
|
+
spec/dock_driver/dock_spec.rb
|
16
|
+
spec/dock_driver/poll_spec.rb
|
17
|
+
spec/dock_driver_spec.rb
|
data/README.txt
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
= dock_driver
|
2
|
+
|
3
|
+
* http://hg.musl.org/projects/dock_driver/
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Provides a simple executable to drive a dzen2 (or similar) dock with a
|
8
|
+
single YAML config file.
|
9
|
+
|
10
|
+
Read +example.dock_driver.yml+ for more information about how to get
|
11
|
+
started. It's located in this gem's data directory, which is usually:
|
12
|
+
$GEM_HOME/gems/dock_driver-x.y.z/data/
|
13
|
+
|
14
|
+
== FEATURES/PROBLEMS:
|
15
|
+
|
16
|
+
=== Features:
|
17
|
+
|
18
|
+
* easy, flexible configuration
|
19
|
+
* field extraction with +scan_regex+ (no grep or pipes necessary)
|
20
|
+
|
21
|
+
=== Problems:
|
22
|
+
|
23
|
+
* undiscovered bugs
|
24
|
+
* unwritten test cases
|
25
|
+
* cleanup
|
26
|
+
|
27
|
+
== SYNOPSIS:
|
28
|
+
|
29
|
+
There really isn't an API or interface other than the YAML config file.
|
30
|
+
|
31
|
+
== REQUIREMENTS:
|
32
|
+
|
33
|
+
* This gem proudly has zero dependencies besides ruby and rubygems.
|
34
|
+
|
35
|
+
== INSTALL:
|
36
|
+
|
37
|
+
* sudo gem install dock_driver
|
38
|
+
* copy <gemdir>/etc/example.dock_driver.yml to ~/.dock_driver.yml
|
39
|
+
* edit ~/.dock_driver.yml to taste
|
40
|
+
* run dock_driver from your xsession or ~/.i3/config file.
|
41
|
+
|
42
|
+
== DEVELOPERS:
|
43
|
+
|
44
|
+
After checking out the source, run:
|
45
|
+
|
46
|
+
$ rake newb
|
47
|
+
|
48
|
+
This task will install any missing dependencies, run the tests/specs,
|
49
|
+
and generate the RDoc.
|
50
|
+
|
51
|
+
== LICENSE:
|
52
|
+
|
53
|
+
(The MIT License)
|
54
|
+
|
55
|
+
Copyright (c) 2012 Michael Hix
|
56
|
+
|
57
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
58
|
+
a copy of this software and associated documentation files (the
|
59
|
+
'Software'), to deal in the Software without restriction, including
|
60
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
61
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
62
|
+
permit persons to whom the Software is furnished to do so, subject to
|
63
|
+
the following conditions:
|
64
|
+
|
65
|
+
The above copyright notice and this permission notice shall be included
|
66
|
+
in all copies or substantial portions of the Software.
|
67
|
+
|
68
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
69
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
70
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
71
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
72
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
73
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
74
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
data/bin/dock_driver
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'dock_driver/dock'
|
5
|
+
|
6
|
+
include Signal
|
7
|
+
|
8
|
+
opts = {}
|
9
|
+
parser = OptionParser.new do |p|
|
10
|
+
|
11
|
+
p.on(
|
12
|
+
'-c',
|
13
|
+
'--conf FILE',
|
14
|
+
'Specify an alternate config file. (Default: ~/.dock_driver.yml)'
|
15
|
+
) do |file|
|
16
|
+
opts[:conf] = file
|
17
|
+
end
|
18
|
+
|
19
|
+
p.on(
|
20
|
+
'-d',
|
21
|
+
'--dock COMMAND',
|
22
|
+
'Override the dock command from the config file.'
|
23
|
+
) do |command|
|
24
|
+
opts[:dock] = command
|
25
|
+
end
|
26
|
+
|
27
|
+
p.on( '-e', '--debug', 'Enable $DEBUG' ) { |debug| $DEBUG = true }
|
28
|
+
|
29
|
+
p.on( '-h', '--help', 'Print this help text.' ) do
|
30
|
+
cmd = File.basename( $0 )
|
31
|
+
puts "\n%s\n%s\n\nVersion: %s\n%s\n\n" % [
|
32
|
+
cmd,
|
33
|
+
'=' * cmd.length,
|
34
|
+
DockDriver::VERSION,
|
35
|
+
DockDriver::REVISION,
|
36
|
+
]
|
37
|
+
puts p
|
38
|
+
puts "\nAuthors:\n\t%s\n\n" % [
|
39
|
+
DockDriver::AUTHORS.join( '\n\t' ),
|
40
|
+
]
|
41
|
+
exit
|
42
|
+
end
|
43
|
+
|
44
|
+
end.parse!
|
45
|
+
|
46
|
+
puts "Options: %p" % opts if $DEBUG
|
47
|
+
|
48
|
+
dock = DockDriver::Dock.new( opts[:conf], opts[:dock] )
|
49
|
+
|
50
|
+
trap 'INT' do
|
51
|
+
dock.kill
|
52
|
+
puts "\nKilled!" if $DEBUG
|
53
|
+
end
|
54
|
+
|
55
|
+
dock.run
|
56
|
+
|
@@ -0,0 +1,80 @@
|
|
1
|
+
---
|
2
|
+
# Specify a program to run here. This bit of code was initially meant to run
|
3
|
+
# dzen2 but other docks are certainly appropriate, provided they accept commands
|
4
|
+
# and data over standard input. You'll probably need to adjust your formats and
|
5
|
+
# templates below if you use a different dock program.
|
6
|
+
#
|
7
|
+
dock_command:
|
8
|
+
dzen2 -dock -ta r -x 0 -y 0 -w 1680 -h 24 -bg black -fg white
|
9
|
+
-fn "-*-terminus-medium-r-*-*-12-*-*-*-*-*-*-*"
|
10
|
+
-e "button3="
|
11
|
+
|
12
|
+
# See the manpages for strftime and dzen2.
|
13
|
+
#
|
14
|
+
time_format:
|
15
|
+
^fg(white)%A ^fg(gray80)%Y-%m-%d ^fg(gray60)%H:%M:%S ^fg(gray30)%Z
|
16
|
+
|
17
|
+
# Determines the layout and styling of the dock.
|
18
|
+
#
|
19
|
+
# Processed by +ERB+. +Poll+ objects as defined by the 'commands' section are
|
20
|
+
# available. Since +Poll+ objects respond to to_s intelligently, it's usually
|
21
|
+
# sufficient to just specify the object name. Since they're also subclasses of
|
22
|
+
# +OpenStruct+, you can also reference any method or member here.
|
23
|
+
#
|
24
|
+
# See the dzen2 documentation.
|
25
|
+
#
|
26
|
+
template:
|
27
|
+
^fg(white)rx ^fg(gray60)<%= eth0_rx %> ^fg(gray30)<%= eth0_rx.unit %> ·
|
28
|
+
^fg(white)tx ^fg(gray60)<%= eth0_tx %> ^fg(gray30)<%= eth0_tx.unit %> ·
|
29
|
+
^fg(white)cpu0 ^fg(gray60)<%= cpu0_temp %>^fg(gray30) C ·
|
30
|
+
^fg(white)cpu1 ^fg(gray60)<%= cpu1_temp %>^fg(gray30) C ·
|
31
|
+
^fg(white)gpu ^fg(gray60)<%= gpu_temp %>^fg(gray30) C ·
|
32
|
+
^fg(white)mail <%= mail %>^fg(gray30) ·
|
33
|
+
^fg(gray60)<%= time %>^fg(gray30) ·
|
34
|
+
|
35
|
+
# Commands to poll. Each command will be polled every second, but specifying a
|
36
|
+
# +delay+ greater than one will cache the value and only run the command every
|
37
|
+
# +delay+ seconds. This defaults to evaluating a string with a shell, but if
|
38
|
+
# your YAML fu is strong enough, you can specify a Proc here too.
|
39
|
+
#
|
40
|
+
# To narrow down the output, you may use +scan_regex+. A single-quoted regex
|
41
|
+
# without forward slashes works great. Since scan returns an array, using one
|
42
|
+
# or more capture groups will allow you to pull multiple values from a single
|
43
|
+
# command's output - the values returned will be joined with single spaces.
|
44
|
+
#
|
45
|
+
# To change the class used to poll the command, you can specify a +type+.
|
46
|
+
# Right now, only 'rate' is recognized. It uses a separate class that expects
|
47
|
+
# a string containing a single number. Best to use +scan_regex+ here. It
|
48
|
+
# calculates a running average based on the changes in the value over time.
|
49
|
+
# A unit of measurement (b, kb, mb, gb, tb, pb, eb, zb, yb) or its full name
|
50
|
+
# (bits, kibibits, mebibits, ...) is also useful here. You may also specify a
|
51
|
+
# +number_format+ here if you don't like the default. See +sprintf+ for details
|
52
|
+
# on the format string.
|
53
|
+
#
|
54
|
+
commands:
|
55
|
+
mail:
|
56
|
+
cmd: '/home/joeuser/bin/dockmail'
|
57
|
+
delay: 5
|
58
|
+
cpu0_temp:
|
59
|
+
cmd: 'sensors'
|
60
|
+
scan_regex: 'Core 0:\s+\+(\d+\.\d+)'
|
61
|
+
delay: 30
|
62
|
+
cpu1_temp:
|
63
|
+
cmd: 'sensors'
|
64
|
+
scan_regex: 'Core 1:\s+\+(\d+\.\d+)'
|
65
|
+
delay: 30
|
66
|
+
gpu_temp:
|
67
|
+
cmd: 'nvidia-smi -q -d TEMPERATURE'
|
68
|
+
scan_regex: '(\d+) C'
|
69
|
+
delay: 30
|
70
|
+
eth0_rx:
|
71
|
+
cmd: 'ifconfig eth0'
|
72
|
+
scan_regex: 'RX bytes:(\d+)'
|
73
|
+
type: rate
|
74
|
+
unit: mb
|
75
|
+
eth0_tx:
|
76
|
+
cmd: 'ifconfig eth0'
|
77
|
+
scan_regex: 'TX bytes:(\d+)'
|
78
|
+
type: rate
|
79
|
+
unit: mb
|
80
|
+
|
@@ -0,0 +1,141 @@
|
|
1
|
+
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
require 'dock_driver' unless defined? DockDriver
|
5
|
+
require 'dock_driver/poll'
|
6
|
+
|
7
|
+
# A class to provide simple, periodic command execution, caching, and a
|
8
|
+
# running average in changes in a value.
|
9
|
+
#
|
10
|
+
class DockDriver::DataRatePoll < DockDriver::Poll
|
11
|
+
|
12
|
+
# Default values to populate the OpenStruct with.
|
13
|
+
#
|
14
|
+
DEFAULTS = {
|
15
|
+
:unit => :mb,
|
16
|
+
:history_size => 2,
|
17
|
+
:number_format => '%0.3f',
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
# Create a new DataRatePoll.
|
21
|
+
#
|
22
|
+
# === Arguments:
|
23
|
+
#
|
24
|
+
# +opts+ - A hash containing options. Merged with DEFAULTS.
|
25
|
+
#
|
26
|
+
# +block+ - If a block is given, replace :cmd with that block.
|
27
|
+
#
|
28
|
+
# === Options:
|
29
|
+
#
|
30
|
+
# +:delay+ - Determines the minimum amount of time to let elapse between executions.
|
31
|
+
#
|
32
|
+
# +:cmd+ - A string or block to execute.
|
33
|
+
#
|
34
|
+
# +:scan_regex+ - A bare string to be interpreted as a regex for use with +scan+. Any
|
35
|
+
# capture group matches will be joined with single spaces.
|
36
|
+
#
|
37
|
+
# +:history_size+ - How many samples to keep
|
38
|
+
#
|
39
|
+
# +:unit+ - The unit to use. See the methods and aliases available.
|
40
|
+
# (b, kb, mb, gb, tb, pb, eb, zb, yb, and full names: bits, kibibits, mebibits, ...)
|
41
|
+
#
|
42
|
+
# +:number_format+ - The format used by +to_s+ to turn a number into a string. See: +printf+
|
43
|
+
#
|
44
|
+
def initialize( opts = {} )
|
45
|
+
super DEFAULTS.merge( opts )
|
46
|
+
|
47
|
+
@last_sample = nil
|
48
|
+
@history = Array.new( @table[:history_size] ) { 0 }
|
49
|
+
@average = 0
|
50
|
+
end
|
51
|
+
|
52
|
+
# Runs the command if enough time has elapsed since the last
|
53
|
+
# poll. "Enough Time" is defined by the :delay option. Maintains the
|
54
|
+
# history of output, average and formatting.
|
55
|
+
#
|
56
|
+
def poll
|
57
|
+
return false unless super
|
58
|
+
|
59
|
+
@sample = @sample.to_s.to_f unless @sample.is_a? Float
|
60
|
+
@last_sample ||= @sample
|
61
|
+
sample = 8 * ( @sample - @last_sample )
|
62
|
+
@history.shift if @history.length >= @table[:history_size]
|
63
|
+
@history.push sample
|
64
|
+
@last_sample = @sample
|
65
|
+
@average = @table[:number_format] % send( @table[:unit] )
|
66
|
+
|
67
|
+
true
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns the average value in bits.
|
71
|
+
#
|
72
|
+
def bits
|
73
|
+
@history.inject( :+ ) / @history.count.to_f
|
74
|
+
end
|
75
|
+
alias :b :bits
|
76
|
+
|
77
|
+
# Returns the average value in kibibits.
|
78
|
+
#
|
79
|
+
def kibibits
|
80
|
+
bits / 1024.0
|
81
|
+
end
|
82
|
+
alias :kb :kibibits
|
83
|
+
|
84
|
+
# Returns the average value in mebibits.
|
85
|
+
#
|
86
|
+
def mebibits
|
87
|
+
kibibits / 1024.0
|
88
|
+
end
|
89
|
+
alias :mb :mebibits
|
90
|
+
|
91
|
+
# Returns the average value in gibibits.
|
92
|
+
#
|
93
|
+
def gibibits
|
94
|
+
mebibits / 1024.0
|
95
|
+
end
|
96
|
+
alias :gb :gibibits
|
97
|
+
|
98
|
+
# Returns the average value in tebibits.
|
99
|
+
#
|
100
|
+
def tebibits
|
101
|
+
gibibits / 1024.0
|
102
|
+
end
|
103
|
+
alias :tb :tebibits
|
104
|
+
|
105
|
+
# Returns the average value in pebibits.
|
106
|
+
#
|
107
|
+
def pebibits
|
108
|
+
tebibits / 1024.0
|
109
|
+
end
|
110
|
+
alias :pb :pebibits
|
111
|
+
|
112
|
+
# Returns the average value in exbibits.
|
113
|
+
#
|
114
|
+
def exbibits
|
115
|
+
pebibits / 1024.0
|
116
|
+
end
|
117
|
+
alias :eb :exbibits
|
118
|
+
|
119
|
+
# Returns the average value in zebibits.
|
120
|
+
#
|
121
|
+
def zebibits
|
122
|
+
exbibits / 1024.0
|
123
|
+
end
|
124
|
+
alias :zb :zebibits
|
125
|
+
|
126
|
+
# Returns the average value in yobibits.
|
127
|
+
#
|
128
|
+
def yobibits
|
129
|
+
zebibits / 1024.0
|
130
|
+
end
|
131
|
+
alias :yb :yobibits
|
132
|
+
|
133
|
+
# Returns the output of the command this Poll runs, formated as per the
|
134
|
+
# option :number_format.
|
135
|
+
#
|
136
|
+
def to_s
|
137
|
+
@average
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
@@ -0,0 +1,126 @@
|
|
1
|
+
|
2
|
+
require 'erb'
|
3
|
+
require 'yaml'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
require 'dock_driver' unless defined? DockDriver
|
7
|
+
require 'dock_driver/laika_hashutils'
|
8
|
+
require 'dock_driver/poll'
|
9
|
+
require 'dock_driver/data_rate_poll'
|
10
|
+
|
11
|
+
# An object that represents and drives a dock program, periodically
|
12
|
+
# updating what it displays.
|
13
|
+
#
|
14
|
+
class DockDriver::Dock < OpenStruct
|
15
|
+
|
16
|
+
include LAIKA::HashUtilities
|
17
|
+
|
18
|
+
# Default values to populate this OpenStruct with.
|
19
|
+
#
|
20
|
+
DEFAULTS = {
|
21
|
+
:polls => {},
|
22
|
+
:time_format => '%Y-%m-%d %H:%M:%S %Z',
|
23
|
+
:config_file => File.expand_path( '~/.dock_driver.yml' ),
|
24
|
+
:template => '',
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
# Create and start a new Dock.
|
28
|
+
#
|
29
|
+
# === Arguments:
|
30
|
+
#
|
31
|
+
# +:config_file+ - Provide a path to a config file different from
|
32
|
+
# the default: ~/.dock_driver.yml
|
33
|
+
#
|
34
|
+
# +:dock_command+ - Override the dock command from the config file.
|
35
|
+
#
|
36
|
+
def initialize( config_file = nil, dock_command = nil )
|
37
|
+
super DEFAULTS
|
38
|
+
@worker = nil
|
39
|
+
@table[:config_file] = config_file if config_file
|
40
|
+
load_config
|
41
|
+
@table[:dock_command] = dock_command if dock_command
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the result of the template.
|
45
|
+
#
|
46
|
+
def to_s()
|
47
|
+
# Poll everything.
|
48
|
+
@table[:polls].values.map( &:poll )
|
49
|
+
|
50
|
+
# Prepare a binding and render the ERB template.
|
51
|
+
vbind = OpenStruct.new( @table[:polls] ).send( :binding )
|
52
|
+
@table[:template].result( vbind ).gsub( /\s+/, ' ' )
|
53
|
+
end
|
54
|
+
|
55
|
+
# Start thiis Dock if it isn't already running, does nothing otherwise.
|
56
|
+
#
|
57
|
+
def run
|
58
|
+
@worker ||= Thread.new do
|
59
|
+
while true
|
60
|
+
File.popen( @table[:dock_command], 'w' ) do |pipe|
|
61
|
+
while true
|
62
|
+
pipe.puts self
|
63
|
+
pipe.flush
|
64
|
+
sleep 1
|
65
|
+
break if load_config
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
@worker.run unless @worker.status
|
71
|
+
@worker.join
|
72
|
+
end
|
73
|
+
|
74
|
+
# Forcibly stops this Dock if it is running, does nothing otherwise.
|
75
|
+
#
|
76
|
+
def kill
|
77
|
+
@worker.kill if @worker.status
|
78
|
+
end
|
79
|
+
|
80
|
+
#########
|
81
|
+
protected
|
82
|
+
#########
|
83
|
+
|
84
|
+
# Loads the configuration file defined by the option +:config_file+ if it has
|
85
|
+
# changed since this method was last called. If not, returns false.
|
86
|
+
#
|
87
|
+
# === Arguments:
|
88
|
+
#
|
89
|
+
# +force+ - If true, force a reload, even if the config file hasn't changed.
|
90
|
+
#
|
91
|
+
def load_config( force = false )
|
92
|
+
|
93
|
+
mtime = File.mtime @table[:config_file]
|
94
|
+
return false if not force and mtime == @table[:config_mtime]
|
95
|
+
|
96
|
+
@table.delete :template
|
97
|
+
hash = symbolify_keys( YAML.load_file( @table[:config_file] ) )
|
98
|
+
hash.delete :config_file
|
99
|
+
@table.merge! hash
|
100
|
+
@table[:config_mtime] = mtime
|
101
|
+
|
102
|
+
@table[:polls] = {}
|
103
|
+
|
104
|
+
if @table[:commands] and @table[:commands].is_a? Hash
|
105
|
+
@table[:commands].each do |name,hash|
|
106
|
+
klass = DockDriver::Poll
|
107
|
+
case hash[:type]
|
108
|
+
when /rate/i
|
109
|
+
klass = DockDriver::DataRatePoll
|
110
|
+
end
|
111
|
+
@table[:polls][name] = klass.new( hash )
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
if @table[:polls]['time'].nil?
|
116
|
+
@table[:polls]['time'] = DockDriver::Poll.new( { :delay => 1 } ) do
|
117
|
+
Time.now.strftime( @table[:time_format] )
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
@table[:template] = ERB.new( @table[:template] )
|
122
|
+
|
123
|
+
return true
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# A module borrowed from my coworkers at LAIKA.
|
2
|
+
#
|
3
|
+
module LAIKA
|
4
|
+
|
5
|
+
### A collection of utilities for working with Hashes.
|
6
|
+
module HashUtilities
|
7
|
+
|
8
|
+
###############
|
9
|
+
module_function
|
10
|
+
###############
|
11
|
+
|
12
|
+
### Return a version of the given +hash+ with its keys transformed
|
13
|
+
### into Strings from whatever they were before.
|
14
|
+
def stringify_keys( hash )
|
15
|
+
newhash = {}
|
16
|
+
|
17
|
+
hash.each do |key,val|
|
18
|
+
if val.is_a?( Hash )
|
19
|
+
newhash[ key.to_s ] = stringify_keys( val )
|
20
|
+
else
|
21
|
+
newhash[ key.to_s ] = val
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
return newhash
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
### Return a duplicate of the given +hash+ with its identifier-like keys
|
30
|
+
### transformed into symbols from whatever they were before.
|
31
|
+
def symbolify_keys( hash )
|
32
|
+
newhash = {}
|
33
|
+
|
34
|
+
hash.each do |key,val|
|
35
|
+
keysym = key.to_s.dup.untaint.to_sym
|
36
|
+
|
37
|
+
if val.is_a?( Hash )
|
38
|
+
newhash[ keysym ] = symbolify_keys( val )
|
39
|
+
else
|
40
|
+
newhash[ keysym ] = val
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
return newhash
|
45
|
+
end
|
46
|
+
alias_method :internify_keys, :symbolify_keys
|
47
|
+
|
48
|
+
|
49
|
+
# Recursive hash-merge function
|
50
|
+
def merge_recursively( key, oldval, newval )
|
51
|
+
case oldval
|
52
|
+
when Hash
|
53
|
+
case newval
|
54
|
+
when Hash
|
55
|
+
oldval.merge( newval, &method(:merge_recursively) )
|
56
|
+
else
|
57
|
+
newval
|
58
|
+
end
|
59
|
+
|
60
|
+
when Array
|
61
|
+
case newval
|
62
|
+
when Array
|
63
|
+
oldval | newval
|
64
|
+
else
|
65
|
+
newval
|
66
|
+
end
|
67
|
+
|
68
|
+
else
|
69
|
+
newval
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end # HashUtilities
|
74
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
require 'dock_driver' unless defined? DockDriver
|
5
|
+
|
6
|
+
# A class to provide simple, periodic command execution and caching.
|
7
|
+
#
|
8
|
+
class DockDriver::Poll < OpenStruct
|
9
|
+
|
10
|
+
# Default values to populate the OpenStruct with.
|
11
|
+
#
|
12
|
+
DEFAULTS = {
|
13
|
+
:delay => 1,
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
# Create a new Poll.
|
17
|
+
#
|
18
|
+
# === Arguments:
|
19
|
+
#
|
20
|
+
# +opts+ - A hash containing options. Merged with DEFAULTS.
|
21
|
+
#
|
22
|
+
# +block+ - If a block is given, replace :cmd with that block.
|
23
|
+
#
|
24
|
+
# === Options:
|
25
|
+
#
|
26
|
+
# +:delay+ - Determines the minimum amount of time to let elapse between executions.
|
27
|
+
#
|
28
|
+
# +:cmd+ - A string or block to execute.
|
29
|
+
#
|
30
|
+
# +:scan_regex+ - A bare string to be interpreted as a regex for use with +scan+. Any
|
31
|
+
# capture group matches will be joined with single spaces.
|
32
|
+
#
|
33
|
+
def initialize( opts = {}, &block )
|
34
|
+
super DEFAULTS.merge( opts )
|
35
|
+
@sample = nil
|
36
|
+
|
37
|
+
if block_given?
|
38
|
+
@table[:cmd] = block
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Runs the command if enough time has elapsed since the last poll. "Enough Time" is
|
43
|
+
# defined by the :delay option.
|
44
|
+
#
|
45
|
+
def poll
|
46
|
+
return false if @sample and (Time.now - (@table[:last_poll] || 0 )) < @table[:delay]
|
47
|
+
|
48
|
+
@table[:last_poll] = Time.now
|
49
|
+
|
50
|
+
case @table[:cmd]
|
51
|
+
when Proc
|
52
|
+
@sample = @table[:cmd].call
|
53
|
+
else
|
54
|
+
@sample = `#{@table[:cmd].to_s}`
|
55
|
+
end
|
56
|
+
|
57
|
+
if @table[:scan_regex]
|
58
|
+
extract = @sample.scan( Regexp.new( @table[:scan_regex] ) ).flatten
|
59
|
+
@sample = extract.join( ' ' ) unless extract.empty?
|
60
|
+
end
|
61
|
+
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns the output of the command this Poll runs.
|
66
|
+
#
|
67
|
+
def to_s
|
68
|
+
@sample
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
data/lib/dock_driver.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
# A namespace for the project.
|
3
|
+
#
|
4
|
+
module DockDriver
|
5
|
+
|
6
|
+
# Version Constant
|
7
|
+
VERSION = '0.1.0'
|
8
|
+
|
9
|
+
# Revision Constant
|
10
|
+
REVISION = %q$Revision: d17549778789 $
|
11
|
+
|
12
|
+
# Behold the author(s) responsible for this mess.
|
13
|
+
AUTHORS = [ 'Mike Hix <mike@musl.org>' ]
|
14
|
+
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
require 'pathname'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'dock_driver/dock'
|
5
|
+
|
6
|
+
describe DockDriver::Dock do
|
7
|
+
|
8
|
+
subject do
|
9
|
+
conf = Pathname( __FILE__ ).parent.parent.parent + 'data'
|
10
|
+
conf += 'example.dock_driver.yml'
|
11
|
+
dock = Dock.new conf, 'cat'
|
12
|
+
dock
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should be able to load a config file properly' do
|
16
|
+
subject.dock_command.should_not be nil
|
17
|
+
subject.template.should_not be nil
|
18
|
+
subject.polls.should have_at_least( 2 ).things
|
19
|
+
subject.polls.values.each { |p| p.should be_a_kind_of Poll }
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should only attempt to reload a config file with a more recent mtime' do
|
23
|
+
subject.send( :load_config ).should be false
|
24
|
+
FileUtils.touch subject.config_file
|
25
|
+
subject.send( :load_config ).should be true
|
26
|
+
subject.send( :load_config ).should be false
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should produce a string properly' do
|
30
|
+
|
31
|
+
# make sure we don't fail if a particular binary for an example doesn't exist.
|
32
|
+
subject.polls.values.each { |p| p.cmd = 'echo' }
|
33
|
+
|
34
|
+
string = subject.to_s
|
35
|
+
string.should be_a_kind_of String
|
36
|
+
string.should_not be ''
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
|
2
|
+
require 'dock_driver/poll'
|
3
|
+
require 'dock_driver/data_rate_poll'
|
4
|
+
|
5
|
+
include DockDriver
|
6
|
+
|
7
|
+
shared_examples 'Poll' do |klass|
|
8
|
+
|
9
|
+
it 'should be able to execute shell commands' do
|
10
|
+
p = Poll.new( :cmd => "echo" )
|
11
|
+
lambda { p.poll }.should_not raise_error
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should be able to execute blocks' do
|
15
|
+
p = Poll.new do; end
|
16
|
+
lambda { p.poll }.should_not raise_error
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should return the same object if polled quickly' do
|
20
|
+
p = Poll.new( :cmd => "echo" )
|
21
|
+
p.poll
|
22
|
+
a = p.to_s
|
23
|
+
p.poll
|
24
|
+
b = p.to_s
|
25
|
+
a.object_id.should == b.object_id
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should return a different object if polled slowly' do
|
29
|
+
p = Poll.new( :cmd => "echo", :delay => 0.1 )
|
30
|
+
p.poll
|
31
|
+
a = p.to_s
|
32
|
+
sleep 0.5
|
33
|
+
p.poll
|
34
|
+
b = p.to_s
|
35
|
+
a.object_id.should_not == b.object_id
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
describe Poll do
|
41
|
+
|
42
|
+
include_examples 'Poll', Poll
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
describe DataRatePoll do
|
47
|
+
|
48
|
+
include_examples 'Poll', DataRatePoll
|
49
|
+
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
require 'dock_driver'
|
3
|
+
|
4
|
+
describe DockDriver do
|
5
|
+
|
6
|
+
it 'should have a standard version number' do
|
7
|
+
defined?( VERSION ).should == 'constant'
|
8
|
+
VERSION.should =~ /^\d+\.\d+\.\d+$/
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should have a mercurial revision' do
|
12
|
+
defined?( REVISION ).should == 'constant'
|
13
|
+
REVISION.should =~ /^Revision: [[:xdigit:]]+ $/
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
data.tar.gz.sig
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3k�S���^���p���
|
metadata
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dock_driver
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Michael Hix
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain:
|
17
|
+
- |
|
18
|
+
-----BEGIN CERTIFICATE-----
|
19
|
+
MIIDKDCCAhCgAwIBAgIBADANBgkqhkiG9w0BAQUFADA6MQ0wCwYDVQQDDARtaWtl
|
20
|
+
MRQwEgYKCZImiZPyLGQBGRYEbXVzbDETMBEGCgmSJomT8ixkARkWA29yZzAeFw0x
|
21
|
+
MjA0MjUxODM4MjdaFw0xMzA0MjUxODM4MjdaMDoxDTALBgNVBAMMBG1pa2UxFDAS
|
22
|
+
BgoJkiaJk/IsZAEZFgRtdXNsMRMwEQYKCZImiZPyLGQBGRYDb3JnMIIBIjANBgkq
|
23
|
+
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyYKo5tTirdfMTksMM5DnnwLtGUnAPE++
|
24
|
+
pTPOenB29rVurrI2Vw8ndjbiWtaBOwb6jVdMzEwxBwVBa4r4FdSQO8rzEm1jV/+B
|
25
|
+
LEbl5xvOy/PRAXKvaUrQYCvuYtJA1py1mGIC8evNiH4jxZl0Fn9GrjNzzuRoSi/O
|
26
|
+
nLA4dcI5InCWAtUTUtMkMPn9mz9eZRnqd2yxziciwHzHkXBqVDACeAvo4PZUtTEL
|
27
|
+
aAs/yP3Hc/AH7StrwXb2KUJexSJximkAGg1J0iMCuKTBE9e534OXnE0NgCuyOrle
|
28
|
+
lpPTxF5umMpkiTwHLrmBWez3/JyAt1wt+5YWXamQBO1bGqzG/aGU1QIDAQABozkw
|
29
|
+
NzAdBgNVHQ4EFgQUPdX0JADsZMOtODSP0KlgqTgMG1cwCQYDVR0TBAIwADALBgNV
|
30
|
+
HQ8EBAMCBLAwDQYJKoZIhvcNAQEFBQADggEBADnlpP5hNPaM2x1q/lumvpHi1NU5
|
31
|
+
n1TJ13ZdVbRT1e9HbCs2nQ6TahIdf/1vdVlVldeNRDWxe2yXSG8RrpFZV1ATu+L/
|
32
|
+
mCGPM2NBUh3zT52kMiVJFh4jqI6SWLU/6vojxZms8jpghT7giA5KNQYN6ZK3jrTK
|
33
|
+
yocRBh/rTKHm7C207qTf/iLZj4kG60ozVdkYkZPlrP6/DkfyamnTHrmtN0W30xWC
|
34
|
+
V9AGPfVm7XVfb/wPkWPD3jgA77/j4KJrIH0ML0W9qypcYgH11X3RS0hBM+cx1WcX
|
35
|
+
Edc7ShBTME9+u6K/HFwVxTOH8WJVxkwGTmxKgCevr5mZzbINH/C6LbkmfEA=
|
36
|
+
-----END CERTIFICATE-----
|
37
|
+
|
38
|
+
date: 2012-05-16 00:00:00 Z
|
39
|
+
dependencies:
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: hoe-mercurial
|
42
|
+
prerelease: false
|
43
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ~>
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
hash: 7
|
49
|
+
segments:
|
50
|
+
- 1
|
51
|
+
- 4
|
52
|
+
- 0
|
53
|
+
version: 1.4.0
|
54
|
+
type: :development
|
55
|
+
version_requirements: *id001
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rdoc
|
58
|
+
prerelease: false
|
59
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ~>
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
hash: 19
|
65
|
+
segments:
|
66
|
+
- 3
|
67
|
+
- 10
|
68
|
+
version: "3.10"
|
69
|
+
type: :development
|
70
|
+
version_requirements: *id002
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: hoe
|
73
|
+
prerelease: false
|
74
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ~>
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
hash: 7
|
80
|
+
segments:
|
81
|
+
- 3
|
82
|
+
- 0
|
83
|
+
version: "3.0"
|
84
|
+
type: :development
|
85
|
+
version_requirements: *id003
|
86
|
+
description: |-
|
87
|
+
Provides a simple executable to drive a dzen2 (or similar) dock with a
|
88
|
+
single YAML config file.
|
89
|
+
|
90
|
+
Read +example.dock_driver.yml+ for more information about how to get
|
91
|
+
started. It's located in this gem's data directory, which is usually:
|
92
|
+
$GEM_HOME/gems/dock_driver-x.y.z/data/
|
93
|
+
email:
|
94
|
+
- mike@musl.org
|
95
|
+
executables:
|
96
|
+
- dock_driver
|
97
|
+
extensions: []
|
98
|
+
|
99
|
+
extra_rdoc_files:
|
100
|
+
- History.txt
|
101
|
+
- Ideas.txt
|
102
|
+
- Manifest.txt
|
103
|
+
- README.txt
|
104
|
+
files:
|
105
|
+
- .autotest
|
106
|
+
- .hgignore
|
107
|
+
- History.txt
|
108
|
+
- Ideas.txt
|
109
|
+
- Manifest.txt
|
110
|
+
- README.txt
|
111
|
+
- Rakefile
|
112
|
+
- bin/dock_driver
|
113
|
+
- data/example.dock_driver.yml
|
114
|
+
- lib/dock_driver.rb
|
115
|
+
- lib/dock_driver/laika_hashutils.rb
|
116
|
+
- lib/dock_driver/poll.rb
|
117
|
+
- lib/dock_driver/data_rate_poll.rb
|
118
|
+
- lib/dock_driver/dock.rb
|
119
|
+
- spec/dock_driver/dock_spec.rb
|
120
|
+
- spec/dock_driver/poll_spec.rb
|
121
|
+
- spec/dock_driver_spec.rb
|
122
|
+
- .gemtest
|
123
|
+
homepage: http://hg.musl.org/projects/dock_driver/
|
124
|
+
licenses: []
|
125
|
+
|
126
|
+
post_install_message:
|
127
|
+
rdoc_options:
|
128
|
+
- --main
|
129
|
+
- README.txt
|
130
|
+
require_paths:
|
131
|
+
- lib
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
133
|
+
none: false
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
hash: 57
|
138
|
+
segments:
|
139
|
+
- 1
|
140
|
+
- 8
|
141
|
+
- 7
|
142
|
+
version: 1.8.7
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
none: false
|
145
|
+
requirements:
|
146
|
+
- - ">="
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
hash: 31
|
149
|
+
segments:
|
150
|
+
- 1
|
151
|
+
- 8
|
152
|
+
version: "1.8"
|
153
|
+
requirements: []
|
154
|
+
|
155
|
+
rubyforge_project: dock_driver
|
156
|
+
rubygems_version: 1.8.17
|
157
|
+
signing_key:
|
158
|
+
specification_version: 3
|
159
|
+
summary: Provides a simple executable to drive a dzen2 (or similar) dock with a single YAML config file
|
160
|
+
test_files: []
|
161
|
+
|
metadata.gz.sig
ADDED
Binary file
|