watchful 0.0.0.pre1
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 +3 -0
- data/LICENSE +13 -0
- data/README.md +65 -0
- data/Rakefile +68 -0
- data/VERSION +1 -0
- data/bin/watchful +60 -0
- data/doc/DSL-Specification.rb +51 -0
- data/lib/watchful/action.rb +78 -0
- data/lib/watchful/configuration.rb +42 -0
- data/lib/watchful/defaultconfiguration.rb +34 -0
- data/lib/watchful/paths.rb +20 -0
- data/lib/watchful/watch.rb +106 -0
- data/lib/watchful.rb +17 -0
- data/test/action_test.rb +159 -0
- data/test/configuration_test.rb +80 -0
- data/test/customconfiguration_test.rb +20 -0
- data/test/paths_test.rb +63 -0
- data/test/test_helper.rb +13 -0
- data/watchful.gemspec +78 -0
- metadata +135 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (c) 2010, Kyle E. Mitchell (http://kemitchell.com)
|
2
|
+
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
4
|
+
purpose with or without fee is hereby granted, provided that the above
|
5
|
+
copyright notice and this permission notice appear in all copies.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
8
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
9
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
10
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
11
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
12
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
13
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
Watchful
|
2
|
+
========
|
3
|
+
|
4
|
+
A shell script for busy web developers
|
5
|
+
|
6
|
+
What?
|
7
|
+
-----
|
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 and Javascript.
|
10
|
+
|
11
|
+
You can think of Watchful as kind of recursive, specialized [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 are on OS X and have [Growl](http://growl.info/) installed, you’ll get a notification.
|
12
|
+
|
13
|
+
Where?
|
14
|
+
------
|
15
|
+
|
16
|
+
Watchful is currently hosted on [GitHub](http://github.com):
|
17
|
+
|
18
|
+
[http://github.com/kemitchell/watchful](http://github.com/kemitchell/watchful)
|
19
|
+
|
20
|
+
Please feel free to fork me!
|
21
|
+
|
22
|
+
Eventually, Watchful will be available as a Ruby Gem:
|
23
|
+
|
24
|
+
sudo gem install watchful
|
25
|
+
|
26
|
+
How?
|
27
|
+
----
|
28
|
+
|
29
|
+
All configuration is done in pure Ruby. Configurations are created by subclassing `Watchful::Configuration` in “watchful” files. An example follows:
|
30
|
+
|
31
|
+
require 'watchful/configuration'
|
32
|
+
|
33
|
+
class MyConfiguration < Watchful::Configuration
|
34
|
+
|
35
|
+
description 'LESS CSS and YUI Compressor'
|
36
|
+
|
37
|
+
# actions are applied in order, hence Less is defined before YUI
|
38
|
+
|
39
|
+
action :name => "Less CSS",
|
40
|
+
:in => ".less", # The extension of files to use as input
|
41
|
+
:out => ".css", # Extension to replace the above for output files
|
42
|
+
:command => "lessc %s %s", # The command to create output files from input files
|
43
|
+
# Currently the command is built using sprintf.
|
44
|
+
# In the future all of the above will also take blocks.
|
45
|
+
# For now, the first %s is the full input path,
|
46
|
+
# and the second the full output path.
|
47
|
+
:dependencies => ['less'] # If “less” isn’t found in PATH, this action will be disabled.
|
48
|
+
|
49
|
+
action :name => 'YUI Compressor',
|
50
|
+
:in => '.css',
|
51
|
+
:out => '.min.css', # Notice that Watchful computers extensions differently than basename
|
52
|
+
# Namely, the extension is anything in the file name from the
|
53
|
+
# first period onward.
|
54
|
+
:command => 'java -jar /usr/local/utils/yuicompressor-2.4.2.jar --type css --charset utf-8 %s -o %s',
|
55
|
+
:dependencies => ['java', '/usr/local/utils/yuicompressor-2.4.2.jar']
|
56
|
+
# Dependencies can be programs (e.g. “java”), or full file paths.
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
When Watchful is started, it searches in the following locations (in the following order) for files called “watchful” and loads them:
|
61
|
+
|
62
|
+
1. In the directory Watchful is told to monitor
|
63
|
+
2. In the current user’s home directory
|
64
|
+
|
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 on github or send me a patch. I would be happy to include any tools with current stable or Beta releases.
|
data/Rakefile
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "watchful"
|
8
|
+
gem.summary = 'Watchful, a tool for busy web devs'
|
9
|
+
gem.description = 'Applies intermediary tools to modified files'
|
10
|
+
gem.email = "kyleevanmitchell@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/kemitchell/watchful"
|
12
|
+
gem.authors = ["KEM"]
|
13
|
+
gem.add_development_dependency "thoughtbot-shoulda"
|
14
|
+
gem.add_dependency('commandline')
|
15
|
+
gem.add_dependency('extensions')
|
16
|
+
gem.add_dependency('open4')
|
17
|
+
gem.rubyforge_project = 'watchful'
|
18
|
+
end
|
19
|
+
Jeweler::RubyforgeTasks.new do |rubyforge|
|
20
|
+
rubyforge.doc_task = 'watchful'
|
21
|
+
end
|
22
|
+
rescue LoadError
|
23
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
24
|
+
end
|
25
|
+
|
26
|
+
Jeweler::RubyforgeTasks.new do |rf|
|
27
|
+
rf.doc_task = 'rdoc'
|
28
|
+
end
|
29
|
+
|
30
|
+
require 'rake/testtask'
|
31
|
+
|
32
|
+
Rake::TestTask.new(:test) do |test|
|
33
|
+
test.libs << 'lib' << 'test'
|
34
|
+
test.pattern = 'test/**/*_test.rb'
|
35
|
+
test.verbose = true
|
36
|
+
end
|
37
|
+
|
38
|
+
begin
|
39
|
+
require 'rcov/rcovtask'
|
40
|
+
Rcov::RcovTask.new do |test|
|
41
|
+
test.libs << 'test'
|
42
|
+
test.pattern = 'test/**/*_test.rb'
|
43
|
+
test.verbose = true
|
44
|
+
end
|
45
|
+
rescue LoadError
|
46
|
+
task :rcov do
|
47
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
task :test => :check_dependencies
|
52
|
+
|
53
|
+
task :default => [:test, :build]
|
54
|
+
|
55
|
+
require 'rake/rdoctask'
|
56
|
+
|
57
|
+
Rake::RDocTask.new do |rdoc|
|
58
|
+
if File.exist?('VERSION')
|
59
|
+
version = File.read('VERSION')
|
60
|
+
else
|
61
|
+
version = ""
|
62
|
+
end
|
63
|
+
|
64
|
+
rdoc.rdoc_dir = 'rdoc'
|
65
|
+
rdoc.title = "watchful #{version}"
|
66
|
+
rdoc.rdoc_files.include('README*')
|
67
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
68
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0.pre1
|
data/bin/watchful
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'commandline'
|
7
|
+
require 'watchful'
|
8
|
+
|
9
|
+
begin
|
10
|
+
require 'growl' # OS X notifications
|
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
|
22
|
+
def initialize
|
23
|
+
version Watchful::version
|
24
|
+
author 'KEM'
|
25
|
+
copyright '2010, Kyle Mitchell'
|
26
|
+
short_description 'runs tools on updated files'
|
27
|
+
long_description <<-eos
|
28
|
+
A configurable deamon for running source code translation and minification tools–
|
29
|
+
such as CSS and Javascript minification tools and generators—on updated files.
|
30
|
+
eos
|
31
|
+
synopsis "[-dhv] path"
|
32
|
+
option :help
|
33
|
+
option :debug
|
34
|
+
option :version
|
35
|
+
option :names => %w{--use-default-configuration --default},
|
36
|
+
:opt_found => get_args,
|
37
|
+
:opt_description => 'Use the default configuration even if a local or user-level configuration is found.'
|
38
|
+
expected_args :path
|
39
|
+
end
|
40
|
+
def main
|
41
|
+
Watchful::Configuration.active_configuration = Watchful::DefaultConfiguration
|
42
|
+
# todo: check dependencies of actions of active configuration
|
43
|
+
|
44
|
+
path = File.expand_path(args[0])
|
45
|
+
fail "No such directory: #{path} " unless File.directory? path
|
46
|
+
|
47
|
+
Watchful::load_configuration(path, opt['--use-default-configuration'])
|
48
|
+
|
49
|
+
# show the active config
|
50
|
+
debug "Config: " + Watchful::Configuration.active_configuration._description
|
51
|
+
|
52
|
+
# list active actions
|
53
|
+
Watchful::Configuration.active_configuration.actions.each do |a|
|
54
|
+
debug "Action: \t#{a.name} (#{a.in} -> #{a.out})"
|
55
|
+
end
|
56
|
+
|
57
|
+
puts "Watching #{Dir.pwd}..."
|
58
|
+
Watchful::watch_files(path)
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
|
2
|
+
# The name MyConfig should be accessible from the command line, e.g.
|
3
|
+
#
|
4
|
+
# watchful MyConfiguration
|
5
|
+
#
|
6
|
+
# By default (without a named config) watchful should use:
|
7
|
+
#
|
8
|
+
# 1) The configuration found in Watchful.rb, which should search PATH
|
9
|
+
# for known tools
|
10
|
+
#
|
11
|
+
# or failing that
|
12
|
+
#
|
13
|
+
# 2) The default configuration in the gem
|
14
|
+
#
|
15
|
+
|
16
|
+
class MyConfiguration < Watchful::Configuration
|
17
|
+
|
18
|
+
# hash style action description
|
19
|
+
|
20
|
+
action :name => 'CSS Minifier' # for messages
|
21
|
+
:in => ".css",
|
22
|
+
:out => ".min.css",
|
23
|
+
:command => "minify $IN $OUT",
|
24
|
+
:dependencies => ['minify']
|
25
|
+
|
26
|
+
# block style action description
|
27
|
+
|
28
|
+
action 'CSS Minifier' do |a|
|
29
|
+
|
30
|
+
# file matchers should also support blocks
|
31
|
+
|
32
|
+
a.filter do |path|
|
33
|
+
true if /.+\.css/ =~ path
|
34
|
+
end
|
35
|
+
|
36
|
+
# or regexps
|
37
|
+
|
38
|
+
a.in = /.+\.css/
|
39
|
+
|
40
|
+
# without $IN assume standard input
|
41
|
+
|
42
|
+
a.command = 'minify -o $OUT' # => "cat $IN | minify -o $OUT"
|
43
|
+
|
44
|
+
a.command = 'minify' # => "cat $IN | minify > $OUT"
|
45
|
+
# => a.dependencies << 'minify'
|
46
|
+
|
47
|
+
a.dependencies = ['minify']
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'commandline' # for debug
|
2
|
+
|
3
|
+
module Watchful
|
4
|
+
|
5
|
+
class Action
|
6
|
+
|
7
|
+
# todo: support blocks for @in, @out, and @command
|
8
|
+
|
9
|
+
PROPERTIES = [:name, :dependencies, :command, :in, :out]
|
10
|
+
|
11
|
+
PROPERTIES.each { |k| attr_accessor k }
|
12
|
+
|
13
|
+
@enabled = true
|
14
|
+
def enabled?; @enabled ;end
|
15
|
+
|
16
|
+
|
17
|
+
def initialize(options = {})
|
18
|
+
|
19
|
+
options[:dependencies] ||= []
|
20
|
+
|
21
|
+
PROPERTIES.each do |key|
|
22
|
+
self.instance_variable_set('@' + key.to_s, options[key])
|
23
|
+
end
|
24
|
+
|
25
|
+
@dependencies = [@dependencies] if @dependencies.kind_of? String
|
26
|
+
|
27
|
+
raise "Dependencies option must be a string or array" if not @dependencies.kind_of? Array
|
28
|
+
|
29
|
+
[@in, @out].each do |i|
|
30
|
+
@enabled = false unless i and i.instance_of? String and i.starts_with? '.'
|
31
|
+
end
|
32
|
+
|
33
|
+
@enabled = @enabled && self.has_dependencies?
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def has_dependencies?
|
38
|
+
return true if @dependencies.empty?
|
39
|
+
have_all = @dependencies.any? do |d|
|
40
|
+
(Action.have_command?(d)) || (File.exists?(File.expand_path(d)))
|
41
|
+
end
|
42
|
+
# todo: more detailed messages about missing dependencies
|
43
|
+
debug "Missing dependencies for action \"#{@name}\"" unless have_all
|
44
|
+
return have_all
|
45
|
+
end
|
46
|
+
|
47
|
+
def command_string(input_path)
|
48
|
+
Kernel.sprintf(@command, input_path, self.output_path_for(input_path))
|
49
|
+
end
|
50
|
+
|
51
|
+
# todo: better have_command?
|
52
|
+
def Action.have_command?(cmd)
|
53
|
+
raise 'Argument must be a string' if not cmd.kind_of? String
|
54
|
+
raise 'Argument cannot be an empty string' if cmd.empty?
|
55
|
+
|
56
|
+
ENV['PATH'].split(':').each do |dir|
|
57
|
+
return true if File.exists?("#{dir}/#{cmd}")
|
58
|
+
end
|
59
|
+
false
|
60
|
+
end
|
61
|
+
|
62
|
+
def output_path_for(source_path)
|
63
|
+
return File.dirname(source_path) + '/' + File.basename(source_path, @in) + @out
|
64
|
+
end
|
65
|
+
|
66
|
+
# does the given path look like a path to which this action might write output?
|
67
|
+
def output_file?(path)
|
68
|
+
Watchful::compound_extension_of(path) == @in
|
69
|
+
end
|
70
|
+
|
71
|
+
# does the given path look like a path to which this action could be applied?
|
72
|
+
def input_file?(path)
|
73
|
+
Watchful::compound_extension_of(path) == @in
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'watchful/action'
|
2
|
+
|
3
|
+
module Watchful
|
4
|
+
|
5
|
+
class Configuration
|
6
|
+
|
7
|
+
@@active_configuration = nil
|
8
|
+
|
9
|
+
def self.active_configuration; @@active_configuration; end
|
10
|
+
def self.active_configuration=(x)
|
11
|
+
unless x.is_a? Class and x.ancestors.include? Watchful::Configuration
|
12
|
+
throw "Not a valid configuration class"
|
13
|
+
end
|
14
|
+
@@active_configuration = x
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
|
19
|
+
attr_reader :actions
|
20
|
+
|
21
|
+
def description(s); @description = s; end
|
22
|
+
|
23
|
+
def _description; @description; end
|
24
|
+
|
25
|
+
def action(args)
|
26
|
+
# set the description in case the user doesn’t
|
27
|
+
@description ||= 'Custom Configuration'
|
28
|
+
|
29
|
+
# set to active configuration
|
30
|
+
@@active_configuration = self
|
31
|
+
|
32
|
+
# todo: support block-passing style
|
33
|
+
@actions ||= []
|
34
|
+
a = Watchful::Action.new(args)
|
35
|
+
@actions << a
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'watchful/configuration'
|
2
|
+
|
3
|
+
module Watchful
|
4
|
+
|
5
|
+
class DefaultConfiguration < Configuration
|
6
|
+
|
7
|
+
description "Default Configuration"
|
8
|
+
|
9
|
+
# LESS CSS, if installed
|
10
|
+
|
11
|
+
begin
|
12
|
+
gem "less"
|
13
|
+
action :name => 'LESS CSS',
|
14
|
+
:in => '.less',
|
15
|
+
:out => '.css',
|
16
|
+
:command => 'lessc %s %s',
|
17
|
+
:dependencies => ['less']
|
18
|
+
rescue GEM::LoadError
|
19
|
+
# pass
|
20
|
+
end
|
21
|
+
|
22
|
+
# SASS, if installed
|
23
|
+
|
24
|
+
if Action.have_command? 'sass'
|
25
|
+
action :name => 'SASS',
|
26
|
+
:in => '.sass',
|
27
|
+
:out => '.min.css',
|
28
|
+
:command => 'sass --style compressed %s %s',
|
29
|
+
:dependencies => 'sass'
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'extensions/string'
|
2
|
+
|
3
|
+
module Watchful
|
4
|
+
|
5
|
+
def self.younger_than(a, b)
|
6
|
+
if (not File.exists? a) or (not File.exists? b)
|
7
|
+
raise Exception, "cannot compare modification times of non-existant files"
|
8
|
+
end
|
9
|
+
return (File.stat(a).mtime > File.stat(b).mtime)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.compound_extension_of(str)
|
13
|
+
return nil if str.empty?
|
14
|
+
basename = File.basename(str)
|
15
|
+
periodpos = basename.index('.')
|
16
|
+
return nil if not periodpos or periodpos < 1
|
17
|
+
return basename[basename.index('.')..-1]
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'find'
|
2
|
+
require 'open4'
|
3
|
+
require 'watchful/defaultconfiguration'
|
4
|
+
require "commandline"
|
5
|
+
|
6
|
+
module Watchful
|
7
|
+
|
8
|
+
def self.each_source_file_in_dir(path = Dir.pwd)
|
9
|
+
Find.find(path) do |filepath|
|
10
|
+
next if FileTest.directory?(filepath)
|
11
|
+
yield(filepath)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.check_for_interrupts
|
16
|
+
begin
|
17
|
+
sleep 1
|
18
|
+
rescue Interrupt
|
19
|
+
puts # new line
|
20
|
+
exit 0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
CONFIGURATION_FILE_NAME = 'watchful'
|
25
|
+
|
26
|
+
def self.load_configuration(path, use_default = false)
|
27
|
+
|
28
|
+
# todo: more efficient loading of configurations
|
29
|
+
# todo: check that relative paths work on Windows
|
30
|
+
|
31
|
+
local = File.expand_path("#{path}/#{CONFIGURATION_FILE_NAME}")
|
32
|
+
user = File.expand_path("~/#{CONFIGURATION_FILE_NAME}")
|
33
|
+
|
34
|
+
if File.exists? local
|
35
|
+
debug "Loading #{local}"
|
36
|
+
load local
|
37
|
+
elsif File.exists? user
|
38
|
+
debug "Loading #{user}"
|
39
|
+
load user
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
@files_with_errors = Hash.new # {:path => '', :mtime => ### }
|
45
|
+
|
46
|
+
def self.watch_files(path)
|
47
|
+
@output_file_extensions = Configuration.active_configuration.actions.collect do |a|
|
48
|
+
a.out
|
49
|
+
end
|
50
|
+
loop do
|
51
|
+
check_for_interrupts
|
52
|
+
each_source_file_in_dir(path) do |file_path|
|
53
|
+
extension = compound_extension_of(file_path)
|
54
|
+
|
55
|
+
action = Configuration.active_configuration.actions.find { |a| a.input_file? file_path}
|
56
|
+
|
57
|
+
next if action.nil?
|
58
|
+
|
59
|
+
output_path = action.output_path_for(file_path)
|
60
|
+
|
61
|
+
if (!File.exists?(output_path)) or younger_than(file_path, output_path)
|
62
|
+
error_mtime = @files_with_errors[file_path]
|
63
|
+
next if error_mtime && error_mtime >= File.stat(file_path).mtime
|
64
|
+
do_action(file_path, action)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.do_action(source_file, action)
|
72
|
+
|
73
|
+
puts "#{action.name}: #{source_file}"
|
74
|
+
|
75
|
+
command = action.command_string(source_file)
|
76
|
+
|
77
|
+
debug "\t#{command}"
|
78
|
+
|
79
|
+
proc_pid, proc_stdin, proc_stdout, proc_stderr = Open4::popen4(command)
|
80
|
+
proc_ignored, proc_status = Process::waitpid2(proc_pid)
|
81
|
+
|
82
|
+
error = proc_stderr.gets
|
83
|
+
|
84
|
+
if proc_status && !error
|
85
|
+
if HAVE_GROWL
|
86
|
+
growl = Growl.new
|
87
|
+
growl.title = action.name
|
88
|
+
growl.message = "#{source_file}"
|
89
|
+
growl.run
|
90
|
+
end
|
91
|
+
@files_with_errors.delete(source_file)
|
92
|
+
else
|
93
|
+
$stderr.puts("ERROR -- #{action.name} -- #{source_file}")
|
94
|
+
$stderr.puts(error)
|
95
|
+
if HAVE_GROWL
|
96
|
+
growl = Growl.new
|
97
|
+
growl.title = "ERROR: #{action.name}"
|
98
|
+
growl.message = error
|
99
|
+
growl.run
|
100
|
+
end
|
101
|
+
@files_with_errors[source_file] = File.stat(source_file).mtime
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
data/lib/watchful.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Author:: K E Mitchell (mailto:kyleevanmitchell@gmail.com)
|
2
|
+
# License:: ISC License (http://www.isc.org/software/license)
|
3
|
+
|
4
|
+
WATCHFUL_ROOT = File.expand_path(File.dirname(__FILE__))
|
5
|
+
|
6
|
+
$:.unshift File.dirname(__FILE__)
|
7
|
+
|
8
|
+
require 'watchful/paths'
|
9
|
+
require 'watchful/watch'
|
10
|
+
require 'watchful/action'
|
11
|
+
require 'watchful/defaultconfiguration'
|
12
|
+
|
13
|
+
module Watchful
|
14
|
+
def self.version
|
15
|
+
File.read(File.join(File.dirname(__FILE__), '..', 'VERSION')).strip
|
16
|
+
end
|
17
|
+
end
|
data/test/action_test.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class WatchfulTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context 'Action class' do
|
6
|
+
|
7
|
+
should 'throw an exception with incomplete constructor parameters' do
|
8
|
+
begin
|
9
|
+
Watchful::Action.new(:name => 'Incomplete Action')
|
10
|
+
flunk
|
11
|
+
rescue Exception
|
12
|
+
assert true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
should 'properly construct with a complete set of options' do
|
17
|
+
a = Watchful::Action.new(
|
18
|
+
:name => 'Test Action',
|
19
|
+
:dependencies => ['cat'], :command => 'cat %s %s',
|
20
|
+
:in => '.txt', :out => '.copy'
|
21
|
+
)
|
22
|
+
assert a.instance_of? Watchful::Action
|
23
|
+
assert_equal a.name, 'Test Action'
|
24
|
+
end
|
25
|
+
|
26
|
+
should 'throw an exception when a non-string, non-array value is passed as dependencies' do
|
27
|
+
begin
|
28
|
+
a = Watchful::Action.new(
|
29
|
+
:name => 'Test Action',
|
30
|
+
:dependencies => Date.new, :command => 'cat %s %s',
|
31
|
+
:in => '.txt', :out => '.copy'
|
32
|
+
)
|
33
|
+
flunk
|
34
|
+
rescue Exception
|
35
|
+
assert true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'output_path_for' do
|
40
|
+
|
41
|
+
setup do
|
42
|
+
a = Watchful::Action.new(
|
43
|
+
:name => 'Test Action',
|
44
|
+
:dependencies => ['cat'], :command => 'cat %s %s',
|
45
|
+
:in => '.txt', :out => '.copy'
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
should 'produce correct path' do
|
50
|
+
a = Watchful::Action.new(
|
51
|
+
:name => 'Test Action',
|
52
|
+
:dependencies => ['cat'], :command => 'cat %s %s',
|
53
|
+
:in => '.txt', :out => '.copy'
|
54
|
+
)
|
55
|
+
assert_equal a.output_path_for('/tmp/test.txt'), '/tmp/test.copy'
|
56
|
+
end
|
57
|
+
|
58
|
+
should 'produce correct path with complex extensions' do
|
59
|
+
a = Watchful::Action.new(
|
60
|
+
:name => 'Test Action',
|
61
|
+
:dependencies => ['cat'], :command => 'cat %s %s',
|
62
|
+
:in => '.max.txt', :out => '.min.copy'
|
63
|
+
)
|
64
|
+
assert_equal a.output_path_for('/tmp/test.max.txt'), '/tmp/test.min.copy'
|
65
|
+
end
|
66
|
+
|
67
|
+
should 'produce correct with blank output extension' do
|
68
|
+
a = Watchful::Action.new(
|
69
|
+
:name => 'Test Action',
|
70
|
+
:dependencies => ['cat'], :command => 'cat %s %s',
|
71
|
+
:in => '', :out => '.copy'
|
72
|
+
)
|
73
|
+
assert_equal a.output_path_for('/tmp/test.txt'), '/tmp/test.txt.copy'
|
74
|
+
end
|
75
|
+
|
76
|
+
should 'produce correct with blank source extension' do
|
77
|
+
a = Watchful::Action.new(
|
78
|
+
:name => 'Test Action',
|
79
|
+
:dependencies => ['cat'], :command => 'cat %s %s',
|
80
|
+
:in => '.txt', :out => ''
|
81
|
+
)
|
82
|
+
assert_equal a.output_path_for('/tmp/test.txt'), '/tmp/test'
|
83
|
+
end
|
84
|
+
|
85
|
+
should 'produce correct with blank extensions' do
|
86
|
+
a = Watchful::Action.new(
|
87
|
+
:name => 'Test Action',
|
88
|
+
:dependencies => ['cat'], :command => 'cat %s %s',
|
89
|
+
:in => '', :out => ''
|
90
|
+
)
|
91
|
+
assert_equal a.output_path_for('/tmp/test'), '/tmp/test'
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'dependencies' do
|
97
|
+
|
98
|
+
context 'have_command?' do
|
99
|
+
|
100
|
+
should 'raise an exception for an empty string' do
|
101
|
+
begin
|
102
|
+
Watchful::Action.have_command?('')
|
103
|
+
flunk
|
104
|
+
rescue Exception
|
105
|
+
assert true
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
should 'raise an exception for a non-string argument' do
|
110
|
+
begin
|
111
|
+
Watchful::Action.have_command?(nil)
|
112
|
+
flunk
|
113
|
+
rescue Exception
|
114
|
+
assert true
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
should 'return false for missing commands' do
|
119
|
+
assert_equal Watchful::Action.have_command?('Y0CbR4CEP2XY'), false
|
120
|
+
end
|
121
|
+
|
122
|
+
should 'return true for available commands' do
|
123
|
+
assert_equal Watchful::Action.have_command?('cat'), true
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
should 'confirm valid dependencies' do
|
129
|
+
a = Watchful::Action.new(
|
130
|
+
:name => 'Test Action',
|
131
|
+
:dependencies => ['cat', 'echo'], :command => 'cat %s %s',
|
132
|
+
:in => '.txt', :out => '.copy'
|
133
|
+
)
|
134
|
+
assert_equal a.has_dependencies?, true
|
135
|
+
end
|
136
|
+
|
137
|
+
should 'detect a missing binary dependency' do
|
138
|
+
a = Watchful::Action.new(
|
139
|
+
:name => 'Test Action',
|
140
|
+
:dependencies => ['Y0CbR4CEP2XY'], :command => 'Y0CbR4CEP2XY %s %s',
|
141
|
+
:in => '.txt', :out => '.copy'
|
142
|
+
)
|
143
|
+
assert_equal a.has_dependencies?, false
|
144
|
+
end
|
145
|
+
|
146
|
+
should 'confirm availability of no dependencies' do
|
147
|
+
a = Watchful::Action.new(
|
148
|
+
:name => 'Test Action',
|
149
|
+
:dependencies => nil, :command => '',
|
150
|
+
:in => '.txt', :out => '.copy'
|
151
|
+
)
|
152
|
+
assert_equal a.has_dependencies?, true
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require "watchful/configuration"
|
4
|
+
|
5
|
+
class FirstTestConfiguration < Watchful::Configuration
|
6
|
+
|
7
|
+
action :name => 'Placeholder Action'
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
puts "FirstTestConfiguration is a Configuration?".kind_of? Watchful::Configuration
|
12
|
+
|
13
|
+
class WatchfulTest < Test::Unit::TestCase
|
14
|
+
|
15
|
+
context 'Configuration class' do
|
16
|
+
|
17
|
+
context 'dependencies' do
|
18
|
+
|
19
|
+
context 'have_command?' do
|
20
|
+
|
21
|
+
should 'raise an exception for an empty string' do
|
22
|
+
begin
|
23
|
+
Watchful::Action.have_command?('')
|
24
|
+
flunk
|
25
|
+
rescue Exception
|
26
|
+
assert true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
should 'raise an exception for a non-string argument' do
|
31
|
+
begin
|
32
|
+
Watchful::Action.have_command?(nil)
|
33
|
+
flunk
|
34
|
+
rescue Exception
|
35
|
+
assert true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
should 'return false for missing commands' do
|
40
|
+
assert_equal Watchful::Action.have_command?('Y0CbR4CEP2XY'), false
|
41
|
+
end
|
42
|
+
|
43
|
+
should 'return true for available commands' do
|
44
|
+
assert_equal Watchful::Action.have_command?('cat'), true
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
should 'confirm valid dependencies' do
|
50
|
+
a = Watchful::Action.new(
|
51
|
+
:name => 'Test Action',
|
52
|
+
:dependencies => ['cat', 'echo'], :command => 'cat %s %s',
|
53
|
+
:in => '.txt', :out => '.copy'
|
54
|
+
)
|
55
|
+
assert_equal a.has_dependencies?, true
|
56
|
+
end
|
57
|
+
|
58
|
+
should 'detect a missing binary dependency' do
|
59
|
+
a = Watchful::Action.new(
|
60
|
+
:name => 'Test Action',
|
61
|
+
:dependencies => ['Y0CbR4CEP2XY'], :command => 'Y0CbR4CEP2XY %s %s',
|
62
|
+
:in => '.txt', :out => '.copy'
|
63
|
+
)
|
64
|
+
assert_equal a.has_dependencies?, false
|
65
|
+
end
|
66
|
+
|
67
|
+
should 'confirm availability of no dependencies' do
|
68
|
+
a = Watchful::Action.new(
|
69
|
+
:name => 'Test Action',
|
70
|
+
:dependencies => nil, :command => '',
|
71
|
+
:in => '.txt', :out => '.copy'
|
72
|
+
)
|
73
|
+
assert_equal a.has_dependencies?, true
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
class WatchfulTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
['/tmp/first', '/tmp/second'].each do |f|
|
8
|
+
FileUtils.touch(f)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class FirstTestConfiguration < Watchful::Configuration
|
13
|
+
action :name => 'Placeholder Action'
|
14
|
+
end
|
15
|
+
|
16
|
+
should 'correctly compile list of actions' do
|
17
|
+
assert_equal FirstTestConfiguration.actions.length, 1
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
data/test/paths_test.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
class WatchfulTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
['/tmp/first', '/tmp/second'].each do |f|
|
8
|
+
FileUtils.touch(f)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'younger_than' do
|
13
|
+
|
14
|
+
should 'correctly compare mtimes' do
|
15
|
+
|
16
|
+
sleep 1
|
17
|
+
FileUtils.touch('/tmp/first')
|
18
|
+
assert Watchful.younger_than('/tmp/first', '/tmp/second')
|
19
|
+
|
20
|
+
sleep 1
|
21
|
+
FileUtils.touch('/tmp/second')
|
22
|
+
assert Watchful.younger_than('/tmp/second', '/tmp/first')
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
should 'raise exception when comparing nonexistant files' do
|
27
|
+
begin
|
28
|
+
Watchful.younger_than('/tmp/does/not/exist', @files[1])
|
29
|
+
flunk
|
30
|
+
rescue Exception
|
31
|
+
assert true
|
32
|
+
end
|
33
|
+
begin
|
34
|
+
Watchful.younger_than(@files[0], '/tmp/does/not/exist')
|
35
|
+
flunk
|
36
|
+
rescue Exception
|
37
|
+
assert true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'compound_extension_of' do
|
44
|
+
|
45
|
+
should "detect simple extensions" do
|
46
|
+
assert_equal Watchful.compound_extension_of('a_long_file_name.css'), '.css'
|
47
|
+
end
|
48
|
+
|
49
|
+
should "detect compound extensions" do
|
50
|
+
assert_equal Watchful.compound_extension_of('test.min.css'), '.min.css'
|
51
|
+
end
|
52
|
+
|
53
|
+
should "return nil for files without extensions" do
|
54
|
+
assert_nil Watchful.compound_extension_of('test_file')
|
55
|
+
end
|
56
|
+
|
57
|
+
should 'return nil for files without basenames' do
|
58
|
+
assert_nil Watchful.compound_extension_of('.extension')
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
|
5
|
+
# todo: move to raw Test::Unit
|
6
|
+
|
7
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
8
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
9
|
+
|
10
|
+
require 'watchful'
|
11
|
+
|
12
|
+
class Test::Unit::Watchful
|
13
|
+
end
|
data/watchful.gemspec
ADDED
@@ -0,0 +1,78 @@
|
|
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{watchful}
|
8
|
+
s.version = "0.0.0.pre1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["KEM"]
|
12
|
+
s.date = %q{2010-04-22}
|
13
|
+
s.default_executable = %q{watchful}
|
14
|
+
s.description = %q{Applies intermediary tools to modified files}
|
15
|
+
s.email = %q{kyleevanmitchell@gmail.com}
|
16
|
+
s.executables = ["watchful"]
|
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/watchful",
|
28
|
+
"doc/DSL-Specification.rb",
|
29
|
+
"lib/watchful.rb",
|
30
|
+
"lib/watchful/action.rb",
|
31
|
+
"lib/watchful/configuration.rb",
|
32
|
+
"lib/watchful/defaultconfiguration.rb",
|
33
|
+
"lib/watchful/paths.rb",
|
34
|
+
"lib/watchful/watch.rb",
|
35
|
+
"test/action_test.rb",
|
36
|
+
"test/configuration_test.rb",
|
37
|
+
"test/customconfiguration_test.rb",
|
38
|
+
"test/paths_test.rb",
|
39
|
+
"test/test_helper.rb",
|
40
|
+
"watchful.gemspec"
|
41
|
+
]
|
42
|
+
s.homepage = %q{http://github.com/kemitchell/watchful}
|
43
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
44
|
+
s.require_paths = ["lib"]
|
45
|
+
s.rubyforge_project = %q{watchful}
|
46
|
+
s.rubygems_version = %q{1.3.6}
|
47
|
+
s.summary = %q{Watchful, a tool for busy web devs}
|
48
|
+
s.test_files = [
|
49
|
+
"test/action_test.rb",
|
50
|
+
"test/configuration_test.rb",
|
51
|
+
"test/customconfiguration_test.rb",
|
52
|
+
"test/paths_test.rb",
|
53
|
+
"test/test_helper.rb"
|
54
|
+
]
|
55
|
+
|
56
|
+
if s.respond_to? :specification_version then
|
57
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
58
|
+
s.specification_version = 3
|
59
|
+
|
60
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
61
|
+
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
62
|
+
s.add_runtime_dependency(%q<commandline>, [">= 0"])
|
63
|
+
s.add_runtime_dependency(%q<extensions>, [">= 0"])
|
64
|
+
s.add_runtime_dependency(%q<open4>, [">= 0"])
|
65
|
+
else
|
66
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
67
|
+
s.add_dependency(%q<commandline>, [">= 0"])
|
68
|
+
s.add_dependency(%q<extensions>, [">= 0"])
|
69
|
+
s.add_dependency(%q<open4>, [">= 0"])
|
70
|
+
end
|
71
|
+
else
|
72
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
73
|
+
s.add_dependency(%q<commandline>, [">= 0"])
|
74
|
+
s.add_dependency(%q<extensions>, [">= 0"])
|
75
|
+
s.add_dependency(%q<open4>, [">= 0"])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
metadata
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: watchful
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: true
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- pre1
|
10
|
+
version: 0.0.0.pre1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- KEM
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-04-22 00:00:00 -05:00
|
19
|
+
default_executable: watchful
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: thoughtbot-shoulda
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :development
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: commandline
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
segments:
|
41
|
+
- 0
|
42
|
+
version: "0"
|
43
|
+
type: :runtime
|
44
|
+
version_requirements: *id002
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: extensions
|
47
|
+
prerelease: false
|
48
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
segments:
|
53
|
+
- 0
|
54
|
+
version: "0"
|
55
|
+
type: :runtime
|
56
|
+
version_requirements: *id003
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: open4
|
59
|
+
prerelease: false
|
60
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
version: "0"
|
67
|
+
type: :runtime
|
68
|
+
version_requirements: *id004
|
69
|
+
description: Applies intermediary tools to modified files
|
70
|
+
email: kyleevanmitchell@gmail.com
|
71
|
+
executables:
|
72
|
+
- watchful
|
73
|
+
extensions: []
|
74
|
+
|
75
|
+
extra_rdoc_files:
|
76
|
+
- LICENSE
|
77
|
+
- README.md
|
78
|
+
files:
|
79
|
+
- .gitignore
|
80
|
+
- LICENSE
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- VERSION
|
84
|
+
- bin/watchful
|
85
|
+
- doc/DSL-Specification.rb
|
86
|
+
- lib/watchful.rb
|
87
|
+
- lib/watchful/action.rb
|
88
|
+
- lib/watchful/configuration.rb
|
89
|
+
- lib/watchful/defaultconfiguration.rb
|
90
|
+
- lib/watchful/paths.rb
|
91
|
+
- lib/watchful/watch.rb
|
92
|
+
- test/action_test.rb
|
93
|
+
- test/configuration_test.rb
|
94
|
+
- test/customconfiguration_test.rb
|
95
|
+
- test/paths_test.rb
|
96
|
+
- test/test_helper.rb
|
97
|
+
- watchful.gemspec
|
98
|
+
has_rdoc: true
|
99
|
+
homepage: http://github.com/kemitchell/watchful
|
100
|
+
licenses: []
|
101
|
+
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options:
|
104
|
+
- --charset=UTF-8
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
segments:
|
112
|
+
- 0
|
113
|
+
version: "0"
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">"
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
segments:
|
119
|
+
- 1
|
120
|
+
- 3
|
121
|
+
- 1
|
122
|
+
version: 1.3.1
|
123
|
+
requirements: []
|
124
|
+
|
125
|
+
rubyforge_project: watchful
|
126
|
+
rubygems_version: 1.3.6
|
127
|
+
signing_key:
|
128
|
+
specification_version: 3
|
129
|
+
summary: Watchful, a tool for busy web devs
|
130
|
+
test_files:
|
131
|
+
- test/action_test.rb
|
132
|
+
- test/configuration_test.rb
|
133
|
+
- test/customconfiguration_test.rb
|
134
|
+
- test/paths_test.rb
|
135
|
+
- test/test_helper.rb
|