watchful 0.0.0.pre1 → 0.0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +7 -0
- data/.gitignore +3 -0
- data/README.md +44 -35
- data/Rakefile +17 -19
- data/VERSION +1 -1
- data/bin/watchful +28 -26
- data/lib/watchful.rb +12 -0
- data/lib/watchful/action.rb +16 -8
- data/lib/watchful/configuration.rb +8 -8
- data/lib/watchful/defaultconfiguration.rb +5 -5
- data/lib/watchful/paths.rb +1 -0
- data/lib/watchful/watch.rb +70 -28
- data/test/unit/action_test.rb +68 -0
- data/test/unit/configuration_test.rb +39 -0
- data/test/unit/paths_test.rb +48 -0
- data/test/unit/watch_test.rb +81 -0
- metadata +31 -42
- data/doc/DSL-Specification.rb +0 -51
- data/test/action_test.rb +0 -159
- data/test/configuration_test.rb +0 -80
- data/test/customconfiguration_test.rb +0 -20
- data/test/paths_test.rb +0 -63
- data/test/test_helper.rb +0 -13
- data/watchful.gemspec +0 -78
data/.autotest
ADDED
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -6,60 +6,69 @@ A shell script for busy web developers
|
|
6
6
|
What?
|
7
7
|
-----
|
8
8
|
|
9
|
-
Watchful is a lightweight tool that runs command line tools on updated files. It was built by a web developer who got seriously tired of managing a whole range of CLI minification, translation, and compression tools on his CSS
|
9
|
+
Watchful is a lightweight tool that runs command line tools on updated files. It was built by a web developer who got seriously tired of managing a whole range of CLI minification, translation, and compression tools on his CSS, Javascript, PNGs, etc.
|
10
10
|
|
11
|
-
You can think of Watchful as kind of recursive,
|
11
|
+
You can think of Watchful as kind of recursive, constantly-running [Rake](http://rake.rubyforge.org/): you tell it which tools to run on which files, then fire it off in your current project directory and go back to your text editor. When you save a change to a relevant file under that directory, Watchful will run the proper tool on it. If you have [Growl](http://growl.info/) installed, you’ll get a notification. If you’re on OS X Leopard and have [the FSEvents gem](http://rubygems.org/gems/fsevents) installed, you’ll get a rather nice performance to boot.
|
12
12
|
|
13
13
|
Where?
|
14
14
|
------
|
15
15
|
|
16
|
-
Watchful is
|
16
|
+
Watchful is available as a Gem:
|
17
|
+
|
18
|
+
sudo gem install watchful
|
19
|
+
|
20
|
+
The development git repo is currently hosted on [GitHub](http://github.com):
|
17
21
|
|
18
22
|
[http://github.com/kemitchell/watchful](http://github.com/kemitchell/watchful)
|
19
23
|
|
20
24
|
Please feel free to fork me!
|
21
|
-
|
22
|
-
Eventually, Watchful will be available as a Ruby Gem:
|
23
|
-
|
24
|
-
sudo gem install watchful
|
25
25
|
|
26
|
+
To install the latest from GitHub:
|
27
|
+
|
28
|
+
cd /tmp
|
29
|
+
git clone git://github.com/kemitchell/watchful.git
|
30
|
+
cd watchful
|
31
|
+
rake build
|
32
|
+
sudo rake install
|
33
|
+
|
26
34
|
How?
|
27
35
|
----
|
28
36
|
|
29
|
-
All configuration is done in pure Ruby. Configurations are created by subclassing `Watchful::Configuration` in
|
37
|
+
All configuration is done in pure Ruby. Configurations are created by subclassing `Watchful::Configuration` in `watchful` files. Configurations contain `action` lines which describe CLI commands, the extension of the files they should be run on, and the extension that should be used for the resulting output. An example follows:
|
30
38
|
|
39
|
+
require 'rubygems'
|
31
40
|
require 'watchful/configuration'
|
32
|
-
|
41
|
+
|
33
42
|
class MyConfiguration < Watchful::Configuration
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
43
|
+
|
44
|
+
description 'LESS CSS and YUI Compressor'
|
45
|
+
|
46
|
+
# actions are applied in order, hence Less is defined before YUI
|
47
|
+
|
48
|
+
action :name => "Less CSS", # Used for command line and Growl notifications
|
49
|
+
:in => ".less", # The extension of files to use as input
|
50
|
+
:out => ".css", # Extension to replace the above for output files
|
51
|
+
:command => "lessc %s %s", # The command to create output files from input files
|
52
|
+
# In the future all of the above will also take blocks.
|
53
|
+
# Currently the command is built using sprintf.
|
54
|
+
# The first %s is the full input path.
|
55
|
+
# The second %s is the full output path.
|
56
|
+
:dependencies => ['less'] # If “less” isn’t found in PATH, this action will be disabled.
|
57
|
+
|
58
|
+
action :name => 'YUI Compressor',
|
59
|
+
:in => '.css',
|
60
|
+
:out => '.min.css', # Notice that Watchful computes extensions differently than basename
|
61
|
+
# Namely, the extension is anything in the file name from the
|
62
|
+
# first period onward, hence this.big.ext.name => '.big.ext.name'
|
63
|
+
:command => 'java -jar /usr/local/utils/yuicompressor-2.4.2.jar --type css --charset utf-8 %s -o %s',
|
64
|
+
:dependencies => ['java', '/usr/local/utils/yuicompressor-2.4.2.jar']
|
65
|
+
# Dependencies can be programs (e.g. “java”), or full file paths.
|
66
|
+
|
58
67
|
end
|
59
68
|
|
60
|
-
When Watchful is started, it searches in the following locations (in the following order) for files called
|
69
|
+
When Watchful is started, it searches in the following locations (in the following order) for files called `watchful` and loads them:
|
61
70
|
|
62
71
|
1. In the directory Watchful is told to monitor
|
63
72
|
2. In the current user’s home directory
|
64
73
|
|
65
|
-
If no such files are found, Watchful will use the default configuration, which searches your PATH for tools it recognizes. If your tool isn't listed in [http://github.com/kemitchell/watchful/blob/master/lib/watchful/defaultconfiguration.rb](defaultconfiguration.rb) and you would like it to be, please fork the project
|
74
|
+
If no such files are found, Watchful will use the default configuration, which searches your PATH for tools it recognizes. If your tool isn't listed in [http://github.com/kemitchell/watchful/blob/master/lib/watchful/defaultconfiguration.rb](defaultconfiguration.rb) and you would like it to be, please fork the project or send me a patch. I would be happy to include any web development tools with current stable or Beta releases.
|
data/Rakefile
CHANGED
@@ -9,38 +9,36 @@ begin
|
|
9
9
|
gem.description = 'Applies intermediary tools to modified files'
|
10
10
|
gem.email = "kyleevanmitchell@gmail.com"
|
11
11
|
gem.homepage = "http://github.com/kemitchell/watchful"
|
12
|
-
gem.authors = ["
|
13
|
-
gem.
|
14
|
-
gem.add_dependency('
|
15
|
-
gem.add_dependency('
|
16
|
-
gem.add_dependency('open4')
|
12
|
+
gem.authors = ["K E Mitchell"]
|
13
|
+
gem.add_dependency('commandline', '>= 0.7.10')
|
14
|
+
gem.add_dependency('extensions', '>= 0.6.0')
|
15
|
+
gem.add_dependency('open4', '>= 1.0.1')
|
17
16
|
gem.rubyforge_project = 'watchful'
|
18
17
|
end
|
19
18
|
Jeweler::RubyforgeTasks.new do |rubyforge|
|
20
|
-
rubyforge.doc_task = '
|
19
|
+
rubyforge.doc_task = 'rdoc'
|
21
20
|
end
|
22
21
|
rescue LoadError
|
23
22
|
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
24
23
|
end
|
25
24
|
|
26
|
-
Jeweler::RubyforgeTasks.new do |rf|
|
27
|
-
rf.doc_task = 'rdoc'
|
28
|
-
end
|
29
|
-
|
30
25
|
require 'rake/testtask'
|
31
26
|
|
27
|
+
TEST_FILE_PATTERN = 'test/unit/*_test.rb'
|
28
|
+
|
32
29
|
Rake::TestTask.new(:test) do |test|
|
33
30
|
test.libs << 'lib' << 'test'
|
34
|
-
test.pattern =
|
31
|
+
test.pattern = TEST_FILE_PATTERN
|
35
32
|
test.verbose = true
|
36
33
|
end
|
37
34
|
|
38
35
|
begin
|
39
36
|
require 'rcov/rcovtask'
|
40
|
-
Rcov::RcovTask.new do |
|
41
|
-
|
42
|
-
|
43
|
-
|
37
|
+
Rcov::RcovTask.new do |t|
|
38
|
+
t.libs << 'lib' << 'test'
|
39
|
+
t.test_files = FileList[TEST_FILE_PATTERN]
|
40
|
+
t.verbose = true
|
41
|
+
t.rcov_opts << '--exclude gems'
|
44
42
|
end
|
45
43
|
rescue LoadError
|
46
44
|
task :rcov do
|
@@ -48,10 +46,6 @@ rescue LoadError
|
|
48
46
|
end
|
49
47
|
end
|
50
48
|
|
51
|
-
task :test => :check_dependencies
|
52
|
-
|
53
|
-
task :default => [:test, :build]
|
54
|
-
|
55
49
|
require 'rake/rdoctask'
|
56
50
|
|
57
51
|
Rake::RDocTask.new do |rdoc|
|
@@ -66,3 +60,7 @@ Rake::RDocTask.new do |rdoc|
|
|
66
60
|
rdoc.rdoc_files.include('README*')
|
67
61
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
68
62
|
end
|
63
|
+
|
64
|
+
task :test => :check_dependencies
|
65
|
+
|
66
|
+
task :default => :test
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.1.1
|
data/bin/watchful
CHANGED
@@ -6,19 +6,8 @@ require 'rubygems'
|
|
6
6
|
require 'commandline'
|
7
7
|
require 'watchful'
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
rescue LoadError
|
12
|
-
HAVE_GROWL = false
|
13
|
-
else
|
14
|
-
if (Growl.installed? rescue false)
|
15
|
-
HAVE_GROWL = true
|
16
|
-
else
|
17
|
-
HAVE_GROWL = false
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
class App < CommandLine::Application
|
9
|
+
class App < CommandLine::Application_wo_AutoRun
|
10
|
+
|
22
11
|
def initialize
|
23
12
|
version Watchful::version
|
24
13
|
author 'KEM'
|
@@ -28,33 +17,46 @@ class App < CommandLine::Application
|
|
28
17
|
A configurable deamon for running source code translation and minification tools–
|
29
18
|
such as CSS and Javascript minification tools and generators—on updated files.
|
30
19
|
eos
|
31
|
-
synopsis "[-dhv] path"
|
20
|
+
synopsis "[-dhv] [--default] [--poll] path"
|
32
21
|
option :help
|
33
22
|
option :debug
|
34
23
|
option :version
|
35
|
-
option
|
36
|
-
|
37
|
-
|
24
|
+
option :flag,
|
25
|
+
:names => %w{--use-default-configuration --default -D},
|
26
|
+
:opt_description => 'Override custom configurations.'
|
27
|
+
option :flag,
|
28
|
+
:names => %w{--force-polling --poll -P},
|
29
|
+
:opt_description => 'Use stat() polling, even if filesystem events are supported.'
|
38
30
|
expected_args :path
|
39
31
|
end
|
32
|
+
|
40
33
|
def main
|
41
|
-
Watchful::Configuration.active_configuration = Watchful::DefaultConfiguration
|
42
|
-
# todo: check dependencies of actions of active configuration
|
43
|
-
|
44
34
|
path = File.expand_path(args[0])
|
45
|
-
fail "No such directory: #{path} " unless File.directory? path
|
35
|
+
fail "? No such directory: #{path} " unless File.directory? path
|
46
36
|
|
47
37
|
Watchful::load_configuration(path, opt['--use-default-configuration'])
|
48
38
|
|
49
39
|
# show the active config
|
50
|
-
|
40
|
+
puts "=> " + Watchful::Configuration.active_configuration._description
|
51
41
|
|
52
42
|
# list active actions
|
53
43
|
Watchful::Configuration.active_configuration.actions.each do |a|
|
54
|
-
|
44
|
+
puts "=>\t+ #{a.name} (#{a.in} -> #{a.out})"
|
55
45
|
end
|
56
46
|
|
57
|
-
|
58
|
-
|
47
|
+
begin
|
48
|
+
Watchful::watch_files(path, opt['--force-polling'])
|
49
|
+
rescue Exception => e
|
50
|
+
if HAVE_GROWL
|
51
|
+
growl = Growl.new
|
52
|
+
growl.title = "GOING DOWN"
|
53
|
+
growl.message = 'Uncaught exception'
|
54
|
+
growl.run
|
55
|
+
end
|
56
|
+
raise e
|
57
|
+
end
|
59
58
|
end
|
60
|
-
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
App.run
|
data/lib/watchful.rb
CHANGED
@@ -10,6 +10,18 @@ require 'watchful/watch'
|
|
10
10
|
require 'watchful/action'
|
11
11
|
require 'watchful/defaultconfiguration'
|
12
12
|
|
13
|
+
begin
|
14
|
+
require 'growl'
|
15
|
+
rescue LoadError
|
16
|
+
HAVE_GROWL = false
|
17
|
+
else
|
18
|
+
if (Growl.installed? rescue false)
|
19
|
+
HAVE_GROWL = true
|
20
|
+
else
|
21
|
+
HAVE_GROWL = false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
13
25
|
module Watchful
|
14
26
|
def self.version
|
15
27
|
File.read(File.join(File.dirname(__FILE__), '..', 'VERSION')).strip
|
data/lib/watchful/action.rb
CHANGED
@@ -1,36 +1,40 @@
|
|
1
|
+
require 'rubygems'
|
1
2
|
require 'commandline' # for debug
|
3
|
+
require 'extensions/string'
|
2
4
|
|
3
5
|
module Watchful
|
4
6
|
|
5
7
|
class Action
|
6
8
|
|
7
|
-
# todo: support blocks for @in, @out,
|
9
|
+
# todo: support blocks for @in, @out, @command, @dependencies
|
8
10
|
|
9
11
|
PROPERTIES = [:name, :dependencies, :command, :in, :out]
|
10
12
|
|
11
13
|
PROPERTIES.each { |k| attr_accessor k }
|
12
14
|
|
13
|
-
@enabled = true
|
14
15
|
def enabled?; @enabled ;end
|
15
16
|
|
16
|
-
|
17
17
|
def initialize(options = {})
|
18
18
|
|
19
19
|
options[:dependencies] ||= []
|
20
20
|
|
21
|
+
@enabled = true
|
22
|
+
|
21
23
|
PROPERTIES.each do |key|
|
22
24
|
self.instance_variable_set('@' + key.to_s, options[key])
|
23
25
|
end
|
24
26
|
|
25
27
|
@dependencies = [@dependencies] if @dependencies.kind_of? String
|
26
28
|
|
27
|
-
raise "Dependencies option must be a string or array" if not @dependencies.kind_of? Array
|
29
|
+
raise ArgumentError.new("Dependencies option must be a string or array") if not @dependencies.kind_of? Array
|
28
30
|
|
29
31
|
[@in, @out].each do |i|
|
30
|
-
@enabled = false unless i and i.instance_of?
|
32
|
+
@enabled = false unless i and i.instance_of?(String) and i.starts_with?('.')
|
31
33
|
end
|
32
34
|
|
33
|
-
@enabled =
|
35
|
+
@enabled = false unless @command.instance_of?(String)
|
36
|
+
|
37
|
+
@enabled = false unless self.has_dependencies?
|
34
38
|
|
35
39
|
end
|
36
40
|
|
@@ -40,7 +44,7 @@ module Watchful
|
|
40
44
|
(Action.have_command?(d)) || (File.exists?(File.expand_path(d)))
|
41
45
|
end
|
42
46
|
# todo: more detailed messages about missing dependencies
|
43
|
-
|
47
|
+
puts "Missing dependencies for action \"#{@name}\"" unless have_all
|
44
48
|
return have_all
|
45
49
|
end
|
46
50
|
|
@@ -59,13 +63,17 @@ module Watchful
|
|
59
63
|
false
|
60
64
|
end
|
61
65
|
|
66
|
+
# given an input file, return the path of an output file
|
62
67
|
def output_path_for(source_path)
|
68
|
+
unless self.input_file?(source_path)
|
69
|
+
raise ArgumentError.new("#{source_path} is not an input file for action #{@name}")
|
70
|
+
end
|
63
71
|
return File.dirname(source_path) + '/' + File.basename(source_path, @in) + @out
|
64
72
|
end
|
65
73
|
|
66
74
|
# does the given path look like a path to which this action might write output?
|
67
75
|
def output_file?(path)
|
68
|
-
Watchful::compound_extension_of(path) == @
|
76
|
+
Watchful::compound_extension_of(path) == @out
|
69
77
|
end
|
70
78
|
|
71
79
|
# does the given path look like a path to which this action could be applied?
|
@@ -7,20 +7,20 @@ module Watchful
|
|
7
7
|
@@active_configuration = nil
|
8
8
|
|
9
9
|
def self.active_configuration; @@active_configuration; end
|
10
|
-
def self.active_configuration=(x)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
def self.active_configuration=(x); @@active_configuration = x; end
|
11
|
+
|
12
|
+
def self.action_for_file(path)
|
13
|
+
@actions ||= []
|
14
|
+
@actions.find { |a| a.input_file?(path) && a.enabled?}
|
15
15
|
end
|
16
16
|
|
17
17
|
class << self
|
18
18
|
|
19
19
|
attr_reader :actions
|
20
20
|
|
21
|
-
def description(s); @description = s; end
|
22
|
-
|
23
21
|
def _description; @description; end
|
22
|
+
|
23
|
+
def description(s); @description = s; end
|
24
24
|
|
25
25
|
def action(args)
|
26
26
|
# set the description in case the user doesn’t
|
@@ -29,7 +29,7 @@ module Watchful
|
|
29
29
|
# set to active configuration
|
30
30
|
@@active_configuration = self
|
31
31
|
|
32
|
-
# todo: support block-
|
32
|
+
# todo: support block-style action definition
|
33
33
|
@actions ||= []
|
34
34
|
a = Watchful::Action.new(args)
|
35
35
|
@actions << a
|
@@ -1,15 +1,15 @@
|
|
1
1
|
require 'watchful/configuration'
|
2
2
|
|
3
|
+
# todo: Don't require DefaultConfiguration by default
|
4
|
+
|
3
5
|
module Watchful
|
4
6
|
|
5
7
|
class DefaultConfiguration < Configuration
|
6
8
|
|
7
9
|
description "Default Configuration"
|
8
10
|
|
9
|
-
# LESS CSS, if installed
|
10
|
-
|
11
11
|
begin
|
12
|
-
|
12
|
+
require "less"
|
13
13
|
action :name => 'LESS CSS',
|
14
14
|
:in => '.less',
|
15
15
|
:out => '.css',
|
@@ -19,8 +19,6 @@ module Watchful
|
|
19
19
|
# pass
|
20
20
|
end
|
21
21
|
|
22
|
-
# SASS, if installed
|
23
|
-
|
24
22
|
if Action.have_command? 'sass'
|
25
23
|
action :name => 'SASS',
|
26
24
|
:in => '.sass',
|
@@ -29,6 +27,8 @@ module Watchful
|
|
29
27
|
:dependencies => 'sass'
|
30
28
|
end
|
31
29
|
|
30
|
+
# todo: Add more actions to DefaultConfiguration
|
31
|
+
|
32
32
|
end
|
33
33
|
|
34
34
|
end
|