Thursday, May 05, 2005

Foosball

Someone at our office brought in their own table about a month ago and I've found myself rediscovering the fun of foos. I've also found myself checking into the history of the game. I was quite surprised to learn that in foosball's heyday in the 70's people won cars (porche, corvettes) and quite a bit of money (there was a $1,000,000 tour at one point)!

I even started looking into possibly buying a table. I went down to the local dealer showroom (pool tables, foos tables, poker tables, video games etc) and the salesman there took some time to really show me what makes a good table and what the differences are as you move up in price. Quite the education (much more fun than C#). So I'm looking at a Tornado brand table. The Cyclone or the Storm. We'll see if my fascination with the game lasts long enough for me to plunk down the money...

So do you want to see an interesting video? Check this one out. It's for the Bonzini table not the Tornado. It's amazing to see how well someone can control a foosball.

NeoOfficeJ

I've been using OpenOffice for a couple of years now but the thing I dislike most about it, is that it runs under X11 and just doesn't look or feel like a Mac app. It looks all gray and Windows95-ish and worse, the command keys are all messed up. So I finally tried NeoOffice/J and I'm quite a bit happier. It still looks more like a Windows app than a Mac app BUT the menus are at the top where they should be and the command keys are correct. It's fast, the fonts look better, it has a double clickable application icon and it doesn't use X11. So far so good. And its free. :-)

PSync to the rescue

PSync is a little Perl command app written for Mac OS X that synchronizes two directories. Perfect for doing backups. I finally put my second drive to work as a backup volume. Now when the time is right I can upgrade to Mac OS X 10.4/Tiger and see for myself what all the good press is about.

Two weeks after my C# course

Well all that course really did for me was to get me to appreciate Java more. C# is a pretty good copy of Java but it seems that most of the differences/compromises from Java seem to be for supporting legacy Windows components. I don't think that's a bad thing necessarily but unless you have a huge pile of C++ or COM code lying around, I think the choice of Java or C# would be pretty difficult, especially if you're developing web apps. Afterall, why constrain yourself to one platform and one application server? Also having experienced the limitations of Visual Studio for a week, I know that code editing can be made way more efficient (yay Eclipse!).

I think the place where C#, .NET and Visual Studio shines is in the creation of rich client applications. Dot-NET leverages all the native Windows components so your apps look good and behave well. Rich client .NET apps, like Java apps, seem to take a little while to get going but once you're there you don't really know you're running a .NET app. I think if IBM ever gets their act together and make a decent SWT GUI editor then Java may (finally) be a contender on the Windows platform. But for now MS has this all to themselves

So what have I really done with my new knowledge? Well not much.
  1. I wrote a little application to interface with iTunes (which has a pretty full fledged COM interface). It just grabs the song title and artist of the currently playing track and throws it on the clipboard. Handy for sending someone an instant message.
  2. I also tried out ADO.NET with Mono. Mono ships with a driver for PostgreSQL. So I wrote a little command line app to connect, execute a query, and print out the results. Not too exciting but pretty easy.
  3. I spoke with the instructor about Object Relational Mapping and without any prompting from me, he suggested NHibernate. He's using it on his current project and says it works quite well. I really dig Hibernate so it's good to hear that C# developers can benefit from this technology as well.
  4. I tried to create a GUI using Windows.Forms on my Mac. That was a bust. Apparently it's possible but you have to install a bunch of other junk. I just wasn't that interested.
The next things for me is to probably find a gig where I can really exercise this thing called .NET...

Friday, April 15, 2005

C# Course day five

I installed the latest version of Mono on my Mac and the latest version of NAnt (an open source build tool) and managed to compile some of the Lab sample code from this course.
  • System.Threading namespace for creating threads, mutexes, etc.
  • Thread class takes a ThreadStart object in its constructor. A ThreadStart class is a delegate that points to the method you want to run in the thread. Call Thread.Start() to actually kick things off.
  • There are foreground and background threads and a Thread.IsBackground property. An AppDomain will not close as long as there is one foreground thread.
  • Signalling between threads can be done with the WaitHandle class.
  • You can create thread local storage by putting an attribute on a static variable [ThreadStatic] public static in count;
  • Visual Studio kinda sucks for working with Threads. Eclipse is better for this.
  • You can call Thread.Abort() to abort a thread, however a magic ThreadAbortException will get thrown and rethrown out of any catch block unless you call ResetAbort().
  • To Synchronize a method use [MethodImpl(MethodImplOptions.Synchronized}] I think its easier to use Java's synchronized keyword.
  • Can use lock{...} to define a critical section.
  • The Interlocked class's methods can create more optimized locking for simple operations on shared data like increment and decrement.
  • Can use a Windows OS Mutex to lock across processes. A Mutex is more expensive to use.
  • A ReaderWriterLock allows multiple reader or only one writer at a time.
  • The Timer class uses a delegate to call a callback method on a particular interval
  • There is a ThreadPool class that is handy for executing short running methods in the background with a normal priority.
  • IOCompletionCallback is used for processing data in a thread from the pool after an IO operation.
  • Use syntax like [DllImport (, Entrypoint=, CharSet= MyMethodName([...]); to call externall DLL methods.
  • tlbimp.exe is for importing a COM DLL.
  • There are roughly 10-40 instructions called when going from/to .NET from/to COM, not including the data marshalling. It is expensive so be careful.
  • SQL & Oracle clients are written all in managed code (kinda like JDBC drivers) & are much faster than going through OLE DB drivers

Thursday, April 14, 2005

C# Course day four

Wow. The Serialization and Remoting functionality in C# looks just like Serialization and RMI in Java.


  • Serialization is marked with an attribute [Serializable]
  • Have the option of Serializing to XML (with a SOAP flavour)
  • Remoting has the option of using HTTP, XML & SOAP or a binary protocol. Unfortunately the XML option isn't very interoperable with frameworks other than .NET.
  • use {WebService(Namespace=http://www.example.com/endpoint)] attribute for declaring web service
  • use [WebMethod] attribute for declaring exposed methods
  • Suggest creating a virtual directory in IIS before creating Web Service project in VisualStudio so that you can control where the IDE puts your files.
  • Can use WSDL.exe to read a wsdl XML file exposed through IIS to generate client proxies.
  • When adding reference in Visual Studio you can change a property of the reference to dynamic so that the URL is stored in a config file rather than in the C# code.

So to summarize:

  • .NET Remoting isn't getting much attention from MS
  • The Web Services available in Visual Studio are good for simple stuff without transactions, security, reliability, etc.
  • WSE 2.0 (Web Services Extensions) introduces transactions etc but there isn't and IDE support
  • WSE 3.0 still being defined
  • Indigo a MS term for their technology to make "serious" web services with transactions, security, etc. commonplace and easy to build with Visual Studio (1-1.5 years away)

Wednesday, April 13, 2005

C# Course day three


  • COM is done

  • Classic VB (6) is done

  • Big sell job on garbage collection. Commentary: It's good (I am a Java developer after all) and yes you can introduce bugs doing it manually but most of the time developers are quite capable of managing memory.

  • GC does heap compaction to deal with memory fragmentation

  • destructors/finalizers in C# look like ~ClassName(){} and are used for implicitly freeing resources. More important in C# than Java because of Windows' finite resource limitations (e.g., Fonts)

  • Unreachable objects with finalizers are moved to freachable queue & finalizers are not called on GC thread. Finalize thread comes along and calls finalizers and then on second GC run the object is collected. Which all means objects with finalizers stay around longer. Secondly their generational flags are bumped up meaning they're more expensive to clean up (i.e., more exhaustive GC search required). Don't use finalizers if possible.

  • C# idiom is to provide an explicit method for freeing finite resources as well as an implicit destructor. If using the explicit method then call GC.SuppressFinalize(object) to prevent the GC from duplicating the work. The explicit method should be an implementation of IDisposable. Eclipse's SWT suffers some of this same dispose problem because it uses native Windows resources. Ugh!

  • Finalizers are not guaranteed to run (like Java) so avoid them if possible.

  • A try-finally block for freeing allocated resources can be replaced with something like using(Resource r1 = new Resource()){}

  • WeakReference class allows an object to be collected if memory is low. Used for something like caching.

  • Can use the Windows Performance Monitor (i.e., perfmon) for viewing the CLR Memory. Pretty graphs... ;-)

  • There is multiprocessor support but uses a different CLR DLL (i.e., Workstation and Server). All console apps run using the Workstation VM unless you make some config changes.

  • An unsafe block is available for executing code and preventing the GC from moving objects around in memory. This is important for calling C DLLs for example. Looks like unsafe{}

  • IO Streams are very similar to Java. Functionality is added to the basic stream using the Decorator pattern. And there are Reader and Writer classes for dealing with text. C#'s one major difference is that there is just Stream not InputStream and OutputStream. Instead you define read or write using constructor parameters.

  • There is a File and a Directory class is C#. Java only has a File class with an isDirectory() method. The File class has some handy Factory methods for creating Streams.

  • There is a utility Path class for manipulating path strings.

  • FileSystemWatcher allows you to eliminate directory polling and receive events for things happening to a file or a directory.

  • IsolatedStorage is a handy class for providing 10MB of storage on the local file system for applications started from an http address. The data is stored in "c:\Documents & Settings\\LocalSettings\Application Data\Isolated Storage\". The only problem is that the directories in here are cryptically named.

  • CredentialCache class useful for getting the credentials of the current user.

iPod Shuffle on Windows XP (at last)

During my C# course I tried plugging my iPod shuffle into the USB port of my Windows XP machine and voila! It mounted like a hard drive without any trouble at all. I could drag and drop files onto it and even see all my MP3s. Very nice. Now if I can only figure out why my Windows machine at work can't see it I would be very happy.

Tuesday, April 12, 2005

C# Course day two

Not a single Java comment today. Amazing.


  • System.Globalization for i18n (internationalization)

  • String.Format does println kind-of functionality. The patterns are a bit different however "The value is {0,-10:f2}"

  • Did my first lab with Visual Studio. Struggled a bit with the syntax differences. Things like defining Properties in an Interface, or operator overloading, overriding methods (stupid virtual/override keywords), and defining an explicit cast operator.

  • Eclipse is a way better code editor than Visual Studio. Studio may have other advantages (like visual form editing) but Eclipse owns this one.

  • Can use syntax like @"c:\folder\folder2\file.txt" so that you don't have to escape each back slash. Well maybe if MS used a forward slash in the first place this wouldn't be an issue ;-)

  • C# supports regular expressions that are designed to be compatible with Perl 5.

  • IComparable = Java's Comparable interface and IComparerer = Java's Comparator

  • There is not equivalent of Java's Set collection in .NET. Seems like an odd omission

  • Can use the indexing syntax of square brackets (e.g., collection[2] ) with both hashmaps and lists.

  • ArrayList.ReadOnly(list) is an easy way to make an immutable collection

  • System.Collections namespace contains a StringCollection that is specially tuned for dealing with strings.

  • Can call delegates asynchronously with IASyncCallback

  • Can use += and -= instead of the methods Combine() and Remove() respectively for multicast delegates.

Monday, April 11, 2005

C# Course day one

A day-by-day, blow-by-blow account of my Microsoft C# course. I'll update this particular entry throughout the day.

First of all, why would I attend a C# course when I've been a Java guy for so long... Well this just comes down to ensuring that I'm open to all technologies. I know people who talk poorly about technologies that aren't their favorite and usually they bash away without knowing what they're talking about. Personally I like to bash from a position of knowledge not ignorance. ;-)

Secondly, I already looked at C# about a month ago and was fairly happy with what I saw. I'm really only lacking some knowledge about the frameworks.

Finally, it's free. A company I worked for about two and a half years ago already paid for this so why not take advantage of it.

So now onto the course...

  • 10:30 AM I've learned nothing so far. Boring.

  • I've sat quietly through some Sun, Java bashing from the instructor no less. That was a bit irritating. That will definitely come up in my evaluation.

  • 12:16 PM Namespaces usually take the form of CompanyName.Application.Layer (e.g., Sundog.Retail.Data)

  • Can have multiple entry points in an app (many classes with Main methods) just need to specify which one in compiler settings

  • csc.exe is the command line compiler

  • a "module" is equivalent to a Java .class file

  • an "assembly" is equivalent to a Java .jar file. Contains a Manifest file that specifies version and other meta data.

  • ilasm.exe produces assemblies (not sure about this...)

  • ildasm.exe is a disassembler to look at manifest and generated MSIL code. ".NET Reflector" is a 3rd party tool that disassembles into C#, VB, or Delphi. Obfuscator may be good for commercial .NET

  • Can use ngen.exe to precompile assemblies into native code. Not always better.

  • Application Domains are like separate processes for different applications in one VM. It's done programatically so usually used by app server not individual apps.

  • Can create C# alias when multiple classes from multiple namespaces have the same name. e.g., using CSStringComp = CompCS.StringComponent;

  • 4:11 PM ASP Output cache allows the reserving of previously generated output. If the data's not changing quickly, this may be useful. Also works with page fragments.

  • .NET Framework around 24MB. How does that compare to Java. Everyone used to complain about how bit it was.

  • App can be launched from HTTP or File server and execution rights are reduced unless app is signed. Very much like Java WebStart.

  • No DLL Hell. Assemblies are typically deployed privately (in the same dir as the exe).

  • There is a "Gloabl Assembly Cache" (GAC) in c:\windows\assembly which is shared and can contain multiple versions of assemblies.

  • Assemblies must be signed to go into the GAC. Apps are compiled against a specific version. Versioning is much better handled in .NET than Java.

  • Visual Studio has a Setup project that can be added to a solution to create an MSI file (MS Installer). Nice touch.

  • Java got bashed a couple more times and I had to correct the instructor and let him know that autoboxing, enums and attributes are all part of Java and that the default behavior of virtual methods isn't necessarily a bad thing. Jeesh. I hope he shuts up now. ;-)

  • C# has structs but the instructor and apparently MS discourages using them. You don't get a performance boost and they're less flexible. Use only when you're interfacing with legacy code.

  • enums are also good for interfacing with legacy code. The [Flags] attribute can be used to assign bit values.

  • The internal keyword marks something for use by classes in the same assembly. Not quite the same as the package scope in Java.

  • Use Object.ReferenceEquals() instead of Equals() (unlike Java). Here == and Equals() should mean the same thing.

  • Can overload operators (+,-,*,/,%,<,>,==, etc) in C# using something like static public bool operator == (Class obj1, Class obj2){[...]}

  • ToString() is here too

  • Instructor made a big deal that implementing a Singleton in Java was hard. Here's an article he was referencing. Basically you do this in C#: public static readonly ExampleClass Instance = new ExampleClass(); Well in Java you do private static ExampleClass instance = new ExampleClass(); and provide a static accessor.

  • Instead of using ExampleClass.class in Java you use typeof(ExampleClass) in C#.

  • Operators like is, as, and typeof for working with Types.

  • generics will show up in .NET 2.0. Already there in Java 1.5, but Anders Hejlsberg (MS .NET Architect) suggests that the Java implementation isn't as good, primarily because Sun chose not to introduce new bytecodes in the VM to implement this functionality.

  • Can implement two interfaces that specify the same signature by explicitly defining the name of the interface in the method implementation.

  • pinvoke is the way to call "classic" DLLs. Looks much easier than JNI.

  • For COM compatibility .NET objects can be exposed as COM objects and COM objects can show up as .NET classes. Very reminiscent of Delphi.

Thursday, April 07, 2005

Libraries

Apparently someone was dumping some older computer books by donating them to our local public library to sell. Well I happened along and picked up the "pickaxe" book Programming Ruby (1st edition), Learning the bash Shell, and Learning Python (the last two from O-Reilly) for the princely sum of $1.50! Sweet! BTW: bash is the default shell and both Python and Ruby are installed with Mac OS X. Naturally. ;-) Public Libraries truly are wonderful things!

Wednesday, April 06, 2005

Those Damn Liberals

Over the last few days the Canadian press has been talking about an American blogger dishing the details about Jean Brault's testimony at the Gomery Inquiry into the federal sponsorship scandal. So I went over to Google, did a little search and found the blog that the press was talking about. If some of that stuff is correct then the Liberal party and former prime minister Jean Chretian are going to have a lot to answer for. It must be nice to be Stephen Harper right now...

Hibernate lets me down...

Well the new version of Hibernate is out there and apparently version 3.0 has been seriously worked over by the Hibernate team. Specifically I'm referring to the brand-new ANTLR-based HQL/SQL query translator... but back to that in a second.

I generally keep up with the latest version of Hibernate and up until I tried 3.0 it was a no-brainer. You just downloaded it and everything just worked. Not this time:

First of all, the packages all changed from net.sf.hibernate to org.hibernate. While it's nice that the package reflects Hibernate's domain I don't think this change really needed to be inflicted on its users. TopLink did that to me when it changed hands from The Object People to WebGain and then to Oracle. It's a pain in the ass.

Secondly, "Since it is best practice to map almost all classes and collections using lazy="true", that is now the default." While I agree with the point, I was a little annoyed to discover that much of my second level caching stopped working. Easy enough to fix but still...

Thirdly, back to the new HQL/SQL Query translator. It fails to parse several of my existing queries. Damn. Worse is that the documentation says I can set the hibernate.query.factory_class property to use the ClassicQueryTranslatorFactory but that doesn't work either. So I'm forced to use native JDBC calls instead. That's what Hibernate is supposed to help me avoid!

Finally, Hibernate 3.0 has a "revamped Query API". This was my whole reason for trying Hibernate 3.0 and unfortunately when I use criteria.createCriteria() or criteria.createAlias() the alias for the joined table doesn't show up in my generated SQL!!

Agghhh! I still like Hibernate (most of it still works) and I'm going to stick with v3 but this was not what I've come to expect of the Hibernate team. It sounds like the HQL/SQL parser thing was the right choice but this release needed a little more time in the oven.

Cocoa Core Data

When Apple introduced the binding controllers in Panther (Mac OS 10.3) they eliminated a lot of the glue code people used to write to keep the model and view of their applications in sync. Now they're aiming to make the persistence of the model simple too with Core Data, a new framework in Tiger (Mac OS 10.4). This article on Apple's Developer Connection website explains it with a bit more detail. While it somewhat resembles Object Relational mapping it looks to be a file based persistence mechanism. The developers of Delicious Library had good things to say about Cocoa Bindings:
We rewrote everything in a day or two—I think we deleted over a thousand lines of code that just wasn’t needed any more.

...so I'm hoping that Core Data will be equally well received and productive.

Tuesday, March 22, 2005

SSH

Although I haven't written anything here about SSH before I have written e-mail to friends about the usefulness of this great Unix command line utility. Basically SSH stands for Secure SHell. It differs from your normal shell in that it uses encryption. Now while in and of itself encrypted communications is a good thing, the thing that has made SSH so fantastic is it's ability to tunnel TCP/IP ports over the SSH connection. What does that mean? Well I have access to my work computer via Citrix. From that machine, through a cygwin initiated SSH session back to my home computer I can tunnel traffic to various services around the network. Like what you ask? How about Oracle, SQL Server, Tomcat, IMAP Mail, CVS, etc. anything with a TCP/IP port.

Now finally I'd like to point out that you can do all this with Mac OS X out of the box by simply clicking a checkbox. But you can do a few more things to make things more secure. One of those things is to use public-key based encryption which relies on not just a password but a encrypted public key as well (i.e., something you have and something you know). Checkout this series of articles for more information. They're Mac-centric but the content is fairly generic.

Monday, March 21, 2005

Scripting

One of the Pragmatic Programmers' rules is
Don't Use Manual Procedures.

A shell script or batch file will execute the same instructions, in the same order, time after time.

The most common way I use scripting is to use Ant to create build scripts for my Java projects. Any IDE will do a lot of the things an Ant script typically does, but an Ant script is far more portable and can be scheduled to execute without a GUI (e.g., automated nightly builds).

After using Ant, I looked at languages like Ruby and Groovy. In the beginning I was mostly interested in them from a bit of an acedemic perspective (i.e., dynamic languages vs strongly typed languages) but I've also used them for automating the occasional non-visual tasks.

Lately I've come to realize that there are also plenty of tasks that could benefit from scripting GUI applications. Afterall there is plenty of functionality in GUI apps that I may want to leverage in a routine way. Enter Applescript. Somewhat surprisingly, Mac OS X's user interface and most Mac applications are pervasively scriptable. In fact just drop into XCode to build a native Mac OS X Cocoa application and your application will be Applescriptable out of the box. You can call command line scripts from Applescripts and you can call Applescripts from command line scripts. The best of both worlds.

Applescript first shipped with Mac System 7.5 in 1994. Having done some HyperCard development back in University in the late 80's I can say that Applescript seems to share a syntactical heritage with Hypertalk. For you non-Apple folks that means that Applescript attempts to be English like in its syntax in an effort to improve readability. So for example a simple script to get the Mac to move a file around might look like this:
move file "Bob" of startup disk to folder "Joe" of startup disk

I'm just starting to get into Applescript and while it's unfortunately one of those technologies that's platform dependent, I figure I should probably get somewhat comfortable with it since it's everywhere in my current OS of choice. I'll write up another entry if I discover anything particularly interesting.

[Update] Here's an article espousing the goodness of Scripting/Dynamic Languages.

Friday, March 11, 2005

Eclipse Icons

I was building a web application yesterday and decided I needed some icons to replace some buttons on one of the pages. So I started googling without much success. Although I could find the odd icon or two that would work, they were different sizes, had different colour palettes etc. They just didn't come across as being part of a coherent design.

Then I was looking at Eclipse thinking I want something like that. After a little investigation I discovered where Eclipse stores its icons. Take a look in any of the plugin subdirectories that have "ui" in their names and you'll frequently find icons in a web-friendly GIF format ready for pillaging. (e.g., [ECLIPSE_HOME]/plugins/org.eclipse.jdt.ui_3.0.1/icons/full/etool16). With some of the basic icons and some of the overlay images (e.g., [ECLIPSE_HOME]/plugins/org.eclipse.jdt.ui_3.0.1/icons/full/ovr16) and a little bit of photoshopping you can create some nice consistent icons for your web application.

Tuesday, March 08, 2005

Writing "Software"

I think part of being a good father is helping your kids understand what you do for a living. Well I write software but despite my best efforts to explain that to my six year old son he didn't quite get it. A couple of weeks ago he said something like "I can't believe someone pays Daddy for writing software". Of course my mother in law chimed in with "I can't believe it either". Nice. So I let that one go.

But yesterday, he said something like "I don't think I'd like writing software all day. I think I'd get bored." Then at supper I was trying to explain that it's not so bad and that I try to learn something new everyday to make it interesting. And he said "Well I guess you'd learn how to spell 'software'".... Wait a sec.... He thought I was writing the word "software" over and over again, like I was in some kind of permanent high school detention. LOL. From his perspective that's what writing is so it made sense (sorta). Well now I 'll have to try and sit him down and show him what I really meant. I don't think my mother-in-law would sit still for it. I'll save that conversation for another day.

Friday, March 04, 2005

Internet Explorer Sucks

I'm trying to put together a little HTML page with some Javascript to hopefully give my users a slightly better experience and I am brutally reminded why I hate using Javascript and the HTML-DOM, it just doesn't work.

Okay it works, but it only works in Firefox right now. Following good development practices I started with the browser that best supports the standards and that's Firefox. After messing around for a couple hours and getting a half-assed simulation of a popup menu, I tried it in Microsoft's crappy Internet Explorer to discover it does nothing. I don't know why yet, but I'd wager it has something to do with IE's lack of standards support. I'm no Javascript guru so this is frustrating enough already but to have IE flip me the middle finger is just infuriating. FOR THE LOVE OF GOD!! IE SUCKS!

Monday, February 28, 2005

Tapestry and Eclipse

So first Tapestry... It's an alternative Java web application framework that is now hosted on the Jakarta Apache website. I don't know if I'm reading anything into the fact that it's an Apache project right beside Struts. It just surprised me a bit.

It's certainly a lot different than Struts and maybe that's not a bad thing. I haven't seriously gotten into it yet but one of the things I like is that you don't replace HTML tags with some proprietary tags (unlike ASP, JSP, PHP, etc.). That means that you can use whatever HTML editor you want, including WYSIWYG editors. A lot of Java people I know dismiss WYSIWYG editors but I actually like them. Anything that allows me to design (in the graphical sense) without writing code can't be all bad. So far all I've done is the requisite "Hello World", but so far so good. BTW, the next version of Tapestry includes HiveMind, one of those Dependency Injection frameworks everyone loves.

As for Eclipse I borrowed a book from the library this weekend called Contributing to Eclipse: Principles, Patterns, and Plug-Ins. One of the interesting things is that the authors are Kent Beck and Erich Gamma. Yes that Kent Beck and Erich Gamma! Big names in the geeky programmer realm. The book starts off with some background of Eclipse. When you use the Java IDE project within Eclipse it's so easy to forget that it was intended as a programming language neutral environment and that everything in Eclipse is a plug-in. Everything! Very cool. I already have some ideas for plug-ins but we'll see how the rest of the book goes.