JavaScript

First Steps With MongoDB, Mongoose and Jasmine-Node On Node.JS

2 commentsWritten on July 20th, 2011 by
Categories: JavaScript, mongodb, mongoose, node.js

(NOTE: i'm still learning since all of this stuff is very new to me, so everything i say here should be taken with a grain of salt. Better solutions/approaches than what i'm doing are likely available and i'd definitely appreciate good tips from anyone who reads this.)

I started working on my breakable toy, and the first thing i wanted to get working was storing my documents in MongoDB and getting them back out again. I first read Karl Seguin's The Little MongoDB Book because it's a great introduction to MongoDB and to working with document databases in general (btw: it's free, only 33 pages and very well written => highly recommended!).

I originally intended to use MongoDB directly in my code, but once i realized that i wouldn't be able to simply create entity types with their own behavior and just store them in the database, i looked for alternatives. The 'problem' is that MongoDB just expects documents, which are JavaScript objects. But if you pass it a JavaScript object with functions, it'll just store those functions as strings. Not exactly what you want. You can move the functions to the prototype of the object, and if you have more than a few instances that's almost always better than storing the functions in the object itself, but MongoDB doesn't care about the prototype and doesn't return objects with their original prototype when you execute queries. There is a rather ugly trick that you can use to change the prototype of the object (settings its proto property) but it's not official JavaScript and even though Node itself relies on that ability, it felt like too much of a hack to use it. I also didn't feel like writing a layer of infrastructure code on top of the MongoDB driver because then i'd just spend too much time working on infrastructure stuff which i was hoping to avoid on this project.

So i looked around and found Mongoose. It presents itself as an ORM-like layer on top of MongoDB which it kinda is, though i prefer to think of it as an Object Document Mapper instead of an Object Relational Mapper since they definitely differ in significant ways. Anyways, let's get to some code. As mentioned in the first post about my toy project, the goal is to generate invoices and timesheets for the work i do for my customers. So i started off with 2 entities (obviously, i'll need more, but this is enough to get started):

    var mongoose = require('mongoose'),
        Schema = mongoose.Schema,
        ObjectId = Schema.ObjectId;
        
    var customerSchema = new Schema({
        name: { type: String, required: true },
        address: {
            street: { type: String, required: true },
            postalCode: { type: String, required: true },
            city: { type: String, required: true },
            country: String
        },
        phoneNumber: String,
        vatNumber: { type: String, required: true },
        contact: {
            name: String,
            email: String
        },
        includeContactOnInvoice: { type: Boolean, required: true, default: false }
    }); 

    mongoose.model('Customer', customerSchema);
    var Customer = mongoose.model('Customer');

    var performedWorkSchema = new Schema({
        date: { type: Date, required: true },
        hours: { type: Number, min: 1, max: 8, required: true }
    });
    
    mongoose.model('PerformedWork', performedWorkSchema);
    var PerformedWork = mongoose.model('PerformedWork');
    
    var activitySchema = new Schema({
        customer: { type: ObjectId, required: true },
        description: { type: String, required: true },
        hourlyRate: { type: Number, required: true },
        performedWork: [performedWorkSchema],
        billed: { type: Boolean, required: true, default: false }
    });

    activitySchema.methods.addPerformedWork = function(date, hours) {
        this.performedWork.push(new PerformedWork({ date: date, hours: hours }));
    };
    
    mongoose.model('Activity', activitySchema);
    var Activity = mongoose.model('Activity');

There's quite a bit going on in this piece of code already. This defines the schema of our entities. Some of you might be thinking "wait a sec, i thought document databases were schema-less?". They are indeed, but Mongoose uses these 'Schema' instances to generate constructor functions for your entity objects and to give them some interesting behavior out of the box. Those schema-definitions in the code are still meaningless as far as MongoDB is concerned. But for Mongoose and our code, they certainly are important.

As you can see, we define a Customer type which has some properties, as well as embedded Address and Contact objects. When properties are made required or given default values, it only has an influence on Mongoose. I could still connect to MongoDB through its shell (which is awesome btw, check Karl Seguin's book for some interesting examples) and insert whatever i want in the collections (similar to tables in a relational database, though there is no schema that is upheld for the elements in the collection). We also have a PerformedWork type, though there won't be a collection in the database for those instances. You can see in the schema of Activity that its performedWork property holds an array of PerformedWork instances. We just mapped a one-to-many without requiring a separate MongoDB Collection (or table, if you prefer to think of it that way). If you're using MongoDB directly, you could just put whatever you want in an array-property of a document. For Mongoose, it's important to know the structure of the data, so you have to define a schema for embedded documents in arrays. Notice also that i can easily define min and max values for the hours property of PerformedWork. You can go a lot further with validation in your entity objects, but i haven't looked further into that yet. Also interesting to note is that Activity has a customer property, in which we'll store an ObjectId. It means that the customer property will refer to a customer through the id value that it holds, but it is not an actual Customer reference. I'll show you how the actual documents are stored in the database later on in this post.

Another thing that you'll probably find weird is this:

    mongoose.model('Customer', customerSchema);
    var Customer = mongoose.model('Customer');

The first line basically tells Mongoose that there is a 'Customer' type and the passed in schema instance is the one that Customer instances should be based on. The second call to mongoose.model actually returns a constructor function which we can use to create our types or to execute queries through methods on the constructor function. Sounds weird, but in JavaScript a function is a first class object, which means that a function can have properties as well, and those properties can in turn contain other functions. Think of it as static methods... it's not entirely the same, but it's sort of similar, sometimes. I'll try to make the rest of this post less confusing than this paragraph was :)

To make sure that my entity objects can be stored in MongoDB and retrieved again, i wanted to write some automated tests. While there are a few good testing frameworks for JavaScript, i'm going to (try to) use Jasmine exclusively, through Jasmine-node (which just runs the tests on Node). Jasmine is a BDD framework, so a lot of people probably wouldn't use it for more technical tests or exploratory tests, but i'm testing technical behavior here so i think it still fits.

Suppose i want to test some of the behavior related to saving a customer, i'd start with something like this:

describe('when a customer is saved', function() {

});

Within the function that is passed to the describe method, i can start adding some tests. For instance, here's one that tests whether or not Mongoose applies the validation rules i specified on my CustomerSchema:

    describe('with none of its required fields filled in', function() {
        it('should fail with validation errors for each required field', function() {
            var customer = new Customer();
            customer.save(function(err) {
                expect(err).not.toBeNull();
                expect(err).toHaveRequiredValidationErrorFor('name');
                expect(err).toHaveRequiredValidationErrorFor('vatNumber');
                expect(err).toHaveRequiredValidationErrorFor('address.street');
                expect(err).toHaveRequiredValidationErrorFor('address.postalCode');
                expect(err).toHaveRequiredValidationErrorFor('address.city');
                asyncSpecDone();
            });
            asyncSpecWait();
        });
    });

This test tries to save an empty customer object to the database, but our CustomerSchema specifies that a couple of its properties are required. Our customer object has a save method (created by Mongoose), and we need to pass it a callback which will be executed after the customer has been inserted. On Node, all I/O calls are asynchronous so you have to tell Jasmine-node to wait for the callback to executed, which is what the call to asyncSpecWait() does. When we get in our callback, we assert that the passed in error object (typically named 'err') is not null, and then we use the toHaveRequiredValidationErrorFor method to assert whether the expected validation error messages are present. The toHaveRequiredValidationErrorFor method doesn't come with Jasmine, it's a custom matcher which we make available before each test:

beforeEach(function() {
    this.addMatchers((function() {
        var toHaveValidationErrorFor = function(err, validatorName, propertyName) {
            if (!err) { return false; }
            if (err.name !== 'ValidationError') { return false; }
            var value = err.errors[propertyName];
            if (!value) { return false; }
            return (value === 'Validator "' + validatorName + '" failed for path ' + propertyName);
        };
                
        return {
            toHaveRequiredValidationErrorFor : function(propertyName) {
                return toHaveValidationErrorFor(this.actual, 'required', propertyName);
            },
            toHaveMaxValidationErrorFor: function(propertyName) {
                return toHaveValidationErrorFor(this.actual, 'max', propertyName);
            }
        };
    }()));
});

We pass an object containing 2 functions to the addMatchers function, which will in turn make those 2 methods available to our expectations.

Let's take a look at a more interesting example, saving an Activity object with an array of PerformedWork instances:

    describe('with valid performed work added to it', function() {
        it('should be inserted as well', function() {
            var activity = new ActivityBuilder().build();
            var today = new Date();
            var yesterday = new Date();
            yesterday.setDate(yesterday.getDate() -1);
            activity.addPerformedWork(yesterday, 8);
            activity.addPerformedWork(today, 6);
            activity.save(function(err) {
                expect(err).toBeNull();
                Activity.findById(activity.id, function(err, result) {
                    expect(result.performedWork.length).toBe(2);
                    expect(result.performedWork[0].date).toEqual(yesterday);
                    expect(result.performedWork[0].hours).toEqual(8);
                    expect(result.performedWork[1].date).toEqual(today);
                    expect(result.performedWork[1].hours).toEqual(6);
                    asyncSpecDone();
                });
            });
            asyncSpecWait();
        });
    });

The ActivityBuilder constructor constructs a typical builder object. I won't go into the details of this pattern, and i won't list the code since this post is already getting a bit too long but you can look at the code here if you're interested. Anyways, back to the test. We're creating an activity object and using the addPerformedWork function (which we added to ActivitySchema.methods in the first code snippet) to add some performed hours to the activity. We expect the save function to not cause errors, and then we launch a simple query: finding an activity by its id value. Note how we use the findById function through the Activity variable. That Activity variable points to the constructor function which creates activity instances when invoked directly, but as i mentioned earlier it can have properties, which can hold functions, of its own. Notice the syntactic similarity with calling a static method in a static language. Behind the scenes it's entirely different, but from a conceptual point of view, it's sorta the same. As you can see, in this test the save operation works, so what does the activity object, or better yet, document look like in the database? Here it is:

{ 
    "performedWork" : [
        {
            "_id" : ObjectId("4e25f7d2041ec8c006000006"),
            "date" : ISODate("2011-07-18T21:32:02.652Z"),
            "hours" : 8
        },
        {
            "_id" : ObjectId("4e25f7d2041ec8c006000008"),
            "date" : ISODate("2011-07-19T21:32:02.652Z"),
            "hours" : 6
        }
    ], 
    "billed" : false, 
    "_id" : ObjectId("4e25f7d2041ec8c006000005"), 
    "hourlyRate" : 75, 
    "description" : "some cool project", 
    "customer" : ObjectId("4e25937456436de850000006") 
}

It doesn't actually store it with all that whitespace, i just formatted it to increase readability. Anyways, what's interesting here is that we have our array of PerformedWork instances embedded right here in our document. So whenever we retrieve this activity instance, we automatically get its PerformedWork instances as well. Also notice the _id properties. We never defined id properties in our schemas, so MongoDB automatically adds an _id property. The id value is filled in by the MongoDB driver before the document is sent to the database. And as you can see, our customer property simply holds an ObjectId, not an actual customer document. Customers are stored in the customers collection, and an instance of a customer in MongoDB looks like this:

{ 
    "address" : { 
        "country" : "some country", 
        "postalCode" : "1234", 
        "city" : "some city", 
        "street" : "some street" 
    }, 
    "contact" : { 
        "email" : "some.email@gmail.com", 
        "name" : "some name" 
    }, 
    "includeContactOnInvoice" : true, 
    "_id" : ObjectId("4e25f7d2041ec8c006000016"), 
    "vatNumber" : "0456.876.234", 
    "name" : "some customer", 
    "phoneNumber" : "123456789" 
}

Pretty self-explanatory i think.

That's enough for this post. We've seen how we defined our objects in Mongoose, got a glimpse of Jasmine and covered some very basic interactions with MongoDB. I'm going to post more on this stuff as i continue working on my toy project, though i won't make any promises on how long it'll take before new posts will show up :)

This code will likely evolve significantly in the next couple of weeks/months, and if you're interested you can always follow its evolution on github.

Going Down The Rabbit Hole

16 commentsWritten on July 10th, 2011 by
Categories: JavaScript

I've been wanting to work on a breakable toy project for a while now, and now i finally have enough spare time to spend on it. The objective of the project isn't very interesting: i just want to be able to generate invoices and timesheets for a task/project for a customer, and i want them in PDF format. I want to be able to do this from an easy-to-use web interface, and later on i want to add a mobile version of that interface to the project. I know there are tools available to do this but the whole point is to learn how to use new technologies, which is why i went with some interesting technological choices.

On the server, i'm going to use Node with Express as the web-development framework. For data storage, i'm going with MongoDB. On the client, i'll probably use jQuery (and later on, jQuery Mobile) together with Backbone. I want to test both the server-code and the client-code with a mixture of Jasmine and QUnit.

I want the resulting code to be clean, working, and fully tested. Of course, due to the fact that every technology i'm going to use (except for jQuery) is entirely new to me, this is going to be a bit of a challenge. I'm going to make tons of mistakes and i'm assuming that there will be continuous refactoring going on. I'm also going to do all that learning out in the open. You'll be able to follow the codebase on github and i'm going to document the whole experience on this blog. It might take a short while for code and posts to show up, because i've got some initial reading to do before i get started. All in all, i expect this to be a very interesting project that's going to teach me a lot, and i'm pretty sure i'm gonna have a lot of fun building it, and writing about it.

For those of you who already have experience with any of these technologies: if you catch me doing something stupid either in code or in a blog post, i'd appreciate a nudge in the right direction :)

You Might Want To Start Taking JavaScript Seriously

19 commentsWritten on June 2nd, 2011 by
Categories: JavaScript, Opinions

A very large group of developers still thinks of JavaScript as a joke. I used to be one of them, as you might have been, or perhaps still are. They have such negative views on JavaScript: about how horrible it is in general, that it is slow, that it's just a toy language, that it's not even object oriented (which shows you how little they know of it). But the reality of the matter is that JavaScript is increasingly being used for more and more serious things. You can do wonderful and truly powerful stuff with it to enrich your users' client-side experience of your web applications. It's also becoming increasingly popular on the server-side with frameworks like node.js, known for its high performance and throughput, not exactly attributes you'd expect to be linked to a framework written in a supposedly slow language. And even Microsoft appears to be promoting JavaScript ever increasingly with their renewed focus on IE and now even as one of the preferred ways of developing applications for Windows 8's new UI.

To those of you who dislike JavaScript: it's not going to go anywhere and in fact, it seems like it's only going to get bigger and bigger over the next couple of years since it appears to be one of the few languages that practically every major player in the industry can benefit from in a variety of ways. All browser vendors are continuously improving the performance of their JavaScript engines. Plenty of projects/startups/companies have shown that JavaScript can indeed be the right tool for many different jobs. Simply put: there's a lot of innovation going around in the JavaScript community and there's no reason to believe it'll slow down anytime soon.

My advice: start taking it seriously. Try to actually learn the language and experiment with it. I can definitely recommend reading JavaScript: The Good Parts and JavaScript Patterns. There are indeed plenty of bad parts in JavaScript, but if you stick to the good parts and learn and understand the patterns that enable you to use it very effectively, you might end up pretty surprised at how nice it can be. Even if you don't appreciate how nice it is, you'll at least have learned a language that is becoming increasingly relevant which means you have one more skill that will be usable in many different situations, and probably for a long time to come.

What’s Interesting About This Piece Of Code?

24 commentsWritten on April 18th, 2011 by
Categories: JavaScript
        $.get('/Home/Locations', function (data) {
            $.each(data, function () {
                var id = this.Id;
                var marker = new google.maps.Marker({
                    position: new google.maps.LatLng(this.Coordinates.Lat, this.Coordinates.Lng),
                    map: map
                });

                google.maps.event.addListener(marker, 'click', function (event) {
                    showDialogFor(id);
                });
            });
        });

Who Needs Classes Anyway?

12 commentsWritten on March 28th, 2011 by
Categories: JavaScript

I needed to do some client-side formatting of dates that i get back from an AJAX request in JSON format, and to my surprise, JavaScript nor jQuery has something built in for this. I briefly looked around for some jQuery plugins or JavaScript libraries but was quickly lost because there are so many of them and most of them do a lot more than the tiny bit that i need right now. So i figured i'd be better off if i'd just quickly do it myself for now. Granted, i might be better off in the long run using a specific library for this but that's not really the point of this post, so bear with me for a second. What i'd normally do in a situation like this is to think about what i need to solve and create a small class for that which takes care of the job for me. In JavaScript however, there are no classes, just objects and prototypes.

The thing is though: do i really need to think about what kind of class i'll need? Am i not better off thinking "what kind of functionality do i need to implement here?" or "what kind of behavior am i trying to implement?". Does it really matter whether or not the physical packaging of that functionality is a class, an object, a module, or something else? Not really. I just want to provide some behavior, while encapsulating some of the details of how that behavior is implemented.

With a class, that's easy to do because we're all so used to doing that... just create a class with some public methods for the behavior you want to provide and whatever you want to keep private you just mark as private. There, done. So how do you do that in JavaScript? There are actually a bunch of ways to do this in JavaScript, and the code you'll see below is just one example. Some of you will read the code and think "oh ok, you're just emulating private methods here". And while you'd be right in some way, it's a somewhat unfortunate way of thinking about the code because it means you mainly think in terms of classes and objects, whereas it might be more interesting to start thinking more in terms of 'functionality' which can come in many shapes or forms.

Take a look at the code, which i'll discuss further below:

var MyNameSpace = MyNameSpace || {};

MyNameSpace.jsondateformatter = (function () {
    var that = this;

    var toDate = function (jsonDateString) {
        var time = jsonDateString.replace(/\/Date\(([0-9]*)\)\//, '$1');
        var date = new Date();
        date.setTime(time);
        return date;
    };

    var prefixWithZeroIfNecessary = function (number) {
        return number < 10 ? '0' + number : number;
    };

    var dayAsDoubleDigitDayString = function (date) {
        return prefixWithZeroIfNecessary(date.getDate());
    };

    var monthAsDoubleDigitMonthString = function (date) {
        // for some reason, the result of getMonth is zero-based
        return prefixWithZeroIfNecessary(date.getMonth() + 1); 
    };

    var hourAsDoubleDigitHourString = function (date) {
        return prefixWithZeroIfNecessary(date.getHours());
    };

    var minutesAsDoubleDigitMinuteString = function (date) {
        return prefixWithZeroIfNecessary(date.getMinutes());
    }

    return {
        toDate: function (jsonDateString) {
            return that.toDate(jsonDateString);
        },

        toShortDateString: function (jsonDateString) {
            var date = toDate(jsonDateString);
            return dayAsDoubleDigitDayString(date) + '/' +
                   monthAsDoubleDigitMonthString(date) + '/' +
                   date.getFullYear();
        },

        toShortDateTimeString: function (jsonDateString) {
            var date = toDate(jsonDateString);
            return dayAsDoubleDigitDayString(date) + '/' +
                   monthAsDoubleDigitMonthString(date) + '/' +
                   date.getFullYear() + ' ' +
                   hourAsDoubleDigitHourString(date) + ':' +
                   minutesAsDoubleDigitMinuteString(date);
        }
    }
})();

First, i create an object (if it doesn't already exist) to serve as a namespace. Then i add a property called 'jsondateformatter' to that 'namespace object'. So far this is pretty boring. But the value that gets assigned to the jsondateformatter property is what's interesting here. As you can see, i'm wrapping a function expression between 2 braces, and all the way at the bottom of the code i add the invoke operator, which is (). That means that my function expression will be immediately evaluated (typically referred to as an immediate function), and the result of that will be assigned to the jsondateformatter property. And what exactly is the return value of my immediate function? It's an object which contains 3 properties: toDate, toShortDateString and toShortDateTimeString. Each of those properties contains a function, and each of those functions refers to one or more variables that i created in the immediate function.

And this is where it gets beautiful (at least, in my opinion and i have to admit i'm kinda weird that way): due to the power of closures, the values of the variables that i created in the immediate function remain accessible in the functions that have been assigned to the properties of the object that my immediate function returns. Every function that i've created within the scope of the immediate function can only be accessed by the functions of the object that i return, and that's it. It's completely encapsulated and reusable. And you don't need classes to do it. You might think "yeah well ok, i guess that's cool but you're still using an object so it's not all too different!" and again, you'd be right in some way. But the object i'm returning here is nothing more than some kind of holder of the 3 functions that i want to make available. If i only needed to make one function available, i wouldn't return an object... i'd just return a specific function which closes over whatever i want to keep encapsulated.

And that is one of the cool things about trying to think in the language you're working with: you learn new ways of doing essentially the same thing, though that which you thought was the 'thing' is just one of many representations of its essence. Writing that last line made my head hurt, so i think i'll just stop here.