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.
@@ -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