win32-changejournal 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES ADDED
@@ -0,0 +1,31 @@
1
+ == 0.3.2 - 27-Apr-2008
2
+ * Fixed RubyForge bug #10555 (bignum too big to convert into long).
3
+ * Lots of internal reorganization, partially in preparation for the
4
+ pure Ruby version that will be version 0.4.0.
5
+ * Added a Rakefile with tasks for testing, building and installation.
6
+ * Added a gemspec file.
7
+ * Merged the documentation that was in the doc/changejournal.txt file
8
+ into the README.
9
+ * Added a gemspec
10
+
11
+ == 0.3.1 - 2-Jan-2006
12
+ * Internal cleanup only (now compiles cleanly with -W3).
13
+
14
+ == 0.3.0 - 30-Dec-2005
15
+ * The Struct::ChangeJournalStruct now contains a third member: path. This
16
+ is the full path to the file which was modified. Thanks go to Stephen
17
+ Haberman for the idea, and Heesob for the implementation.
18
+ * More tests added.
19
+ * Example script updated.
20
+
21
+ == 0.2.0 - 24-Apr-2005
22
+ * Modified ChangeJournal#wait to return an array of structs, rather than a
23
+ single struct, because multiple events can be associated with a single
24
+ action.
25
+ * Removed the changejournal.rd file. The changejournal.txt file is rdoc
26
+ friendly, so you can generate html from that if you wish.
27
+ * Fixed the release date for 0.1.0.
28
+ * Minor doc and test changes or updates.
29
+
30
+ == 0.1.0 - 13-Feb-2005
31
+ * Initial release
data/MANIFEST ADDED
@@ -0,0 +1,11 @@
1
+ CHANGES
2
+ README
3
+ MANIFEST
4
+ Rakefile
5
+ win32-changejournal.gemspec
6
+ ext/extconf.rb
7
+ ext/win32/changejournal.c
8
+ ext/win32/changejournal.h
9
+ examples/test.rb
10
+ lib/win32/changejournal.rb
11
+ test/tc_changejournal.rb
data/README ADDED
@@ -0,0 +1,76 @@
1
+ == Description
2
+ A class for monitoring events related to files and directories on NTFS.
3
+
4
+ == Installation
5
+ rake install (non-gem)
6
+ rake gem-install (gem)
7
+
8
+ == Synopsis
9
+ require 'win32/changejournal'
10
+ include Win32
11
+
12
+ # Indefinitely wait for a change in 'C:\' and any of its
13
+ # subdirectories. Print the file and action affected.
14
+ #
15
+ cj = ChangeJournal.new('C:\')
16
+
17
+ cj.wait{ |array|
18
+ array.each{ |info|
19
+ p info.file_name
20
+ p info.action
21
+ p info.path
22
+ }
23
+ }
24
+
25
+ c.delete
26
+
27
+ == Class Methods
28
+ ChangeJournal.new(drive)
29
+ Returns a new ChangeJournal object and places a monitor on +drive+.
30
+
31
+ == Instance Methods
32
+ ChangeJournal#wait(num_seconds=INFINITE)
33
+ ChangeJournal#wait(num_seconds=INFINITE){ |arr| ... }
34
+
35
+ Waits up to 'num_seconds' for a notification to occur, or infinitely if
36
+ no value is specified.
37
+
38
+ If a block is provided, yields an array of ChangeJournalStruct's that
39
+ contains three members: file_name, action, and path. An array is returned
40
+ because multiple actions can be associated with a single event.
41
+
42
+ == Constants
43
+ === Standard constants
44
+ VERSION
45
+ Returns the current version number of this library as a String.
46
+
47
+ == Notes
48
+ Based on what the MSDN documentation says, this library requires NTFS, and
49
+ should be preferred on that filesystem. On FAT filesystems, you should
50
+ use the win32-changenotify library instead.
51
+
52
+ == Acknowledgements
53
+ This class was originally based on the CJTest module by Jeffrey
54
+ Cooperstein & Jeffrey Richter.
55
+
56
+ == Future Plans
57
+ Add a method for iterating over all change records.
58
+
59
+ == Known Bugs
60
+ None that I know of. Please log any bug reports on the RubyForge
61
+ project page at http://www.rubyforge.net/projects/win32utils
62
+
63
+ == License
64
+ Ruby's
65
+
66
+ == Copyright
67
+ (C) 2003-2008 Daniel J. Berger, All Rights Reserved
68
+
69
+ == Warranty
70
+ This library is provided "as is" and without any express or
71
+ implied warranties, including, without limitation, the implied
72
+ warranties of merchantability and fitness for a particular purpose.
73
+
74
+ == Authors
75
+ Park Heesob
76
+ Daniel J. Berger
@@ -0,0 +1,487 @@
1
+ /* changejournal.c */
2
+ #include "ruby.h"
3
+ #include <windows.h>
4
+ #include <winioctl.h>
5
+ #include "changejournal.h"
6
+
7
+ // Function Prototypes
8
+ void InitialzeForMonitoring(ChangeJournalStruct *ptr);
9
+
10
+ /* This is a helper function that opens a handle to the volume specified
11
+ * by the cDriveLetter parameter.
12
+ */
13
+ HANDLE Open(TCHAR cDriveLetter, DWORD dwAccess, BOOL fAsyncIO){
14
+ TCHAR szVolumePath[_MAX_PATH];
15
+ HANDLE hCJ;
16
+ wsprintf(szVolumePath, TEXT("\\\\.\\%c:"), cDriveLetter);
17
+
18
+ hCJ = CreateFile(
19
+ szVolumePath,
20
+ dwAccess,
21
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
22
+ NULL,
23
+ OPEN_EXISTING,
24
+ (fAsyncIO ? FILE_FLAG_OVERLAPPED : 0),
25
+ NULL
26
+ );
27
+
28
+ return(hCJ);
29
+ }
30
+
31
+ /* This function creates a journal on the volume. If a journal already
32
+ * exists this function will adjust the MaximumSize and AllocationDelta
33
+ * parameters of the journal.
34
+ */
35
+ BOOL Create(ChangeJournalStruct *ptr, DWORDLONG MaximumSize,
36
+ DWORDLONG AllocationDelta)
37
+ {
38
+
39
+ DWORD cb;
40
+ BOOL fOk;
41
+ CREATE_USN_JOURNAL_DATA cujd;
42
+
43
+ cujd.MaximumSize = MaximumSize;
44
+ cujd.AllocationDelta = AllocationDelta;
45
+
46
+ fOk = DeviceIoControl(
47
+ ptr->hCJ,
48
+ FSCTL_CREATE_USN_JOURNAL,
49
+ &cujd,
50
+ sizeof(cujd),
51
+ NULL,
52
+ 0,
53
+ &cb,
54
+ NULL
55
+ );
56
+
57
+ return(fOk);
58
+ }
59
+
60
+ /* If DeleteFlags specifies USN_DELETE_FLAG_DELETE, the specified journal
61
+ * will be deleted. If USN_DELETE_FLAG_NOTIFY is specified, the function
62
+ * waits until the system has finished the delete process.
63
+ * USN_DELETE_FLAG_NOTIFY can be specified alone to wait for the system
64
+ * to finish if another application has started deleting a journal.
65
+ */
66
+ BOOL Delete(ChangeJournalStruct *ptr,DWORDLONG UsnJournalID,DWORD DeleteFlags){
67
+ DWORD cb;
68
+ DELETE_USN_JOURNAL_DATA dujd;
69
+ BOOL fOk;
70
+
71
+ dujd.UsnJournalID = UsnJournalID;
72
+ dujd.DeleteFlags = DeleteFlags;
73
+
74
+ fOk = DeviceIoControl(
75
+ ptr->hCJ,
76
+ FSCTL_DELETE_USN_JOURNAL,
77
+ &dujd,
78
+ sizeof(dujd),
79
+ NULL,
80
+ 0,
81
+ &cb,
82
+ NULL
83
+ );
84
+
85
+ return(fOk);
86
+ }
87
+
88
+ /* Return statistics about the journal on the current volume
89
+ */
90
+ BOOL Query(ChangeJournalStruct *ptr, PUSN_JOURNAL_DATA pUsnJournalData) {
91
+
92
+
93
+ DWORD cb;
94
+
95
+ BOOL fOk = DeviceIoControl(ptr->hCJ, FSCTL_QUERY_USN_JOURNAL, NULL, 0,
96
+ pUsnJournalData, sizeof(*pUsnJournalData), &cb, NULL);
97
+
98
+ return(fOk);
99
+ }
100
+
101
+ /* This function starts the process of reading data from the journal. The
102
+ * parameters specify the initial location and filters to use when
103
+ * reading the journal. The usn parameter may be zero to start reading
104
+ * at the start of available data. The EnumNext function is called to
105
+ * actualy get journal records.
106
+ */
107
+ void SeekToUsn(ChangeJournalStruct *ptr,
108
+ USN usn, DWORD ReasonMask,
109
+ DWORD ReturnOnlyOnClose, DWORDLONG UsnJournalID) {
110
+
111
+ // Store the parameters in rujd. This will determine how we load
112
+ // buffers with the EnumNext function.
113
+ ptr->rujd.StartUsn = usn;
114
+ ptr->rujd.ReasonMask = ReasonMask;
115
+ ptr->rujd.ReturnOnlyOnClose = ReturnOnlyOnClose;
116
+ ptr->rujd.Timeout = 0;
117
+ ptr->rujd.BytesToWaitFor = 0;
118
+ ptr->rujd.UsnJournalID = UsnJournalID;
119
+ ptr->cbCJData = 0;
120
+ ptr->pUsnRecord = NULL;
121
+ }
122
+
123
+ /* This will return the next record in the journal that meets the
124
+ * requirements specified with the SeekToUsn function. If no more
125
+ * records are available, the functions returns NULL immediately
126
+ * (since BytesToWaitFor is zero). This function will also return
127
+ * NULL if there is an error loading a buffer with the DeviceIoControl
128
+ * function. Use the GetLastError function to determine the cause of the
129
+ * error.
130
+ *
131
+ * If the EnumNext function returns NULL, GetLastError() may return one
132
+ * of the following.
133
+ *
134
+ * S_OK - There was no error, but there are no more available journal
135
+ * records. Use the NotifyMoreData function to wait for more data to
136
+ * become available.
137
+ *
138
+ * ERROR_JOURNAL_DELETE_IN_PROGRESS - The journal is being deleted. Use
139
+ * the Delete function with USN_DELETE_FLAG_NOTIFY to wait for this
140
+ * process to finish. Then, call the Create function and then SeekToUsn
141
+ * to start reading from the new journal.
142
+ *
143
+ * ERROR_JOURNAL_NOT_ACTIVE - The journal has been deleted. Call the Create
144
+ * function then SeekToUsn to start reading from the new journal.
145
+ *
146
+ * ERROR_INVALID_PARAMETER - Possibly caused if the journal's ID has changed.
147
+ * Flush all cached data. Call the Query function and then SeekToUsn to
148
+ * start reading from the new journal.
149
+ *
150
+ * ERROR_JOURNAL_ENTRY_DELETED - Journal records were purged from the journal
151
+ * before we got a chance to process them. Cached information should be
152
+ * flushed.
153
+ */
154
+ PUSN_RECORD EnumNext(ChangeJournalStruct *ptr) {
155
+
156
+ // Make sure we have a buffer to use
157
+ if(ptr->pbCJData == NULL)
158
+ rb_raise(cChangeJournalError, "make sure we have a buffer to use");
159
+
160
+ // If we do not have a record loaded, or enumerating to the next record
161
+ // will point us past the end of the output buffer returned
162
+ // by DeviceIoControl, we need to load a new block of data into memory
163
+ if( (NULL == ptr->pUsnRecord) ||
164
+ ((PBYTE)ptr->pUsnRecord + ptr->pUsnRecord->RecordLength) >=
165
+ (ptr->pbCJData + ptr->cbCJData)
166
+ )
167
+ {
168
+ BOOL fOk;
169
+ ptr->pUsnRecord = NULL;
170
+
171
+ fOk = DeviceIoControl(ptr->hCJ, FSCTL_READ_USN_JOURNAL,
172
+ &ptr->rujd, sizeof(ptr->rujd), ptr->pbCJData,
173
+ HeapSize(GetProcessHeap(), 0, ptr->pbCJData), &ptr->cbCJData, NULL);
174
+ if(fOk){
175
+ // It is possible that DeviceIoControl succeeds, but has not
176
+ // returned any records - this happens when we reach the end of
177
+ // available journal records. We return NULL to the user if there's
178
+ // a real error, or if no records are returned.
179
+
180
+ // Set the last error to NO_ERROR so the caller can distinguish
181
+ // between an error, and the case where no records were returned.
182
+ SetLastError(NO_ERROR);
183
+
184
+ // Store the 'next usn' into m_rujd.StartUsn for use the
185
+ // next time we want to read from the journal
186
+ ptr->rujd.StartUsn = * (USN *) ptr->pbCJData;
187
+
188
+ // If we got more than sizeof(USN) bytes, we must have a record.
189
+ // Point the current record to the first record in the buffer
190
+ if (ptr->cbCJData > sizeof(USN))
191
+ ptr->pUsnRecord = (PUSN_RECORD) &ptr->pbCJData[sizeof(USN)];
192
+ }
193
+ } else {
194
+ // The next record is already available in our stored
195
+ // buffer - Move pointer to next record
196
+ ptr->pUsnRecord = (PUSN_RECORD)
197
+ ((PBYTE) ptr->pUsnRecord + ptr->pUsnRecord->RecordLength);
198
+ }
199
+ return(ptr->pUsnRecord);
200
+ }
201
+
202
+
203
+ /* Cleanup the memory and handles we were using.
204
+ */
205
+ void CleanUp(ChangeJournalStruct *ptr) {
206
+ USN_JOURNAL_DATA ujd;
207
+
208
+ Query(ptr,&ujd);
209
+ Delete(ptr,ujd.UsnJournalID, USN_DELETE_FLAG_DELETE);
210
+
211
+ if (ptr->hCJ != INVALID_HANDLE_VALUE)
212
+ CloseHandle(ptr->hCJ);
213
+ if (ptr->hCJAsync != INVALID_HANDLE_VALUE)
214
+ CloseHandle(ptr->hCJAsync);
215
+ if (ptr->pbCJData != NULL)
216
+ HeapFree(GetProcessHeap(), 0, ptr->pbCJData);
217
+ if (ptr->oCJAsync.hEvent != NULL) {
218
+
219
+ // Make sure the helper thread knows that we are exiting. We set
220
+ // m_hwndApp to NULL then signal the overlapped event to make sure the
221
+ // helper thread wakes up.
222
+ SetEvent(ptr->oCJAsync.hEvent);
223
+ CloseHandle(ptr->oCJAsync.hEvent);
224
+ }
225
+ }
226
+
227
+ /* Call this to initialize the structure. The cDriveLetter parameter
228
+ * specifies the drive that this instance will access. The cbBuffer
229
+ * parameter specifies the size of the interal buffer used to read records
230
+ * from the journal. This should be large enough to hold several records
231
+ * (for example, 10 kilobytes will allow this class to buffer several
232
+ * dozen journal records at a time).
233
+ */
234
+ BOOL Init(ChangeJournalStruct *ptr, TCHAR cDriveLetter, DWORD cbBuffer){
235
+ if(ptr->pbCJData != NULL){
236
+ rb_raise(cChangeJournalError,
237
+ "you should not call this function twice for one instance."
238
+ );
239
+ }
240
+
241
+ ptr->cDriveLetter = cDriveLetter;
242
+
243
+ // Allocate internal buffer
244
+ ptr->pbCJData = (PBYTE) HeapAlloc(GetProcessHeap(), 0, cbBuffer);
245
+ if(NULL == ptr->pbCJData){
246
+ CleanUp(ptr);
247
+ return(FALSE);
248
+ }
249
+
250
+ // Open a handle to the volume
251
+ ptr->hCJ = Open(cDriveLetter, GENERIC_WRITE | GENERIC_READ, FALSE);
252
+ if(INVALID_HANDLE_VALUE == ptr->hCJ){
253
+ CleanUp(ptr);
254
+ return(FALSE);
255
+ }
256
+
257
+ // Open a handle to the volume for asynchronous I/O. This is used to wait
258
+ // for new records after all current records have been processed
259
+ ptr->hCJAsync = Open(cDriveLetter, GENERIC_WRITE | GENERIC_READ, TRUE);
260
+ if(INVALID_HANDLE_VALUE == ptr->hCJAsync){
261
+ CleanUp(ptr);
262
+ return(FALSE);
263
+ }
264
+
265
+ // Create an event for asynchronous I/O.
266
+ ptr->oCJAsync.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
267
+ if(NULL == ptr->oCJAsync.hEvent){
268
+ CleanUp(ptr);
269
+ return(FALSE);
270
+ }
271
+
272
+ return(TRUE);
273
+ }
274
+
275
+ // This function ensures that the journal on the volume is active
276
+ void InitialzeForMonitoring(ChangeJournalStruct *ptr) {
277
+ BOOL fOk = TRUE;
278
+ USN_JOURNAL_DATA ujd;
279
+
280
+ // Try to query for current journal information
281
+ while(fOk && !Query(ptr,&ujd)){
282
+ switch(GetLastError()){
283
+ case ERROR_JOURNAL_DELETE_IN_PROGRESS:
284
+ // The system is deleting a journal. We need to wait for it to finish
285
+ // before trying to query it again.
286
+ Delete(ptr, 0, USN_DELETE_FLAG_NOTIFY);
287
+ break;
288
+
289
+ case ERROR_JOURNAL_NOT_ACTIVE:
290
+ // The journal is not active on the volume. We need to create it and
291
+ // then query for its information again
292
+ Create(ptr,0x800000, 0x100000);
293
+ break;
294
+
295
+ default:
296
+ // Some other error happened while querying the journal information.
297
+ // There is nothing we can do from here
298
+ rb_raise(cChangeJournalError, "unable to query journal");
299
+ fOk = FALSE;
300
+ break;
301
+ }
302
+ }
303
+
304
+ // We were not able to query the volume for journal information
305
+ if(!fOk)
306
+ return;
307
+
308
+ // Start processing records at the start of the journal
309
+ SeekToUsn(ptr,ujd.FirstUsn, 0xFFFFFFFF, FALSE, ujd.UsnJournalID);
310
+
311
+ // Initialize Path DB
312
+ PopulatePath(ptr);
313
+ }
314
+
315
+
316
+ /*
317
+ * :no-doc:
318
+ */
319
+ static VALUE changejournal_allocate(VALUE klass){
320
+ ChangeJournalStruct* ptr = malloc(sizeof(ChangeJournalStruct));
321
+ return Data_Wrap_Struct(klass, 0, changejournal_free, ptr);
322
+ }
323
+
324
+ /*
325
+ * :call-seq:
326
+ *
327
+ * ChangeJournal.new(drive)
328
+ *
329
+ * Returns a new ChangeJournal object and places a monitor on 'drive'.
330
+ */
331
+ static VALUE changejournal_init(VALUE self, VALUE v_drive)
332
+ {
333
+ ChangeJournalStruct* ptr;
334
+ LPCTSTR lpDriveLetter = StringValuePtr(v_drive);
335
+
336
+ Data_Get_Struct(self, ChangeJournalStruct, ptr);
337
+
338
+ // Do not allow a block for this class
339
+ if(rb_block_given_p())
340
+ rb_raise(cChangeJournalError, "block not permitted for this class");
341
+
342
+ // Initialize member variables
343
+ ptr->hCJ = INVALID_HANDLE_VALUE;
344
+ ptr->hCJAsync = INVALID_HANDLE_VALUE;
345
+ ZeroMemory(&(ptr->oCJAsync), sizeof(ptr->oCJAsync));
346
+ ptr->pbCJData = NULL;
347
+ ptr->pUsnRecord = NULL;
348
+
349
+ // Initialize the ChangeJournal object with the current drive letter and tell
350
+ // it to allocate a buffer of 10000 bytes to read journal records.
351
+ if(!Init(ptr, lpDriveLetter[0], 10000))
352
+ rb_raise(rb_eTypeError, "initialization error");
353
+
354
+ InitialzeForMonitoring(ptr);
355
+
356
+ return self;
357
+ }
358
+
359
+ /*
360
+ * :call-seq:
361
+ *
362
+ * ChangeJournal#wait(num_seconds=INFINITE)
363
+ * ChangeJournal#wait(num_seconds=INFINITE){ |s| ... }
364
+ *
365
+ * Waits up to 'num_seconds' for a notification to occur, or infinitely if
366
+ * no value is specified.
367
+ *
368
+ * If a block is provided, yields a ChangeJournalStruct that contains two
369
+ * members - file_name and action.
370
+ */
371
+ static VALUE changejournal_wait(int argc, VALUE* argv, VALUE self){
372
+ VALUE v_timeout, v_block;
373
+ ChangeJournalStruct *ptr;
374
+ DWORD dwTimeout, dwWait;
375
+ READ_USN_JOURNAL_DATA rujd;
376
+ BOOL fOk;
377
+
378
+ Data_Get_Struct(self, ChangeJournalStruct, ptr);
379
+
380
+ rb_scan_args(argc, argv, "01&", &v_timeout, &v_block);
381
+
382
+ if(NIL_P(v_timeout)){
383
+ dwTimeout = INFINITE;
384
+ }
385
+ else{
386
+ dwTimeout = NUM2UINT(v_timeout);
387
+ dwTimeout *= 1000; // Convert milliseconds to seconds
388
+ }
389
+
390
+ rujd = ptr->rujd;
391
+ rujd.BytesToWaitFor = 1;
392
+
393
+ // Try to read at least one byte from the journal at the specified USN.
394
+ // When 1 byte is available, the event in m_oCJAsync will be signaled.
395
+
396
+ fOk = DeviceIoControl(
397
+ ptr->hCJAsync,
398
+ FSCTL_READ_USN_JOURNAL,
399
+ &rujd,
400
+ sizeof(rujd),
401
+ &ptr->UsnAsync,
402
+ sizeof(ptr->UsnAsync),
403
+ NULL,
404
+ &ptr->oCJAsync
405
+ );
406
+
407
+ // Do nothing
408
+ if(!fOk && (GetLastError() != ERROR_IO_PENDING)) { }
409
+
410
+ dwWait = WaitForSingleObject(ptr->oCJAsync.hEvent, dwTimeout);
411
+
412
+ switch(dwWait){
413
+ case WAIT_FAILED:
414
+ rb_raise(cChangeJournalError, ErrorDescription(GetLastError()));
415
+ break;
416
+ case WAIT_OBJECT_0:
417
+ rb_iv_set(self, "@signaled", Qtrue);
418
+ if(Qnil != v_block){
419
+ rb_yield(get_file_action(ptr));
420
+ }
421
+ return INT2NUM(1);
422
+ break;
423
+ case WAIT_ABANDONED_0:
424
+ return INT2NUM(-1);
425
+ break;
426
+ case WAIT_TIMEOUT:
427
+ return INT2NUM(0);
428
+ break;
429
+ default:
430
+ rb_raise(cChangeJournalError,
431
+ "unknown return value from WaitForSingleObject()"
432
+ );
433
+ };
434
+
435
+ return self;
436
+ }
437
+
438
+ /*
439
+ * call-seq:
440
+ *
441
+ * ChangeJournal#delete
442
+ *
443
+ * Deletes the change journal on a volume, or waits for notification of
444
+ * change journal deletion.
445
+ */
446
+ static VALUE changejournal_delete(VALUE self){
447
+ ChangeJournalStruct *ptr;
448
+ USN_JOURNAL_DATA ujd;
449
+
450
+ Data_Get_Struct(self, ChangeJournalStruct, ptr);
451
+
452
+ Query(ptr, &ujd);
453
+ Delete(ptr, ujd.UsnJournalID, USN_DELETE_FLAG_DELETE);
454
+
455
+ return self;
456
+ }
457
+
458
+ void Init_changejournal()
459
+ {
460
+ VALUE mWin32, cChangeJournal;
461
+
462
+ // Module and class definitions
463
+ mWin32 = rb_define_module("Win32");
464
+ cChangeJournal = rb_define_class_under(mWin32, "ChangeJournal", rb_cObject);
465
+ cChangeJournalError = rb_define_class_under(mWin32, "ChangeJournalError",
466
+ rb_eStandardError);
467
+
468
+ // ChangeJournal class and instance methods
469
+ rb_define_alloc_func(cChangeJournal,changejournal_allocate);
470
+ rb_define_method(cChangeJournal, "initialize", changejournal_init, 1);
471
+ rb_define_method(cChangeJournal, "wait", changejournal_wait, -1);
472
+ rb_define_method(cChangeJournal, "delete", changejournal_delete, 0);
473
+
474
+ // Struct definitions
475
+ sChangeJournalStruct = rb_struct_define(
476
+ "ChangeJournalStruct",
477
+ "action",
478
+ "file_name",
479
+ "path",
480
+ 0
481
+ );
482
+
483
+ // 0.3.2 - The version of the win32-changejournal library
484
+ rb_define_const(cChangeJournal, "VERSION",
485
+ rb_str_new2(WIN32_CHANGEJOURNAL_VERSION));
486
+ }
487
+
Binary file
@@ -0,0 +1,60 @@
1
+ #############################################################################
2
+ # tc_changejournal.rb
3
+ #
4
+ # Test suite for the win32-changejournal package. You should run this
5
+ # via the 'rake test' task.
6
+ #############################################################################
7
+ require 'test/unit'
8
+ require 'win32/changejournal'
9
+ include Win32
10
+
11
+ STDOUT.print "\n\nThis may take a few seconds - be patient\n\n"
12
+
13
+ class TC_Win32_ChangeJournal < Test::Unit::TestCase
14
+ # The thread here is used to force an event to happen for one of the tests
15
+ def setup
16
+ @journal = ChangeJournal.new("c:\\")
17
+ @file = "C:\\delete_me.txt"
18
+ @thread = Thread.new{
19
+ sleep 2
20
+ File.open(@file, 'w+'){ |fh| fh.puts 'Delete me!' }
21
+ }
22
+ end
23
+
24
+ def test_version
25
+ assert_equal('0.3.2', ChangeJournal::VERSION)
26
+ end
27
+
28
+ def test_changejournal_action
29
+ @thread.join
30
+ @journal.wait(2){ |c|
31
+ assert_kind_of(Array, c)
32
+ assert_kind_of(Struct::ChangeJournalStruct, c.first)
33
+ assert_equal(['action', 'file_name', 'path'], c.first.members)
34
+ }
35
+ end
36
+
37
+ def test_delete
38
+ assert_respond_to(@journal, :delete)
39
+ assert_nothing_raised{ @journal.delete }
40
+ end
41
+
42
+ # We provide some very short timeouts here - shouldn't slow the tests down
43
+ def test_wait_basic
44
+ assert_respond_to(@journal, :wait)
45
+ assert_nothing_raised{ @journal.wait(0.01) }
46
+ assert_nothing_raised{ @journal.wait(0.01){ |s| } }
47
+ end
48
+
49
+ def test_wait_expected_errors
50
+ assert_raise(ArgumentError){ @journal.wait(1,1) }
51
+ assert_raise(TypeError){ @journal.wait('a') }
52
+ end
53
+
54
+ def teardown
55
+ @thread.kill rescue nil
56
+ File.delete(@file) if File.exists?(@file)
57
+ @journal = nil
58
+ @flags = nil
59
+ end
60
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: win32-changejournal
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.2
5
+ platform: ruby
6
+ authors:
7
+ - Daniel J. Berger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-05-05 00:00:00 -06:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A library for monitoring files and directories on NTFS
17
+ email: djberg96@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ - CHANGES
25
+ - MANIFEST
26
+ - ext/win32/changejournal.c
27
+ files:
28
+ - lib/win32
29
+ - lib/win32/changejournal.so
30
+ - test/tc_changejournal.rb
31
+ - README
32
+ - CHANGES
33
+ - MANIFEST
34
+ - ext/win32/changejournal.c
35
+ has_rdoc: true
36
+ homepage: http://www.rubyforge.org/projects/win32utils
37
+ post_install_message:
38
+ rdoc_options: []
39
+
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 1.8.0
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project: win32utils
57
+ rubygems_version: 1.1.1
58
+ signing_key:
59
+ specification_version: 2
60
+ summary: A library for monitoring files and directories on NTFS
61
+ test_files:
62
+ - test/tc_changejournal.rb