watchful 0.0.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|