Welcome to the Inner Circle

Inner Circle is a simple game of predicting outcomes of events and comparing yourself against your friends.  I’ve started learning iOS dev and I am building Inner Circle to help me hone my iOS skills.  I have recruited 3 additional players and we just wrapped up the first week.  All questions were NFL selections.  Results are in the pictures below.

I decided to build Inner Circle to give me an opportunity to practice across several areas of iOS development.  This includes:

  • Getting and posting JSON to a server
  • Customizing the Table View to make the question selection cells bow to my command 
  • Using a WebView to view content from the web (this is the tables view)
  • Saving data locally between app restarts (username)
  • App icons – just anything besides the default!
  • And to see if we can make this game fun enough for us to want to continue playing

I’ve also coddled together a simple node.js app to manage all the data.  Feels good to work with Coffeescript again.  So clean!  And Heroku and Mongolab again to the quick and dirty rescue.

And big kudos to Test Flight for making it simple to share builds with your users.  All for free.

Next on the product timeline:

  • Adding the notion of Quizzes, or a grouping of questions
  • This will allow you to work on multiple simultaneous sets of questions
  • Display a selection list of the current open Quizzes

Make your picks

Make your picks

The scores after the first week.

The scores after the first week.

Advertisements

Using a single global DB connection in Node.js

“And I’m single, yeah, I’m single,
And I’m single, tonight I’m single,
And I ain’t tripping on nothing, I’m sipping on something,
And my homeboy say he got a bad girl for me tonight” – Lil Wayne

Since Javascript, and in turn Node.js, are single threaded, we can get by with just using one database connection throughout an entire application.  Pretty cool!  We no longer have to open/close/maintain connections and connection pools.  Let’s see how this could be done.

Think Globally, Code Locally

In pure Javascript, any variable you declare that is not defined within another scope (e.g. a function, within an object), will be added to the global scope.  However, because Node wraps each file into its own module with CommonJS, each module does not have direct access to the global scope.  But, Node provides a handy workaround:

global.db = ...

global.varName is accessible from any module.  Perfect.  So we can simply set a db connection on global.db and throw a reuse celebration party!  But before that party, let’s see how we would code this.

Global DB in Express with Mongoose
In this example we will create a single connection to Mongo using the Mongoose library.  In app.js, we define the global connection where we can use the local environment to determine the db uri. We will lazily create it so it will get created only when it is first requested.

// app.js
var express = require('express'); 
var app = express(); 
var mongoose = require('mongoose');

// Configuration 
app.configure('development', function(){ 
  app.set('dburi', 'localhost/mydevdb'); }); 

app.configure('production', function(){ 
  app.set('dburi', 'mongodb://somedb.mongolab.com:1234/myproddb'); }); 

global.db = (global.db ? global.db : mongoose.createConnection(app.settings.dburi));

Now let’s say in another file, we want to create a Mongoose schema for an Alien being.  We simply do it as such:

// otherfile.js
var alienSchema = new Schema({
  planet : { type: String }
});
var Alien = db.model('Alien', alienSchema);

What is happening here is that db.model is looking for db within the current module otherfile.js, not finding it, and then going up the chain till it gets to global.db.  Then boom!  We’ve got aliens.

Closing the connection

Since we want the connection open the entire lifespan of the application, we can simply allow it to terminate upon the application’s termination.  No cleanup needed.  (Caveat: if you know of an explicit way of doing this, perhaps to do some additional cleanup or logging, I’d love to hear about it).

Using Node cronjobs to replace Heroku worker dynos

“Time keeps on slipping, slippin…into the future.”  – Steve Miller Band

Hopscotch.fm relies on data that is pulled in from several music related services.  I have created multiple tasks each of which can be run from the command line, like:

node run manualrun getShows sf

This will get the shows for San Francisco. On Heroku, I was using the Scheduler to automatically run this periodically. All good so far.

The Problem

The solution worked great up until I started needing several of these workers, to get shows, to get artist metadata, to get artist songs, cleanup, and more.  Easy enough I thought.  I just added more tasks to the Heroku Scheduler. Except there is a limit to the free tier on Heroku…

The Surprise (the Heroku bill!)

My Heroku bill was over $70!  How did this happen??  Turns out I had exceeded the free monthly hours with all the worker dynos I had been spinning up.  So I needed a solution quick.  I host hopscotch.fm on nodejitsu so I figured why not just use that.

The Solution (cron!)

Enter node-cron. If you’ve ever used Linux/UNIX cron jobs, it’s nearly identical.  The syntax for dates is the same.  All you need to do is specify the function to run.  Here is a cron job in file cronjobs.js that crunches radio stations for a city:

