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.
@@ -1,3 +1,4 @@
1
+ require 'rubygems'
1
2
  require 'extensions/string'
2
3
 
3
4
  module Watchful
@@ -12,15 +12,6 @@ module Watchful
12
12
  end
13
13
  end
14
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
15
  CONFIGURATION_FILE_NAME = 'watchful'
25
16
 
26
17
  def self.load_configuration(path, use_default = false)
@@ -32,49 +23,100 @@ module Watchful
32
23
  user = File.expand_path("~/#{CONFIGURATION_FILE_NAME}")
33
24
 
34
25
  if File.exists? local
35
- debug "Loading #{local}"
26
+ puts "=> Loading configuration #{local}"
36
27
  load local
37
28
  elsif File.exists? user
38
- debug "Loading #{user}"
29
+ puts "=> Loading configuration #{user}"
39
30
  load user
40
31
  end
41
32
 
42
33
  end
43
34
 
35
+ # If a tool returns an error code when running, don’t run the tool on the
36
+ # file again until the source file has been saved again. Otherwise the error
37
+ # will be repeated every time the errant source file is traversed.
44
38
  @files_with_errors = Hash.new # {:path => '', :mtime => ### }
45
39
 
46
- def self.watch_files(path)
47
- @output_file_extensions = Configuration.active_configuration.actions.collect do |a|
48
- a.out
40
+ def self.watch_files(path, force_polling = false)
41
+
42
+ if force_polling
43
+ use_fsevents = false
44
+ elsif RUBY_PLATFORM.include?('darwin') # todo: better check for OS fsevents support
45
+ begin
46
+ require 'fsevents'
47
+ use_fsevents = true
48
+ rescue LoadError
49
+ use_fsevents = false
50
+ puts
51
+ puts "*****************************************************************"
52
+ puts "* Using fsevents significantly improves Watchful’s performance. *"
53
+ puts "* *"
54
+ puts "* Install it with: *"
55
+ puts "* *"
56
+ puts "* sudo gem install fsevents *"
57
+ puts "* *"
58
+ puts "*****************************************************************"
59
+ puts
60
+ end
49
61
  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)
62
+
63
+ puts "... Watching #{path} ..."
64
+
65
+ if use_fsevents # OS X filesystem events API
66
+
67
+ begin
68
+
69
+ fsevents_stream = FSEvents::Stream.watch(path) do |events|
70
+ events.modified_files.each do |file|
71
+ action = Configuration.active_configuration.action_for_file(file)
72
+ do_action(file, action) unless action.nil?
73
+ end
74
+ end
75
+ fsevents_stream.run
76
+
77
+ rescue Interrupt
78
+ fsevents_stream.stop
79
+ end
80
+
81
+ else # stat polling
82
+
83
+ begin
84
+ loop do
85
+ check_for_interrupts
86
+ each_source_file_in_dir(path) do |file_path|
87
+
88
+ extension = compound_extension_of(file_path)
89
+
90
+ action = Configuration.active_configuration.action_for_file(file_path)
54
91
 
55
- action = Configuration.active_configuration.actions.find { |a| a.input_file? file_path}
92
+ next if action.nil?
56
93
 
57
- next if action.nil?
94
+ output_path = action.output_path_for(file_path)
58
95
 
59
- output_path = action.output_path_for(file_path)
96
+ if (!File.exists?(output_path)) or younger_than(file_path, output_path)
97
+ error_mtime = @files_with_errors[file_path]
98
+ next if error_mtime && error_mtime >= File.stat(file_path).mtime
99
+ do_action(file_path, action)
100
+ end
60
101
 
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)
102
+ end
65
103
  end
66
-
104
+ rescue Interrupt
105
+ # pass
67
106
  end
107
+
68
108
  end
109
+
69
110
  end
70
111
 
112
+ # apply an action to a change file and post notifications
71
113
  def self.do_action(source_file, action)
72
114
 
73
- puts "#{action.name}: #{source_file}"
115
+ puts "! #{action.name}: #{source_file}"
74
116
 
75
117
  command = action.command_string(source_file)
76
118
 
77
- debug "\t#{command}"
119
+ puts "!\t#{command}"
78
120
 
79
121
  proc_pid, proc_stdin, proc_stdout, proc_stderr = Open4::popen4(command)
80
122
  proc_ignored, proc_status = Process::waitpid2(proc_pid)
