Friday, April 29, 2005

Eronel.Common 1.3 Released

That pretty much says it all. The features promised for 1.3 are in, and it passes all the tests so...

Thursday, April 28, 2005

Eronel.Common

I've published a project at ProjectDistributor called Eronel.Common. It's a personal library I've come up with through thinking about useful things for my own projects in addition to what I could use at work.

Currently it revolves mostly around the XmlActivator class since I really needed something like that at work, I've talked about it in the past, it was a small/doable project, and it was just plain fun. :)

My current progress on it is: The next release (1.3) should be out by Monday. I've introduced the CasingStrategies enum which lets you choose between Insensitive, CamelCased, and ExactMatch (to match XmlNode names to MemberInfos in the XmlActivator). I plan to add IDictionary support next, and that's probably it before release. My one static library is called Utility. It's a catch-all for methods that are short, but useful, and I can't think of a good factoring for them as objects. Over time the methods often grow in functionality, or several are added together, and then as a whole they make more sense and I factor them out into their own classes. In my current build I've added the Console helper mentioned in the previous post. I'm not sure what other functionality I want to include in the XmlActivator, so this mini-project (the class, not the library) may be nearing the end.

I had originally thought I might add constructors and method invocations to it, but as it evolves, that seems less appropriate. I'll probably add constructors anyways. It shouldn't be hard, and it removes the current constraint of requiring a default constructor. I might also add support for non-public members. Besides that though, I think it's pretty robust.

One thing I don't have for it right now are a lot of obvious use-cases. I've used it mostly for configuration, though it's been used at work in a prototype parser I think is actually really neat. To use it in configuration right now you have to load up the Xml manually though. I wrote a TypedConfigurationSectionHandler at work to let me just do stuff like the following:

MailSettings settings = (MailSettings) ConfigurationSettings.GetConfig("mailSettings");


It's a lot cleaner IMO than using App.Config. I know VS2005 makes this kind of thing a lot easier, but I don't like the Xml it produces, so I'll probably try and adapt it to my own preferences when I find the time. Plus we're probably a year off from doing much at all with c#2 at work so in the meantime this is pretty useful for me personally.

Going back to that parsing app, it was actually a combination of the TypedConfigurationSectionHandler, XmlActivator, RegexActivator, and a class I wrote called the RegexStreamParser. That last one I'm pretty proud of. It lets you do things like this:

void Run() {
RegexStreamParser parser = new RegexStreamParser("invoices.txt", "(?<data>^begin\sinvoice.*\r\n(^.*\r\n)*?^end\sinvoice)");
parser.FoundMatch += new FoundMatchEventHandler(this.ProcessMatch);
parser.Start();
}

void ProcessMatch(RegexStreamParser parser, Match match) {
using(ISession session = Database.GetSession()) {
Invoice invoice = (Invoice) RegexActivator.CreateInstance(typeof(Invoice), parser.Expression, match);
session.Save(invoice);
}
}


So using NHibernate, and the custom classes, I can write parsers for multi-Gigabyte files. The RegexStreamParser uses another class internally that buffers a file, allows you to resize the buffer on the fly, allows you to advance the buffer n-number of bytes, and allows exposes the buffered byte[] array in a property; this lets me keep memory usage low even on huge files and still be 99.99999999999999999999999% sure that I don't miss any possible matches since a single match spanning more than double the default buffer (it auto-expands/advances in 10% increments (up to 2x for expansions) if no match is found) of 5MB would be insane. Anyways, the point is I can write some pretty wicked, (and performant) parsers for large files with only what, a dozen lines of code in a simple case like above? Like I said, I'm pretty proud of it. :)

So that's probably the direction I'll head with the library after v1.3. Add more examples, and build some "support" classes (including the configSectionHandler, and hopefully a spiffy new implementation of a RegexStreamParser).

After that I might get around to finishing up the UIBroker since I'm over the configuration hump now, and it's already been half-way rewritten anyways. Hard to get motivated about something I don't use though (since I don't really do any business Forms development right now) and don't get any feedback on. :( We'll see...

Sometimes performance means nothing.

The code pretty much says it all here, so let's just dig in m'kay?

namespace Eronel.Common {
public class Utility {
Utility() {}

public static void ExecuteStaticArgsForConsole(Type type, string[] args) {
string option = Regex.Replace(args[0], "^[/-]", String.Empty);
try {
type.GetMethod(option,
BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase).Invoke(null, null);
} catch {
Console.WriteLine("Option not found.");

MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Static);

if(methods.Length > 0) {
Console.Write("Valid options are:");
for(int i = 0; i < methods.Length; i++)
if(i == 0) {
Console.Write(" {0}", methods[i].Name);
} else if(i == methods.Length - 1) {
Console.WriteLine(" and {0}.", methods[i].Name);
} else {
Console.Write(", {0}", methods[i].Name);
}
}
}
}
}
}
}


