win32-changejournal 0.3.2

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,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