@@ -0,0 +1,68 @@
1
+ require "test/unit"
2
+
3
+ require "watchful/action"
4
+
5
+ class TestWatchfulAction < Test::Unit::TestCase
6
+
7
+ def test_construction
8
+ assert_equal Watchful::Action.new(:name => 'test', :out => '.out').enabled?,
9
+ false,
10
+ 'Disable new action w/o @in'
11
+ assert_equal Watchful::Action.new(:name => 'test', :in => '.in').enabled?,
12
+ false,
13
+ 'Disable new action w/o @out'
14
+ assert_equal Watchful::Action.new(:name => 'test', :in => '.in', :out => '.out').enabled?,
15
+ false,
16
+ 'Disable new action w/o @command'
17
+ assert_equal Watchful::Action.new(
18
+ :name => 'test', :in => '.in', :out => '.out',
19
+ :command => 'blargh %s %s', :dependencies => ['blargh']
20
+ ).enabled?,
21
+ false,
22
+ 'Disable new action w/ missing dependencies'
23
+ assert Watchful::Action.new(
24
+ :name => 'a', :in => ".in", :out => ".out", :command => "cat %s > %s"
25
+ ),
26
+ 'Successful creation'
27
+ assert_raise ArgumentError do
28
+ Watchful::Action.new :dependencies => Hash.new
29
+ end
30
+ assert Watchful::Action.new(
31
+ :name => 'a', :in => ".in", :out => ".out", :command => "cat %s > %s"
32
+ ).enabled?,
33
+ 'Complete action is enabled'
34
+ assert Watchful::Action.new(
35
+ :name => 'a', :in => ".in", :out => ".out"
36
+ ).enabled? == false,
37
+ 'Incomplete action is disabled'
38
+ assert Watchful::Action.new(
39
+ :name => 'a', :in => ".in", :command => "cat %s > %s"
40
+ ).enabled? == false,
41
+ 'Incomplete action is disabled'
42
+ end
43
+
44
+ def test_have_command
45
+ assert Watchful::Action.have_command? 'cat'
46
+ assert Watchful::Action.have_command? 'echo'
47
+ assert Watchful::Action.have_command? 'less'
48
+ assert_equal (Watchful::Action.have_command? 'asdfjkasdf'), false
49
+ end
50
+
51
+ def test_paths_computations
52
+ a = Watchful::Action.new :name => "Test Action",
53
+ :in => ".in",
54
+ :out => ".out",
55
+ :command => "cat %s > %s",
56
+ :dependencies => ['cat']
57
+ assert a.input_file?('/test.in')
58
+ assert_equal a.input_file?('/test.blah'), false
59
+ assert a.output_file?('/test.out')
60
+ assert_equal a.output_file?('/test.blah'), false
61
+ assert_equal a.output_path_for('/tmp/test.in'), '/tmp/test.out'
62
+ assert_equal a.command_string('/tmp/test.in'), 'cat /tmp/test.in > /tmp/test.out'
63
+ assert_raise ArgumentError do
64
+ a.output_path_for('/test.someother')
65
+ end
66
+ end
67
+
68
+ end
@@ -0,0 +1,39 @@
1
+ require "test/unit"
2
+
3
+ require "watchful/configuration"
4
+
5
+ class TestWatchfulConfiguration < Test::Unit::TestCase
6
+
7
+ class FirstConf < Watchful::Configuration; action :name => 'Placeholder Action'; end
8
+
9
+ def test_action_added
10
+ assert FirstConf.actions.length == 1
11
+ end
12
+
13
+ DESC = 'This is a test'
14
+
15
+ class SecondTestConfiguration < Watchful::Configuration; description DESC; end
16
+
17
+ def test_description
18
+ assert SecondTestConfiguration._description == DESC
19
+ end
20
+
21
+ class ThirdConfig < Watchful::Configuration
22
+ action :key => "First"
23
+ action :name => "Second"
24
+ end
25
+
26
+ def test_multiple_actions
27
+ assert ThirdConfig.actions.length == 2
28
+ end
29
+
30
+ class FourthConfig < Watchful::Configuration
31
+ action :name => 'One', :in => ".one", :out => ".one.out", :command => "cat %s > %s"
32
+ action :name => 'Two', :in => ".two", :out => ".two.out", :command => "cat %s > %s"
33
+ end
34
+
35
+ def test_action_for_file
36
+ assert FourthConfig.action_for_file('test.two')
37
+ end
38
+
39
+ end
@@ -0,0 +1,48 @@
1
+ require "test/unit"
2
+
3
+ require "watchful/paths"
4
+
5
+ require "fileutils"
6
+
7
+ class TestWatchfulPaths < Test::Unit::TestCase
8
+
9
+ def setup
10
+ @testfiles = ['/tmp/a', '/tmp/b']
11
+ @testfiles.each do |f|
12
+ FileUtils.touch f
13
+ end
14
+ end
15
+
16
+ def test_younger_than
17
+
18
+ sleep 1
19
+ FileUtils.touch @testfiles[0]
20
+ assert Watchful.younger_than(@testfiles[0], @testfiles[1]), 'Compares mtimes'
21
+
22
+ sleep 1
23
+ FileUtils.touch @testfiles[1]
24
+ assert Watchful.younger_than(@testfiles[1], @testfiles[0]), 'Compares mtimes'
25
+
26
+ assert_raises Exception do
27
+ Watchful.younger_than(@testfiles[1], '/asdfasdf/asdfasdf')
28
+ end
29
+
30
+ assert_raises Exception do
31
+ Watchful.younger_than('/asdfasdf/asdfasdf', @testfiles[1])
32
+ end
33
+
34
+ end
35
+
36
+ def test_compound_extension_of
37
+
38
+ assert_equal Watchful.compound_extension_of('test.min.css'), '.min.css', 'Compound extension'
39
+
40
+ assert_equal Watchful.compound_extension_of('test.css'), '.css', 'Simple extension'
41
+
42
+ assert_nil Watchful.compound_extension_of('test'), 'File with no extension'
43
+
44
+ assert_nil Watchful.compound_extension_of('.test'), '.profile type'
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,81 @@
1
+ require "test/unit"
2
+
3
+ require "watchful/watch"
4
+
5
+ require 'fileutils'
6
+
7
+ class TestWatchfulWatch < Test::Unit::TestCase
8
+
9
+ TMPDIR = '/tmp/watchfultest'
10
+
11
+ def test_each_source_file_in_dir
12
+
13
+ FileUtils.rm_r(TMPDIR) if File.directory?(TMPDIR)
14
+
15
+ Dir.mkdir(TMPDIR)
16
+
17
+ FileUtils.touch("#{TMPDIR}/a.txt")
18
+ FileUtils.touch("#{TMPDIR}/b.txt")
19
+ Dir.mkdir("#{TMPDIR}/subdir")
20
+
21
+ found_files = []
22
+
23
+ Watchful::each_source_file_in_dir(TMPDIR) { |f| found_files << f }
24
+
25
+ assert found_files.include? "#{TMPDIR}/a.txt"
26
+ assert found_files.include? "#{TMPDIR}/b.txt"
27
+ assert_equal found_files.length, 2
28
+
29
+ FileUtils.rm_r(TMPDIR)
30
+
31
+ end
32
+
33
+ USER_CONFIG = File.expand_path("~/#{Watchful::CONFIGURATION_FILE_NAME}")
34
+ USER_CONFIG_BACKUP = File.expand_path("~/#{Watchful::CONFIGURATION_FILE_NAME}.backup")
35
+
36
+ def test_load_configuration
37
+
38
+ # preserve existing user config file
39
+ replace_config = File.exists?(USER_CONFIG)
40
+ FileUtils.mv(USER_CONFIG, USER_CONFIG_BACKUP) if replace_config
41
+
42
+ FileUtils.rm_r(TMPDIR) if File.directory?(TMPDIR)
43
+ Dir.mkdir(TMPDIR)
44
+
45
+ Watchful::load_configuration(TMPDIR, false)
46
+ assert_equal Watchful::Configuration.active_configuration, Watchful::DefaultConfiguration
47
+
48
+
49
+ File.open("#{TMPDIR}/#{Watchful::CONFIGURATION_FILE_NAME}", 'w') do |f|
50
+ f.write <<-eos
51
+ class Conf < Watchful::Configuration
52
+ description 'the key'
53
+ action :name => 'test action'
54
+ end
55
+ eos
56
+ end
57
+ Watchful::load_configuration(TMPDIR, false)
58
+ assert_equal Watchful::Configuration.active_configuration._description, 'the key'
59
+ FileUtils.rm("#{TMPDIR}/#{Watchful::CONFIGURATION_FILE_NAME}")
60
+
61
+
62
+ File.open(USER_CONFIG, 'w') do |f|
63
+ f.write <<-eos
64
+ class Conf < Watchful::Configuration
65
+ description 'USER CONFIG'
66
+ action :name => 'an action'
67
+ end
68
+ eos
69
+ end
70
+ Watchful::load_configuration(TMPDIR, false)
71
+ assert_equal Watchful::Configuration.active_configuration._description, 'USER CONFIG'
72
+ FileUtils.rm(USER_CONFIG)
73
+
74
+ FileUtils.rm_r(TMPDIR)
75
+ FileUtils.mv(USER_CONFIG_BACKUP, USER_CONFIG) if replace_config
76
+
77
+ end
78
+
79
+ # todo: find a way to temporarily disable gems while running unit tests
80
+
81
+ end
metadata CHANGED
@@ -1,25 +1,25 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: watchful
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: true
4
+ prerelease: false
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 0
9
- - pre1
10
- version: 0.0.0.pre1
8
+ - 1
9
+ - 1
10
+ version: 0.0.1.1
11
11
  platform: ruby
