rstake 0.0.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/.gitignore +2 -0
- data/LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +18 -0
- data/VERSION +1 -0
- data/bin/rstake +54 -0
- data/lib/rstake.rb +12 -0
- data/lib/rstake/app.rb +55 -0
- data/lib/rstake/growl_notifier.rb +31 -0
- data/lib/rstake/print_notifier.rb +43 -0
- data/lib/rstake/watcher.rb +36 -0
- data/rstake.gemspec +54 -0
- metadata +91 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Rico Sta. Cruz
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
RStake
|
2
|
+
======
|
3
|
+
|
4
|
+
Watches a list of files for changes.
|
5
|
+
Heavily inspired by the original RStakeout by topfunky.
|
6
|
+
|
7
|
+
Installation
|
8
|
+
------------
|
9
|
+
|
10
|
+
gem install rstake
|
11
|
+
|
12
|
+
Usage
|
13
|
+
-----
|
14
|
+
|
15
|
+
Type 'rstake' for more information on usage.
|
16
|
+
|
17
|
+
Examples:
|
18
|
+
|
19
|
+
# Watch Ruby files for changes, then run a test when needed.
|
20
|
+
rstake "rake test" **/**/*.rb
|
21
|
+
|
22
|
+
# Automatically compile CSS files using Less.
|
23
|
+
rstake "lessc {}" css/*.less
|
24
|
+
|
25
|
+
Tips
|
26
|
+
----
|
27
|
+
|
28
|
+
- Install Growl and growlnotify if you're on OSX.
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "rstake"
|
8
|
+
gem.summary = "Watch a directory for changes."
|
9
|
+
gem.description = "Watches a list of files for changes, and executes a given command when any changes are detected."
|
10
|
+
gem.email = "rico@sinefunc.com"
|
11
|
+
gem.homepage = "http://github.com/sinefunc/rstake"
|
12
|
+
gem.authors = ["Rico Sta. Cruz", "Sinefunc, Iinc."]
|
13
|
+
gem.add_dependency "growl", ">= 1.0.3"
|
14
|
+
end
|
15
|
+
Jeweler::GemcutterTasks.new
|
16
|
+
rescue LoadError
|
17
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
18
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/bin/rstake
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
require File.join(File.dirname(__FILE__), %w[.. lib rstake])
|
7
|
+
|
8
|
+
options = { :sleep_time => 1, :async => false, :no_growl => false }
|
9
|
+
parser = OptionParser.new do |opts|
|
10
|
+
opts.banner = "Usage: #{File.basename($0)} [options] <command> <filespec>+\n\n" +
|
11
|
+
" Watches a list of files <filespec>, and executes <command> if any changes\n" +
|
12
|
+
" are detected.\n\n"+
|
13
|
+
" <command> is the command to be executed. If '%f' is found in the command,\n" +
|
14
|
+
" it will be replaced by the filenames that changed. <filespec> can be list\n" +
|
15
|
+
" of filenames or globs.\n\n" +
|
16
|
+
"Options:\n"
|
17
|
+
opts.on("-t", "--sleep-time T", Integer, "time to sleep after each loop iteration") do |t|
|
18
|
+
options[:sleep_time] = t
|
19
|
+
end
|
20
|
+
opts.on("-a", "--async", "asynchronous mode (allow simultaneous runs)") do |v|
|
21
|
+
options[:async] = v
|
22
|
+
end
|
23
|
+
opts.on("--no-growl", "disable Growl notifications") do |v|
|
24
|
+
options[:no_growl] = true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
parser.parse!
|
28
|
+
|
29
|
+
if ARGV.size < 2
|
30
|
+
cmd = File.basename($0)
|
31
|
+
puts parser
|
32
|
+
puts ""
|
33
|
+
puts "Examples:"
|
34
|
+
puts ""
|
35
|
+
puts " # Runs a test for any changes in any of the Ruby files."
|
36
|
+
puts " #{cmd} \"rake test\" **/**/*.rb"
|
37
|
+
puts ""
|
38
|
+
puts " # Automatically compile CSS files using Less."
|
39
|
+
puts " #{cmd} \"lessc %f\" css/*.less"
|
40
|
+
puts ""
|
41
|
+
exit
|
42
|
+
end
|
43
|
+
|
44
|
+
app = RStake::App.new(ARGV[0], ARGV[1..-1], options)
|
45
|
+
app.notifiers << RStake::PrintNotifier.new
|
46
|
+
unless !options[:no_growl]
|
47
|
+
if RStake.const_defined?(:GrowlNotifier)
|
48
|
+
app.notifiers << RStake::GrowlNotifier.new
|
49
|
+
else
|
50
|
+
puts "Type `sudo gem install growl` to enable growl notifications."
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
app.run!
|
data/lib/rstake.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
module RStake
|
2
|
+
PREFIX = File.join(File.dirname(__FILE__), 'rstake')
|
3
|
+
autoload :App, "#{PREFIX}/app"
|
4
|
+
autoload :PrintNotifier, "#{PREFIX}/print_notifier"
|
5
|
+
autoload :Watcher, "#{PREFIX}/watcher"
|
6
|
+
end
|
7
|
+
|
8
|
+
begin
|
9
|
+
require "#{RStake::PREFIX}/growl_notifier"
|
10
|
+
rescue LoadError
|
11
|
+
# No growl
|
12
|
+
end
|
data/lib/rstake/app.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module RStake
|
2
|
+
class App
|
3
|
+
attr_accessor :notifiers
|
4
|
+
|
5
|
+
def initialize(command, files, options={})
|
6
|
+
require 'shellwords'
|
7
|
+
|
8
|
+
@command = command
|
9
|
+
@files = Watcher.new(files)
|
10
|
+
@notifiers = Array.new
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def notify(msg, *args)
|
15
|
+
@notifiers.each { |n| n.send(msg, *args) if n.respond_to?(msg) }
|
16
|
+
end
|
17
|
+
|
18
|
+
# Runs a command
|
19
|
+
def exec(*a)
|
20
|
+
@options[:async] ? fork { _exec(*a) } : _exec(*a)
|
21
|
+
end
|
22
|
+
|
23
|
+
def run!
|
24
|
+
notify :on_start, :count => @files.size
|
25
|
+
|
26
|
+
trap("INT") { notify :on_exit; exit }
|
27
|
+
|
28
|
+
loop do
|
29
|
+
sleep @options[:sleep_time]
|
30
|
+
changes = @files.last_change
|
31
|
+
exec changes if changes.any?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Delegate function of exec
|
38
|
+
def _exec(files)
|
39
|
+
@id = (@id || 0) + 1
|
40
|
+
command = @command.gsub('%f', files.shelljoin)
|
41
|
+
options = { :id => @id, :files => files, :command => command }
|
42
|
+
|
43
|
+
notify :on_change, options
|
44
|
+
notify :on_exec, options
|
45
|
+
|
46
|
+
system command
|
47
|
+
|
48
|
+
if $?.to_i > 0
|
49
|
+
notify :on_fail, options.merge(:error => $?)
|
50
|
+
else
|
51
|
+
notify :on_success, options
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module RStake
|
2
|
+
class GrowlNotifier
|
3
|
+
def initialize
|
4
|
+
require 'growl'
|
5
|
+
end
|
6
|
+
|
7
|
+
def on_start(options={})
|
8
|
+
Growl.notify_info "Waiting for changes on #{options[:count]} files.",
|
9
|
+
:title => "Watching changes"
|
10
|
+
end
|
11
|
+
|
12
|
+
def on_change(options={})
|
13
|
+
Growl.notify "#{options[:files].join("\n")}\n\nRunning...",
|
14
|
+
:title => "#{options[:files].size} files changed",
|
15
|
+
#:sticky => true,
|
16
|
+
:identifier => "g#{options[:id]}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def on_fail(options={})
|
20
|
+
Growl.notify_error "The command exited with error #{options[:error].to_i}.",
|
21
|
+
:title => "Failed",
|
22
|
+
:identifier => "g#{options[:id]}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_success(options={})
|
26
|
+
Growl.notify_ok "The command ran successfully.",
|
27
|
+
:title => "Success!",
|
28
|
+
:identifier => "g#{options[:id]}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module RStake
|
2
|
+
class PrintNotifier
|
3
|
+
def on_start(options={})
|
4
|
+
msg "Watching #{options[:count]} files, ^C to abort."
|
5
|
+
end
|
6
|
+
|
7
|
+
def on_fail(options={})
|
8
|
+
msg_error "Done with errors; exited with #{options[:error]}."
|
9
|
+
msg "Still watching..."
|
10
|
+
end
|
11
|
+
|
12
|
+
def on_success(options={})
|
13
|
+
msg "Done. Still watching..."
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_exit(options={})
|
17
|
+
puts ''
|
18
|
+
msg "Ctrl+C. That's all folks!"
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_change(options={})
|
22
|
+
msg "Changed #{options[:files].size} files (#{options[:files].join(', ')})"
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_exec(options={})
|
26
|
+
msg_command options[:command]
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
def msg(message, glyph='***', color_1=32, color_2=33)
|
31
|
+
# 30 grey, 32 green, 31 red, 33 yellow
|
32
|
+
warn "\033[1;#{color_1}m#{glyph}\033[1;#{color_2}m #{message}\033[0m"
|
33
|
+
end
|
34
|
+
|
35
|
+
def msg_command(message)
|
36
|
+
msg message, ' >', 32, 30
|
37
|
+
end
|
38
|
+
|
39
|
+
def msg_error(message)
|
40
|
+
msg message, '***', 31, 31
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module RStake
|
2
|
+
# Watches a list of files for changes.
|
3
|
+
#
|
4
|
+
# Common usage:
|
5
|
+
#
|
6
|
+
# w = Watcher.new([file1, file2])
|
7
|
+
# w.last_change
|
8
|
+
#
|
9
|
+
class Watcher
|
10
|
+
def initialize(files)
|
11
|
+
@files = files
|
12
|
+
@times = mtimes
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns an array of filenames that have been changed since it
|
16
|
+
# was last called.
|
17
|
+
def last_change
|
18
|
+
new_times = mtimes
|
19
|
+
ret = new_times.select { |k, v| @times[k] != v }
|
20
|
+
@times = new_times
|
21
|
+
ret.keys
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns a hash with filenames as keys, and their mtimes as values.
|
25
|
+
def mtimes
|
26
|
+
@files.inject({}) do |hash, g|
|
27
|
+
Dir[g].each { |file| hash[file] = File.mtime(file) }
|
28
|
+
hash
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def size
|
33
|
+
@files.size
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/rstake.gemspec
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{rstake}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Rico Sta. Cruz", "Sinefunc, Iinc."]
|
12
|
+
s.date = %q{2010-11-17}
|
13
|
+
s.default_executable = %q{rstake}
|
14
|
+
s.description = %q{Watches a list of files for changes, and executes a given command when any changes are detected.}
|
15
|
+
s.email = %q{rico@sinefunc.com}
|
16
|
+
s.executables = ["rstake"]
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"LICENSE",
|
19
|
+
"README.md"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".gitignore",
|
23
|
+
"LICENSE",
|
24
|
+
"README.md",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"bin/rstake",
|
28
|
+
"lib/rstake.rb",
|
29
|
+
"lib/rstake/app.rb",
|
30
|
+
"lib/rstake/growl_notifier.rb",
|
31
|
+
"lib/rstake/print_notifier.rb",
|
32
|
+
"lib/rstake/watcher.rb",
|
33
|
+
"rstake.gemspec"
|
34
|
+
]
|
35
|
+
s.homepage = %q{http://github.com/sinefunc/rstake}
|
36
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
37
|
+
s.require_paths = ["lib"]
|
38
|
+
s.rubygems_version = %q{1.3.7}
|
39
|
+
s.summary = %q{Watch a directory for changes.}
|
40
|
+
|
41
|
+
if s.respond_to? :specification_version then
|
42
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
43
|
+
s.specification_version = 3
|
44
|
+
|
45
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
46
|
+
s.add_runtime_dependency(%q<growl>, [">= 1.0.3"])
|
47
|
+
else
|
48
|
+
s.add_dependency(%q<growl>, [">= 1.0.3"])
|
49
|
+
end
|
50
|
+
else
|
51
|
+
s.add_dependency(%q<growl>, [">= 1.0.3"])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rstake
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Rico Sta. Cruz
|
13
|
+
- Sinefunc, Iinc.
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-11-17 00:00:00 +08:00
|
19
|
+
default_executable: rstake
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: growl
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
segments:
|
30
|
+
- 1
|
31
|
+
- 0
|
32
|
+
- 3
|
33
|
+
version: 1.0.3
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
description: Watches a list of files for changes, and executes a given command when any changes are detected.
|
37
|
+
email: rico@sinefunc.com
|
38
|
+
executables:
|
39
|
+
- rstake
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
extra_rdoc_files:
|
43
|
+
- LICENSE
|
44
|
+
- README.md
|
45
|
+
files:
|
46
|
+
- .gitignore
|
47
|
+
- LICENSE
|
48
|
+
- README.md
|
49
|
+
- Rakefile
|
50
|
+
- VERSION
|
51
|
+
- bin/rstake
|
52
|
+
- lib/rstake.rb
|
53
|
+
- lib/rstake/app.rb
|
54
|
+
- lib/rstake/growl_notifier.rb
|
55
|
+
- lib/rstake/print_notifier.rb
|
56
|
+
- lib/rstake/watcher.rb
|
57
|
+
- rstake.gemspec
|
58
|
+
has_rdoc: true
|
59
|
+
homepage: http://github.com/sinefunc/rstake
|
60
|
+
licenses: []
|
61
|
+
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options:
|
64
|
+
- --charset=UTF-8
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
version: "0"
|
83
|
+
requirements: []
|
84
|
+
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.3.7
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: Watch a directory for changes.
|
90
|
+
test_files: []
|
91
|
+
|