What's Up With the dispose Method?
In .NET there is an interface IDisposabledispose method when you're done with the object.
In the case of the FileSystemWatcher, this is important because this class uses what is called "unmanaged resources". These are resources that are beyond the control of the CLR and its garbage collector so it's very difficult for the CLR to clean up these resources. When you leave this program running for a sufficient amount of time to watch a folder that has a sufficient amount of files, you will have all these file handles lying around to objects that may or may not still exist on the disk.
When you're using events, it might also be a good idea to detach those in the dispose method. Not detaching event handlers in applications that run for longer periods of time is one of the most common sources of memory leaks in services.
Neglecting to use the dispose method can be seen as the equivalent of borrowing someone's car and returning it dirty with an empty gas tank. The CLR and its GC perceive this is anywhere from mildly annoying (memory leaks) to thoroughly aggravating. It may also cause your program to stop working correctly.
Now that we've now gotten all the bits and pieces together for our file system watcher DSL, I'm curious to find out if it actually works. Listing 10 shows the code for a very small test program that takes two commands. The first one is hit, which will create a file in the folder tmp. The other one is exit, which quits the application.
require 'lib/fs_watcher'
watchers = filesystem do
watch(File.expand_path(File.join(File.dirname(__FILE__), "tmp"))) do #A
on_change { |args| puts "changed #{args.full_path}" }
end
end
fpath = File.expand_path(File.join(File.dirname(__FILE__), "tmp", Time.now.strftime("%Y%m%d%H%M%s")))
puts fpath
while cmd = gets.chomp
exit(0) if cmd == "exit"
open(fpath, 'w'){ |f| f.puts '"Mr. Worf, scan that ship." "Aye Captain. 300 dpi?"' } if cmd == "hit"
end
#A Defining file watch
This little program has a quirk in Mono because it doesn't show the characters I type on Mac. On Windows, it shows fine and it might already be fixed in Mono by the time you read this. So the output I see is:
+ivan@ivan-mbp:~/projects/ironruby-in-action/Samples/fs_watcher (master)» ir test.rb /ironruby-in-action/Samples/fs_watcher/tmp/200911271311s changed /ironruby-in-action/Samples/fs_watcher/tmp/200911271311s changed /ironruby-in-action/Samples/fs_watcher/tmp/200911271311s changed /ironruby-in-action/Samples/fs_watcher/tmp/200911271311s
This shows the event handlers being raised all three times I typed the hit command.
Summary
In this article, we looked at building a DSL to improve the behavior of the .NET FileSystemWatcher. We've seen how we can leverage various metaprogramming techniques to manipulate objects and how we can use the flexibility of the Ruby language to build a proper DSL for watching the file system for changes. The key takeaway here is that with a little effort you can make most of the less friendly APIs in .NET easier by giving them some Ruby attention.


