watchful 0.0.0.pre1 → 0.0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/.autotest 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