win32-dirmonitor 1.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 50e164eadb4120514620c9163557cd77668cebe5
4
+ data.tar.gz: 022e4a74724f9bd61066091b311585124ac4a043
5
+ SHA512:
6
+ metadata.gz: eac0b85c2ba9bdfb1f963c8f2238421346590e708507cccf1197b9f8f171115bde6f228e2653118dc16e017de726ab2364acc70e159380dff1a6582f47c63814
7
+ data.tar.gz: 8209e7917136b19b92ee66195a4184a142dcaf5aed18503c2f3bd88b591cc8e4d0e636db3fc35e441013f800ddc19dd2cc38e39f9c282d111acdba3c29c4ea74
data/CHANGES ADDED
@@ -0,0 +1,3 @@
1
+ == 1.0.0 - 22-Jan-2014
2
+ * Initial release.
3
+ * This library replaces win32-changejournal.
data/MANIFEST ADDED
@@ -0,0 +1,8 @@
1
+ * CHANGES
2
+ * README
3
+ * MANIFEST
4
+ * Rakefile
5
+ * win32-dirmonitor.gemspec
6
+ * lib/win32/dirmonitor.rb
7
+ * examples/example_dirmonitor.rb
8
+ * test/test_win32_dirmonitor.rb
data/README ADDED
@@ -0,0 +1,52 @@
1
+ == Description
2
+ A class for monitoring directories on MS Windows.
3
+
4
+ == Installation
5
+ gem install win32-dirmonitor
6
+
7
+ == Synopsis
8
+ require 'win32/dirmonitor'
9
+ include Win32
10
+
11
+ # Wait for a change in your home directory and report any changes.
12
+
13
+ monitor = Win32::DirMonitor.new(ENV['HOME'])
14
+
15
+ # Wait up to 5 minutes for a change.
16
+ monitor.wait(300){ |struct|
17
+ puts 'Something changed'
18
+ puts 'File: ' + struct.file
19
+ puts 'Action: ' + struct.action
20
+ struct.changes.each do |change|
21
+ puts "Change: " + change[0].to_s
22
+ puts "Old: " + change[1].to_s
23
+ puts "New: " + change[2].to_s
24
+ end
25
+ }
26
+
27
+ == Notes
28
+ This replaces the win32-changejournal library and supplants the
29
+ win32-changenotify library since it gives details about the changes
30
+ that occur.
31
+
32
+ == Future Plans
33
+ Allow files in subdirectories to be monitored as well.
34
+
35
+ == Known Bugs
36
+ None that I know of. Please log any bug reports on the RubyForge
37
+ project page at https://github.com/djberg96/win32-dirmonitor.
38
+
39
+ == License
40
+ Artistic 2.0
41
+
42
+ == Copyright
43
+ (C) 2014 Daniel J. Berger, All Rights Reserved
44
+
45
+ == Warranty
46
+ This library is provided "as is" and without any express or
47
+ implied warranties, including, without limitation, the implied
48
+ warranties of merchantability and fitness for a particular purpose.
49
+
50
+ == Authors
51
+ * Daniel J. Berger
52
+ * Park Heesob
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/testtask'
4
+
5
+ CLEAN.include("**/*.gem", "**/*.rbc", "**/*.rbx")
6
+
7
+ namespace :gem do
8
+ desc "Create the win32-dirmonitor gem"
9
+ task :create => [:clean] do
10
+ spec = eval(IO.read('win32-dirmonitor.gemspec'))
11
+ if Gem::VERSION < "2.0"
12
+ Gem::Builder.new(spec).build
13
+ else
14
+ require 'rubygems/package'
15
+ Gem::Package.build(spec)
16
+ end
17
+ end
18
+
19
+ desc "Install the win32-clipboard library"
20
+ task :install => [:create] do
21
+ file = Dir["*.gem"].first
22
+ sh "gem install #{file}"
23
+ end
24
+ end
25
+
26
+ desc 'Run the example program'
27
+ task :example do |t|
28
+ ruby '-Ilib examples/example_dirmonitor.rb'
29
+ end
30
+
31
+ Rake::TestTask.new(:test) do |t|
32
+ t.warning = true
33
+ t.verbose = true
34
+ end
35
+
36
+ task :default => :test
@@ -0,0 +1,23 @@
1
+ ########################################################################
2
+ # example_changejournal.rb
3
+ #
4
+ # A test script for general futzing. Modify as you see fit. You can
5
+ # run this test script via the 'rake example' task.
6
+ ########################################################################
7
+ require 'win32/dirmonitor'
8
+
9
+ puts 'VERSION: ' + Win32::DirMonitor::VERSION
10
+
11
+ monitor = Win32::DirMonitor.new(ENV['HOME'])
12
+
13
+ # Wait up to 5 minutes for a change journals
14
+ monitor.wait(300){ |struct|
15
+ puts 'Something changed'
16
+ puts 'File: ' + struct.file
17
+ puts 'Action: ' + struct.action
18
+ struct.changes.each do |change|
19
+ puts "Change: " + change[0].to_s
20
+ puts "Old: " + change[1].to_s
21
+ puts "New: " + change[2].to_s
22
+ end
23
+ }
@@ -0,0 +1,146 @@
1
+ require 'socket'
2
+ require 'win32ole'
3
+ require 'timeout'
4
+
5
+ module Win32
6
+ class DirMonitor
7
+ class Error < StandardError; end
8
+
9
+ # The version of the win32-dirmonitor library
10
+ VERSION = '1.0.0'
11
+
12
+ # The struct yielded to the wait method
13
+ DirMonitorStruct = Struct.new(
14
+ 'DirMonitorStruct',
15
+ :file,
16
+ :action,
17
+ :changes
18
+ )
19
+
20
+ # The path to be monitored.
21
+ attr_reader :path
22
+
23
+ # The host on which to monitor the path.
24
+ attr_reader :host
25
+
26
+ # Creates a new DirMonitor object which sets the path and hostname
27
+ # on which the filesystem will be monitored for changes. If no hostname
28
+ # is provided, then the current host is assumed.
29
+ #
30
+ def initialize(path, host = Socket.gethostname)
31
+ raise ArgumentError unless File.exists?(path)
32
+ raise TypeError unless path.is_a?(String)
33
+ raise TypeError unless host.is_a?(String)
34
+
35
+ @path = path.tr("/", "\\")
36
+ @host = host
37
+ @conn = "winmgmts:{impersonationlevel=impersonate}!//#{host}/root/cimv2"
38
+ end
39
+
40
+ # A event loop that will yield a DirMonitorStruct object whenever there
41
+ # is a change in a file on the path set in the constructor. The loop will
42
+ # timeout after the number of seconds provided, or indefinitely if no
43
+ # argument is provided.
44
+ #
45
+ # The DirMonitorStruct contains the following members:
46
+ #
47
+ # * file # The full path of the file that was changed.
48
+ # * action # Either create, delete or modify.
49
+ # * changes # If modified, includes an array of changes.
50
+ #
51
+ # If a modification occurs, the 'changes' struct member contains an
52
+ # array of arrays that describes the change. Each sub-array contains
53
+ # three members - the item that was modified, the previous value, and
54
+ # the current value.
55
+ #
56
+ # Example:
57
+ #
58
+ # mon = Win32::DirMonitor.new("C:/Users/foo")
59
+ #
60
+ # # Monitor filesystem for one minute.
61
+ # mon.wait(60){ |s| p s }
62
+ #
63
+ # # Sample struct returned after file was marked
64
+ # # hidden and read-only:
65
+ #
66
+ # #<struct Struct::DirMonitorStruct
67
+ # file="c:\\Users\\foo\\test.txt",
68
+ # action="modify",
69
+ # changes=[
70
+ # ["AccessMask", "2032127", "2032057"],
71
+ # ["Hidden", "false", "true"],
72
+ # ["Writeable", "true", "false"]
73
+ # ]
74
+ # >
75
+ #
76
+ def wait(seconds = nil)
77
+ raise TypeError unless seconds.is_a?(Numeric) if seconds
78
+
79
+ ole = WIN32OLE.connect(@conn)
80
+ drive = @path.split(':').first + ":"
81
+ folder = @path.split(':').last.gsub("\\", "\\\\\\\\")
82
+ folder << "\\\\" unless folder[-1] == "\\"
83
+
84
+ query = %Q{
85
+ select * from __instanceOperationEvent
86
+ within 2
87
+ where targetInstance isa 'CIM_DataFile'
88
+ and targetInstance.Drive='#{drive}'
89
+ and targetInstance.Path='#{folder}'
90
+ }
91
+
92
+ # Asynchronous call. This will let the user break out of it manually.
93
+ sink = WIN32OLE.new('WbemScripting.SWbemSink')
94
+ event = WIN32OLE_EVENT.new(sink)
95
+
96
+ ole.execNotificationQueryAsync(sink, query)
97
+ sleep 0.5
98
+
99
+ event.on_event("OnObjectReady"){ |object, context|
100
+ target = object.TargetInstance
101
+ struct = DirMonitorStruct.new
102
+ struct[:file] = target.Name
103
+
104
+ case object.Path_.Class.to_s
105
+ when "__InstanceCreationEvent"
106
+ struct[:action] = 'create'
107
+ when "__InstanceDeletionEvent"
108
+ struct[:action] = 'delete'
109
+ when "__InstanceModificationEvent"
110
+ previous = object.PreviousInstance
111
+ struct[:action] = 'modify'
112
+ struct[:changes] = []
113
+
114
+ target.Properties_.each{ |prop|
115
+ if prop.Value != previous.send(prop.Name)
116
+ struct[:changes] << [
117
+ prop.Name,
118
+ previous.send(prop.Name).to_s,
119
+ prop.Value.to_s
120
+ ]
121
+ end
122
+ }
123
+ end
124
+
125
+ yield struct
126
+ }
127
+
128
+ # If an argument is provided, timeout after that many seconds.
129
+ if seconds
130
+ begin
131
+ Timeout.timeout(seconds){
132
+ loop do
133
+ WIN32OLE_EVENT.message_loop
134
+ end
135
+ }
136
+ rescue Timeout::Error
137
+ # Do nothing, return control to user
138
+ end
139
+ else
140
+ loop do
141
+ WIN32OLE_EVENT.message_loop
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,103 @@
1
+ #############################################################################
2
+ # test_win32_dirmonitor.rb
3
+ #
4
+ # Test suite for the win32-dirmonitor library. You should run this
5
+ # via the 'rake test' task.
6
+ #############################################################################
7
+ require 'test-unit'
8
+ require 'win32/dirmonitor'
9
+ include Win32
10
+
11
+ class TC_Win32_DirMonitor < Test::Unit::TestCase
12
+ def self.startup
13
+ Dir.chdir(File.expand_path(File.dirname(__FILE__)))
14
+ @@file = 'win32_dirmonitor_test.txt'
15
+ end
16
+
17
+ def setup
18
+ # The thread is used to force an event to happen for the tests
19
+ @monitor = DirMonitor.new(Dir.pwd)
20
+ @thread = Thread.new{
21
+ sleep 2
22
+ File.open(@@file, 'w'){ |fh| fh.puts 'Delete me!' }
23
+ }
24
+ end
25
+
26
+ test "version constant is set to expected value" do
27
+ assert_equal('1.0.0', DirMonitor::VERSION)
28
+ end
29
+
30
+ test "constructor requires a path argument" do
31
+ assert_raise(ArgumentError){ DirMonitor.new }
32
+ end
33
+
34
+ test "path and host arguments must be strings" do
35
+ assert_raise(TypeError){ DirMonitor.new(1) }
36
+ assert_raise(TypeError){ DirMonitor.new(Dir.pwd, 1) }
37
+ end
38
+
39
+ test "path argument must exist" do
40
+ assert_raise(ArgumentError){ DirMonitor.new("C:/Bogus/Bogus") }
41
+ end
42
+
43
+ test "wait method yields a frozen DirMonitorStruct" do
44
+ @thread.join
45
+ @monitor.wait(2){ |s|
46
+ assert_kind_of(Struct::DirMonitorStruct, s)
47
+ assert_equal(['action', 'file', 'changes'], s.members)
48
+ assert_kind_of(Array, s.changes)
49
+ assert_kind_of(Array, s.changes.first)
50
+ assert_true(s.frozen?)
51
+ }
52
+ end
53
+
54
+ test "wait method basic functionality" do
55
+ assert_respond_to(@monitor, :wait)
56
+ end
57
+
58
+ # We provide some very short timeouts here - shouldn't slow the tests down
59
+ test "wait method accepts an optional timeout value" do
60
+ assert_nothing_raised{ @monitor.wait(0.01){ |s| } }
61
+ end
62
+
63
+ test "wait method accepts a single integer argument only" do
64
+ assert_raise(ArgumentError){ @monitor.wait(1,1) }
65
+ assert_raise(TypeError){ @monitor.wait('a') }
66
+ end
67
+
68
+ test "a custom error class is defined" do
69
+ assert_kind_of(Object, Win32::DirMonitor::Error)
70
+ end
71
+
72
+ test "path attribute basic functionality" do
73
+ assert_respond_to(@monitor, :path)
74
+ assert_nothing_raised{ @monitor.path }
75
+ assert_kind_of(String, @monitor.path)
76
+ end
77
+
78
+ test "path method returns expected value" do
79
+ assert_equal(Dir.pwd, @monitor.path.tr("\\", "/"))
80
+ end
81
+
82
+ test "host attribute basic functionality" do
83
+ assert_respond_to(@monitor, :host)
84
+ assert_nothing_raised{ @monitor.host}
85
+ assert_kind_of(String, @monitor.host)
86
+ end
87
+
88
+ test "host method returns expected value" do
89
+ assert_equal(Socket.gethostname, @monitor.host)
90
+ end
91
+
92
+ def teardown
93
+ @thread.kill if @thread.alive?
94
+
95
+ @monitor = nil
96
+ @flags = nil
97
+ @thread = nil
98
+ end
99
+
100
+ def self.shutdown
101
+ File.delete(@@file) if File.exists?(@@file)
102
+ end
103
+ end
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'win32-dirmonitor'
5
+ spec.version = '1.0.0'
6
+ spec.authors = ['Daniel J. Berger', 'Park Heesob']
7
+ spec.license = 'Artistic 2.0'
8
+ spec.email = 'djberg96@gmail.com'
9
+ spec.homepage = 'https://github.com/djberg96/win32-dirmonitor'
10
+ spec.summary = 'A library for monitoring files on MS Windows'
11
+ spec.test_file = 'test/test_win32_dirmonitor.rb'
12
+ spec.files = Dir['**/*'].reject{ |f| f.include?('git') }
13
+
14
+ spec.rubyforge_project = 'win32utils'
15
+
16
+ spec.extra_rdoc_files = [
17
+ 'README',
18
+ 'CHANGES',
19
+ 'MANIFEST',
20
+ ]
21
+
22
+ spec.add_development_dependency('test-unit')
23
+ spec.add_development_dependency('rake')
24
+
25
+ spec.description = <<-EOF
26
+ The win32-dirmonitor library provides a way to asynchronously monitor
27
+ changes to files in a given directory, and provides detailed information
28
+ about the changes that occurred.
29
+ EOF
30
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: win32-dirmonitor
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Daniel J. Berger
8
+ - Park Heesob
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-01-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: test-unit
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - '>='
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - '>='
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ description: |2
43
+ The win32-dirmonitor library provides a way to asynchronously monitor
44
+ changes to files in a given directory, and provides detailed information
45
+ about the changes that occurred.
46
+ email: djberg96@gmail.com
47
+ executables: []
48
+ extensions: []
49
+ extra_rdoc_files:
50
+ - README
51
+ - CHANGES
52
+ - MANIFEST
53
+ files:
54
+ - CHANGES
55
+ - examples/example_dirmonitor.rb
56
+ - lib/win32/dirmonitor.rb
57
+ - MANIFEST
58
+ - Rakefile
59
+ - README
60
+ - test/test_win32_dirmonitor.rb
61
+ - win32-dirmonitor.gemspec
62
+ homepage: https://github.com/djberg96/win32-dirmonitor
63
+ licenses:
64
+ - Artistic 2.0
65
+ metadata: {}
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project: win32utils
82
+ rubygems_version: 2.0.3
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: A library for monitoring files on MS Windows
86
+ test_files:
87
+ - test/test_win32_dirmonitor.rb