win32-changenotify 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES ADDED
@@ -0,0 +1,56 @@
1
+ == 0.5.0 - 9-Aug-2007
2
+ * Now pure Ruby!
3
+ * Now requires the win32-event library.
4
+ * The constructor now accepts a block. If provided, it yields itself
5
+ and automatically closes itself when the block terminates.
6
+ * Removed the ChangeNotify#reset method. It was never especially useful,
7
+ and now it's useless because of the way we wait for events (see below).
8
+ * Added a Rakefile with tasks for installation and testing.
9
+ * The code now uses completion ports internally, which greatly improves the
10
+ number of simultaneous events it can detect and report. See the 'Known
11
+ Issues' in the README for details on potential misses, however.
12
+ * Some internal reorganization, with the C related code now moved into a
13
+ separate directory.
14
+ * Doc, test, and example program updates.
15
+
16
+ == 0.4.2 - 22-Nov-2006
17
+ * Fixed bugs caused by changes to the Ruby internals with regards to question
18
+ marks and instance variables. Thanks go to an anonymous user for the spot.
19
+ * Minor documentation updates.
20
+
21
+ == 0.4.1 - 2-Jan-2006
22
+ * Code cleanup
23
+
24
+ == 0.4.0 - 20-Apr-2005
25
+ * ChangeNotify#wait now returns an array of ChangeNotifyStruct's, rather than
26
+ a single struct, because multiple notifications can occur for a single
27
+ event.
28
+ * Example program and test suite updates.
29
+ * Removed the changenotify.rd file.
30
+
31
+ == 0.3.1 - 4-Feb-2005
32
+ * Fixed a bug in ChangeNotify#wait where the timeout was not set to the
33
+ proper default if a timeout was not specified by the user. Thanks go
34
+ to CT for the spot.
35
+ * Made this document rdoc friendly.
36
+ * Corrected the release date for 0.3.0.
37
+
38
+ == 0.3.0 - 25-Jan-2005
39
+ * The ChangeNotify#wait method now yields a ChangeNotifyStruct if a block is
40
+ provided. This struct contains more detail on what changes occurred.
41
+ * Added the ChangeNotify#path, ChangeNotify#recursive? and ChangeNotify#filter
42
+ methods.
43
+ * The ChangeNotify.new method no longer accepts a block. See the developer's
44
+ notes if you want the nitty gritty details.
45
+ * Added more code to the extconf.rb file. This was needed to set the
46
+ _WIN32_WINNT macro properly.
47
+ * Moved the 'examples' directory to the toplevel.
48
+ * Test suite and documentation updates.
49
+
50
+ == 0.2.0 - 17-Jul-2004
51
+ * Now uses the newer allocation framework. This means that, as of this
52
+ releas, this package requires Ruby 1.8.0 or later.
53
+ * Moved the test.rb file to doc/examples.
54
+
55
+ == 0.1.0 - 7-May-2004
56
+ * Initial release
@@ -0,0 +1,12 @@
1
+ * CHANGES
2
+ * README
3
+ * MANIFEST
4
+ * Rakefile
5
+ * win32-changenotify.gemspec
6
+ * doc/changenotify.txt
7
+ * examples/test.rb
8
+ * ext/extconf.rb
9
+ * ext/win32/changenotify.c
10
+ * ext/win32/changenotify.h
11
+ * lib/win32/changenotify.rb
12
+ * test/tc_changenotify.rb
data/README ADDED
@@ -0,0 +1,100 @@
1
+ = Description
2
+ A class for monitoring events related to files and directories. This
3
+ package is deprecated in favor of win32-changejournal on NTFS filesystems.
4
+
5
+ = Prerequisites
6
+ * Ruby 1.8.0 or later
7
+ * win32-ipc 0.4.0 or later
8
+
9
+ = Installation
10
+ rake test (optional)
11
+ rake install (non-gem) OR rake install_gem (gem)
12
+
13
+ = Synopsis
14
+ require 'win32/changenotify'
15
+ include Win32
16
+
17
+ # Indefinitely wait for a change in 'C:\some\path' and any of its
18
+ # subdirectories. Print the file and action affected.
19
+
20
+ filter = ChangeNotify::FILE_NAME | ChangeNotify::DIR_NAME
21
+ path = 'C:\some\path'
22
+
23
+ cn = ChangeNotify.new(path, true, filter)
24
+
25
+ cn.wait{ |arr|
26
+ arr.each{ |info|
27
+ p info.file_name
28
+ p info.action
29
+ }
30
+ }
31
+
32
+ cn.close
33
+
34
+ # OR
35
+
36
+ ChangeNotify.new(path, true, filter) do |events|
37
+ events.each{ |event|
38
+ p event.file_name
39
+ p event.action
40
+ }
41
+ end
42
+
43
+ = Notes
44
+ The Win32::ChangeNotify class is a subclass of Win32::Ipc, and thus has
45
+ all of its methods available as well.
46
+
47
+ This library is deprecated in favor of win32-changejournal on NTFS
48
+ filesystems.
49
+
50
+ For additional documentation please see the 'changenotify.txt' file under
51
+ the 'doc' directory.
52
+
53
+ = Pure Ruby vs C Extension
54
+ The C code we used for this library prior to 0.5.0 is still available in
55
+ CVS for this project, but is not distributed with official releases. Note
56
+ that it does NOT contain the completion port approach we use in 0.5.0 and
57
+ later and is actually less reliable (in terms of how many simultaneous
58
+ events it can pickup) as a result.
59
+
60
+ = Known Issues
61
+ Despite the improvements yielded as a result of using completion ports,
62
+ it's still possible that events could be missed. To be more precise,
63
+ any events that occur in the fraction of a second between the call to
64
+ GetQueuedCompletionStatus() and ReadDirectoryChangesW() in the wait loop
65
+ will not get picked up. As a general rule, the faster your system is,
66
+ the less likely you are to encounter this scenario.
67
+
68
+ We do not believe it is possible to resolve this issue without native
69
+ thread support. We are, however, open to suggestions. :)
70
+
71
+ = Acknowledgements
72
+ This class was originally based on the Win32::ChangeNotify Perl module by
73
+ Christopher Madsen.
74
+
75
+ = Future Plans
76
+ Probably none. This library is deprecated in favor of win32-changejournal.
77
+ However, that library only works on NTFS filesystems, so this package will
78
+ be maintained as well.
79
+
80
+ That being said, I'm always open to improvements, so feel free to submit
81
+ ideas and patches.
82
+
83
+ = Known Bugs
84
+ None that I know of. Please log any other bug reports on the RubyForge
85
+ project page at http://www.rubyforge.net/projects/win32utils
86
+
87
+ = License
88
+ Ruby's
89
+
90
+ = Copyright
91
+ (C) 2003-2007 Daniel J. Berger, All Rights Reserved
92
+
93
+ = Warranty
94
+ This package is provided "as is" and without any express or
95
+ implied warranties, including, without limitation, the implied
96
+ warranties of merchantability and fitness for a particular purpose.
97
+
98
+ = Authors
99
+ Park Heesob
100
+ Daniel J. Berger
@@ -0,0 +1,28 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rbconfig'
4
+ include Config
5
+
6
+ desc 'Install the win32-changenotify package (non-gem)'
7
+ task :install do
8
+ sitelibdir = CONFIG['sitelibdir']
9
+ installdir = File.join(sitelibdir, 'win32')
10
+ file = 'lib\win32\changenotify.rb'
11
+
12
+ Dir.mkdir(installdir) unless File.exists?(installdir)
13
+ FileUtils.cp(file, installdir, :verbose => true)
14
+ end
15
+
16
+ desc 'Install the win32-changenotify library as a gem'
17
+ task :install_gem do
18
+ ruby 'win32-changenotify.gemspec'
19
+ file = Dir["*.gem"].first
20
+ sh "gem install #{file}"
21
+ end
22
+
23
+ Rake::TestTask.new do |t|
24
+ t.libs << 'test'
25
+ t.verbose = true
26
+ t.warning = true
27
+ t.test_files = FileList['test/tc_changenotify.rb']
28
+ end
@@ -0,0 +1,263 @@
1
+ require 'win32/event'
2
+ require 'windows/nio'
3
+ require 'windows/file'
4
+ require 'windows/directory'
5
+ require 'windows/unicode'
6
+ require 'windows/msvcrt/buffer'
7
+
8
+ module Win32
9
+ class ChangeNotify < Ipc
10
+ include Windows::NIO
11
+ include Windows::File
12
+ include Windows::Directory
13
+ include Windows::Unicode
14
+ include Windows::MSVCRT::Buffer
15
+
16
+ # The version of this library
17
+ VERSION = '0.5.0'
18
+
19
+ # Aliased constants
20
+
21
+ # Filter: Attribute changes
22
+ ATTRIBUTES = FILE_NOTIFY_CHANGE_ATTRIBUTES
23
+
24
+ # Filter: Directory name changes
25
+ DIR_NAME = FILE_NOTIFY_CHANGE_DIR_NAME
26
+
27
+ # Filter: File name changes
28
+ FILE_NAME = FILE_NOTIFY_CHANGE_FILE_NAME
29
+
30
+ # Filter: Write changes
31
+ LAST_WRITE = FILE_NOTIFY_CHANGE_LAST_WRITE
32
+
33
+ # Filter: File security changes
34
+ SECURITY = FILE_NOTIFY_CHANGE_SECURITY
35
+
36
+ # Filter: File size changes
37
+ SIZE = FILE_NOTIFY_CHANGE_SIZE
38
+
39
+ # Private constants
40
+
41
+ # File was added
42
+ FILE_ACTION_ADDED = 0x00000001
43
+
44
+ # File was deleted
45
+ FILE_ACTION_REMOVED = 0x00000002
46
+
47
+ # File was modified
48
+ FILE_ACTION_MODIFIED = 0x00000003
49
+
50
+ # File was renamed, old (original) name
51
+ FILE_ACTION_RENAMED_OLD_NAME = 0x00000004
52
+
53
+ # File was renamed, new (current) name
54
+ FILE_ACTION_RENAMED_NEW_NAME = 0x00000005
55
+
56
+ # Yielded by the ChangeNotify#wait method
57
+ ChangeNotifyStruct = Struct.new('ChangeNotifyStruct', :action, :file_name)
58
+
59
+ # The path that was provided to the constructor
60
+ attr_reader :path
61
+
62
+ # The value of the filter (OR'd constants) passed to the constructor
63
+ attr_reader :filter
64
+
65
+ # Returns a new ChangeNotify object and places a monitor on +path+.
66
+ # The +path+ argument may be a file or a directory.
67
+ #
68
+ # If +recursive is true and +path+ is a directory, then the monitor
69
+ # applies to all subdirectories of +path+.
70
+ #
71
+ # The +filter+ tells the monitor what to watch for, such as file
72
+ # changes, attribute changes, etc.
73
+ #
74
+ # If the +event+ option is specified, it must be a Win32::Event object.
75
+ # It is then set as the event that will be set to the signaled state
76
+ # when a notification has been completed.
77
+ #
78
+ # Yields itself if a block is provided, and automatically closes itself
79
+ # when the block terminates.
80
+ #
81
+ def initialize(path, recursive, filter, event=nil)
82
+ @path = path
83
+ @recursive = recursive
84
+ @filter = filter
85
+ @overlap = 0.chr * 20 # OVERLAPPED struct
86
+
87
+ # Because Win32API doesn't do type checking, we do it expicitly here.
88
+ raise TypeError unless path.is_a?(String)
89
+ raise TypeError unless [true, false].include?(recursive)
90
+ raise TypeError unless filter.is_a?(Fixnum)
91
+
92
+ if event
93
+ raise TypeError unless event.respond_to?(:handle)
94
+ @handle = event.handle
95
+ else
96
+ event = Win32::Event.new
97
+ @handle = event.handle
98
+ end
99
+
100
+ @event = event
101
+
102
+ @overlap[16,4] = [@handle].pack('L') # hEvent member
103
+
104
+ super(@handle)
105
+
106
+ if block_given?
107
+ begin
108
+ yield self
109
+ ensure
110
+ close
111
+ end
112
+ end
113
+ end
114
+
115
+ # Returns whether or not the ChangeNotify object is monitoring
116
+ # subdirectories of the path being monitored.
117
+ #
118
+ def recursive?
119
+ @recursive
120
+ end
121
+
122
+ alias ipc_wait wait
123
+
124
+ # Waits up to 'seconds' for a notification to occur, or infinitely
125
+ # if no value is specified.
126
+ #
127
+ # Yields an array of ChangeNotifyStruct's that contains two
128
+ # members: file_name and action.
129
+ #
130
+ def wait(seconds = INFINITE)
131
+ seconds *= 1000 unless seconds == INFINITE
132
+
133
+ fni = 0.chr * 65536 # FILE_NOTIFY_INFORMATION struct buffer
134
+ rbytes = [0].pack('L')
135
+ qbytes = [0].pack('L')
136
+
137
+ subtree = @recursive ? 1 : 0
138
+ dir_handle = get_dir_handle(@path)
139
+ comp_key = [12345].pack('L')
140
+
141
+ comp_port = CreateIoCompletionPort(dir_handle, 0, comp_key, 0)
142
+
143
+ if comp_port == 0
144
+ raise Error, get_last_error
145
+ end
146
+
147
+ bool = ReadDirectoryChangesW(
148
+ dir_handle,
149
+ fni,
150
+ fni.size,
151
+ subtree,
152
+ @filter,
153
+ rbytes,
154
+ @overlap,
155
+ 0
156
+ )
157
+
158
+ unless bool
159
+ raise Error, get_last_error
160
+ end
161
+
162
+ while true
163
+ bool = GetQueuedCompletionStatus(
164
+ comp_port,
165
+ qbytes,
166
+ comp_key,
167
+ @overlap,
168
+ seconds
169
+ )
170
+
171
+ unless bool
172
+ raise Error, get_last_error
173
+ end
174
+
175
+ @signaled = true
176
+ @event.signaled = true
177
+
178
+ break if comp_key.unpack('L').first == 0
179
+
180
+ yield get_file_action(fni) if block_given?
181
+
182
+ bool = ReadDirectoryChangesW(
183
+ dir_handle,
184
+ fni,
185
+ fni.size,
186
+ subtree,
187
+ @filter,
188
+ rbytes,
189
+ @overlap,
190
+ 0
191
+ )
192
+
193
+ unless bool
194
+ raise Error, get_last_error
195
+ end
196
+ end
197
+
198
+ CloseHandle(dir_handle)
199
+ end
200
+
201
+ private
202
+
203
+ def get_file_action(fni2)
204
+ fni = fni2.dup
205
+ array = []
206
+
207
+ while true
208
+ int_action = fni[4,4].unpack('L')[0]
209
+
210
+ str_action = 'unknown'
211
+
212
+ case int_action
213
+ when FILE_ACTION_ADDED
214
+ str_action = 'added'
215
+ when FILE_ACTION_REMOVED
216
+ str_action = 'removed'
217
+ when FILE_ACTION_MODIFIED
218
+ str_action = 'modified'
219
+ when FILE_ACTION_RENAMED_OLD_NAME
220
+ str_action = 'renamed old name'
221
+ when FILE_ACTION_RENAMED_NEW_NAME
222
+ str_action = 'renamed new name'
223
+ end
224
+
225
+ len = fni[8,4].unpack('L').first # FileNameLength struct member
226
+ file = fni[12,len] + "\0\0" # FileName struct member + null
227
+ buf = 0.chr * 260
228
+
229
+ WideCharToMultiByte(CP_ACP, 0, file, -1, buf, 260, 0, 0)
230
+
231
+ file = File.join(@path, buf.unpack('A*')[0])
232
+
233
+ struct = ChangeNotifyStruct.new(str_action, file)
234
+ array.push(struct)
235
+ break if fni[0,4].unpack('L')[0] == 0
236
+ fni = fni[fni[0,4].unpack('L').first .. -1] # Next offset
237
+ break if fni.nil?
238
+ end
239
+
240
+ array
241
+ end
242
+
243
+ # Returns a HANDLE to the directory +path+ created via CreateFile().
244
+ #
245
+ def get_dir_handle(path)
246
+ handle = CreateFile(
247
+ path,
248
+ FILE_LIST_DIRECTORY,
249
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
250
+ 0,
251
+ OPEN_EXISTING,
252
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
253
+ 0
254
+ )
255
+
256
+ if handle == INVALID_HANDLE_VALUE
257
+ raise Error, get_last_error
258
+ end
259
+
260
+ handle
261
+ end
262
+ end
263
+ end
@@ -0,0 +1,70 @@
1
+ #############################################################################
2
+ # tc_changenotify.rb
3
+ #
4
+ # Test suite for the win32-changenotify package. You should run this
5
+ # test via the 'rake test' task.
6
+ #############################################################################
7
+ require 'test/unit'
8
+ require 'win32/changenotify'
9
+ include Win32
10
+
11
+ class TC_ChangeNotify < Test::Unit::TestCase
12
+ def setup
13
+ @filter = ChangeNotify::FILE_NAME | ChangeNotify::DIR_NAME
14
+ @cn = ChangeNotify.new("c:\\", false, @filter)
15
+ end
16
+
17
+ def test_version
18
+ assert_equal('0.5.0', ChangeNotify::VERSION)
19
+ end
20
+
21
+ def test_path
22
+ assert_respond_to(@cn, :path)
23
+ assert_equal("c:\\", @cn.path)
24
+ end
25
+
26
+ def test_recursive
27
+ assert_respond_to(@cn, :recursive?)
28
+ assert_equal(false, @cn.recursive?)
29
+ end
30
+
31
+ def test_filter
32
+ assert_respond_to(@cn, :filter)
33
+ assert_equal(@filter, @cn.filter)
34
+ end
35
+
36
+ # The errors here are expected because of the timeout.
37
+ def test_wait
38
+ assert_respond_to(@cn, :wait)
39
+ assert_raises(ChangeNotify::Error){ @cn.wait(0.001) }
40
+ assert_raises(ChangeNotify::Error){ @cn.wait(0.001){} }
41
+ end
42
+
43
+ def test_wait_any
44
+ assert_respond_to(@cn, :wait_any)
45
+ end
46
+
47
+ def test_wait_all
48
+ assert_respond_to(@cn, :wait_all)
49
+ end
50
+
51
+ def test_expected_errors
52
+ assert_raises(TypeError){
53
+ ChangeNotify.new(1, true, @filter)
54
+ }
55
+ assert_raises(TypeError){
56
+ ChangeNotify.new("C:\\", 'foo', @filter)
57
+ }
58
+ assert_raises(TypeError){
59
+ ChangeNotify.new("C:\\", false, "foo")
60
+ }
61
+ assert_raises(TypeError){
62
+ ChangeNotify.new(1, true, @filter, 'bogus')
63
+ }
64
+ end
65
+
66
+ def teardown
67
+ @cn = nil
68
+ @filter = nil
69
+ end
70
+ end
@@ -0,0 +1,24 @@
1
+ require 'rubygems'
2
+
3
+ spec = Gem::Specification.new do |gem|
4
+ gem.name = 'win32-changenotify'
5
+ gem.version = '0.5.0'
6
+ gem.author = 'Daniel J. Berger'
7
+ gem.email = 'djberg96@gmail.com'
8
+ gem.homepage = 'http://www.rubyforge.org/projects/win32utils'
9
+ gem.platform = Gem::Platform::RUBY
10
+ gem.summary = 'A way to monitor files and directories on MS Windows'
11
+ gem.description = 'A way to monitor files and directories on MS Windows'
12
+ gem.test_file = 'test/tc_changenotify.rb'
13
+ gem.has_rdoc = true
14
+ gem.files = Dir['lib/win32/*.rb'] + Dir['test/*'] + Dir['[A-Z]*']
15
+ gem.files.reject! { |fn| fn.include? 'CVS' }
16
+ gem.require_path = 'lib'
17
+ gem.extra_rdoc_files = ['MANIFEST', 'README', 'CHANGES']
18
+ gem.add_dependency('windows-pr', '>= 0.7.0')
19
+ end
20
+
21
+ if $0 == __FILE__
22
+ Gem.manage_gems
23
+ Gem::Builder.new(spec).build
24
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.4
3
+ specification_version: 1
4
+ name: win32-changenotify
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.5.0
7
+ date: 2007-08-09 00:00:00 -06:00
8
+ summary: A way to monitor files and directories on MS Windows
9
+ require_paths:
10
+ - lib
11
+ email: djberg96@gmail.com
12
+ homepage: http://www.rubyforge.org/projects/win32utils
13
+ rubyforge_project:
14
+ description: A way to monitor files and directories on MS Windows
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Daniel J. Berger
31
+ files:
32
+ - lib/win32/changenotify.rb
33
+ - test/CVS
34
+ - test/tc_changenotify.rb
35
+ - CHANGES
36
+ - CVS
37
+ - doc
38
+ - examples
39
+ - ext
40
+ - lib
41
+ - MANIFEST
42
+ - Rakefile
43
+ - README
44
+ - test
45
+ - win32-changenotify.gemspec
46
+ test_files:
47
+ - test/tc_changenotify.rb
48
+ rdoc_options: []
49
+
50
+ extra_rdoc_files:
51
+ - MANIFEST
52
+ - README
53
+ - CHANGES
54
+ executables: []
55
+
56
+ extensions: []
57
+
58
+ requirements: []
59
+
60
+ dependencies:
61
+ - !ruby/object:Gem::Dependency
62
+ name: windows-pr
63
+ version_requirement:
64
+ version_requirements: !ruby/object:Gem::Version::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 0.7.0
69
+ version: