This isn't necessarily new per se, but as I just completed some small tweaks I figured I'd share that I've migrated CFLib to Node.js. For the most part the conversion was fairly simple, but I thought I'd share some of the highlights (and issues) of the new code base.
First, let's talk about the old stack. The code base was very old. The last revision was written in 2008. It used ModelGlue and Transfer. MySQL was used as the database. For the most part, the site ran fine. I ran into issues with Transfer if I accidentally saved a UDF with the same name as another but as that was pretty rare I never got around to investigating it or even fixing it. I started on a FW/1 rewrite over a year ago and actually got everything but the admin done, but I hate writing admins so much I never got around to finishing it. (Yes, that is lame. And guess what part I just finished in the Node version?)
Content submissions have slowed down quite a bit, but honestly, I put the blame on me for that. Adam Cameron helped out a bit for a while, as have others, but at the end of the day, I was the sticking point for things getting released, no one else. I apologize for that. The site still gets "ok" traffic, nearly 100K page views last year, so I'm hoping that having a new home for it will give me less of an excuse to ignore it. Y'all can nag me if I don't do a better job this year.
For the new site, my stack couldn't be more different:
- Node.js, of course.
- Express 4.X
- MongoDB
- Mongoose (a wrapper for Mongo)
As I rebuilt the site, I decided to kill off some features, the biggest being ratings (there were maybe 200 ratings) and author pages. I can return author pages if people feel strongly enough about it. I also switched the code renderer to PrismJS, my favorite library for code coloring.
You can see all the code up on GitHub: https://github.com/cfjedimaster/cflibwww. Before you look at the code, please be aware that I'm a Node noob! I've played around a lot with Node and have released multiple sites, but I still feel like a first year freshman with it. I'd say my code is slightly better than my first year ColdFusion code, but only slightly.
This is - I think - the largest site I've built in Node.js so far. I feel like the biggest issue is my core application file, index.js. It isn't too big (323 lines currently), but it feels messy. When I began working on an Admin controller, I moved code for those routes (think code that handles a particular request) into a unique file, and I'd like to do the same with the main views as well. Ditto for all the helpers I wrote in Handlebars.
Basically - I can look at my code and at least "smell" the things that seem wrong. I think that's good.
Mongo is - of course - a pleasure to work with. I won't pretend NoSQL DBs are a silver bullet, but my God, if I never write another line of SQL again I'll be happy. Working asynchronously can be a bit of a pain at times. So for example, I needed a way to get my libraries, and for each library, get a count of the number of UDFs in it. I used a library named async which allowed me to take those N async calls and listen for them to complete:
this.find().sort({name:1}).exec(function(err, libs) {
async.map(libs, getUDFCount, function(err, result) {
for(var i=0, len=libs.length; i<len; i++) {
libs[i].udfCount = result[i];
}
locals["libraryCache"] = libs;
cb(null,libs);
});
});
The thing that bugged me the most, and I never really realized it before, is how limited Handlebars can be at times -- specifically in terms of doing anything in logic. Handlebars wants to minimize the amount of logic you include in your views, and I can get that, but it really bit me in the rear at times. As an example, when editing a UDF, I need to display a list of libraries the UDF can belong to, and then set the current library as the selected item. Handlebars supports IF clauses, but the expression must be simpler. So you can't do: {{if something is something}}. Nope. You have to actually write a helper function for it:
selected:function(option,value) {
if(option == value) {
return ' selected';
} else {
return '';
}
}
Here's how that looks in the Handlebars template:
<tr>
<td><b>Library:</b></td>
<td>
<select name="libraryid">
{{#each libraries}}
<option value="{{this.id}}" {{selected this.id ../udf.library_id}}>{{this.name}}</option>
{{/each}}
</select>
</td>
</tr>
<tr>
Not horrible, but not ideal either. Also, ColdFusion spoiled me. Being able to do this: dateFormat(something, mask)
is nice compared to how I did it in the new site. First I got the MomentJS module. I then used it in a Handlebars helper. Finally I called it from my template like so: {{fullDate udf.lastUpdated}}
That's readable at least, but not necessarily simple.
All in all though, I'm happy with this new version, and I've got three UDFs waiting to be released. I'm going to release those later in the week. For those of you who are Node/Express experts, I will gladly take your feedback. For those who don't know Node and have specific questions about the code base, ask away!
It's come a long way from this...
By the way - that wasn't the first version. Unfortunately I couldn't find it via the Wayback Machine.
p.s. This new version will have broken the API used by the ColdFusion Builder extension. If anyone actually made use of that, speak up, and I'm sure I can get it working again.
p.s.s. The next conversion will be ColdFusion Bloggers.