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.
Files changed (45) hide show
  1. data/bin/ducktest +29 -0
  2. data/lib/duck_test/autoload_config.rb +103 -0
  3. data/lib/duck_test/base.rb +41 -0
  4. data/lib/duck_test/commands.rb +208 -0
  5. data/lib/duck_test/config.rb +675 -0
  6. data/lib/duck_test/config_helper.rb +99 -0
  7. data/lib/duck_test/console.rb +18 -0
  8. data/lib/duck_test/default_config.rb +48 -0
  9. data/lib/duck_test/frame_work/base.rb +587 -0
  10. data/lib/duck_test/frame_work/file_manager.rb +511 -0
  11. data/lib/duck_test/frame_work/filter_set.rb +233 -0
  12. data/lib/duck_test/frame_work/map.rb +331 -0
  13. data/lib/duck_test/frame_work/queue.rb +221 -0
  14. data/lib/duck_test/frame_work/queue_event.rb +29 -0
  15. data/lib/duck_test/frame_work/rspec/base.rb +17 -0
  16. data/lib/duck_test/frame_work/rspec/frame_work.rb +30 -0
  17. data/lib/duck_test/frame_work/test_unit/base.rb +14 -0
  18. data/lib/duck_test/frame_work/test_unit/frame_work.rb +33 -0
  19. data/lib/duck_test/frame_work/watch_config.rb +69 -0
  20. data/lib/duck_test/gem/helper.rb +107 -0
  21. data/lib/duck_test/logger.rb +127 -0
  22. data/lib/duck_test/option_parser.rb +30 -0
  23. data/lib/duck_test/platforms/base.rb +18 -0
  24. data/lib/duck_test/platforms/dependencies.rb +18 -0
  25. data/lib/duck_test/platforms/generic/base.rb +15 -0
  26. data/lib/duck_test/platforms/generic/listener.rb +104 -0
  27. data/lib/duck_test/platforms/linux/base.rb +15 -0
  28. data/lib/duck_test/platforms/linux/listener.rb +76 -0
  29. data/lib/duck_test/platforms/listener.rb +303 -0
  30. data/lib/duck_test/platforms/mac/base.rb +15 -0
  31. data/lib/duck_test/platforms/mac/listener.rb +79 -0
  32. data/lib/duck_test/platforms/mac/listener.rb.orig +147 -0
  33. data/lib/duck_test/platforms/os_helper.rb +102 -0
  34. data/lib/duck_test/platforms/watch_event.rb +47 -0
  35. data/lib/duck_test/platforms/windows/base.rb +15 -0
  36. data/lib/duck_test/platforms/windows/listener.rb +123 -0
  37. data/lib/duck_test/railtie.rb +29 -0
  38. data/lib/duck_test/usage.rb +34 -0
  39. data/lib/duck_test/usage.yml +112 -0
  40. data/lib/duck_test/version.rb +3 -0
  41. data/lib/duck_test.rb +6 -0
  42. data/lib/notes.txt +215 -0
  43. data/lib/tasks/duck_tests.rake +35 -0
  44. data/lib/tasks/gem_tasks.rake +18 -0
  45. 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,15 @@
1
+ module DuckTest
2
+ # ...
3
+ module Platforms
4
+ # ...
5
+ module Generic
6
+
7
+ autoload :Listener, 'duck_test/platforms/generic/listener'
8
+
9
+ # ...
10
+ module Base
11
+ end
12
+
13
+ end
14
+ end
15
+ 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,15 @@
1
+ module DuckTest
2
+ # ...
3
+ module Platforms
4
+ # ...
5
+ module Linux
6
+
7
+ autoload :Listener, 'duck_test/platforms/linux/listener'
8
+
9
+ # ...
10
+ module Base
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -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,15 @@
1
+ module DuckTest
2
+ # ...
3
+ module Platforms
4
+ # ...
5
+ module Mac
6
+
7
+ autoload :Listener, 'duck_test/platforms/mac/listener'
8
+
9
+ # ...
10
+ module Base
11
+ end
12
+
13
+ end
14
+ end
15
+ 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
+