And you've got a simple little ConsoleApp like so:

using Eronel.Common;

namespace StaticInvocation {

public class Program {
[STAThread]
static void Main(string[] args) {
if(args.Length > 0) {
Utility.ExecuteStaticArgsForConsole(typeof(Program), args);
} else {
Program program = new Program();
program.Run();
}
Console.ReadLine();
}

public static void Howl() {
Console.WriteLine("ARRROOOOOOOH!");
}

public static void Bark() {
Console.WriteLine("WOOF!");
}

public static void Whine() {
Console.WriteLine("No kitty that's mah pot pie!");
}

void Run() {
Console.WriteLine("Program Started...");
}
}
}


Of course you could do more, this is just an example. It works, it makes all those switch statements a thing of the past, and it's easy on the eyes. What more could you want?

Thursday, April 14, 2005

Ruby Strikes Back

Don't you just hate when you spend forever trying to figure out how to do something easy? Trying to get Ruby and Microsoft SQL Server talking was kinda like that for me.

I never could get ODBC support to work, all the examples and newsgroup posts I could find only described using it with a DSN (which I didn't want to do). So I fell back on the ADO DatabaseDriver (DBD).

So what does it take? Download and install the Ruby DBI package. (Just Google for Ruby dbi and you'll find it.) Then config/setup/install. To get it to install I had to rename/delete a number of files in my Ruby installation, but I figure I'm just getting later versions with the dbi install, and so far haven't seen any harm.

Oh, btw, there's a newer version of FreeRIDE to try than what comes with the Ruby installer. Not quite as snazzy as ArachnoRuby, but it's simple and has Ruby Documentation integrated, so it's pretty cool, and is what I'm using for now.

Anyways, once you have DBI, it's actually really easy to use.

require 'dbi'

dbh = DBI.connect("DBI:ado:Driver={SQL Server};Server=127.0.0.1;Database=Northwind;Uid=sa;Pwd=password")
sth = dbh.execute("select * from products")
rows = sth.fetch_all
sth.finish
rows do |row| puts row[:ProductName]


That's it!

Cool huh? Well, it gets mo' betta. Check out this code that basically serializes the Products table from Northwind to an Xml file:

dbh = DBI.connect("DBI:ado:Driver={SQL Server};Server=127.0.0.1;Database=Northwind;Uid=sa;Pwd=password")
DBI::Utils::XMLFormatter.table(dbh.select_all("select * from products"), "products", "product", File.new("products.xml", "w+"))

You could actually do this all on one line if you were a little crazy since the .connect method of DBI returns a database handle that you could call .select_all on directly.

If you look at the above examples you can see, besides "DBI:ado:", the connectionStrings are just standard OLEDB ones you'd find at http://www.connectionstrings.com. So once you know how, it's really simple.

I think this goes to show one of the advantages of a statically typed language though: The learning curve is much lower when you have tools like Intellisense and Type cast compilation errors to help you out.

Anyways, cool stuff. I'm taking my newfound Ruby knowledge and applying it to a report generator for work that will use Xml streams of queries as "Resources", and allow you to use Xslt embedded in the "Job" files to do things like send out an Excel report once a week for SalesPerson Commissions. I think I should be able to replace the existing system (written in c#) of about 500 lines of code, with a Ruby version of about half that, that provides more features to boot.

Sunday, April 10, 2005

Ruby

Ruby is cool. AranchoRuby IDE is definitely the one to go for, if for nothing else than it manages to get auto-tabbing right. I love the fact that RegularExpressions are first class citizens in Ruby, and blocks are cool. (I just started learning yesterday, so sue me ;) )

%w{My name is Sam}.each {|s| puts s}

Results in:
My
name
is
Sam

Is that cool or what? Some people may say "meh", but ignore the example and think about what it's doing instead. In c# you'd have to do something like this:

foreach(string s in new string[] {"My", "name", "is", "Sam"}) Console.WriteLine(s);

If you're at all interested in programming you owe yourself to give Ruby a shot. Goto http://www.rubycentral.com/ and click on the "Ruby Book (old)" link. If you're running Windows just find the windows installer at RubyForge. It comes with the FreeRide IDE, which would be nice enough if it had Arachno's color coding and tab completion. Maybe that'll be added soon.

BTW, if you thought Aspect Oriented Programming was cool, but were frustrated at c#'s roadblocks to it, you'll love Ruby. Runtime mixins of any type you could want, and you can emulate a pointcut through overwriting I think.

UIBroker

I really need to polish off the update I made. It's been languishing on my laptop for a month now. It's a lot cleaner, should be faster too (though I haven't profiled it yet), but since I'm not doing any web/winforms development, I haven't had the motivation to finish it. Ah well. Eventually...

This page is powered by Blogger. Isn't yours?