Tag Archives: Backbone.js

Examining Underscore.js Search and Filter Collection Functions

“Price is what you pay. Value is what you get.” – Warren Buffett

By any measure, Underscore.js is a great deal. Underscore.js is a tiny open source JavaScript utility library that packs about 80 useful and eye-opening functions into only about 5kb (minified and gzipped).  A significant number of these functions are applicable to collections.  In Underscore.js, the term “collections” can refer to arrays, objects, and array-like objects such as arguments and NodeList.  Follow this link to examine the Underscore.js documentation for these functions.  In this article we will be looking at actual usage for some of these functions.  You can find a demo/test page that runs these examples here.

A number of these collection functions are ideal for search-related applications. It’s interesting how many of the Underscore collection functions are reminiscent of SQL. If one were to look at the full list, one would find that about half of them have names and/or do things that are related to SQL select functionality, e.g.:

  • each
  • find
  • filter
  • where
  • findWhere
  • reject
  • every
  • contains
  • max
  • min
  • sortBy
  • groupBy
  • countBy

It is worth noting that the popular JavaScript MV* library, Backbone.js, proxies and uses many Underscore.js functions.  It makes particularly good use of many of these SQL-evocative functions for assisting programmers in the management of collections of Backbone Models.  The search functionality that this article examines, or analogs thereof, could just as easily be implemented in Backbone.js as it could via the more low-level Underscore.js.

There are small number of usage patterns shared by these Underscore.js collection functions.  We’ll be looking at four collection functions, chosen as being representatives of both the functionality and pattern usage differences that pertain to the functions in the above list.  We are going to begin by examining the _.where function, which has great functionality even though it follows a simple usage pattern.  Take a look at the code below.  (Note for programmers unfamiliar with Underscore.js:  As befits the name, all Underscore functions begin with an underscore (_) character, similar to how jQuery functions begin with dollar sign ($) character.)

The code above produces the following output:

In the test function shown above, note first the signature for the _.where function.  It takes two arguments, “list” and “properties”.  The first argument, list, is one that is found in all of the Underscore.js collection functions.  Recall that collection functions work on arrays, objects, and array-like objects such as arguments and NodeList.  The list argument can represent any of these things.  It is important to understand that the nature of the reference passed via the list argument will slightly alter the usage pattern for these collection functions.  In this particular test we are passing an array of objects named “testArr” as the list.  The other argument required by _.where is the properties argument, which must be an object containing one or more properties that are used as the where criteria, i.e., what to search for in the given list.  In the test case above we are passing an object containing two search properties – we are looking for things that are green and have a tail.  In the input list we have four objects, all of them containing color properties and all of them containing tail properties.  Of the four, only one of them – an iguana – is both green and has a tail.  As is to be expected, when this test is run, only one object is returned – the iguana object.

The next function we will be looking at is the _.each function.  I chose this function as one of the four because of it’s flexibility and because it is a good one to use in illustrating the full range of usage patterns that may be encountered when using the Underscore.js collection functions.  Take some time to really look at the two code examples for this function – if you can get through this part the rest of the article is down-hill.  Whereas the _.where function is fairly simple – taking only a list and a set of search criteria as its arguments – the _.each function is inherently more capable, but with less specified functionality at the outset.  In addition to the list argument that we have already touched upon in the previous example, the _.each function has an iterator argument and an optional context argument instead of a “properties to search for” argument.  The iterator argument represents a function to be passed and the optional context argument represents a “this pointer” context to be bound to the iterator function. The ability to reassign the this pointer for an object or function is a commonly used JavaScript technique.  An in-depth explanation of this technique is outside the scope of this particular article, however we will see the context argument in use.  The iterator argument concept is a different story.

In this discussion of the _.each function and in this article in general, we will be spending a fair amount of time examining different uses for an iterator function, as many of the collection functions have such an argument.  The basic concept of an iterator function is the same for all collection functions that use them – it is a bit of code that the Underscore collection function will call for each item in the given list argument.  The number and the nature of arguments that Underscore will pass to an iterator function varies with the particular collection function, as does the nature of the expected function return, if any.  Let’s go ahead and take a look at the code for the first of two _.each tests in the demo page.

The code above produces the following output:

In the test function shown above, note first the signature for the _.each function.  When calling _.each, we are passing a simple array of strings for the list argument.  The iterator function is defined on the fly, as an anonymous function.  The third argument to the _.each method – the context – is a reference to an object declared outside of the testEachInArray function. Lets look closely at the iterator function.  It takes three arguments – the Underscore.js documentation for the _.each method specifies this.  It has no return value.  Ok… but what is it doing?  The answer is, not very much.  It is merely printing to console the content of the three arguments for each iteration.  The Underscore.js documentation states  for the _.each method further states that when the list argument is an array, the first argument will be the value of the array element being processed for the current iteration, the second argument the index of that array element, and the third argument will contain the content passed as list (the entire array).

A major purpose of these _.each function examples is to show an iterator function in action – to give an idea of what sorts of things can happen when an iterator is being called.  In actual use, the iterator function passed to the _.each function would be doing some kind of work with each element in the array, as the _.each function iterated.  But what of the final argument, the context?  Note at line 19, the use of: this.contextValue.  The output of the function in the console proves that the this pointer for the iterator function has been set to the this pointer for the anExternalContext object, because contextValue prints out correctly.  Let’s take a look now at  the second _.each function example.

The code above produces the following output:

In the test function shown above, note that we are calling upon the same _.each function that we called in the previous test.  The difference here is that we are passing an object as the list argument, instead of an array.  This example is more or less the same as the previous one except that the usage pattern is slightly different because the list is an object this time.  Here we must make allowances in the iterator function for the fact that each iteration of _.each is working with a property of the given object passed as the list argument, instead of it being an element of a passed in array.  The Underscore documentation tells us that the three arguments passed to the iterator will (naturally) be a little different for an object passed as list than when an array is passed in the list argument.  Here, the first argument will be the property value being processed for the current iteration, the second argument will be the key for that value, i.e. the property name, and the third argument will again be the content passed as list, in this case an object.  The iterator function takes these differences into account (e.g. the use of JSON.stringify to get a string representation of the list object), but otherwise works the same as the iterator function worked in the first _.each example test.

The next function we will be looking at is the _.max function.  I chose this one because it is reasonably straightforward and representative of other similar functions (e.g. _.min, _.find), in addition to being very useful in its own right.  The iterator function is optional for the _.max function, but when used, its purpose is clear and it is easy to comprehend. Lets look at the first of two examples, one that does not employ an iterator function.

The code above produces the following output:

In the test function shown above, note first the signature for the _.max function.  It is the same signature that _.each uses, except that the iterator function is optional.  In this first example test of the _.max function, we are not passing an iterator.  There is no need for one, as the list we are passing is a simple array of strings and the _.max function needs no additional help to be able to iterate through and find the largest number in the array.  Lets look now at the second _.max function example usage, which does require an iterator function to be passed.

The code above produces the following output:

In the test function shown above, note that we are calling upon the same _.max function that we called in the previous test.  The difference here is that we are passing an array of objects as the list argument, instead of an array of numbers.  This example is similar to the previous one in that _.max is trying to iterate through the input list to determine the largest value in set.  However, the data is different here – both in content and in nature.  The _.max function cannot know on its own, the property name within the array of objects that it must access in order to determine the largest value. In some cases, each object might have any of several properties that would be correct.  Even in this simple test case, it is possible to pass the value of the person property instead of the value of the IQ property (the highest sorting person name would be the “max” value in that case) but we want to find the person with the max IQ in our example.  So, we create the anonymous function to help out.  The Underscore documentation for the _.max function states that the iteration function for _.max must take an argument representing the current object being iterated from the array of objects, and return the value of the property that _.max will use to cull the max value.

The last Underscore.js collection function that we will examine is the _.filter function.  It packs a lot of potential power, but requires the programmer to provide most of that power by providing an iterator function. You will find two example uses of the _.filter collection function below.  The first of these will be a simpler use case than the second.  Each of these two examples will be a little different from the previous examples in that an example is made up of two functions – one is a re-usable function and the other is the test driver for the function.  Let’s take a look now at the two functions that make up the first example use case for the _.filter function: simpleFilterLike and testSimpleFilterLike.

The code above produces the following output:

In the test function shown above, note first the signature for the _.filter function.  By now this pattern should look familiar.  In the scenario above, we are passing as the list argument a simple array of strings, containing phrases that will be searched.  We are also passing a non-optional iterator function that returns a result of true (per Underscore specification for _.filter) if a searched-for string is found with the currently iterated phrase.  We are not explicitly passing the searched for string.  A “likeCriteria” object is created within the body of the function that invokes the _.filter function.  Within this likeCriteria object, a “searchFor” property is set, containing the value that we will search for within the phrases.  We could have passed this likeCriteria object as the context and shared it’s contents with the iterator function that way, but there is no need to do so as the scoping rules of JavaScript already make the likeCriteria object contents available to the iterator function.

What is happening inside this iterator function?  For each iteration, the _.filter function is giving us a phrase to search in and we are doing so using the contents of the searchFor property in the likeCriteria object.  The iterator function is “teaming up” with the Underscore _.filter function by returning boolean true if the searched for string is in the phrase – effectively allowing the Underscore function to filter which of the array elements (i.e. which phrases) pass a truth test. Those phrases that pass the truth test will be returned in a filtered array that contains only the right contents from the original array – where “right” means that the searched for string was found in the phrase.  Now lets look at the second scenario, in which two more functions are used in a similar manner to the ones just examined to call upon the Underscore _.filter function to help do a search.

The code above produces the following output:

The two functions shown above are not very different from the first two _.filter leveraging functions we just looked at, there are just a couple of twists.  The first is that instead of a simple array of strings representing phrases, we are passing here an array of objects containing phrases.  The second twist is that we are employing two searchFor criteria, which means that this version of the “likeCriteria” object contains an array property instead of a property holding a single string.  Otherwise, this pair of functions works the same as the previous pair.

The main reason for providing this second example of how to leverage the Underscore.js _.filter function is that this example is much closer to a real world use-case for a utility filter function that returns information based upon matching “like” criteria.  In fact, a more robust and capable version of the “filterLike” function forms the heart of the search implementation for the main Uberiquity site.  You can test the Uberiquity search feature out if you like, by clicking on this Uberiquity Search link. If you were to go there, you could search for this very article by using the following search:  underscore AND search

Node.js Part 1 – Introduction

“Node news is good news.”

As you might gather from the rather cheesy paraphrasing above, I am very impressed with Node.js.  More than that, I am astonished by the open source community and code projects which have which have sprung up around Node.  The popularity of Node is exploding, and in this article I will offer you some basic knowledge about Node.js that may pique your own interest in this “hot” server side development framework.

So then, what is Node.js?  To properly answer that question will take this entire article and perhaps more – but we need to start somewhere, so I am going to list a few truths about node.js that will give you some context upon which to hang the rest of the article:

  • It is JavaScript for the server side – all things related to browser and DOM removed.
  • It is based upon Google’s V8 JavaScript engine, a platform abstraction layer called libuv, and a core library originally written in C but now mostly rewritten in JavaScript.
  • It was created in early 2009 by Ryan Dahl of Joyent (this company still maintains and develops Node.js).  Here is a link to Ryan’s original presentation of Node.js, courtesy of Youtube: http://www.youtube.com/watch?v=ztspvPYybIY
  • It is intended to be used primarily as a high performance back end coding platform that is utilized by front ends like browsers over HTTP or other front end applications using TCP sockets to transmit data to and from the server.
  • It follows an event driven, non-blocking I/O model.  What this means is that the event loop driving Node does not have to wait for IO (typically, IO is glacial in performance compared to other code execution).  This is an asynchronous rather than a synchronous approach, allowing Node to be highly scalable.  (I plan to delve more deeply into asynchronicity with Node.js in a future article.)
  • It’s a hugely popular open source framework upon which hundreds of other liberally licensed, freely available open source frameworks and add-ons.  (See my previous article about Github: Stepping into the New Age of JavaScript using Github).

Let’s try and defragment the above list a bit by focusing on the potential benefits of using Node.js. There are at least three compelling reasons for using Node.js:

  1. The single most compelling reason for using Node.js as the basis for the server half of a client-server solution is that it is highly scalable and highly performant under heavy request loads.  This holds true more so when the bulk of these individual requests tend to be small, rather than large,  Fortunately, that is the usage pattern of many, or perhaps the majority, of web-based applications.
  2. Another often cited reason for using Node.js is that JavaScript is a well known programming language, and usage of Node on the back-end allows all code, both client and server, to be written in JavaScript.  There are even efforts afoot to try and systemize the reuse of portions of code on both client and server (think data model definition and validation).
  3. If the aforementioned benefits of using Node are not enough for you, there is a biggie remaining - the plethora of useful and professionally maintained open source frameworks and libraries available for Node.js.  If there is a particular type of resource you need for your back-end code or some problems you need to solve by writing code, there is a very good chance that it has already been done for you.  I’ll have more to say on the subject of add-ons further on in the article.