12
12
  authors:
13
- - KEM
13
+ - K E Mitchell
14
14
  autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-04-22 00:00:00 -05:00
18
+ date: 2010-04-23 00:00:00 -05:00
19
19
  default_executable: watchful
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- name: thoughtbot-shoulda
22
+ name: commandline
23
23
  prerelease: false
24
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
25
  requirements:
@@ -27,11 +27,13 @@ dependencies:
27
27
  - !ruby/object:Gem::Version
28
28
  segments:
29
29
  - 0
30
- version: "0"
31
- type: :development
30
+ - 7
31
+ - 10
32
+ version: 0.7.10
33
+ type: :runtime
32
34
  version_requirements: *id001
33
35
  - !ruby/object:Gem::Dependency
34
- name: commandline
36
+ name: extensions
35
37
  prerelease: false
36
38
  requirement: &id002 !ruby/object:Gem::Requirement
37
39
  requirements:
@@ -39,33 +41,25 @@ dependencies:
39
41
  - !ruby/object:Gem::Version
40
42
  segments:
41
43
  - 0
42
- version: "0"
44
+ - 6
45
+ - 0
46
+ version: 0.6.0
43
47
  type: :runtime
44
48
  version_requirements: *id002
45
49
  - !ruby/object:Gem::Dependency
