watchful 0.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .DS_Store
2
+ pkg/*
3
+ pkg
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
@@ -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
@@ -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
@@ -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