watchful 0.0.0.pre1 → 0.0.1.1

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