46
- name: extensions
50
+ name: open4
47
51
  prerelease: false
48
52
  requirement: &id003 !ruby/object:Gem::Requirement
49
53
  requirements:
50
54
  - - ">="
51
55
  - !ruby/object:Gem::Version
52
56
  segments:
57
+ - 1
53
58
  - 0
54
- version: "0"
59
+ - 1
60
+ version: 1.0.1
55
61
  type: :runtime
56
62
  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
63
  description: Applies intermediary tools to modified files
70
64
  email: kyleevanmitchell@gmail.com
71
65
  executables:
@@ -76,25 +70,23 @@ extra_rdoc_files:
76
70
  - LICENSE
77
71
  - README.md
78
72
  files:
73
+ - .autotest
79
74
  - .gitignore
80
75
  - LICENSE
81
76
  - README.md
82
77
  - Rakefile
83
78
  - VERSION
84
79
  - bin/watchful
85
- - doc/DSL-Specification.rb
86
80
  - lib/watchful.rb
87
81
  - lib/watchful/action.rb
88
82
  - lib/watchful/configuration.rb
89
83
  - lib/watchful/defaultconfiguration.rb
90
84
  - lib/watchful/paths.rb
91
85
  - 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
86
+ - test/unit/action_test.rb
87
+ - test/unit/configuration_test.rb
88
+ - test/unit/paths_test.rb
89
+ - test/unit/watch_test.rb
98
90
  has_rdoc: true
99
91
  homepage: http://github.com/kemitchell/watchful
100
92
  licenses: []
@@ -113,13 +105,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
113
105
  version: "0"
114
106
  required_rubygems_version: !ruby/object:Gem::Requirement
115
107
  requirements:
116
- - - ">"
108
+ - - ">="
117
109
  - !ruby/object:Gem::Version
118
110
  segments:
119
- - 1
120
- - 3
121
- - 1
122
- version: 1.3.1
111
+ - 0
112
+ version: "0"
123
113
  requirements: []
124
114
 
125
115
  rubyforge_project: watchful
@@ -128,8 +118,7 @@ signing_key:
128
118
  specification_version: 3
129
119
  summary: Watchful, a tool for busy web devs
130
120
  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
121
+ - test/unit/action_test.rb
122
+ - test/unit/configuration_test.rb
123
+ - test/unit/paths_test.rb
124
+ - test/unit/watch_test.rb