Ok, that was a lot of wordage to wade through – let’s look at some code.  We will use some code examples as a springboard from which to begin understanding how Node.js works.  The smallest, simplest node.js program might look something like the following code snippet and exist in a text file named helloworld.js:

Of course this is a useless program, but it will serve to get us started.  Perhaps the first thing worth noting is that it uses standard JavaScript syntax (single quotes instead of double quotes would have worked just as well).  To execute this program on your system, Node.js would first have to be installed (we’ll see how to do this later).  To run it, you would use the command line or open a terminal or command window, whatever your OS provides.  After ensuring that that both Node and helloworld.js are in the executable path, enter the following: node helloworld.js.  Upon execution of the program, you would see the words Hello World in your console, i.e. the same UI you used to execute the program.

Let’s look at a program that does a little bit more – another program that could also be written in client-side JavaScript.  Again, this program must not reference any browser elements - these don’t exist in Node.js JavaScript.  The code snippet for this example is nearly as simple and pointless as the code in the first example, but it goes a little further in reinforcing that fact that server-side Node.js JavaScript looks the same as browser-based JavaScript.  You could put it in a file called helloworlddated.js and run it in a manner similar the previous example, i.e. by entering: node helloworlddated.js.  Here is the source for helloworlddated.js:

The resulting output in your console would be something like this: Hello World.  Today (day/month/year) is: 15/06/2013.  Running this small program gives a more convincing demonstration that Node.js is JavaScript for the server side… yet, it is not a thematic demonstration.  Sure, you might put some similar code into a Node.js program, or you might even create a server side program that runs now and again to perform some useful function.  However, the main idea behind the creation of Node.js was to provide solutions for high volume client-server types of apps.

What might a more thematic Node.js program look like?  Here is an extremely stripped down version of the beginnings of one such program:

This example program was taken from: http://nodejs.org/ , the home of Node.js at Joyent.  What is it doing?  It looks almost as if it is acting like some sort of miniature web server… which is exactly what it is, a little five line web server that serves up some dedicated content instead of HTML files.  This simple web server written in Node responds with “Hello World” for every request.  A very common pattern for Node.js programs is that they will not only provide web content or other content, but they also provide the mechanism to receive client requests and return server responses.  The good news is, that because of some built-in Node.js library code, creation of such a web server is a trivial matter.  Actually, even creating a full blown, actual web server is a fairly trivial matter, if certain add-on frameworks are also put into use.  (I will provide some more detail on this subject further below and will probably eventually write more articles about various modules that extend the basic functionality of Node).  I am getting ahead of myself though.  Let’s dissect the above code snippet, piece by piece.

Line 1:  var http = require(‘http’);

It looks like standard JavaScript syntax, but what does “require” do and what does that argument of ‘http’ mean?  The first thing to understand is that much of the functionality of Node is compartmentalized in to something called a module.  Node comes installed with several built in modules and you can download hundreds of others from Github.  In Line 1, the argument ‘http’ is the name of a module that pulls in functionality surrounding the use of http, in this case the ability to create a simple web server.  the function named “require” is a globally available Node.js function that allows you to pull the content of modules into your code.  As one might guess, it returns an object that can then be used to invoke the functionality exported by the module.

Line 2:  http.createServer(function (req, res) {

In Line 2, we use the http object returned by the require(‘http’) call in Line 1, to create an http server.  We can see the beginning of an anonymous function definition that takes two arguments – a request argument and a response argument. (Note: For those unfamiliar with anonymous JavaScript functions, you may simply think them as an unnamed functions that are dynamically declared at runtime.  This link to Helen Emerson’s blog at Helephant.com offers a very clear explanation.).  The anonymous function that is being passed here (definition completed on subsequent lines), will be invoked when Node receives an HTTP request on the port whose number is passed in Line 5.  This anonymous function will provide the response to that request.

Line 3:  res.writeHead(200, {‘Content-Type’: ‘text/plain’});

Line 3 is the first line of the body of the anonymous function began in Line 2.  When the anonymous function is executed, this line of that function writes an HTTP response header into the response.  You may have seen similar code in other types of back end solutions or if you have ever examined the workings of the HTTP protocol.

Line 4:  res.end(‘Hello World\n’);

Line 4 contains the end of the anonymous function body and it adds the string ‘Hello World\n’ to the body of the response.  It also caps off the response because the end method does that, in addition to accepting the Hello World string content.

Line 5:  }).listen(1337, ’127.0.0.1′);

Line 5 is the end of the createServer call and it chains a call to the listen method of said server after the createServer call is completed.  The first argument to the listen method is the port number that the server should use to listen for requests (a small leet-speak joke if you didn’t notice).  The second argument is the url upon which the server should be listening.

Line 6:  console.log(‘Server running at http://127.0.0.1:1337/’);

Line 6 provides output for the console about what has been done.  After the program has been started (as before this involves saving the code into a JavaScript file and running it from the console by typing; node <the javascript file name goes here>), copying that link from the console and running it from a web browser will cause the words “Hello World” to be displayed in the browser.

That wasn’t much work to code, even for a typical “Hello World” sort of program – but the reality is that program is ever so much more than a typical “Hello World” program.  It is the basis for a server-side program that might perform any of various kinds of behaviors or actions for a client side piece.  That particular example is an HTTP kind of animal, but an analogous TCPIP based program is just as easily created.  Here is an example of a simple TCP server which listens on port 1337 and echoes whatever you send it:

It is beyond the scope of an introductory article to try and bridge the gap from the previous thematic example to showing explicit examples of code that might back real world projects - but here is a link to an eye-opening page at Github: Projects, Applications, and Companies Using Node.  Scan the notes in the two large tables on the page that this link leads to and you will see a list of uses and functionality that empirically demonstrate what Node is capable of.  The list of usages is broader and deeper than one might expect - I think it’s pretty impressive.  Some readers might think this paragraph would be a natural place to end this first introductory article, but wait, there’s more!

There is a wealth of freely available external modules that can be used within a Node program.  I would be remorse in my introductory duties if I did not provide at least a glimpse of what kinds of functionality might be offered by those modules, how one might find out more about them and how they can be installed.  The original creator of Node.js and those that provided additional essential work on Node believed that Node should provide a basic, highly scalable, extensible engine for server-side work.  These external modules offer solutions (some of them competing with one another) that fill in the gaps of what is not built into Node itself.  There is a way that you can search for, find out about, and download nearly all of the external modules that have been created for use with Node.js – follow this link the Github Modules Search page.  However, In addition to downloading these modules directly Github, there is another, better way to get  them onto a system on which Node has already been installed - Node Package Management.  Node has a built-in command line program called npm that can be used to install modules (note that they must still be required into your code).  The syntax to install a module looks like this: npm install <some module>.  To learn more about how to use npm one can either search the web or else on the command line, type: nmp help install.

The functionality provided by this huge number of external modules could be divided into a number of different categories – but I decided upon just four.  My four basic categories are: Middleware, MVC Frameworks, Database Access, and my cop-out category, “Others”.  I will provide no examples for “Others” but a host of very useful library, utility and debugging-oriented modules would fall into this category.  In any event, these four divisions are highly arbitrary and probably woefully inadequate, but categorizing things in this manner offers a way to approach a summarization of what is really a rather extensive topic.

The term “middleware” is unfortunately, a sorely overloaded one.  It has had multiple meanings in the history of our industry and yet another is introduced in the context of Node.js.  In Node, the term is a loose descriptor for functionality that exists between the client portion of an app and the logical portion of the server-side app that makes up the application code.  I am going to give a couple of examples of what I consider to be middleware in Node and you can agree with me or disagree if you wish.  Fortunately, the “rose by any other name” principle applies to the two examples I am going to give you – they are both arguably best of breed in their class – regardless of what name you give that class.  The two examples I will put forth are Connect.js and Socket.io.  Connect is actually a framework of sorts itself, one that offers a surprising amount of functionality.  The bulk of this functionality centers upon fleshing out the things a web server can do that the http module does not do, or does not handle as well (i.e. powerful but elegant: logging, cookie handling, favicon handling, static file serving, error control, etc.).  Socket.io is one of several modules that are available for node that provide Web Sockets functionality.  Socket.io is one of the more popular if not the most popular and it is very good at gracefully degrading into alternative communication methodologies when older web servers and/or browsers are in play.

MVC stands for Model-View-Controller of course, and there are a number of Node.js external modules that offer MVC or MVC-like functionality.  (Note: The scope and definition of the MVC style of architecture is beyond the scope of this article, but here is a link at Wikipedia that offers a starting point of you are unfamiliar with it: Model-View-Controller.)  I am going to mention a few MVC-ish modules here but not go into great detail about any of them.  I will briefly discuss Geddy, Express.js and Sails.js.  Geddy is billed as the “original MVC” solution for Node and was quite popular at one point though is perhaps growing somewhat long in the tooth now.  It is more of a standard, Rails-like MVC solution than say, Express is.   Express.js is almost itself a framework for creating MVC frameworks – in fact some new MVC frameworks are based upon Express.  However, you can do MVC in Express.js, and out of all the MVC-like solutions I read about, it seems to be the most popular.  As of this writing, I have never yet used Geddy or Express (though I do plan to experiment with Express), so bear in mind that my opinions on Geddy and Express are based upon reading from multiple sources, not personal experience.  Sails.js is an up and coming solution that is generating a lot of interest.  Its claim to fame is the ability to easily generate a RESTful API, the delivery content of which is JSON.  This is a natural fit for creating hybrid MVC solutions in conjunction with a front end framework like the popular framework Backbone.js, a partial MVC solution that will natively digest JSON via internal JQuery AJAX calls.

The available modules for database access are in a way, astonishingly rich but in another way, surprisingly poor.  They are rich in that among them there is surprisingly broad support for what these days are being called “NoSQL” databases.  The examples I will list are MongoDB,  CouchDB and Redis.   MongoDB and CouchDB are both document-centric databases that accept, store and re-issue data in JSON format. (Note: If you are not familiar with JSON you should fix that ASAP.  Here is a link at Wikipedia that offers a starting point for beginning to understand JSON.)  Redis is a key-value in memory database that may be optionally persisted to file (the type of persistence in use by Redis is referred to as “durability”).  Between MongoDB and CouchDB, MongoDB seems to be more popular, though CouchDB has strong adherents.  I have played with MongoDB some and I like it.  It is worth noting that accessing MongoDB from Node is much easier if done via a simplifying API such as Mongoose.  Redis is purportedly the most popular key-value store that is out there at this time.  Ok, now we’ve come to the “surprisingly poor” part.  As far as I know, as of this writing, the only available module level support for relational database access for Node is for MySQL.  Doubtless this will be addressed soon, to at least provide support for Oracle and SQL Server.  That is not to say that your application code could not access such resources itself, for example, via web service.  Nonetheless, I consider this to be a fly in the ointment for Node.js.

We are nearing the end of the article and I am finally going to tell you how you can install Node.js - assuming I have interested you enough for you to give it a try.  The easiest way to get Node onto your system is to go to the main Node.js site sponsored by Joyent and either hit the big Install button on that page or else click the smaller Downloads button and choose an explicit installation on the subsequent page.  There are also other ways to get Node onto your system and install it.  You can Fork the project on Github and then Clone it down to the target system.  Alternatively, you could download the source code from Github and build it yourself.  The details of how to apply the latter two of the listed methodologies are beyond the scope of this article and are platform dependent as well.  Undoubtedly though, the best way to get started with Node.js is to install it from the Joyent sponsored site as described above.

In closing, I’d like to re-iterate Node’s strengths and also highlight the weaknesses I have mentioned. When high performance scalability is important, Node.js and judiciously chosen additional modules may offer a great replacement for more traditional solutions involving a combination of traditional web servers and architectures such as Ruby on Rails, Java Servlets or EJB-driven solutions, and ASP.NET style of pages.  Additionally It’s all JavaScript all of the time and offers a number of NoSQL solutions.  Every bit of this code is open source and free under licenses such as the MIT variant and other permissive GPL licenses.  A lot of this code is offered with dual license options as well.  On the other hand, if you are looking for built-in, strong relational database support or if you require a solution involving frequent heavy streaming between client and server, then Node is probably not the best choice, at least not yet.

Node is just warming up – this too may be a weakness – some of the modules you will be interested in will likely be pretty fresh and thus reliability and sustainability may be in question. However, I think most outsiders and insiders would say that it has already achieved critical mass, and this is just the beginning of a bright and beautiful future – Node is just warming up!