kicker 2.3.0 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +1 -1
- data/Rakefile +2 -1
- data/VERSION +1 -1
- data/bin/kicker +6 -1
- data/kicker.gemspec +11 -6
- data/lib/kicker.rb +5 -7
- data/lib/kicker/fsevents.rb +33 -0
- data/lib/kicker/growl.rb +64 -44
- data/lib/kicker/options.rb +14 -6
- data/test/fsevents_test.rb +35 -0
- data/test/growl_test.rb +77 -73
- data/test/initialization_test.rb +21 -27
- data/test/options_test.rb +12 -8
- metadata +35 -8
- data/vendor/rucola/fsevents.rb +0 -136
data/README.rdoc
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
A lean, agnostic, flexible file-change watcher, using OS X FSEvents.
|
4
4
|
|
5
|
-
|
5
|
+
https://github.com/alloy/kicker/raw/master/html/images/kikker.jpg
|
6
6
|
|
7
7
|
Meet king kikker, kicking stuff in your computers is his dream come true!
|
8
8
|
|
data/Rakefile
CHANGED
@@ -14,6 +14,7 @@ begin
|
|
14
14
|
gem.files.concat FileList['vendor/**/*']
|
15
15
|
gem.require_paths = ["lib", "vendor"]
|
16
16
|
gem.has_rdoc = true
|
17
|
+
gem.add_dependency 'rb-fsevent'
|
17
18
|
end
|
18
19
|
rescue LoadError
|
19
20
|
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
@@ -33,4 +34,4 @@ namespace :docs do
|
|
33
34
|
end
|
34
35
|
end
|
35
36
|
|
36
|
-
task :default => :test
|
37
|
+
task :default => :test
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.3.
|
1
|
+
2.3.1
|
data/bin/kicker
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
2
|
+
|
3
|
+
if $0 == __FILE__
|
4
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
5
|
+
$:.unshift File.expand_path('../../vendor', __FILE__)
|
6
|
+
require 'rubygems'
|
7
|
+
end
|
3
8
|
|
4
9
|
require 'kicker'
|
5
10
|
Kicker.run
|
data/kicker.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{kicker}
|
8
|
-
s.version = "2.3.
|
8
|
+
s.version = "2.3.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Eloy Duran"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2011-05-27}
|
13
13
|
s.default_executable = %q{kicker}
|
14
14
|
s.email = %q{eloy.de.enige@gmail.com}
|
15
15
|
s.executables = ["kicker"]
|
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
|
|
31
31
|
"lib/kicker.rb",
|
32
32
|
"lib/kicker/callback_chain.rb",
|
33
33
|
"lib/kicker/core_ext.rb",
|
34
|
+
"lib/kicker/fsevents.rb",
|
34
35
|
"lib/kicker/growl.rb",
|
35
36
|
"lib/kicker/log_status_helper.rb",
|
36
37
|
"lib/kicker/options.rb",
|
@@ -47,6 +48,7 @@ Gem::Specification.new do |s|
|
|
47
48
|
"test/core_ext_test.rb",
|
48
49
|
"test/filesystem_change_test.rb",
|
49
50
|
"test/fixtures/a_file_thats_reloaded.rb",
|
51
|
+
"test/fsevents_test.rb",
|
50
52
|
"test/growl_test.rb",
|
51
53
|
"test/initialization_test.rb",
|
52
54
|
"test/log_status_helper_test.rb",
|
@@ -62,19 +64,19 @@ Gem::Specification.new do |s|
|
|
62
64
|
"test/test_helper.rb",
|
63
65
|
"test/utils_test.rb",
|
64
66
|
"vendor/growlnotifier/growl.rb",
|
65
|
-
"vendor/growlnotifier/growl_helpers.rb"
|
66
|
-
"vendor/rucola/fsevents.rb"
|
67
|
+
"vendor/growlnotifier/growl_helpers.rb"
|
67
68
|
]
|
68
69
|
s.homepage = %q{http://github.com/alloy/kicker}
|
69
70
|
s.rdoc_options = ["--charset=UTF-8"]
|
70
71
|
s.require_paths = ["lib", "vendor"]
|
71
|
-
s.rubygems_version = %q{1.3.
|
72
|
+
s.rubygems_version = %q{1.3.7}
|
72
73
|
s.summary = %q{A lean, agnostic, flexible file-change watcher, using OS X FSEvents.}
|
73
74
|
s.test_files = [
|
74
75
|
"test/callback_chain_test.rb",
|
75
76
|
"test/core_ext_test.rb",
|
76
77
|
"test/filesystem_change_test.rb",
|
77
78
|
"test/fixtures/a_file_thats_reloaded.rb",
|
79
|
+
"test/fsevents_test.rb",
|
78
80
|
"test/growl_test.rb",
|
79
81
|
"test/initialization_test.rb",
|
80
82
|
"test/log_status_helper_test.rb",
|
@@ -95,10 +97,13 @@ Gem::Specification.new do |s|
|
|
95
97
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
96
98
|
s.specification_version = 3
|
97
99
|
|
98
|
-
if Gem::Version.new(Gem::
|
100
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
101
|
+
s.add_runtime_dependency(%q<rb-fsevent>, [">= 0"])
|
99
102
|
else
|
103
|
+
s.add_dependency(%q<rb-fsevent>, [">= 0"])
|
100
104
|
end
|
101
105
|
else
|
106
|
+
s.add_dependency(%q<rb-fsevent>, [">= 0"])
|
102
107
|
end
|
103
108
|
end
|
104
109
|
|
data/lib/kicker.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
$:.unshift File.expand_path('../../vendor', __FILE__)
|
2
|
-
require 'rucola/fsevents'
|
3
2
|
|
3
|
+
require 'kicker/fsevents'
|
4
4
|
require 'kicker/callback_chain'
|
5
5
|
require 'kicker/core_ext'
|
6
6
|
require 'kicker/growl'
|
@@ -30,11 +30,9 @@ class Kicker #:nodoc:
|
|
30
30
|
log "Watching for changes on: #{paths.join(', ')}"
|
31
31
|
log ''
|
32
32
|
|
33
|
-
run_watch_dog!
|
34
33
|
Kicker::Growl.start! if Kicker::Growl.use?
|
35
34
|
run_startup_chain
|
36
|
-
|
37
|
-
OSX.CFRunLoopRun
|
35
|
+
run_watch_dog!
|
38
36
|
end
|
39
37
|
|
40
38
|
private
|
@@ -62,12 +60,12 @@ class Kicker #:nodoc:
|
|
62
60
|
|
63
61
|
def run_watch_dog!
|
64
62
|
dirs = @paths.map { |path| File.directory?(path) ? path : File.dirname(path) }
|
65
|
-
watch_dog =
|
63
|
+
watch_dog = Kicker::FSEvents.start_watching(dirs, :latency => self.class.latency) do |events|
|
66
64
|
process events
|
67
65
|
end
|
68
66
|
|
69
67
|
trap('INT') do
|
70
|
-
log "Exiting
|
68
|
+
log "Exiting ..."
|
71
69
|
watch_dog.stop
|
72
70
|
exit
|
73
71
|
end
|
@@ -121,4 +119,4 @@ end
|
|
121
119
|
|
122
120
|
# Load this as last, because it actually loads all recipes, so everything has
|
123
121
|
# to be defined before that.
|
124
|
-
require 'kicker/recipes'
|
122
|
+
require 'kicker/recipes'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rb-fsevent'
|
4
|
+
|
5
|
+
class Kicker
|
6
|
+
class FSEvents
|
7
|
+
class FSEvent
|
8
|
+
attr_reader :path
|
9
|
+
|
10
|
+
def initialize(path)
|
11
|
+
@path = path
|
12
|
+
end
|
13
|
+
|
14
|
+
def files
|
15
|
+
Dir.glob("#{File.expand_path(path)}/*").map do |filename|
|
16
|
+
begin
|
17
|
+
[File.mtime(filename), filename]
|
18
|
+
rescue Errno::ENOENT
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
end.compact.sort.reverse.map { |_, filename| filename }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.start_watching(paths, options={}, &block)
|
26
|
+
fsevent = ::FSEvent.new
|
27
|
+
fsevent.watch(paths, options) do |directories|
|
28
|
+
yield directories.map { |directory| Kicker::FSEvents::FSEvent.new(directory) }
|
29
|
+
end
|
30
|
+
fsevent.run
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/kicker/growl.rb
CHANGED
@@ -1,59 +1,79 @@
|
|
1
|
-
require 'growlnotifier/growl_helpers'
|
2
|
-
|
3
1
|
class Kicker
|
4
2
|
module Growl #:nodoc:
|
5
|
-
NOTIFICATIONS = {
|
6
|
-
:change => 'Change occured',
|
7
|
-
:succeeded => 'Command succeeded',
|
8
|
-
:failed => 'Command failed'
|
9
|
-
}
|
10
|
-
|
11
|
-
DEFAULT_CALLBACK = lambda do
|
12
|
-
OSX::NSWorkspace.sharedWorkspace.launchApplication('Terminal')
|
13
|
-
end
|
14
|
-
|
15
3
|
class << self
|
16
|
-
include ::Growl
|
17
4
|
attr_accessor :use, :command
|
18
5
|
|
19
|
-
|
20
|
-
|
6
|
+
def usable?
|
7
|
+
false
|
8
|
+
end
|
21
9
|
|
22
10
|
def use?
|
23
11
|
@use
|
24
12
|
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
begin
|
18
|
+
require 'osx/cocoa'
|
19
|
+
require 'growlnotifier/growl_helpers'
|
20
|
+
|
21
|
+
class Kicker
|
22
|
+
module Growl #:nodoc:
|
23
|
+
NOTIFICATIONS = {
|
24
|
+
:change => 'Change occured',
|
25
|
+
:succeeded => 'Command succeeded',
|
26
|
+
:failed => 'Command failed'
|
27
|
+
}
|
25
28
|
|
26
|
-
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
def start!
|
31
|
-
::Growl::Notifier.sharedInstance.register('Kicker', NOTIFICATIONS.values)
|
32
|
-
end
|
33
|
-
|
34
|
-
def change_occured(status)
|
35
|
-
growl(notifications[:change], 'Kicker: Executing', status.call(:growl) || status.command)
|
36
|
-
end
|
37
|
-
|
38
|
-
def command_callback
|
39
|
-
lambda { system(command) } if command
|
40
|
-
end
|
41
|
-
|
42
|
-
def result(status)
|
43
|
-
status.success? ? succeeded(status) : failed(status)
|
44
|
-
end
|
45
|
-
|
46
|
-
def succeeded(status)
|
47
|
-
callback = command_callback || DEFAULT_CALLBACK
|
48
|
-
body = status.call(:growl) || (Kicker.silent? ? '' : status.output)
|
49
|
-
growl(notifications[:succeeded], "Kicker: Success", body, &callback)
|
29
|
+
DEFAULT_CALLBACK = lambda do
|
30
|
+
OSX::NSWorkspace.sharedWorkspace.launchApplication('Terminal')
|
50
31
|
end
|
51
32
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
33
|
+
class << self
|
34
|
+
include ::Growl
|
35
|
+
|
36
|
+
Growl.use = true
|
37
|
+
Growl.command = nil
|
38
|
+
|
39
|
+
def usable?
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
def notifications
|
44
|
+
NOTIFICATIONS
|
45
|
+
end
|
46
|
+
|
47
|
+
def start!
|
48
|
+
::Growl::Notifier.sharedInstance.register('Kicker', NOTIFICATIONS.values)
|
49
|
+
end
|
50
|
+
|
51
|
+
def change_occured(status)
|
52
|
+
growl(notifications[:change], 'Kicker: Executing', status.call(:growl) || status.command)
|
53
|
+
end
|
54
|
+
|
55
|
+
def command_callback
|
56
|
+
lambda { system(command) } if command
|
57
|
+
end
|
58
|
+
|
59
|
+
def result(status)
|
60
|
+
status.success? ? succeeded(status) : failed(status)
|
61
|
+
end
|
62
|
+
|
63
|
+
def succeeded(status)
|
64
|
+
callback = command_callback || DEFAULT_CALLBACK
|
65
|
+
body = status.call(:growl) || (Kicker.silent? ? '' : status.output)
|
66
|
+
growl(notifications[:succeeded], "Kicker: Success", body, &callback)
|
67
|
+
end
|
68
|
+
|
69
|
+
def failed(status)
|
70
|
+
message = "Kicker: Failed (#{status.exit_code})"
|
71
|
+
body = status.call(:growl) || (Kicker.silent? ? '' : status.output)
|
72
|
+
growl(notifications[:failed], message, body, &DEFAULT_CALLBACK)
|
73
|
+
end
|
56
74
|
end
|
57
75
|
end
|
58
76
|
end
|
59
|
-
|
77
|
+
|
78
|
+
rescue LoadError
|
79
|
+
end
|
data/lib/kicker/options.rb
CHANGED
@@ -15,6 +15,10 @@ class Kicker
|
|
15
15
|
def clear_console?
|
16
16
|
@clear_console
|
17
17
|
end
|
18
|
+
|
19
|
+
def has_growl?
|
20
|
+
Kicker.const_defined?(:Growl)
|
21
|
+
end
|
18
22
|
end
|
19
23
|
|
20
24
|
self.latency = 1
|
@@ -49,12 +53,16 @@ class Kicker
|
|
49
53
|
Kicker.clear_console = true
|
50
54
|
end
|
51
55
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
56
|
+
if Kicker::Growl.usable?
|
57
|
+
opt.on('--[no-]growl', 'Whether or not to use Growl. Default is to use growl.') do |growl|
|
58
|
+
Kicker::Growl.use = growl
|
59
|
+
end
|
60
|
+
|
61
|
+
opt.on('--growl-command [COMMAND]', 'The command to execute when the Growl succeeded message is clicked.') do |command|
|
62
|
+
Kicker::Growl.command = command
|
63
|
+
end
|
64
|
+
else
|
65
|
+
Kicker::Growl.use = false
|
58
66
|
end
|
59
67
|
|
60
68
|
opt.on('-l', '--latency [FLOAT]', "The time to collect file change events before acting on them. Defaults to #{Kicker.latency} second.") do |latency|
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class FakeFSEvent
|
4
|
+
def watch(paths, options={}, &block)
|
5
|
+
@paths = paths
|
6
|
+
@block = block
|
7
|
+
end
|
8
|
+
|
9
|
+
def run
|
10
|
+
end
|
11
|
+
|
12
|
+
def fake_event(paths)
|
13
|
+
@block.call(paths)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "Kicker::FSEvents" do
|
18
|
+
it "calls the provided block with changed directories wrapped in an event instance" do
|
19
|
+
events = nil
|
20
|
+
faker = FakeFSEvent.new
|
21
|
+
::FSEvent.expects(:new).returns(faker)
|
22
|
+
Kicker::FSEvents.start_watching(%w(/path/to/first /path/to/second)) do |events|
|
23
|
+
end
|
24
|
+
paths = %w(/path/to/first)
|
25
|
+
faker.fake_event(paths)
|
26
|
+
events.map { |e| e.path }.should == paths
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "Kicker::FSEvents::FSEvent" do
|
31
|
+
it "returns the files from the changed directory ordered by mtime and filename" do
|
32
|
+
fsevent = Kicker::FSEvents::FSEvent.new(File.expand_path('../fixtures', __FILE__))
|
33
|
+
fsevent.files.should == [File.expand_path('../fixtures/a_file_thats_reloaded.rb', __FILE__)]
|
34
|
+
end
|
35
|
+
end
|
data/test/growl_test.rb
CHANGED
@@ -1,85 +1,89 @@
|
|
1
1
|
require File.expand_path('../test_helper', __FILE__)
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
after do
|
9
|
-
Kicker.silent = false
|
10
|
-
end
|
11
|
-
|
12
|
-
it "should growl that an event occurred" do
|
13
|
-
status = Kicker::LogStatusHelper.new(nil, 'ls -l')
|
14
|
-
@growler.expects(:growl).with(@growler.notifications[:change], 'Kicker: Executing', 'ls -l')
|
15
|
-
@growler.change_occured(status)
|
16
|
-
end
|
17
|
-
|
18
|
-
it "should growl that an event occurred with the status callback" do
|
19
|
-
status = Kicker::LogStatusHelper.new(proc { |s| 'foo' if s.growl? }, 'ls -l')
|
20
|
-
@growler.expects(:growl).with(@growler.notifications[:change], 'Kicker: Executing', 'foo')
|
21
|
-
@growler.change_occured(status)
|
22
|
-
end
|
23
|
-
|
24
|
-
it "should use the default click callback if a command succeeded and no user callback is defined" do
|
25
|
-
status = Kicker::LogStatusHelper.new(nil, 'ls -l')
|
26
|
-
status.result("line 1\nline 2", true, 0)
|
3
|
+
if Kicker::Growl.usable?
|
4
|
+
describe "Kicker::Growl" do
|
5
|
+
before do
|
6
|
+
@growler = Kicker::Growl
|
7
|
+
end
|
27
8
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
'Kicker: Success',
|
32
|
-
"line 1\nline 2"
|
33
|
-
).yields
|
9
|
+
after do
|
10
|
+
Kicker.silent = false
|
11
|
+
end
|
34
12
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
status.result("line 1\nline 2", false, 123)
|
13
|
+
it "should growl that an event occurred" do
|
14
|
+
status = Kicker::LogStatusHelper.new(nil, 'ls -l')
|
15
|
+
@growler.expects(:growl).with(@growler.notifications[:change], 'Kicker: Executing', 'ls -l')
|
16
|
+
@growler.change_occured(status)
|
17
|
+
end
|
41
18
|
|
42
|
-
|
43
|
-
|
44
|
-
@growler.notifications[:
|
45
|
-
|
46
|
-
|
47
|
-
).yields
|
19
|
+
it "should growl that an event occurred with the status callback" do
|
20
|
+
status = Kicker::LogStatusHelper.new(proc { |s| 'foo' if s.growl? }, 'ls -l')
|
21
|
+
@growler.expects(:growl).with(@growler.notifications[:change], 'Kicker: Executing', 'foo')
|
22
|
+
@growler.change_occured(status)
|
23
|
+
end
|
48
24
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
25
|
+
it "should use the default click callback if a command succeeded and no user callback is defined" do
|
26
|
+
status = Kicker::LogStatusHelper.new(nil, 'ls -l')
|
27
|
+
status.result("line 1\nline 2", true, 0)
|
28
|
+
|
29
|
+
OSX::NSWorkspace.sharedWorkspace.expects(:launchApplication).with('Terminal')
|
30
|
+
@growler.expects(:growl).with(
|
31
|
+
@growler.notifications[:succeeded],
|
32
|
+
'Kicker: Success',
|
33
|
+
"line 1\nline 2"
|
34
|
+
).yields
|
35
|
+
|
36
|
+
@growler.result(status)
|
37
|
+
end
|
56
38
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
39
|
+
it "should use the default click callback if a command failed and no user callback is defined" do
|
40
|
+
status = Kicker::LogStatusHelper.new(nil, 'ls -l')
|
41
|
+
status.result("line 1\nline 2", false, 123)
|
42
|
+
|
43
|
+
OSX::NSWorkspace.sharedWorkspace.expects(:launchApplication).with('Terminal')
|
44
|
+
@growler.expects(:growl).with(
|
45
|
+
@growler.notifications[:failed],
|
46
|
+
'Kicker: Failed (123)',
|
47
|
+
"line 1\nline 2"
|
48
|
+
).yields
|
49
|
+
|
50
|
+
@growler.failed(status)
|
51
|
+
end
|
65
52
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
53
|
+
it "should only growl that the command succeeded in silent mode" do
|
54
|
+
Kicker.silent = true
|
55
|
+
status = Kicker::LogStatusHelper.new(nil, 'ls -l')
|
56
|
+
status.result("line 1\nline 2", true, 0)
|
57
|
+
|
58
|
+
@growler.expects(:growl).with(@growler.notifications[:succeeded], 'Kicker: Success', '')
|
59
|
+
@growler.result(status)
|
60
|
+
end
|
73
61
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
62
|
+
it "should only growl that the command failed in silent mode" do
|
63
|
+
Kicker.silent = true
|
64
|
+
status = Kicker::LogStatusHelper.new(nil, 'ls -l')
|
65
|
+
status.result("line 1\nline 2", false, 123)
|
66
|
+
|
67
|
+
@growler.expects(:growl).with(@growler.notifications[:failed], 'Kicker: Failed (123)', '')
|
68
|
+
@growler.failed(status)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should growl that the command succeeded with the status callback" do
|
72
|
+
status = Kicker::LogStatusHelper.new(proc { |s| 'foo' if s.growl? }, 'ls -l')
|
73
|
+
status.result("line 1\nline 2", true, 0)
|
74
|
+
|
75
|
+
@growler.expects(:growl).with(@growler.notifications[:succeeded], 'Kicker: Success', 'foo')
|
76
|
+
@growler.succeeded(status)
|
77
|
+
end
|
81
78
|
|
82
|
-
|
83
|
-
|
79
|
+
it "should growl that the command failed with the status callback" do
|
80
|
+
status = Kicker::LogStatusHelper.new(proc { |s| 'foo' if s.growl? }, 'ls -l')
|
81
|
+
status.result("line 1\nline 2", false, 123)
|
82
|
+
|
83
|
+
@growler.expects(:growl).with(@growler.notifications[:failed], 'Kicker: Failed (123)', 'foo')
|
84
|
+
@growler.failed(status)
|
85
|
+
end
|
84
86
|
end
|
87
|
+
else
|
88
|
+
puts "Poo"
|
85
89
|
end
|
data/test/initialization_test.rb
CHANGED
@@ -42,8 +42,7 @@ describe "Kicker, when starting" do
|
|
42
42
|
@kicker = Kicker.new
|
43
43
|
@kicker.stubs(:log)
|
44
44
|
@kicker.startup_chain.stubs(:call)
|
45
|
-
|
46
|
-
OSX.stubs(:CFRunLoopRun)
|
45
|
+
Kicker::FSEvents.stubs(:start_watching)
|
47
46
|
end
|
48
47
|
|
49
48
|
after do
|
@@ -77,7 +76,7 @@ describe "Kicker, when starting" do
|
|
77
76
|
@kicker.stubs(:validate_options!)
|
78
77
|
|
79
78
|
Kicker.latency = 2.34
|
80
|
-
|
79
|
+
Kicker::FSEvents.expects(:start_watching).with(['/some'], :latency => 2.34)
|
81
80
|
@kicker.start
|
82
81
|
end
|
83
82
|
|
@@ -85,14 +84,14 @@ describe "Kicker, when starting" do
|
|
85
84
|
@kicker.stubs(:validate_options!)
|
86
85
|
File.stubs(:directory?).with('/some/file.rb').returns(false)
|
87
86
|
|
88
|
-
|
87
|
+
Kicker::FSEvents.expects(:start_watching).with(['/some'], :latency => Kicker.latency)
|
89
88
|
@kicker.start
|
90
89
|
end
|
91
90
|
|
92
91
|
it "should start a FSEvents stream with a block which calls #process with any generated events" do
|
93
92
|
@kicker.stubs(:validate_options!)
|
94
93
|
|
95
|
-
|
94
|
+
Kicker::FSEvents.expects(:start_watching).yields(['event'])
|
96
95
|
@kicker.expects(:process).with(['event'])
|
97
96
|
|
98
97
|
@kicker.start
|
@@ -101,8 +100,8 @@ describe "Kicker, when starting" do
|
|
101
100
|
it "should setup a signal handler for `INT' which stops the FSEvents stream and exits" do
|
102
101
|
@kicker.stubs(:validate_options!)
|
103
102
|
|
104
|
-
watch_dog = stub('
|
105
|
-
|
103
|
+
watch_dog = stub('Kicker::FSEvents')
|
104
|
+
Kicker::FSEvents.stubs(:start_watching).returns(watch_dog)
|
106
105
|
|
107
106
|
@kicker.expects(:trap).with('INT').yields
|
108
107
|
watch_dog.expects(:stop)
|
@@ -111,20 +110,22 @@ describe "Kicker, when starting" do
|
|
111
110
|
@kicker.start
|
112
111
|
end
|
113
112
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
it "should _not_ register with growl if growl should not be used" do
|
123
|
-
@kicker.stubs(:validate_options!)
|
124
|
-
Kicker::Growl.use = false
|
113
|
+
if Kicker::Growl.usable?
|
114
|
+
it "should register with growl if growl should be used" do
|
115
|
+
@kicker.stubs(:validate_options!)
|
116
|
+
Kicker::Growl.use = true
|
117
|
+
|
118
|
+
Growl::Notifier.sharedInstance.expects(:register).with('Kicker', Kicker::Growl::NOTIFICATIONS.values)
|
119
|
+
@kicker.start
|
120
|
+
end
|
125
121
|
|
126
|
-
|
127
|
-
|
122
|
+
it "should _not_ register with growl if growl should not be used" do
|
123
|
+
@kicker.stubs(:validate_options!)
|
124
|
+
Kicker::Growl.use = false
|
125
|
+
|
126
|
+
Growl::Notifier.sharedInstance.expects(:register).never
|
127
|
+
@kicker.start
|
128
|
+
end
|
128
129
|
end
|
129
130
|
|
130
131
|
it "should call the startup chain" do
|
@@ -133,11 +134,4 @@ describe "Kicker, when starting" do
|
|
133
134
|
@kicker.startup_chain.expects(:call).with([], false)
|
134
135
|
@kicker.start
|
135
136
|
end
|
136
|
-
|
137
|
-
it "should start a CFRunLoop" do
|
138
|
-
@kicker.stubs(:validate_options!)
|
139
|
-
|
140
|
-
OSX.expects(:CFRunLoopRun)
|
141
|
-
@kicker.start
|
142
|
-
end
|
143
137
|
end
|
data/test/options_test.rb
CHANGED
@@ -21,12 +21,14 @@ describe "Kicker::Options.parse" do
|
|
21
21
|
Kicker.paths.should == %w{ /some/file.rb /a/dir /and/some/other/file.rb }
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
if Kicker::Growl.usable?
|
25
|
+
it "should parse if growl shouldn't be used" do
|
26
|
+
Kicker::Options.parse([])
|
27
|
+
Kicker::Growl.should.use
|
27
28
|
|
28
|
-
|
29
|
-
|
29
|
+
Kicker::Options.parse(%w{ --no-growl })
|
30
|
+
Kicker::Growl.should.not.use
|
31
|
+
end
|
30
32
|
end
|
31
33
|
|
32
34
|
it "should parse if we should keep output to a minimum" do
|
@@ -55,9 +57,11 @@ describe "Kicker::Options.parse" do
|
|
55
57
|
Kicker.should.clear_console
|
56
58
|
end
|
57
59
|
|
58
|
-
|
59
|
-
|
60
|
-
|
60
|
+
if Kicker::Growl.usable?
|
61
|
+
it "should parse the Growl command to use when the user clicks the Growl succeeded message" do
|
62
|
+
Kicker::Options.parse(%w{ --growl-command ls })
|
63
|
+
Kicker::Growl.command.should == 'ls'
|
64
|
+
end
|
61
65
|
end
|
62
66
|
|
63
67
|
it "should parse the latency to pass to FSEvents" do
|
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kicker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 1
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 2
|
8
|
+
- 3
|
9
|
+
- 1
|
10
|
+
version: 2.3.1
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- Eloy Duran
|
@@ -9,10 +15,23 @@ autorequire:
|
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date:
|
18
|
+
date: 2011-05-27 00:00:00 +02:00
|
13
19
|
default_executable: kicker
|
14
|
-
dependencies:
|
15
|
-
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rb-fsevent
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
16
35
|
description:
|
17
36
|
email: eloy.de.enige@gmail.com
|
18
37
|
executables:
|
@@ -36,6 +55,7 @@ files:
|
|
36
55
|
- lib/kicker.rb
|
37
56
|
- lib/kicker/callback_chain.rb
|
38
57
|
- lib/kicker/core_ext.rb
|
58
|
+
- lib/kicker/fsevents.rb
|
39
59
|
- lib/kicker/growl.rb
|
40
60
|
- lib/kicker/log_status_helper.rb
|
41
61
|
- lib/kicker/options.rb
|
@@ -52,6 +72,7 @@ files:
|
|
52
72
|
- test/core_ext_test.rb
|
53
73
|
- test/filesystem_change_test.rb
|
54
74
|
- test/fixtures/a_file_thats_reloaded.rb
|
75
|
+
- test/fsevents_test.rb
|
55
76
|
- test/growl_test.rb
|
56
77
|
- test/initialization_test.rb
|
57
78
|
- test/log_status_helper_test.rb
|
@@ -68,7 +89,6 @@ files:
|
|
68
89
|
- test/utils_test.rb
|
69
90
|
- vendor/growlnotifier/growl.rb
|
70
91
|
- vendor/growlnotifier/growl_helpers.rb
|
71
|
-
- vendor/rucola/fsevents.rb
|
72
92
|
has_rdoc: true
|
73
93
|
homepage: http://github.com/alloy/kicker
|
74
94
|
licenses: []
|
@@ -80,21 +100,27 @@ require_paths:
|
|
80
100
|
- lib
|
81
101
|
- vendor
|
82
102
|
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
none: false
|
83
104
|
requirements:
|
84
105
|
- - ">="
|
85
106
|
- !ruby/object:Gem::Version
|
107
|
+
hash: 3
|
108
|
+
segments:
|
109
|
+
- 0
|
86
110
|
version: "0"
|
87
|
-
version:
|
88
111
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
none: false
|
89
113
|
requirements:
|
90
114
|
- - ">="
|
91
115
|
- !ruby/object:Gem::Version
|
116
|
+
hash: 3
|
117
|
+
segments:
|
118
|
+
- 0
|
92
119
|
version: "0"
|
93
|
-
version:
|
94
120
|
requirements: []
|
95
121
|
|
96
122
|
rubyforge_project:
|
97
|
-
rubygems_version: 1.3.
|
123
|
+
rubygems_version: 1.3.7
|
98
124
|
signing_key:
|
99
125
|
specification_version: 3
|
100
126
|
summary: A lean, agnostic, flexible file-change watcher, using OS X FSEvents.
|
@@ -103,6 +129,7 @@ test_files:
|
|
103
129
|
- test/core_ext_test.rb
|
104
130
|
- test/filesystem_change_test.rb
|
105
131
|
- test/fixtures/a_file_thats_reloaded.rb
|
132
|
+
- test/fsevents_test.rb
|
106
133
|
- test/growl_test.rb
|
107
134
|
- test/initialization_test.rb
|
108
135
|
- test/log_status_helper_test.rb
|
data/vendor/rucola/fsevents.rb
DELETED
@@ -1,136 +0,0 @@
|
|
1
|
-
require 'osx/cocoa'
|
2
|
-
OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework'
|
3
|
-
|
4
|
-
module Rucola
|
5
|
-
class FSEvents
|
6
|
-
class FSEvent
|
7
|
-
attr_reader :fsevents_object
|
8
|
-
attr_reader :id
|
9
|
-
attr_reader :path
|
10
|
-
def initialize(fsevents_object, id, path)
|
11
|
-
@fsevents_object, @id, @path = fsevents_object, id, path
|
12
|
-
end
|
13
|
-
|
14
|
-
# Returns an array of the files/dirs in the path that the event occurred in.
|
15
|
-
# The files are sorted by the modification time, the first entry is the last modified file.
|
16
|
-
def files
|
17
|
-
Dir.glob("#{File.expand_path(path)}/*").map do |filename|
|
18
|
-
begin
|
19
|
-
[File.mtime(filename), filename]
|
20
|
-
rescue Errno::ENOENT
|
21
|
-
nil
|
22
|
-
end
|
23
|
-
end.compact.sort.reverse.map { |mtime, filename| filename }
|
24
|
-
end
|
25
|
-
|
26
|
-
# Returns the last modified file in the path that the event occurred in.
|
27
|
-
def last_modified_file
|
28
|
-
files.first
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
class StreamError < StandardError; end
|
33
|
-
|
34
|
-
attr_reader :paths
|
35
|
-
attr_reader :stream
|
36
|
-
|
37
|
-
attr_accessor :allocator
|
38
|
-
attr_accessor :context
|
39
|
-
attr_accessor :since
|
40
|
-
attr_accessor :latency
|
41
|
-
attr_accessor :flags
|
42
|
-
|
43
|
-
# Initializes a new FSEvents `watchdog` object and starts watching the directories you specify for events. The
|
44
|
-
# block is used as a handler for events, which are passed as the block's argument. This method is the easiest
|
45
|
-
# way to start watching some directories if you don't care about the details of setting up the event stream.
|
46
|
-
#
|
47
|
-
# Rucola::FSEvents.start_watching('/tmp') do |events|
|
48
|
-
# events.each { |event| log.debug("#{event.files.inspect} were changed.") }
|
49
|
-
# end
|
50
|
-
#
|
51
|
-
# Rucola::FSEvents.start_watching('/var/log/system.log', '/var/log/secure.log', :since => last_id, :latency => 5) do
|
52
|
-
# Growl.notify("Something was added to your log files!")
|
53
|
-
# end
|
54
|
-
#
|
55
|
-
# Note that the method also returns the FSEvents object. This enables you to control the event stream if you want to.
|
56
|
-
#
|
57
|
-
# fsevents = Rucola::FSEvents.start_watching('/Volumes') do |events|
|
58
|
-
# events.each { |event| Growl.notify("Volume changes: #{event.files.to_sentence}") }
|
59
|
-
# end
|
60
|
-
# fsevents.stop
|
61
|
-
def self.start_watching(*params, &block)
|
62
|
-
fsevents = new(*params, &block)
|
63
|
-
fsevents.create_stream
|
64
|
-
fsevents.start
|
65
|
-
fsevents
|
66
|
-
end
|
67
|
-
|
68
|
-
# Creates a new FSEvents `watchdog` object. You can specify a list of paths to watch and options to control the
|
69
|
-
# behaviour of the watchdog. The block you pass serves as a callback when an event is generated on one of the
|
70
|
-
# specified paths.
|
71
|
-
#
|
72
|
-
# fsevents = FSEvents.new('/etc/passwd') { Mailer.send_mail("Someone touched the password file!") }
|
73
|
-
# fsevents.create_stream
|
74
|
-
# fsevents.start
|
75
|
-
#
|
76
|
-
# fsevents = FSEvents.new('/home/upload', :since => UploadWatcher.last_event_id) do |events|
|
77
|
-
# events.each do |event|
|
78
|
-
# UploadWatcher.last_event_id = event.id
|
79
|
-
# event.files.each do |file|
|
80
|
-
# UploadWatcher.logfile.append("#{file} was changed")
|
81
|
-
# end
|
82
|
-
# end
|
83
|
-
# end
|
84
|
-
#
|
85
|
-
# *:since: The service will report events that have happened after the supplied event ID. Never use 0 because that
|
86
|
-
# will cause every fsevent since the "beginning of time" to be reported. Use OSX::KFSEventStreamEventIdSinceNow
|
87
|
-
# if you want to receive events that have happened after this call. (Default: OSX::KFSEventStreamEventIdSinceNow).
|
88
|
-
# You can find the ID's passed with :since in the events passed to your block.
|
89
|
-
# *:latency: Number of seconds to wait until an FSEvent is reported, this allows the service to bundle events. (Default: 0.0)
|
90
|
-
#
|
91
|
-
# Please refer to the Cocoa documentation for the rest of the options.
|
92
|
-
def initialize(*params, &block)
|
93
|
-
raise ArgumentError, 'No callback block was specified.' unless block_given?
|
94
|
-
|
95
|
-
options = params.last.kind_of?(Hash) ? params.pop : {}
|
96
|
-
@paths = params.flatten
|
97
|
-
|
98
|
-
paths.each { |path| raise ArgumentError, "The specified path (#{path}) does not exist." unless File.exist?(path) }
|
99
|
-
|
100
|
-
@allocator = options[:allocator] || OSX::KCFAllocatorDefault
|
101
|
-
@context = options[:context] || nil
|
102
|
-
@since = options[:since] || OSX::KFSEventStreamEventIdSinceNow
|
103
|
-
@latency = options[:latency] || 0.0
|
104
|
-
@flags = options[:flags] || 0
|
105
|
-
@stream = options[:stream] || nil
|
106
|
-
|
107
|
-
@user_callback = block
|
108
|
-
@callback = Proc.new do |stream, client_callback_info, number_of_events, paths_pointer, event_flags, event_ids|
|
109
|
-
paths_pointer.regard_as('*')
|
110
|
-
events = []
|
111
|
-
number_of_events.times {|i| events << Rucola::FSEvents::FSEvent.new(self, event_ids[i], paths_pointer[i]) }
|
112
|
-
@user_callback.call(events)
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
# Create the stream.
|
117
|
-
# Raises a Rucola::FSEvents::StreamError if the stream could not be created.
|
118
|
-
def create_stream
|
119
|
-
@stream = OSX.FSEventStreamCreate(@allocator, @callback, @context, @paths, @since, @latency, @flags)
|
120
|
-
raise(StreamError, 'Unable to create FSEvents stream.') unless @stream
|
121
|
-
OSX.FSEventStreamScheduleWithRunLoop(@stream, OSX.CFRunLoopGetCurrent, OSX::KCFRunLoopDefaultMode)
|
122
|
-
end
|
123
|
-
|
124
|
-
# Start the stream.
|
125
|
-
# Raises a Rucola::FSEvents::StreamError if the stream could not be started.
|
126
|
-
def start
|
127
|
-
raise(StreamError, 'Unable to start FSEvents stream.') unless OSX.FSEventStreamStart(@stream)
|
128
|
-
end
|
129
|
-
|
130
|
-
# Stop the stream.
|
131
|
-
# You can resume it by calling `start` again.
|
132
|
-
def stop
|
133
|
-
OSX.FSEventStreamStop(@stream)
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|