var cronJob = require('cron').CronJob;
var cruncher = require('./cruncher); // internal class that does the crunching
var jobMetroRadio = new cronJob('00 05 00 * * 1-7', (function() {
  console.log('Starting crunching shows.');
  return cruncher.crunchShows(function() {
    return console.log('Finished crunching shows.');
  });
}), (function() {}), true, time.tzset("America/Los_Angeles"));

Then in app.js I just:

require('./cronjobs');

And lastly add these two packages to package.json and install them

cron  // add to package.json
time  // add to package.json
npm install

The Result
I’ve moved all of the tasks over from Heroku Scheduler onto the nodejitsu deployment and everything is running smoothly. Hooray for cron!

Node.js flash info messages

I was looking for a way to build a simple flash framework to easily display error and info messages throughout my webapp’s pages.  This is a pretty standard web development practice and sure enough there is a simple way to do this with node.js.  Technically this is using Express so if you’re using that then check out this write up: http://dailyjs.com/2011/01/03/node-tutorial-8/

Mongo queries from node.js

You have a Mongo collection that you want to run a standard find on. Create a generic query worker (even the name isn’t exciting) to do the hard work while you simply pass in the name of the collection, the query to run and some optional options if you’re looking for more excitement. First I’ll show the query code then following that is an example of using it.

//Generic worker function to run a find query
exports.runQuery = function(myCollection, query, options, nextFn) {

    // perform the {query} on the collection and invoke the nextFn when done
    var db = new Db(dbname, new Server(host, port, {}), {native_parser:false});
    //console.log("host is " + host);
    db.open(function(err, db) {
        //console.log("reading mongo collection " + myCollection.toString());
        db.collection(myCollection, function(err, collection) {

            var optionsArray = {};
            if (typeof(options) != 'undefined') {
                optionsArray = options;
            } else {
                optionsArray['limit'] = 100;
                optionsArray['sort'] = {};
                optionsArray['sort']['_id']= -1;
            }

            optionsArray['slaveOk'] = true;

            collection.find(query, optionsArray, function (err, cursor){
                if (err) {
                    console.log("error is: " + err);
                }
                cursor.toArray(function(err, docs) {
                    console.log("found " + docs.length + " documents in " + myCollection);
                    var queryResults = [];
                    for(var i=0; i<docs.length; i++) {
                        queryResults[queryResults.length] = docs[i];
                    }
                    db.close();
                    nextFn(queryResults);
                });
            });
        });
    });
}

Calling the Query

Here we want up to 300 applications whose appId is neither sweetAppId1 or sweeterAppId2.  In the optionArray we ask nicely that it sorts them descending on the _id so we get most recent 300.

 queryParams['appId'] = {};
 queryParams['appId']['$nin'] = {};
 queryParams['appId']['$nin'] = ['sweetAppId1', 'sweeterAppId2'];

 var optionsArray = {};
 optionsArray['slaveOk'] = true;
 optionsArray['limit'] = 300;
 optionsArray['sort'] = {};
 optionsArray['sort']['_id']= -1;

 this.runQuery('appUsage', queryParams, optionsArray, function(results) {
      nextFn(results);
 });

Mongo grouping with node.js

Grouping in Mongo is equivalent to doing a GROUP BY in SQL. In this example we will be grouping within the Application collection. There are two bits of code here. First, it is the generic worker method that executes the grouping. It takes in all the parameters such as the key to group by and the reduce method to run and returns the results as JSON.

//Generic worker function to run a 'group by'
exports.runGroup = function(myCollection, inputKey, inputCond, inputInitial, inputReduce, options, nextFn) {

    // perform the {query} on the collection and invoke the nextFn when done
    var db = new Db(dbname, new Server(host, port, {}), {native_parser:false});

    db.open(function (error, client) {
      if (error) throw error;

      db.collection(myCollection, function(err, collection) {
          collection.group(
              inputKey, // key
              inputCond, // cond
              inputInitial,  // initial
              inputReduce, // reduce function
              true, // use the group command
              function(err, results){ 
                  nextFn(results);
              }
         );  // end of collection.group
      }); //end of db.collection
    });  //end of db.open
}

Second, we call the method on the Application collection to group by appId.

	var inputKey = { "appId" : true} ;
	var inputCond = { "timestamp" : { $gte: req.fromDate, $lte: req.toDate } };
	var inputReduce = "function(obj, prev) { prev.count++; }";
	var inputInitial =  { "count" : 0};
	var optionsArray = {};
	optionsArray['slaveOk'] = true;
	optionsArray['limit'] = 300;
	optionsArray['sort'] = {};
	optionsArray['sort']['_id']= -1;

	this.runGroup('restCall', inputKey, inputCond, inputInitial, inputReduce, optionsArray, function(results) {

		results.sort(function(a,b) {
		    // this sorts the array using the total count of the 'count' column
    		    return ((a['count']  b['count']) ? -1 : 0));
		});

		nextFn(results);
	});