win32-changenotify 0.5.0

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/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: