duck_test 0.1.4
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/bin/ducktest +29 -0
- data/lib/duck_test/autoload_config.rb +103 -0
- data/lib/duck_test/base.rb +41 -0
- data/lib/duck_test/commands.rb +208 -0
- data/lib/duck_test/config.rb +675 -0
- data/lib/duck_test/config_helper.rb +99 -0
- data/lib/duck_test/console.rb +18 -0
- data/lib/duck_test/default_config.rb +48 -0
- data/lib/duck_test/frame_work/base.rb +587 -0
- data/lib/duck_test/frame_work/file_manager.rb +511 -0
- data/lib/duck_test/frame_work/filter_set.rb +233 -0
- data/lib/duck_test/frame_work/map.rb +331 -0
- data/lib/duck_test/frame_work/queue.rb +221 -0
- data/lib/duck_test/frame_work/queue_event.rb +29 -0
- data/lib/duck_test/frame_work/rspec/base.rb +17 -0
- data/lib/duck_test/frame_work/rspec/frame_work.rb +30 -0
- data/lib/duck_test/frame_work/test_unit/base.rb +14 -0
- data/lib/duck_test/frame_work/test_unit/frame_work.rb +33 -0
- data/lib/duck_test/frame_work/watch_config.rb +69 -0
- data/lib/duck_test/gem/helper.rb +107 -0
- data/lib/duck_test/logger.rb +127 -0
- data/lib/duck_test/option_parser.rb +30 -0
- data/lib/duck_test/platforms/base.rb +18 -0
- data/lib/duck_test/platforms/dependencies.rb +18 -0
- data/lib/duck_test/platforms/generic/base.rb +15 -0
- data/lib/duck_test/platforms/generic/listener.rb +104 -0
- data/lib/duck_test/platforms/linux/base.rb +15 -0
- data/lib/duck_test/platforms/linux/listener.rb +76 -0
- data/lib/duck_test/platforms/listener.rb +303 -0
- data/lib/duck_test/platforms/mac/base.rb +15 -0
- data/lib/duck_test/platforms/mac/listener.rb +79 -0
- data/lib/duck_test/platforms/mac/listener.rb.orig +147 -0
- data/lib/duck_test/platforms/os_helper.rb +102 -0
- data/lib/duck_test/platforms/watch_event.rb +47 -0
- data/lib/duck_test/platforms/windows/base.rb +15 -0
- data/lib/duck_test/platforms/windows/listener.rb +123 -0
- data/lib/duck_test/railtie.rb +29 -0
- data/lib/duck_test/usage.rb +34 -0
- data/lib/duck_test/usage.yml +112 -0
- data/lib/duck_test/version.rb +3 -0
- data/lib/duck_test.rb +6 -0
- data/lib/notes.txt +215 -0
- data/lib/tasks/duck_tests.rake +35 -0
- data/lib/tasks/gem_tasks.rake +18 -0
- metadata +92 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
module DuckTest
|
2
|
+
# ...
|
3
|
+
module Platforms
|
4
|
+
|
5
|
+
autoload :Generic, 'duck_test/platforms/generic/base'
|
6
|
+
autoload :Linux, 'duck_test/platforms/linux/base'
|
7
|
+
autoload :Listener, 'duck_test/platforms/listener'
|
8
|
+
autoload :Mac, 'duck_test/platforms/mac/base'
|
9
|
+
autoload :OSHelper, 'duck_test/platforms/os_helper'
|
10
|
+
autoload :WatchEvent, 'duck_test/platforms/watch_event'
|
11
|
+
autoload :Windows, 'duck_test/platforms/windows/base'
|
12
|
+
|
13
|
+
# ...
|
14
|
+
module Base
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# this file is loaded via the gem and it's purpose is to load a platform specific gem that provides file watching functionality.
|
2
|
+
# I chose to put this in a separate file in the event that I add more platforms or if more logic is needed making the code a bit lengthy.
|
3
|
+
begin
|
4
|
+
|
5
|
+
if DuckTest::Platforms::OSHelper.is_linux?
|
6
|
+
require "rb-inotify"
|
7
|
+
|
8
|
+
elsif DuckTest::Platforms::OSHelper.is_mac?
|
9
|
+
require "rb-fsevent"
|
10
|
+
|
11
|
+
elsif DuckTest::Platforms::OSHelper.is_windows?
|
12
|
+
require "rb-fchange"
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
rescue Exception => e
|
17
|
+
puts e
|
18
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module DuckTest
|
2
|
+
module Platforms
|
3
|
+
module Generic
|
4
|
+
|
5
|
+
##################################################################################
|
6
|
+
# A generic listener to watch for changed, deleted, and new files on a file system.
|
7
|
+
class Listener
|
8
|
+
include DuckTest::LoggerHelper
|
9
|
+
include DuckTest::Platforms::Listener
|
10
|
+
include DuckTest::Platforms::OSHelpers
|
11
|
+
|
12
|
+
attr_accessor :thread
|
13
|
+
|
14
|
+
##################################################################################
|
15
|
+
def initialize
|
16
|
+
super
|
17
|
+
|
18
|
+
ducklog.console "Platform listener: Generic"
|
19
|
+
|
20
|
+
# i plan to implement feature to allow developer to specify which
|
21
|
+
# listener to use, so, simply notify developer how to enable native listener
|
22
|
+
# if it is not available.
|
23
|
+
if self.is_linux? && !self.available?
|
24
|
+
ducklog.console "########################################################################"
|
25
|
+
ducklog.console "NOTE: Native file listener is NOT enabled."
|
26
|
+
ducklog.console "To enable native file listener:"
|
27
|
+
ducklog.console "Edit your Gemfile and add the following to your test group"
|
28
|
+
ducklog.console "gem 'rb-inotify'"
|
29
|
+
ducklog.console "########################################################################"
|
30
|
+
|
31
|
+
elsif self.is_mac? && !self.available?
|
32
|
+
ducklog.console "########################################################################"
|
33
|
+
ducklog.console "NOTE: Native file listener is NOT enabled."
|
34
|
+
ducklog.console "To enable native file listener:"
|
35
|
+
ducklog.console "Edit your Gemfile and add the following to your test group"
|
36
|
+
ducklog.console "gem 'rb-fsevent'"
|
37
|
+
ducklog.console "########################################################################"
|
38
|
+
|
39
|
+
|
40
|
+
elsif self.is_windows? && !self.available?
|
41
|
+
ducklog.console "########################################################################"
|
42
|
+
ducklog.console "NOTE: Native file listener is NOT enabled."
|
43
|
+
ducklog.console "To enable native file listener:"
|
44
|
+
ducklog.console "Edit your Gemfile and add the following to your test group"
|
45
|
+
ducklog.console "gem 'rb-fchange'"
|
46
|
+
ducklog.console "########################################################################"
|
47
|
+
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
##################################################################################
|
54
|
+
# Starts a thread and listens for changes to all of the directories / files added to the listener
|
55
|
+
# via {DuckTest::Platforms::Listener#watch}
|
56
|
+
# @return [NilClass]
|
57
|
+
def start
|
58
|
+
|
59
|
+
# call super to trap control-C
|
60
|
+
super
|
61
|
+
|
62
|
+
# call refresh once prior to starting the loop so that all of the attributes
|
63
|
+
# for all directories / files get updated.
|
64
|
+
self.refresh
|
65
|
+
|
66
|
+
# now, start the thread
|
67
|
+
self.thread = Thread.new do
|
68
|
+
|
69
|
+
until self.stop do
|
70
|
+
|
71
|
+
sleep(self.speed)
|
72
|
+
|
73
|
+
# grab a list of all changed and new files
|
74
|
+
changed_files = self.refresh
|
75
|
+
|
76
|
+
# now, update all of the attributes for all directories / files so the
|
77
|
+
# next interation of the loop will not return invalid results.
|
78
|
+
update_all
|
79
|
+
|
80
|
+
# call the event listener block for all of the changed / new files
|
81
|
+
changed_files.each do |item|
|
82
|
+
self.call_listener_event(WatchEvent.new(self, item, :update, nil))
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
return nil
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module DuckTest
|
2
|
+
module Platforms
|
3
|
+
module Linux
|
4
|
+
|
5
|
+
##################################################################################
|
6
|
+
# Listener that wraps the native file system notifier for Linux.
|
7
|
+
class Listener
|
8
|
+
include DuckTest::LoggerHelper
|
9
|
+
include DuckTest::Platforms::Listener
|
10
|
+
|
11
|
+
attr_accessor :thread
|
12
|
+
attr_accessor :mechanism
|
13
|
+
|
14
|
+
##################################################################################
|
15
|
+
def initialize
|
16
|
+
super()
|
17
|
+
self.mechanism = INotify::Notifier.new
|
18
|
+
end
|
19
|
+
|
20
|
+
##################################################################################
|
21
|
+
def self.available?
|
22
|
+
return defined?(INotify::Notifier)
|
23
|
+
end
|
24
|
+
|
25
|
+
##################################################################################
|
26
|
+
def start
|
27
|
+
|
28
|
+
self.thread = Thread.new do
|
29
|
+
until self.stop do
|
30
|
+
self.mechanism.process
|
31
|
+
sleep(self.speed)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
##################################################################################
|
38
|
+
def watch(file_spec)
|
39
|
+
|
40
|
+
self.mechanism.watch file_spec, :close_write, :moved_from, :moved_to, :create, :delete, :delete_self do |event|
|
41
|
+
|
42
|
+
value = :unknown
|
43
|
+
|
44
|
+
event.flags.each do |flag|
|
45
|
+
value = flag == :close_write ? :update : value
|
46
|
+
value = flag == :create ? :create : value
|
47
|
+
value = flag == :delete || flag == :delete_self ? :destroy : value
|
48
|
+
value = flag == :moved_from || flag == :moved_from ? :move : value
|
49
|
+
break unless value == :unknown
|
50
|
+
end
|
51
|
+
|
52
|
+
unless value == :unknown
|
53
|
+
self.call_listener_event(WatchEvent.new(self, event.absolute_name, value, event))
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
@@ -0,0 +1,303 @@
|
|
1
|
+
module DuckTest
|
2
|
+
module Platforms
|
3
|
+
|
4
|
+
##################################################################################
|
5
|
+
# Data and methods for implementing a listener. Take a look at {Generic::Listener} for an example
|
6
|
+
# of how to implement a custom listener.
|
7
|
+
#
|
8
|
+
# Basically, {DuckTest::FrameWork::FileManager} will...
|
9
|
+
# - instantiate a listener object.
|
10
|
+
# - assign a listener_event block to call when a directory / file changes
|
11
|
+
# - call listener.watch(file_spec) for each directory / file that should be watched.
|
12
|
+
# - call the start method on the listener to begin the listening thread.
|
13
|
+
#
|
14
|
+
# At this point, it is the listeners responsibility to notify the file manager of changed files. {Generic::Listener}
|
15
|
+
# uses {#refresh} to interrogate the file system for deleted, changed, and new files and will return a list
|
16
|
+
# of changed and new files and call the event listener block for each.
|
17
|
+
#
|
18
|
+
module Listener
|
19
|
+
extend DuckTest::LoggerHelper
|
20
|
+
|
21
|
+
attr_accessor :block
|
22
|
+
|
23
|
+
##################################################################################
|
24
|
+
# Maintains an array of file spec that are directories ONLY. Directories are tracked via {#file_list}, however, this addiontal list
|
25
|
+
# is used for a couple of reasons.
|
26
|
+
# 1. It is an Array instead of a Hash making the logic to interrogate the directory structure for changed files a little faster
|
27
|
+
# and simpler.
|
28
|
+
# 2. Conveinent for listeners using a code base native to the operating system that tracks changes based on directories instead of individual files. rb-fsevent and rb-fchange are
|
29
|
+
# examples. Listeners of this type can simply call {#changed_files} to get a list of changed files for the directory in question.
|
30
|
+
# @return [Array] The current list of watched directories.
|
31
|
+
def dir_list
|
32
|
+
@dir_list ||= []
|
33
|
+
return @dir_list
|
34
|
+
end
|
35
|
+
|
36
|
+
##################################################################################
|
37
|
+
# Maintains a Hash of full file specs representing all directories / files being watched. File spec is a full path and file name
|
38
|
+
# that adheres to {http://ruby-doc.org/core-1.9.3/File.html#method-c-basename File.basename} and is used as a key to find all of the attributes
|
39
|
+
# associated with file spec.
|
40
|
+
#
|
41
|
+
# file_spec = "/home/my_home/test.com/test/unit"
|
42
|
+
# self.file_list[file_spec] # => {:mtime=>1327192086.4123762, :is_dir=>true}
|
43
|
+
#
|
44
|
+
# file_spec = "/home/my_home/test.com/test/unit/book_spec.rb"
|
45
|
+
# self.file_list[file_spec] # => {:mtime=>1327283977.2120826, :sha=>"da39a3ee5e6b4b0d3255bfef95601890afd80709"}
|
46
|
+
#
|
47
|
+
# FileUtils.touch "/home/my_home/test.com/test/unit/book_spec.rb"
|
48
|
+
# self.file_list[file_spec] # => {:mtime=>1327283977.2120826, :sha=>"da39a3ee5e6b4b0d3255bfef95601890afd80709", :changed => true}
|
49
|
+
#
|
50
|
+
# @return [Hash] The full Hash of file specs being watched.
|
51
|
+
def file_list
|
52
|
+
@file_list ||= {}
|
53
|
+
return @file_list
|
54
|
+
end
|
55
|
+
|
56
|
+
##################################################################################
|
57
|
+
# Sets the block that will be executed by the listener when a file event occurs.
|
58
|
+
# @param [Proc] block - The block to execute.
|
59
|
+
# @return [Proc]
|
60
|
+
def listener_event(&block)
|
61
|
+
self.block = block if block_given?
|
62
|
+
return self.block
|
63
|
+
end
|
64
|
+
|
65
|
+
##################################################################################
|
66
|
+
# Executes the block assigned via {Listener#listener_event}
|
67
|
+
# @param [WatchEvent] A {WatchEvent} object that will be passed to the block when executed.
|
68
|
+
# @return [Object] The value returned by the block.
|
69
|
+
def call_listener_event(event)
|
70
|
+
value = nil
|
71
|
+
|
72
|
+
begin
|
73
|
+
|
74
|
+
unless self.block.blank?
|
75
|
+
value = self.block.call event
|
76
|
+
end
|
77
|
+
|
78
|
+
rescue Exception => e
|
79
|
+
ducklog.exception e
|
80
|
+
end
|
81
|
+
|
82
|
+
return value
|
83
|
+
end
|
84
|
+
|
85
|
+
##################################################################################
|
86
|
+
# This method is intended to be overridden by the implementing class.
|
87
|
+
# @return [NilClass]
|
88
|
+
def start
|
89
|
+
Kernel.trap("INT") do
|
90
|
+
self.stop = true
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
##################################################################################
|
95
|
+
# The stop attribute is intended to be used by listeners to control a thread loop
|
96
|
+
# while listening for changes to directories / files.
|
97
|
+
# @return [Boolean] True if the listener should stop listening, otherwise, false.
|
98
|
+
def stop
|
99
|
+
@stop = false unless defined?(@stop)
|
100
|
+
return @stop
|
101
|
+
end
|
102
|
+
|
103
|
+
# Sets the stop attribute.
|
104
|
+
def stop=(value)
|
105
|
+
@stop = value
|
106
|
+
end
|
107
|
+
|
108
|
+
##################################################################################
|
109
|
+
# The speed value is intended for use within the thread loop that listens for changes to directories / files.
|
110
|
+
# @return [Number] The current value of speed.
|
111
|
+
def speed
|
112
|
+
@speed = 1 unless defined?(@speed)
|
113
|
+
return @speed
|
114
|
+
end
|
115
|
+
|
116
|
+
# Sets the speed attribute.
|
117
|
+
def speed=(value)
|
118
|
+
@speed = value
|
119
|
+
end
|
120
|
+
|
121
|
+
##################################################################################
|
122
|
+
# @note Be sure to call {#watch_file_spec} if you override this method.
|
123
|
+
# Instructs the listener to watch a file spec via a call to {#watch_file_spec}
|
124
|
+
# @param [String] file_spec See {#watch_file_spec}
|
125
|
+
# @return [NilClass]
|
126
|
+
def watch(file_spec)
|
127
|
+
watch_file_spec(file_spec)
|
128
|
+
end
|
129
|
+
|
130
|
+
##################################################################################
|
131
|
+
# Instructs the listener to watch a file spec. File should be a full path and can be a directory or file. The file spec is tracked internally
|
132
|
+
# and methods of this module can be used to interrogate the status and attributes of a file. mtime, sha, changed, etc.
|
133
|
+
# @param [String] file_spec A file name that adheres to {http://ruby-doc.org/core-1.9.3/File.html#method-c-basename File.basename}.
|
134
|
+
# @return [NilClass]
|
135
|
+
def watch_file_spec(file_spec)
|
136
|
+
update_file_spec(file_spec) unless self.watched?(file_spec)
|
137
|
+
return nil
|
138
|
+
end
|
139
|
+
|
140
|
+
##################################################################################
|
141
|
+
# Determines if a file_spec is being watched. File spec can be a directory or file and should included full path.
|
142
|
+
#
|
143
|
+
# file_spec = "/home/my_home/test.com/test/unit"
|
144
|
+
# watch(file_spec)
|
145
|
+
# puts watched?(file_spec) # => true
|
146
|
+
#
|
147
|
+
# file_spec = "/home/my_home/test.com/test/unit/book_spec.rb"
|
148
|
+
# watch(file_spec)
|
149
|
+
# puts watched?(file_spec) # => true
|
150
|
+
#
|
151
|
+
# file_spec = "/home/my_home/test.com/test/unit/bike_spec.rb"
|
152
|
+
# puts watched?(file_spec) # => false
|
153
|
+
#
|
154
|
+
# @param [String] file_spec A file name that adheres to {http://ruby-doc.org/core-1.9.3/File.html#method-c-basename File.basename}.
|
155
|
+
# @return [Boolean] Returns true if file_spec is being watched.
|
156
|
+
def watched?(file_spec)
|
157
|
+
return self.file_list[file_spec] ? true : false
|
158
|
+
end
|
159
|
+
|
160
|
+
##################################################################################
|
161
|
+
# Determines if a directory / file has changed.
|
162
|
+
#
|
163
|
+
# file_spec = "/home/my_home/test.com/test/unit/bike_spec.rb"
|
164
|
+
# watched?(file_spec) # => false should not be considered changed unless watched
|
165
|
+
#
|
166
|
+
# file_spec = "/home/my_home/test.com/test/unit/bike_spec.rb"
|
167
|
+
# watch(file_spec)
|
168
|
+
# watched?(file_spec) # => false not changed yet
|
169
|
+
#
|
170
|
+
# file_spec = "/home/my_home/test.com/test/unit/bike_spec.rb"
|
171
|
+
# watch(file_spec)
|
172
|
+
# watched?(file_spec) # => false not changed yet
|
173
|
+
# FileUtils.touch(file_spec)
|
174
|
+
# watched?(file_spec) # => true
|
175
|
+
#
|
176
|
+
# @return [Boolean] Returns true if file_spec has changed.
|
177
|
+
def changed?(file_spec)
|
178
|
+
value = false
|
179
|
+
|
180
|
+
# must have a valid file object.
|
181
|
+
file_object = self.file_list[file_spec]
|
182
|
+
if file_object
|
183
|
+
|
184
|
+
# no SHA for directories
|
185
|
+
if file_object[:is_dir]
|
186
|
+
value = File.mtime(file_spec).to_f > file_object[:mtime]
|
187
|
+
else
|
188
|
+
value = File.mtime(file_spec).to_f > file_object[:mtime] || !Digest::SHA1.file(file_spec).to_s.eql?(file_object[:sha])
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
return value
|
193
|
+
end
|
194
|
+
|
195
|
+
##################################################################################
|
196
|
+
# Updates all of the tracked attributes of a watched directory / file. This method is called by methods such as {#watch} and {#watch_file_spec} to obtain
|
197
|
+
# attributes about a directory / file and store them for later use.
|
198
|
+
#
|
199
|
+
# file_spec = "/home/my_home/test.com/test/unit"
|
200
|
+
# update_file_spec(file_spec)
|
201
|
+
# puts file_list(file_spec) # => {:mtime=>1327290092.5563293, :is_dir=>true}
|
202
|
+
#
|
203
|
+
# file_spec = "/home/my_home/test.com/test/unit/book_spec.rb"
|
204
|
+
# update_file_spec(file_spec)
|
205
|
+
# puts file_list(file_spec) # => {:mtime=>1327290031.8203268, :sha=>"da39a3ee5e6b4b0d3255bfef95601890afd80709"}
|
206
|
+
#
|
207
|
+
# @param [String] file_spec A file name that adheres to {http://ruby-doc.org/core-1.9.3/File.html#method-c-basename File.basename}.
|
208
|
+
# @return [NilClass]
|
209
|
+
def update_file_spec(file_spec)
|
210
|
+
buffer = {}
|
211
|
+
|
212
|
+
buffer[:mtime] = File.mtime(file_spec).to_f
|
213
|
+
|
214
|
+
if File.directory?(file_spec)
|
215
|
+
buffer[:is_dir] = true
|
216
|
+
self.dir_list.push(file_spec) unless self.dir_list.include?(file_spec)
|
217
|
+
else
|
218
|
+
# assume it is a file if not a directory
|
219
|
+
buffer[:sha] = Digest::SHA1.file(file_spec).to_s
|
220
|
+
end
|
221
|
+
|
222
|
+
self.file_list[file_spec] = buffer
|
223
|
+
|
224
|
+
return nil
|
225
|
+
end
|
226
|
+
|
227
|
+
##################################################################################
|
228
|
+
# Updates all of tracked attributes for all directories / files being watched.
|
229
|
+
# Since this method calls {#update_file_spec}, all of the existing attributes will be replaced, therefore,
|
230
|
+
# addiontal attributes such as :changed true/false will be wiped out.
|
231
|
+
# @return [NilClass]
|
232
|
+
def update_all
|
233
|
+
self.file_list.each do |item|
|
234
|
+
update_file_spec(item.first)
|
235
|
+
end
|
236
|
+
return nil
|
237
|
+
end
|
238
|
+
|
239
|
+
##################################################################################
|
240
|
+
# Traverses all watched directories and builds a list of all changed files. It does not include directories in the returned list.
|
241
|
+
# refresh loops thru all of the items in {#dir_list} and calls {#changed_files} for each of them.
|
242
|
+
# @return [Array] Returns changed files
|
243
|
+
def refresh
|
244
|
+
list = []
|
245
|
+
index = 0
|
246
|
+
|
247
|
+
# remove directories / files from the internal lists that no longer exist on disk.
|
248
|
+
self.file_list.each do |file_object|
|
249
|
+
unless File.exist?(file_object.first)
|
250
|
+
if file_object.last[:is_dir]
|
251
|
+
self.dir_list.delete(file_object.first)
|
252
|
+
end
|
253
|
+
self.file_list.delete(file_object.first)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
while index < self.dir_list.length
|
258
|
+
list.concat(self.changed_files(self.dir_list[index]))
|
259
|
+
index += 1
|
260
|
+
|
261
|
+
end
|
262
|
+
|
263
|
+
return list
|
264
|
+
end
|
265
|
+
|
266
|
+
##################################################################################
|
267
|
+
# Returns changed files within a single directory. This method is intended to be used by listeners that process a single directory as opposed
|
268
|
+
# to single files.
|
269
|
+
#
|
270
|
+
# what does this thing do?
|
271
|
+
# - get a list of all the directories / files within the requested directory specified by file_spec
|
272
|
+
# - loop thru each directory / file in the list
|
273
|
+
# - each directory / file is added to the watch list. this should cover directories / files that are added after the listener has been started.
|
274
|
+
# - directories are not added to the return list.
|
275
|
+
# - if the file being compared is not currently watched or if {#changed?} says it has been changed, then, the file spec is added to the return list.
|
276
|
+
#
|
277
|
+
# @param [String] file_spec A file spec that adheres to {http://ruby-doc.org/core-1.9.3/File.html#method-c-basename File.basename}, however,
|
278
|
+
# the actual file name should be excluded. ONLY the path should be passed.
|
279
|
+
# @return [Array] Returns changed files
|
280
|
+
def changed_files(file_spec)
|
281
|
+
list = []
|
282
|
+
|
283
|
+
file_list = Dir.glob(File.join(file_spec, "*"))
|
284
|
+
file_list.each do |file_spec|
|
285
|
+
|
286
|
+
currently_watched = self.watched?(file_spec)
|
287
|
+
# ensure the directory / file is being watched
|
288
|
+
# a new directory / file could have been added by the user
|
289
|
+
self.watch_file_spec(file_spec)
|
290
|
+
|
291
|
+
unless File.directory?(file_spec)
|
292
|
+
if !currently_watched || self.changed?(file_spec)
|
293
|
+
list.push(file_spec)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
return list
|
299
|
+
end
|
300
|
+
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
3
|
+
module DuckTest
|
4
|
+
module Platforms
|
5
|
+
module Mac
|
6
|
+
|
7
|
+
##################################################################################
|
8
|
+
# Listener that wraps the native file system notifier for the Mac.
|
9
|
+
class Listener
|
10
|
+
include DuckTest::LoggerHelper
|
11
|
+
include DuckTest::Platforms::Listener
|
12
|
+
|
13
|
+
attr_accessor :thread
|
14
|
+
attr_accessor :mechanism
|
15
|
+
|
16
|
+
##################################################################################
|
17
|
+
def initialize
|
18
|
+
super()
|
19
|
+
ducklog.console "Platform listener: Mac"
|
20
|
+
end
|
21
|
+
|
22
|
+
##################################################################################
|
23
|
+
def self.available?
|
24
|
+
return defined?(FSEvent)
|
25
|
+
end
|
26
|
+
|
27
|
+
##################################################################################
|
28
|
+
def start
|
29
|
+
|
30
|
+
self.thread = Thread.new do
|
31
|
+
|
32
|
+
self.mechanism = FSEvent.new
|
33
|
+
|
34
|
+
self.mechanism.watch self.dir_list do |dir|
|
35
|
+
if dir.kind_of?(Array) && dir.length > 0
|
36
|
+
dir_spec = dir.first.to_s
|
37
|
+
# couldn't make this work using File::SEPARATOR
|
38
|
+
# look at it again later
|
39
|
+
if dir_spec =~ /\/$/
|
40
|
+
dir_spec = dir_spec.slice(0, dir_spec.length - 1)
|
41
|
+
end
|
42
|
+
|
43
|
+
changed_files = self.changed_files(dir_spec)
|
44
|
+
update_all
|
45
|
+
changed_files.each do |item|
|
46
|
+
self.call_listener_event(WatchEvent.new(self, item, :update, nil))
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
until self.stop do
|
53
|
+
|
54
|
+
self.mechanism.run
|
55
|
+
sleep(self.speed)
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
|