Sunday, January 11, 2009
Friday, January 02, 2009
Another "Dead Java" Proclamation
{
I was browsing to Elliote Rusty Harold's blog to see if he had any predictions for XML in 2009 and to my surprise I found his post: "Java is Dead. Long Live Python!"
My familiarity with Harold comes from a time in my life where I worked heavily with XML and later Java and it was his books on both subjects that proved invaluable when I hit my rather frequent roadblocks. As invested as he is in Java, what would make him say as much? In a nutshell, it's the way the language has been implemented over the last few years - claims which, even if you don't agree with the premise that Java is "dead like COBOL", are worth some attention.
What is interesting to me about Java right now is not the language itself but dynamic languages and their platforms built on top of the JVM. JRuby is what comes to mind first and I'm recently interested in what a JRuby/Glassfish world looks and feels like.
}
Wednesday, December 31, 2008
Using SGMLParser With IronPython
{
Mark Pilgrim's excellent Dive Into Python has a section on using SGMLParser and having seen nothing similar (and imagining its many uses!) I thought I'd give it a whirl in IronPython. I thought a good proof of concept would be creating a database out of link heavy sites. Since I visit Arts & Letters Daily every so often and the closet intellectual in me likes to hang onto what I find there, I thought I'd target it:
import urllib2
import sgmllib
from sgmllib import SGMLParser
import clr
clr.AddReference("System.Data")
clr.AddReference("System.Net")
from System import *
from System.Data import *
from System.Net import *
class AlReader(SGMLParser):
def reset(self):
SGMLParser.reset(self)
self.urls = []
self.pieces = []
self.track = 0
self.prePend = "No Category"
self.counter = 0
def start_a(self, attrs):
href = [v for k,v in attrs if k == "href"]
key = [v for k,v in attrs if k == "name"]
if href:
self.urls.extend(href)
self.track = 1
elif key:
self.prePend = attrs[0][1]
def handle_data(self, text):
if self.track:
self.pieces.append("|".join([self.prePend, text]))
self.counter = self.counter + 1
def end_a(self):
self.track = 0
def get_links(self):
links = []
for i in range(0, len(self.urls)):
links.append("|".join([self.pieces[i], self.urls[i]]))
return links
#print "%s %s" % (self.counter, "Total links")
def get_link_datatable(self):
d = DataTable()
d.Columns.Add(DataColumn("Category", Type.GetType("System.String")))
d.Columns.Add(DataColumn("Site", Type.GetType("System.String")))
d.Columns.Add(DataColumn("Url", Type.GetType("System.String")))
for text in self.get_links():
newRow = d.NewRow()
newRow["Category"], newRow["Site"], newRow["Url"] = text.split("|")
d.Rows.Add(newRow)
return d
response = urllib2.urlopen("http://www.aldaily.com")
a = AlReader()
a.feed(response.read())
linkdata = a.get_link_datatable()
# write it out to prove we got it.
ds = DataSet()
ds.Tables.Add(linkdata)
ds.WriteXml("c:\\temp\\arts and letters links.xml")
If you find tihs interesting do make sure you look at Pilgrim's chapter on HTML Processing.
}
Saturday, December 20, 2008
Parameterized IN Queries
{
I haven't listened to the podcast yet but saw a cool trick from Joel Spolsky on approaching parameterized IN queries. Purists will bemoan its lack of premature optimization but I think it's novel enough to study because of the approach: using the SQL LIKE operator on your parameter rather than a field, which is what people like me are used to. There's code on the StackOverflow post but I thought I'd paste some of the poking around I did in Sql Management Studio:
-- setup
CREATE TABLE Person
(
PersonID INT IDENTITY(1,1) PRIMARY KEY,
FirstName VARCHAR(50) NULL
)
GO
-- some data
INSERT INTO Person VALUES('David')
INSERT INTO Person VALUES('Jonathan')
INSERT INTO Person VALUES('Zlatan')
INSERT INTO Person VALUES('Trilby')
-- here's the magic
DECLARE @FirstName VARCHAR(50)
SET @FirstName = '|David|Trilby|'
SELECT * FROM Person WHERE @FirstName like '%|' + FirstName + '|%'
-- ported to a proc
CREATE PROC uspPersonSelector
@FirstNames VARCHAR(500)
AS
SELECT * FROM Person WHERE @FirstNames like '%|' + FirstName + '|%'
GO
-- showing it works
uspPersonSelector '|David|Trilby|'
-- somewhere in the netherworld of C#:
/*
string[] names = {"David", "Trilby"};
SqlCommand cmd = GetACommandFromSomewhere();
cmd.Parameters.AddWithValue("@FirstNames", "|".Join(names));
*/
--teardown
Drop Table Person
}
Friday, December 19, 2008
Programmers as Goalkeepers
{
The 8th annual New York Times magazine Year in Ideas featured a section on Goalkeeper Science profiling this paper by some Israeli scientists called Action bias among elite soccer goalkeepers: The case of penalty kicks. In looking at the approach of keepers in some 286 penalty kicks they found that though 94 percent of the time they dived to the right or left, the chances of stopping the kick were highest when the goalie stayed in the center. The researchers theorized that the reason keepers behaved in this way was that they were afraid of appearing that they were doing nothing.
Immediately I remembered a blurb from an Paul Graham's What Business Can Learn from Open Source essay where he expressed a similar dynamic for programmers:
"The other problem with pretend work is that it often looks better than real work. When I'm writing or hacking I spend as much time just thinking as I do actually typing. Half the time I'm sitting drinking a cup of tea, or walking around the neighborhood. This is a critical phase-- this is where ideas come from-- and yet I'd feel guilty doing this in most offices, with everyone else looking busy."
I wonder what Paul would say about the IBM commercial on ideating in which concludes that people should "start doing" after showing an image of people laying inert on an office floor, a stark portrayal of how a manager at IBM might see someone like Paul Graham.
As programmers much of what we should do may not appear to be work for the nonprogrammer and as a result many of us end up doing it at home. I spend a lot of time at home exploring different technologies in a kind of tangential approach that wouldn't look like "working" at work but often my best ideas and solutions come from here. I also spend a lot of time reading technical books and blogs.
I'm wondering what it would look like if we could step back and look in a quantitative way at the performance deficits resulting from the desire to look busy at work. What would the workday look like? I'm wondering what an hour for reading, a few hours for exploratory/research programming, and the rest as project time would do for my own productivity.
If programming was goalkeeping was programming, Edwin Van der Sar would be quite the Python hacker.
}
Thursday, December 18, 2008
Windows Forms + Web, WIB part II
{
A while back I made the case for applications that put together the strengths of Windows Forms and Web technologies (I thought of the catchy "WIB" as a name for this approach). The example I’d given then was a Windows Forms hosted Web Browser for local images that one could use for annotation that leveraged Windows for local file storage and a Web technology like jQuery for doing transitions in the user interface.
Today I thought of another use for this approach that wrapped itself nicely into a tool I've been using for some time to download mp3s from a given website1,2. I call the tool "Fortinbras" and if you find it useful I'd be delighted.
So how was Fortinbras changed?
Parsing the HTML for mp3 files was a little tricky. My initial approach was to use a regular expression against the text of the document which, truth be told, is a brittle approach. Part of why I never trumpted the tool was because I never completely perfected this tactic (while it worked well enough for me personally). My code looked as follows:
WebClient wc = new WebClient();
string pageText = wc.DownloadString(browser.Url.ToString());
Regex re = new Regex("href=\"(?<url>.+?mp3)\"", RegexOptions.IgnoreCase);
Match mp3Matches = re.Match(text);
while (mp3Matches.Success)
{
string matchUrl = mp3Matches.Groups["url"].Value.ToString();
AddMp3(browser.Url.ToString(), data, matchUrl);
mp3Matches = mp3Matches.NextMatch();
}
Today my epiphany was that I didn't need to use a regular expression when I could use the DOM from Windows Forms to pull out the anchors that have mp3 destinations. Here's what that looks like:
while (browser.Document.Body == null)Application.DoEvents();
HtmlElementCollection anchors = browser.Document.Body.GetElementsByTagName("a");
foreach (HtmlElement anch in anchors)
{
string linkUrl = anch.GetAttribute("href");
if (linkUrl.ToLower().EndsWith("mp3"))
{
AddMp3(browser.Url.ToString(), data, linkUrl);
}
}
As usual feedback is welcome - you can download a copy of the Fortinbras project here.
1I am aware of the Firefox extensions that do this but someday (imagine a pie in the sky look on my face) I was hoping to incorporate a "favorites" list with URLs / locations so that this would be a one stop shop for my downloading and organizing of podcasts. My goal here is embarrasment driven development so I'll probably be bummed enough about the code I've just posted to put in some enhancements as time permits.
2My friend's music blog is a great stop, try a couple of tracks at The Look Back.
}
Wednesday, December 17, 2008
Growing Open Source Community
{
Kevin Dangoor recorded an interesting screencast on some of the essentials of getting an open source project more widely circulated. Essentially Dangoor explains that having a successful open source project is not just about code, it's about good product management. I wanted to title this post with Dangoor's most quotable quote: "Rails is not where it is because of great code." But there are a lot of people who would take that as an opener for a religious war without seeing his real intent of highlighting marketing and management of a project.
I won't rehash it, it's worth watching it for yourself.
}
Tuesday, November 25, 2008
Bruce Eckel's Python Book
{
It escaped my notice but Eckel's been writing a new Python book. Should be worth digging into sometime in the future.
}
Sunday, November 23, 2008
How to steal time: On Programming and being a Father
{
It's been alluded to but here it is neither shaken nor stirred: I became a father exactly on month ago. While it hasn't been without challenges the joy of being a father is something I understand now firsthand after being told or observing it in others. Even though I first was quite seduced by a "mystique" in programming - a nocturnal person drinking jolt with binary being reflected from mirror shades - I now realize not only that nearly every programmer and professional person I admire is a parent, but that they find their core of happiness in that well beyond the realm of work.
Nevertheless, I have been trying to keep up my personal work and interests so with that goal in mind I've adjusted my schedule to wake up as early as I can (usually 5am) and get a few hours of solid time while leaving the evenings as a time when I can help out and be a "family man." It still doesn't feel natural but the payoff is that I can still advance my work with IronPython and other interests. It's already been pointed out to me that my productivity here and other places has dropped since my daughter was born.
So question for other programmer/dad people: how do you work the family schedule? Any tips and tricks for a one month father who is still daydreaming about what it was like to sleep all night?
}
Monday, November 10, 2008
Loop Reordering, Optimizing Face Recognition
{
Got this a few weeks ago from a friend but didn't have a chance to sit down and digest the entry until tonight. Pretty cool and although premature optimization gets a bad rap, it's a reminder that sometimes what seems intuitive and quick could actually be costing a lot -
}
Wednesday, November 05, 2008
Lake Wobegon Distribution
{
Not sure where I saw this one first but it's a great article. I wonder if the Lake Wobegon distribution applies as well to programmers...
}
Friday, October 31, 2008
LINQ to SQL death rumors
{
I think it all started with this post from Tim Mallalieu reporting some directions for LINQ to SQL and the ADO.NET Entity Framework. Ayende and others have had some strong reactions (LINQ to SQL is dead) and there's now a StackOverflow thread you can use to follow the discussion.
It will be interesting to track over the next few days what other responses pop up. A few things that come to my mind:
1. This is an opening for people using Linq to SQL to migrate to SubSonic or NHibernate. I've been using SubSonic lately and enjoying myself quite a bit.
2. A big disappointment for people like Benjamin Day who I saw speak at VSLive and who have incoprorated LINQ to SQL into their development projects. Next time wait for Microsoft to 2.0 something before spending too much time working it into your dev cycle.
3. I wonder if Microsoft hasn't thought of spinning off some of the tool makers as different companies. Perhaps there is an MBA out there who could draw some charts and convince management to have ADO.NET Entity Framework and the LINQ to SQL folks compete/copy in the same space. If the team isn't fully formed, why not make one? And then how cool would that be: profit centers that encourage developers to use Microsoft tools and competition to get the best ideas out the door. The trade off of Balkanization versus One True Product is that the former will mean that cool new ideas belong to you - the company won't have to get the ideas from outside sources.
4. I wonder if we aren't moving towards a tipping point of Microsoft developers "getting" open source and shifting their tool/framework usage outside of Microsoft. I know lots of people who won't use __anything__ unless it has a Microsoft logo because it feels safer and more commoditized. Moves like this seem to go against that since open source projects tend to live a longer time and technologies / frameworks from companies become "obsolete" (how many versions of ADO can you count?) because of a product cycle that requires new purchases every 2 years or so. I would argue with my boss on a new project that it's safer to use NHibernate than ADO.NET Entity Framework if this indicates a chance that the Entity Framework could be the next LINQ to SQL project.
}
Thursday, October 23, 2008
.NET Survey
{
Scott Hanselman did a survey of .NET usage and posted results on his blog. A few interesting notes:
1. Heavy use of older stuff: ASMX, DataSet, WebForms, Windows Forms
You'd be hard pressed to find new blog entries on it but it shows that older (tried) technologies don't sync up with what's popular. The last few projects I've worked on have had development cycles that far exceed the excitement of their underpinning technology. One needs to remember that excitement and hype aren't what make something useful, it's use. So the people who talk down DataSets do so in the face of what obviously works for a silent majority who are busy getting stuff done.
2. Some new stuff won't take, some new stuff takes off
The respondents using CardSpace and WorkFlow are dismal. I can match the "unscientific" survey with a lot of my own experience in the last few years and conclude it's within a broader trend. On the other hand, LINQ To SQL has very heavy use for something relatively new, almost on par with ADO DataSets which one could argue are in the same competitive space. That says to me that in some cases adoption is just slow when there's a "legacy" counterpart for a technology but in others, when the new technology is compelling, people take to it very quickly.
3. WinForms > WPF
More than twice the respondents but here's my conclusion: it's all about the toolset, not the technology. WPFs tools are not on par with their WinForms equivalents. The dual Blend + VS strategy doesn't make sense for a large majority of us who don't have full time designers we work with and the lack of built in controls (such as a DataGridView) mean as cool as rounded edges are, function trumps form when programming.
4. MVC?
Last I checked this was *just* released as a beta. More people claim to use that than WPF which seems to indicate a Hanselman type majority in respondents. Even if I wanted to jump into MVC, until it's at a "1.0" release I couldn't (and I'm wondering how others seem to be doing so) justify starting or migrating a major product to a product/framework that isn't yet complete.
}
Wednesday, October 22, 2008
Recursive Copy ala LINQ
{
Tonight I needed to copy a bunch of images, no matter their level in the hierarchy to a single folder. Cake walk with C# and LINQ, I'll post the Python equivalent tomorrow.
string sourceDir = @"C:\thesource";
string targDir = @"c:\thetarget";
var files = new DirectoryInfo(sourceDir).GetFiles("*", SearchOption.AllDirectories)
.Where(p=> !p.Extension.Contains("db")) // exclude the pesky windows db file
.Select(p => p.FullName);
foreach (string path in files) {
File.Copy(path, Path.Combine(targDir, Path.GetFileName(path)));
}
}
Monday, October 20, 2008
Better Know a Framework: Zip / Package Multiple Files in C#
{
Making a single archive out of multiple files in .NET is easy once you know where to look but I spent far too long realizing there is a System.IO.Packaging namespace (not in the mscorlib, but you need to reference WindowsBase assembly).
// target file
string fileName = @"C:\temp\myZip.zip";
// point to some files, say JPEG - did I say I love LINQ?
var filesToZip = new DirectoryInfo(@"c:\temp\").GetFiles("*.jpg").Select(p => p.FullName);
using (Package exportPackage = Package.Open(fileName))
{
foreach (var file in filesToZip)
{
using (FileStream fileStream = File.OpenRead(file))
{
Uri partUriDocument = PackUriHelper.CreatePartUri(new Uri(Path.GetFileName(file), UriKind.Relative));
PackagePart packagePart = exportPackage.CreatePart(partUriDocument, System.Net.Mime.MediaTypeNames.Image.Jpeg);
CopyStream(fileStream, packagePart.GetStream());
}
}
}
// somewhere else, the CopyStream
private static void CopyStream(Stream source, Stream target)
{
const int bufSize = 0x1000;
byte[] buf = new byte[bufSize];
int bytesRead = 0;
while ((bytesRead = source.Read(buf, 0, bufSize)) > 0)
target.Write(buf, 0, bytesRead);
}
The trap in looking for this was that searches for this all point towards the GZipStream which is nice for a single file but not applicable for packaging multiple files. In my case I wasn't even interested in compression, just a single archive. You'll find additional documentation for the library here.
I'm half tempted to write a nifty tool in IronPython for command line packaging but in a world with 7-zip, that's simply foolish.
}
Saturday, October 18, 2008
Mashing Up: JQuery and Windows Forms, a first WIB
{
A lot of people are down on Windows Forms as something passe. I have a feeling there are a lot of people like me who not only must use it for practical reasons but also think there are advantages to fat clients that sit on the Windows platform. I'm getting to a point where I will start doing more than poking around with WPF but at present two factors hold me at bay: the control library is not robust enough (another tutorial of how to make a custom button in blend isn't incentive enough) and XAML is still a bit daunting for what usually end up being rather involved UIs. Here, for example, is a UI from an application I've been working on for a while (small intentionally to impart an idea of complexity without revealing any details):
I think a lot of "do it in XAML" problems can be solved with a more web development style paradigm in Windows. A simple example is template driven databinding with styles. That's easy to do in web applications but a little less flexible in a control based world (think a GridView with CSS versus the Windows Forms DataGridView).
As a proof of concept I decided to use the System.Windows.Forms.WebBrowser in a Windows Forms application to get cool features from JQuery while maintaining the strengths of a windows app. Here's the scenario: a photo manager application. It builds and manages a database of photos from a digital camera. Rather than uploading the 300+ pictures taken in a single session to Flickr (or in conjunction with that act) one can open it up, start annotating their files and saving the comments to a portable "database" that they can back up along with the files on their home machine. On any machine with the application they point to the directory and can view their original comments along with the pictures. Here is a screen shot:
The strength in this scenario of Windows Forms is the ease with which one can build the editing interface: textboxes, buttons and other "drag / drop" pieces of the control library. The strength of JQuery is in the display - I've chosen to use an animation and the accordion to spruce up the interface where the user looks at their photos.
You can download the whole thing here. (Very 0.1 but posted as an example/idea - just use the next / previous buttons to view some photos, get a feel of things).
I wonder if this paradigm can be a kind of Ajax "aha" moment; Ajax was centered on the use of a single, relatively simple MSXML component. Though System.Web.Forms.WebBrowser is not simple, it's a single component that has a lot of room for leverage. Ajax set up some pretty standard protocols / approaches to using asynchronous HTTP requests from a client - I think an interesting parallel could exist in wrapping jQuery and Html controls into a kind of framework that is easy for any developer to use with the WebBrowser control. Imagine a grid you drop with some properties and a CSS. The last thing that made Ajax pop was the catchy name. I love naming things myself so I've been calling this kind of application a WIB (Windows + Web) but perhaps there's something more catchy one could use.
Just some ideas, I'd be curious in the following:
1. Any existing applications written with this approach.
2. Any potential applications that could benefit with the pros of both Windows and Web
3. Any glaring shortfalls
}
ps. Thanks to Corey for encouragement on the blogging tip.
Wednesday, October 01, 2008
YUI File Disjoin Complexity HELP
{
I got a little help yesterday from Dav Glass and Eric Miraglia for my issue on selection of YUI javascript files. What I'd had distributed over a bunch of files is now:
<script type="text/javascript" src="http://yui.yahooapis.com/combo?2.5.2/build/yahoo-dom-event/yahoo-dom-event.js&2.5.2/build/animation/animation-min.js&2.5.2/build/element/element-beta-min.js&2.5.2/build/container/container_core-min.js&2.5.2/build/menu/menu-min.js&2.5.2/build/button/button-min.js&2.5.2/build/connection/connection-min.js&2.5.2/build/datasource/datasource-beta-min.js&2.5.2/build/datatable/datatable-beta-min.js&2.5.2/build/tabview/tabview-min.js"></script>
Hm... a bit better. Both Dav and Eric confirm that the canonical solution to this problem is to spend some quality time at the YUI Configuration Site which will allow you to pick the widgets and utilities you've leveraged in order to combine it into a more simplified call.
A someday project: the following style of implementation to make the reference a bit more crisp (I didn't add everything from above, but the pattern should be easy to observe):
<script type="text/javascript" src="http://yui.yahooapis.com/combo/2.5.2/dom-event&animation-min&element-beta-min&container_core-min"></script>
Anyhow, thanks for the help.
}
Tuesday, September 30, 2008
YUI File Disjoint Complexity FAIL
{
So here's what has been getting my goat for a while with YUI and a very smple application I've been using it to build.
So for said application, I have to reference 12 separate Javascript files along with 5 separate CSS files. All for this simple interface (datatable, tabs, button, ajax (connection), events and maybe a few more things I am not remembering):
This type of complexity is what makes YUI daunting for many developers and difficult to maintain; over at nRegex I'm referencing a bunch of older versions of YUI and if/when I do decide to update there will be quite a few places in which compatibility and errors have the potential in manifesting themselves.
YUI controls are nice once working but I'm at the point of wondering if this kind of disjointedness should lead me towards jQuery or Dojo. Especially with Microsoft's recent adoption of jQuery, I'm leaning in that direction. But I don't want to be rash so I'll ask:
1. What is the canonical solution e.g. blessed by Yahoo! (I see a YUI loader of some kind)?
2. What does that look like with the 5 CSS and 12 *.JS files I've got above?
}
Monday, July 28, 2008
Reading Source, Minima III
{
As my reading of Minima source code continues I wanted next to comment upon the database interaction which is novel (to me at least) in that it is exclusively a LINQ implementation with no stored procedure or ADO.NET implementation. I've seen LINQ quite a bit, but many using it seem to stick to using the query syntax rather than using raw lambda expressions. All the LINQ in Minima that I've seen uses lambda expressions and I've come to appreciate it as a clean, sparse syntax. To illustrate a more trivial example, assuming I have a table Users with Username / Password columns I wanted to verify for access. The first step with all things LINQ is to make your data model, which amounts to a DBML file that you map database objects to with drag and drop. Next you can write query expressions to pull data out of the database - notice that the db variable here points to the data model as embodied in a "data context" object:
var authUserQuery = from u in db.Users
where u.Email == Login1.UserName && u.Passphrase == Login1.Password
select u;
Unfortunately the query expressions return an IEnumerable<T> that doesn't support a Count or Length property. It's a somewhat inflexible thing that in my *very* limited experience is useful mainly for iterating with a ForEach - and I suspect it's reasons like this that led Betz to using a lambda syntax that is much cleaner and gets more bang with less code:
Func<Data.User, Boolean> checkUser = x => x.Email == Login1.UserName && x.Passphrase = Login1.Password;
Data.User u = db.Users.SingleOrDefault(checkUser);
You can use nullability to test for the presence of a record based on the LINQ query. Some source from Minima that demonstrates this is verification of the existence of a blog author:
Func<AuthorLINQ, Boolean> authorExists = x => x.AuthorEmail == authorEmail;
authorLinq = db.Authors.SingleOrDefault(authorExists);
if (authorLinq == null)
{
throw new ArgumentException(message);
}
It's truly novel and I don't mean to make that sound like a trivial or ornamental type "novel" I write; after spending so much time building a data access layer with stored procedures and ADO.NET (Connection, Command, Reader, DataSet, ad nauseum) it seems like our collective destiny is to walk away from that and let something like a DBML data context do black magic for us.
Even though the ADO.NET Entity Framework is getting a FAIL vote from some people, I'm interested to see how this overlaps with LINQ. Even after listening to Hanselman's interview of Mike Pizzo I'm still a little unclear. My plan of action is to get serious about NHibernate to try to understand, as Scott would say, "the gestalt" of an ORM and then make the call on ADO.NET Entity Framework when it's released.
}
Thursday, July 24, 2008
Twining, TwyDL
{
A quick note that Twining isn't dead and that I actually have been working on it. Some work I've done in the past few months has involved a lot of databases I don't have more than an ISPs user/password level of access to so it's been quite handy to copy data in and out with brief pieces of code. Here's one feature I've used, exporting a table definition and all of its contents to a scripted insert statement in a file:
database(cn).table("MY_TABLE").copyto.script("c:\\temp\\MY_SCRIPT.sql")
Another piece that I've been working on is the ability to generate schema objects in a more fluid, compact way. I like SQL just fine but after seeing how easy it is to make data structures with the Google App Engine I sought to shoot for something similar. Also within the flow of writing a Twining script it's easy to keep this type of syntax Pythonic for certain scenarios (you want to create a temp table and dump data into it from somewhere; you want to create a table and generate sample data, etc, etc). I call it TwyDL as a play on DDL:
database(cn).create.table("Employees",
[
col.ident("EmployeeID"),
col.string("FirstName"),
col.string("LastName"),
col.numeric("Salary")
]
)
The methods you see for defining column types take optional parameters so you can pass additional specifications when you feel the need. For example, the string columns default to varchar(50) but if you wanted to have a specific length all you'd need to do is:
col.string("FirstName", 25)The same is true for numeric types:
col.numeric("Salary", 18, 4)I've got one more thing I'm interested in implementing (time! time!) which is generating sample data given some table definition. I'm hoping to get up a rough cut of it all in the next day or two, after I've updated unit tests and organized things.
