JSONP with Express.js API – cross domain request

The problem:

So you have this awesome API endpoint, but of course it is another domain as the static site.

Web browsers are pretty moody with Cross Domain and there are actually very good security reasons for that, but even using good practices the old Internet Explorer don’t care.

Welcome ancient jsonp techniques!

Solution

For some Express.js API running on Node.js and client using jQuery (or something alike)

In your server:

app.get('/my-unique-endpoint', function(req, res, next) {
  var myAwesomeResource = ["hello", "mundo"];
  if (req.query && req.query.callback) {
    res.set({
      // nice to have, but Chrome dont seem to mind either way
      'Access-Control-Allow-Origin': '*',
      // right content type prevent warnings and errors
      'Content-Type': 'text/javascript; charset=UTF-8',
      // optional, this is in seconds, equivalent to 8h
      'Cache-Control': 'public, max-age=28800'
    });
    return res.send(200, "" + req.query.callback + "(" + JSON.stringify(myAwesomeResource) + ");");
  } else {
    return res.send(200, myAwesomeResource);
  }
});

In the client:

// see the data in your console
$.getJSON("http://myawesomewebsiteurl/my-unique-endpoint?callback=?", function(data){ console.log(data) } )

Caveats

jsonp is works for GET verb, because it effectively execute the code received as if it was downloading a script.
for the same as above, the source should also be fully trusted!

Remove jQTouch + maps lag

Hello!

jQTouch still is a pretty good tool, with performance out-of-the box for old mobiles (being faster then jQMobile), but with some sharp edges.

The problem I deal with in this post may come in a couple of factors, such as:

  • When combined with Google Maps, there is an insane lag when panning (scrolling) the map.
  • General scroll is slow.
  • Some components are slow, particularly when under CSS transition.

At first I thought that either it had a bad interaction with Phonegap, or poor handling of events.

What I find happening that was pretty bad, and almost made me drop it’s use entirely is that it applied some CSS perspective to the body on the bundled apple.css

bad transform

What a bad perspective..

This made the map feel laggy on the Desktop and incredibly bad on iOS!

So, as solution, you have at least 2 options:

1) Remove the property from the .css

2) After jQTouch has been initialized, run a function that remove  the 3D support flag, like so:

$(‘#jqt’).removeClass(‘supports3d’)

Done! =)

Posting a photo (as a business) to a Facebook Page via JavaScript SDK

The Facebook Javascript SDK (API) is not so well documented, what makes not so logical to post to a page, using it. For that I am about to show, make sure your connect scope having at least: “manage_pages,publish_actions,publish_stream”.

I am assuming you already succeeded providing login to the user; if this is not the case, refer to the docs.

We rely heavily on the FB.api method, the docs make a poorly job documenting, but is a starting point anyway. Also, notice the response is async.

Fetch all user pages (actually the first 5k), but only store the first as var page (this is just a sample)

var page = null;
FB.api('/me/accounts', function(response) { console.log(response); page = response.data[0]; })

Now, we want to publish a post to the page timeline, the thing is, we are not posting as ‘me’ anymore, we must post as the page, and it requires both the id and access_token!
I will make a sample function for it, the response is the post id if it all went well, or an error.

function postToPage(page, msg){
  FB.api('/'+page.id+'/feed', 'post', { message: msg, access_token: page.access_token },
    function(res) { console.log(res) }
  )
}
postToPage(page, 'hello fb-page world')

For last, posting a picture requre the url param, like so:

function photoToPage(page, src, msg){
  FB.api('/'+page.id+'/photos', 'post', { url: src, message:msg, access_token: page.access_token },
    function(res) { console.log(res) }
  )
}
photoToPage(page, 'http://24.media.tumblr.com/tumblr_m1ttif5puW1qcrr0lo1_500.png', 'nyan art')

* Don’t try to run all functions in sequence, since the result comes asynchronously you wont have the var page set.

** kudos for Владимир Дворник answer on Stack Overflow for the url param not documented in the Page Graph API.

*** Security note. The JavaScript SDK can be very unsafe in the case a hacker is able to inject JS code in your page (and even happens in Twitter all the time even though they have millions in funding) they may take advantage of all of the user’s permissions.

Done!

Mongoose validate unique field (insensitive)

Making a validation function for Mongoose (Node.js – MongoDB ODM) that checks upon validation field.

function uniqueFieldInsensitive ( modelName, field ){
	return function(val, cb){
		if( val && val.length ){ // if string not empty/null
			
			var query = mongoose.models[modelName]
				.where( field, new RegExp('^'+val+'$', 'i') ) // lookup the collection for somthing that looks like this field 
			
			if( !this.isNew ){ // if update, make sure we are not colliding with itself
				query = query.where('_id').ne(this._id)
			}
			
			query.count(function(err,n){
				// false when validation fails
				cb( n < 1 )
			})
		} else { // raise error of unique if empty // may be confusing, but is rightful
			cb( false )
		}
	}
}

Then call it to a field (aka path):

UserSchema.path('nick').validate( uniqueFieldInsensitive('User', 'nick' ), 'unique' )

Recommend you organize all validations in a file for itself, or even a folder if grows too big.

If you have a unique index set for the path you may just save it and check for the error raised; the thing is the format of the error.

Basic Authentication on Node.js – Express and Mongoose

via Axiom Zen

Hello!

This post is about the most important parts I can think of implementing signup and login to Node.js powered by Mongoose and Express.js.

This post have a huge audience, but guys, please keep in mind that this post was done a while ago. It works pretty well as a cook-book, but there probably are better options for achieving this nowadays, new npm packages and such.

Implementing last night, I was impressed on how fast had the functional part done (around 3 hours), pretty much the same time I’d had it done in Rails (if not faster).

Goal:

  • 3 urls: GET login & signup, POST login, POST signup.
  • User fields: email, nick, password(encrypted).
  • Validate (and show) messages for all fields.
I’ve setup a small Github project that may be used as example (assuming you have mongoDB installed and running in localhost) – DOWNLOAD IT FROM GITHUB  

Model: user.js

var Schema = mongoose.Schema
  , ObjectId = Schema.ObjectId
  , Validations = require('./validations.js')
  , salt = 'mySaltyString'
  , SHA2 = new (require('jshashes').SHA512)()

function encodePassword( pass ){
	if( typeof pass === 'string' && pass.length < 6 ) return ''

	return SHA2.b64_hmac(pass, salt )
}

var UserSchema = new Schema({
    nick        : {type: String, required: true, unique: true, trim: true }
  , email       : {type: String, required: true, unique: true, trim: true, lowercase: true }
  , password    : {type: String, set: encodePassword, required: true }
});

UserSchema.statics.classicLogin = function(login, pass, cb) {
	if( login && pass ){
		mongoose.models.User
			.where( 'email', login )
			.where( 'password', encodePassword(pass) )
	  	.findOne( cb )
	} else {
		// just to launch the standard error
		var o = new this({nick: 'VeryUniquejerewelA', password: '', email: login+'aaa'})
		o.save(cb)
	}
}
UserSchema.path('nick').validate( Validations.uniqueFieldInsensitive('User', 'nick' ), 'unique' )
UserSchema.path('email').validate( Validations.uniqueFieldInsensitive('User', 'email' ), 'unique' )
UserSchema.path('email').validate( Validations.emailFormat, 'format' )
UserSchema.path('password').validate( Validations.cannotBeEmpty, 'password' )
UserSchema.plugin( mongoose.availablePlugins.timestamper )

mongoose.model('User', UserSchema)

.Highlights of this code: We are using the package jshashes, which supplies many convenient encryption methods, among those SHA512 –strong enough
It is a good practice to use a salt along, represented by the var salt. In practice it makes way difficult for a cracker that acquired access to the database do decipher the passwords stored.
The method encodePassword is used at two occasions, when setting the User password, and when retrieving it from database.
UserSchema.statics is a object that stores additional static methods our User model will offer.
The function classicLogin requires both login and pass to search the db for existence, otherwise, it will launch an error (kinda of a smelly workaround to make it work dry )
UserSchema.path(…).validate offers us validations, in our case, we do not allow repeated email or nick, and password should be bigger at least 6 characters long. Also email should at least look like a email.
All those validations work along with the own Schema definition: required, unique, trim, lowercase

Route: auth.js

// app.get( '/auth/popover', auth.popover);
exports.popover = function(req, res){
	//req.session.popover = new Date()
	console.log('My session:', req.session)
  res.render('auth/index_pop', req.viewVars);
};

// CLASSIC LOGIN / SIGNUP       --because everyauth seems too messy for login+pass
// app.post('/auth/classic-signup', auth.classicSignup)
exports.classicSignup = function(req,res,next) {
	if( !req.body ){
		console.log('why u signup nobody?')
		return res.redirect('/?nobodySignup')
	}

	var user = new app.models.User()

	user.set('nick', req.body.nick)
	user.set('email', req.body.email)
	user.set('password', req.body.pass)
	user.set('providers', ['signup:'+user.get('email')])
	user.set('profiles', [{ _name: 'signup'}])

	user.save( function(err) {
		if( err ){ // validation failed

			req.viewVars.u = user
			return classicYieldErr( req, res, 'signUp', err)

		} else { // signup successful

			req.session.user = {
				provider: 'signup',
				id: user.get('id'),
				nick: user.get('nick'),
			}

			req.flash('notice', 'Welcome!')
			req.viewVars.welcome_login = "Welcome, "+user.nick

  		res.render('auth/win_pop', req.viewVars )
		}
	})
};

// app.post('/auth/classic-login',  auth.classicLogin)
exports.classicLogin = function(req,res,next) {
	if( !req.body ){
		console.log('why u login nobody?')
		return res.redirect('/?nobodyLogin')
	}

	app.models.User.classicLogin( req.body.email, req.body.pass, function(err, user) {
		if( err ){ // validation failed

			return classicYieldErr( req, res, 'signIn', err)

		} else {

			if( user ){ // login

				req.session.user = {
					provider: 'signup',
					id: user.get('id'),
					nick: user.get('nick'),
				}

				req.flash('notice', 'Welcome!')
				req.viewVars.welcome_login = "Welcome, "+user.nick

	  		res.render('auth/win_pop', req.viewVars )

			} else { // not found
				return classicYieldErr( req, res, 'signIn', {errors:
					{'loginpass': {
						name: 'V',
						path: 'login+password',
						type: 'loginpass'
					}
				}})
			}

		}
	})
};

// display form error
function classicYieldErr( req, res, mode, err ){
	req.viewVars.erroredForm = mode
	if( mode === 'signIn' ){
		req.viewVars.signin_errors = app.helpers.displayErrors( err )
	} else {
		req.viewVars.signup_errors = app.helpers.displayErrors( err )
	}
	req.viewVars.email = req.body.email

	res.render('auth/index_pop', req.viewVars);
}

Here we define the 3 routes;
– one route(GET) that defines one page for both login or signup
– one route(POST) to submit login
– one route(POST) to submit signup
About the last 2, their only role is to allow access for valid data. That is, valid signup data, or login+password existing in our collection from MongoDB.
The function classicYieldErr is there just to serve the errors in sort of an uniform way. We will see about this function on the next file

Model Helper: validations.js

exports.uniqueFieldInsensitive =  function ( modelName, field ){
	return function(val, cb){
		if( val && val.length ){ // if string not empty/null
			// only for new docs
			if( this.isNew ){
				mongoose.models[modelName].where(
					field, new RegExp('^'+val+'$', 'i')
				).count(function(err,n){
					// false when validation fails
					cb( n < 1 )
				})
			} else {
				cb( true )
			}
		} else { // raise error of unique if empty // may be confusing, but is rightful
			cb( false )
		}
	}
}

exports.emailFormat = function( val ){
	// false when validation fails
	return (/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i).test( val )
}

exports.cannotBeEmpty = function( val ){
	console.log('pass val is:', val)

	// not all passwords should be set, BUT when string, should be encoded
	if( typeof val === 'string' ){
		if( val.length ){ // when it comes empty, something went wrong!
			return true
		} else {
			return false
		}
	} else {
		return false
	}
}

This file contains some very important function, being intuitive, I will only comment on the first.
uniqueFieldInsensitive is actually a validation creator. It uses JS’s closure ability along with the powerful this object to wrap the context where this will represent whichever Model is calling it.
The intent of the function is to search the db for any repeated occurrence of the field, case-insensitive. This way the system will block a user “John” from signinup, if “john” is present.

Helper: form_helper.js


//app.helpers.displayErrors = require('./helpers/form_helper.js').displayErrors

stripErrors = function(mongooseErr){
	var prop, list = [];
	for( prop in mongooseErr.errors ){
		list.push( [mongooseErr.errors[prop].path, mongooseErr.errors[prop].type] )
	}
	return list
}

/*
 * Translate mongoose errors into a <li> of errors
 */
exports.displayErrors = function( mongooseErr ){

	console.log( 'mongoose errs', mongooseErr )

	var list = stripErrors( mongooseErr )

	var output = []
	list.forEach(function(e,i){
		switch( e[1] ){
			case( 'unique' ):
				output.push( e[0]+" is taken" )
				break;
			case( 'required' ):
				output.push( e[0]+" is "+e[1] )
				break;
			case( 'format' ):
				output.push( e[0]+" has a bad format" )
				break;
			case( 'password' ):
				output.push( "password should be at least 6 char long" )
				break;
			case( 'loginpass' ):
				output.push( "login+password not found" )
				break;

			default:
				output.push( e[0]+": "+e[1] )
				break;
		}
	})
	if( output.length ){
		// condense all items in an error list
		output = [output.join( '</li><li>\n' )]
		output.unshift( '<ul><li>' )
		output.push( '</li><ul>' )

		// wrap in a div
		output.unshift( "<div class='error block'>" )
		output.push('</div>')
	}
	return output.join('\n')
}

This file provides a translation from Mongoose errors to user-readable errors. In order to offer a good UX, it is important to give accurate feedback. Notice this file could benefit from some refinement 🙂

Don’t forget to download the code from github, it plays well along everyauth module 🙂

Cheers!

Testing a Node.js Express API server with Vows (functional)

How to test an API that must be authenticated via session?

vows: plural of vow

Noun: A solemn promise.
Verb: Solemnly promise to do a specified thing: “one fan vowed, “I’ll picket every home game.””.

Oh wait..

Vows.js
Asynchronous behaviour driven development for Node.

I should… test every method I expose to the client..
Since the Vows documentation is very awesome, and I already have another post that explains the strategy used on the API, there is no need to much talk; just not that this code is still very early in maturity.
Straight to current code [ test/api-test-authed.js ]

/*
 * INSTRUCTIONS
 *
 * run the site at localhost, port 8010
 *
 * run vows --spec test/api-test-authed.js
 *
 */


var request = require('request'),
    vows = require('vows'),
    assert = require('assert'),
    apiUrl = "http://localhost:8010/",
    cookie = null


var apiTest = {
  general: function( method, url, data, cb ){
    //console.log( 'cb?', cb )
    request(
      {
        method: method,
        url: apiUrl+(url||''),
        json: data || {},
        headers: {Cookie: cookie}
      },
      function(req, res){
        cb( res )
      }
    )
  },
  get: function( url, data, cb  ){ apiTest.general( 'GET', url, data, cb    )  },
  post: function( url, data, cb ){ apiTest.general( 'POST', url, data, cb   )  },
  put: function( url, data, cb  ){ apiTest.general( 'PUT', url, data, cb    )  },
  del: function( url, data, cb  ){ apiTest.general( 'DELETE', url, data, cb )  }
}

function assertStatus(code) {
  return function (res, b, c) {
    assert.equal(res.statusCode, code);
  };
}


function assertJSONHead(){
  return function(res, b, c ){
    assert.equal( res.headers['content-type'], 'application/json; charset=utf-8' )
  }
}

function assertValidJSON(){
  return function(res, b ){
    // this can either be a Object or Array
    assert.ok( typeof( res.body ) == 'object' )
    //assert.isObject( res.body)
  }
}





// TODO include unauthed tests
var suite = vows.describe('API Localhost HTTP Authenticated Tests')

// Very first test!
.addBatch({
  "Server should be UP as in: var apiUrl": {
    topic: function(){
      apiTest.get('', {} ,this.callback )
    },

    '/ should repond something' : function(res, b){
      assert.ok(res.body)
    }
  }
})

.addBatch({
  'Authenticate to /login': {
    topic: function(){
      request.post(
        {
          url: "http://localhost:8010/login",
          json: { user:{ username: 'flockin_lab', password: '123456' }}
        },
        this.callback
      );
    },



    'get a valid Cookie': function(req, res, body, err){
      try{
        cookie = res.headers['set-cookie'].pop().split(';')[0]
        console.log("GOT COOKIE!", cookie)
      } catch(e){ }

      assert.ok( typeof(cookie) == 'string' && cookie.length > 10 )
    }
  }
})
.addBatch({
  'Users#index': {
    topic: function(){
      apiTest.get('admin/employees', {}, this.callback)
    },
    'should be 200': assertStatus(200),
    'should have JSON header' : assertJSONHead(),
    'body is valid JSON' : assertValidJSON(),

  },
})
.addBatch({
  'Qrcodes#index': {
    topic: function(){
      apiTest.get('admin/qrcodes', {}, this.callback)
    },
    'should be 200': assertStatus(200),
    'should have JSON header' : assertJSONHead(),
    'body is valid JSON' : assertValidJSON(),

  },
})

//suite.run( )
suite.export( module )

Brief Discussion

This code is still state-of-art,
We depend on the lib ‘request’, which is pretty good,
The server should already be started, since I see no point in having the test being responsible to bring it up and handle it.

Can improve it? Please leave a comment! 🙂

Starting a Node.js Express API to serve json, (with auth, MySQL – based)

via Axiom Zen

Hello and welcome!

Foreword, and purpose

I will be putting this JSON-API up, to serve USERS as a restful resource, trying to be light as possible, still serving content only to authenticated peers (auth based on cookie).

The regular use will be something as, the client makes login, sending params via POST, ant then is able to access all resources we may serve.

I will be using some packages, all being possible to install via npm install, lets talk about them:

  • Express, itself is like the Ruby’s Sinatra Framework, but async, for Node.js
  • Express-resource, a simple and extendable way to define Restful resources
  • Sequelize, a MySQL ORM! Offer n-m relations, and a .sync() way to define your own Schema! (better than migrations?)
  • Faker, a generator of fake-data that we need
  • Mustache, String template engine. Will be using for minimal tasks, not required.
  • Jade, view template engine. Not required at all, but since I started coding there, will keep it for now.

The app skeleton

Install express with -g and generate an app from it.

We will be keeping 3 core files:

app.js  : The main file; require all other from there, and configure Express!

models.js : The file that has all models definitions exported.

routes.js : Acts as a general router and controller for the app. Also holds the functionality of authenticating all users that get past /admin/*. The authorization method here is static, in this case, one e-mail bounded to one password. See Auth Methods app.post(‘/login’.. for details.

Files

./app.js

// Detect Params  http://nodejs.org/docs/latest/api/process.html#process.argv
if (process.argv.indexOf('--seed') > -1) {
  /**
   * Indicates when the DB should be re-schemed and seeded again
   */
  GLOBAL.db_seed = true;
}

if (process.argv.indexOf('--no-auth') > -1 ){
  /**
   * Indicates when to run without requiring auth for API
   */
  GLOBAL.no_auth = true;
}

var express = require('express'),
    /**
     * Instantiate the unique Express instance
     */
    app = module.exports = express.createServer(),
    models = require('./models.js'),
    S = require('mustache').to_html;

/**
 * @type {Object}
 *
 * A list of all Sequelize Models available, representing the tables.
 */
GLOBAL.models = models;

/**
 * @type {Express}
 *
 * The Singleton of Express app instance
 */
GLOBAL.app = app;

// Configuration

app.configure(function() {
  app.use(express.logger({format: 'dev'}));
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.bodyParser());
  app.use(express.cookieParser());
  app.use(express.session({ secret: 'evilWorldDom1nat10nPlanzisstillsmallshouldhaveNoWords' }));
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));

});

app.configure('development', function() {
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function() {
  app.use(express.errorHandler());
});

// Routes

require('./routes.js');

app.listen(8010);
console.log('Express server listening on port %d in %s mode',
   app.address().port, app.settings.env);

./models.js

var Sequelize = require('sequelize'),
    db = new Sequelize('dev_', 'root', '123456');

  /**
   * @type {Object}
   * Map all attributes of the registry
   * (Instance method useful to every sequelize Table)
   * @this {SequelizeRegistry}
   * @return {Object} All attributes in a Object.
   */
    var map_attributes = function() {
      var obj = new Object(),
          ctx = this;
      ctx.attributes.forEach(
        function(attr) {
          obj[attr] = ctx[attr];
        }
      );
      return obj;
    };

/**
 * @type {Object}
 * All models we have defined over Sequelize, plus the db instance itself
 */
var self = module.exports = {
  'db' : db,

  User: db.define('user',
    {
      name: {
              type: Sequelize.STRING,
              defaultValue: 'Not Big Deal',
              allowNull: false
            }
    },
    {
      timestamps: true,
      freezeTableName: true,

      classMethods: {
        staticExample: function() { this.name }
      },
      instanceMethods: {
        mapAttributes: map_attributes
      }
    }
  ),

  Activity: db.define('activity',
    {
      type: { type: Sequelize.STRING },
      value: { type: Sequelize.STRING }
    },
    {
      timestamps: true,
      freezeTableName: true,

      instanceMethods: {
        mapAttributes: map_attributes
      }
    }
  ),

  Company: db.define('company',
  {
    name: { type: Sequelize.STRING, defaultValue: 'MegaCorp', allowNull: false }
  },
  {
    timestamps: true,
    freezeTableName: true,

    instanceMethods: {
        mapAttributes: map_attributes
      }
  })

};

self.Activity.belongsTo(self.User, {foreignKey: 'user_id'});
self.User.hasMany(self.Activity, {foreignKey: 'user_id'});

self.User.belongsTo(self.Company, {foreignKey: 'company_id'});
self.Company.hasMany(self.User, {foreignKey: 'company_id'});

if (GLOBAL.db_seed) {
  console.info('SEED TIME!');

  var chainer = new Sequelize.Utils.QueryChainer;

  chainer
    .add(self.Company.sync({force: true}))
    .add(self.User.sync({force: true}))
    .add(self.Activity.sync({force: true}))
    .run()
    .on('success', function() {

      var seeds = require('./seeds');
      for (var model_name in seeds) {
        console.log('MODEL', model_name);
        for (var i = 0; i < seeds[model_name].length; i++) {
          self[model_name].create(seeds[model_name][i])
            .on('success', function(novo) { })
            .on('failure', function(erro) {
              console.error('DB SEED ERROR!!', erro);
              process.kill();
            });
          console.log('  ', seeds[model_name][i]);
        }
      }
    })
    .on('failure', function(errors) {

      console.error('DB schema change FAIL', errors);
      process.kill();
    });

}

// User.staticExample()
// User.build({}).instanceExample()

./routes.js

S = require('mustache').to_html;
Resource = require('express-resource'); // paths Express, add .resource

/**
 * ROOT
 */
app.get('/', function(req, res) {

  // This big mess just points out all routes we have, along with the verbs
  var path_list = S(
    '{{#routes_obj}}' +
    "<a href='{{path}}'>{{method}}: {{path}} </a> <br/>" +
    '{{/routes_obj}}',
    {
      routes_obj: (app.routes.routes.get ? app.routes.routes.get : [])
        .concat((app.routes.routes.post ? app.routes.routes.post : [])
          .concat((app.routes.routes.put ? app.routes.routes.put : [])
            .concat(app.routes.routes.delete ? app.routes.routes.delete : [])
              .concat([{ path: '/auth.html', method: 'get'}])))
    }
  );

  res.render('index', {
    title: 'DBServer',
    'path_list': path_list,
    logged: req.session.authed ? 'YES :)' : 'NO :('
  });

});

/**
 * AUTH methods
 */
app.post('/login', function(req, res) {

  console.info('login PARAM: ', req.body);

  var credentials = req.body.user;

  if (GLOBAL.no_auth ||
       (credentials &&
        credentials.username == 'super_safe' &&
        credentials.password == '123456')
      ) {

    req.session.authed = true;
    res.json(['OK']);
  } else {
    res.json(['FAIL']);
  }

});

app.get('/logout', function(req, res) {
  req.session.authed = null;
  res.json(['OK']);
});

/*
 * API Authentication filter
 */
app.all('/admin*', function(req, res, next) {

  if (req.session.authed) {
    next();
  } else {
    res.json(['must auth']);
  }
});

/*
 * Resources
 */
app.resource('admin/users', require('./resources/users'));

/**
 * Just say it is all fine.
 */
module.exports = true;

./seeds.js

// https://github.com/marak/Faker.js/
// npm install Faker
var Faker = require('Faker'),
    times = 0,
    id = 0

var self = {
  Company: [ ],
  User: [ ],
  Activity: [ ],
}

// Company: create 3 - 5
id = 1
times = Faker.Helpers.randomNumber(3)+3
for (; times > 0 ; times--) {
  var novo = {
    id: id++,
    name: Faker.Company.companyName(),
  }
  self.Company.push( novo )
  //console.log("Company", novo)
};

// Users: create 0 - 8 per company
id = 1
for (var i=0; i < self.Company.length; i++) {
  times = Faker.Helpers.randomNumber(9)
  for (; times > 0 ; times--) {
    var novo = {
      id: id++,
      name: Faker.Name.firstName(),
      company_id: Faker.Helpers.randomize( self.Company ).id,
    }
    self.User.push( novo )
    //console.log("User", novo)
  };
};

// Activities: create 0 - 10 per user
var TYPES = ['level_up', 'timeout' ],
    VALUES = ['win', 'fail']
id = 1
for (var i=0; i < self.User.length; i++) {
  times = Faker.Helpers.randomNumber(11)
  for (; times > 0 ; times--) {
    var novo = {
      id: id++,
      type: Faker.Helpers.randomize( TYPES ),
      value: Faker.Helpers.randomize( VALUES ),
      user_id: Faker.Helpers.randomize( self.User ).id,
    }
    self.Activity.push( novo )
    //console.log("Activity", novo)
  };
}

module.exports = self

./resources/users.js

exports.index = function(req, res) {
  models.User.findAll().on('success', function(users) {

    res.json(
      users.map(function(user) {
        return user.mapAttributes();
      })
    );
  });
};

./views/index.jade

h1= title
p Welcome to #{title}
p Paths we have:
div!= path_list
div .
footer Loggedin? #{ logged }

./public/auth.html

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8" />
		<title>auth</title>
		<link rel='stylesheet' href='/stylesheets/style.css' />
	</head>
	<body>
		<div>
			<header>
				<h1>Auth</h1>
			</header>
			<nav>
				<p>
					<a href="/">Home</a>
				</p>
			</nav>
			<div>
				<form action="/login" method="post">
					<label>Username: <input name="user[username]" type="text" /></label></br>
					<label>Password: <input name="user[password]" type="password" /></label></br>
					<button type="submit">OK</button>
				</form>
			</div>
			<footer>
				<p>
					Express API
				</p>
			</footer>
		</div>
	</body>
</html>

Brief Discussion

Overall, this post presents a short and extensible way to start a API that requires authentication, via cookie, and returns data in JSON format.

Very simple invisible JavaScript & Rails captcha

Hello!

Visual captchas are far from being desirable on most public sites, but spam is even less desirable, mainly in contact forms. This solution I am implementing is dead simple, but also, weaker than reCaptcha.

snippet:

Put this in the application_controller.rb

  before_filter :form_spam_control

  private

  def form_spam_control
    if request.post? || request.put?
      unless params['agent_smith'] == 'Mr Anderson, welcome back'
        render :text => "Please make sure cookies and js are enabled"
        return false
      end
    end
  end

Put this in a javascript that is executed on every public page, typically, application.js (*does require jQuery loaded)

$(document).ready( function(){
  $('form').append( j('<input/>', {
    type: 'hidden',
    id: 'agent_smith',
    name: 'agent_smith',
    value: 'Mr Anderson, welcome back'
  }) )
})
//UPDATE! in order to support AJAX without extra params add:
j('body').ajaxSend(function(a,b,c){ if( c.type == 'POST' || c.type == 'PUT' ) c.data = c.data.length > 0 ? c.data+'&agent_smith=Mr+Anderson%2C+welcome+back' : 'agent_smith=Mr+Anderson%2C+welcome+back'})

Discussion:
This is totally invisible and harass-free for the user.
I am based on the principle that spam crawlers does not run JavaScript, which may not be true for all of them. Still this will deny some crawlers that may be considered good, such as Mechanize.
This technique can be easily ported to other backend languages, such as PHP, ASP, C#, Java, since it only requires a parameter filter on POSTs and PUTs
If the attacker focus your website, this will be easily broken.
If the user has JavaScript disabled, he can’t post, but this is a normal drawback on some captchas.
* the part of the error message including ‘cookies’ is just a disguise =)

accepts_nested_attributes_for 3.0.5 :reject_if still have gotchas

Hey

Nested forms are nasty, but many times, a necessary evil 🙂

Rails tackled it since version 2.3x, when added the ActiveRecord method accepts_nested_attributes_for, main ref here. For its complexity, I believe, it is still hard to master it, and still bugs may happen; this was my lesson.

Ryan Bates, (a ruby hero) has made some screencasts from it, watch part1, part2. Soon after, he and some buds, made a gem for it, targeting Adding and Removing via JavaScript, without harassment. You can find on GitHub, along with the necessary doc.

So, using the gem or not, still there are things to cover over this methods params;

From what I could observe, :reject_if, does not work for ‘rejecting’ a existing record, i.e.: those that have ID. But, it is called for those too! – In this topic, (what may be a bug) is that, if the record gets rejected, it does not get updated (it does not go through it’s model’s validations either).

The solution I found was not using :reject_if,  instead, validating whatever I wanted on the nested Model in order to keep dry.

For this scenario, consider the following setup (rails v3.0.5)

class A < ActiveRecord::Base
 has_many :bs
 accepts_nested_attributes_for :bs,
                               :allow_destroy => true
                               #no use. :reject_if => :combo_elem_zero?

 # wont use this function at all
 def combo_elem_zero?( att )
  #puts(atribs.to_yaml)
  #att['_destroy'] = '1' # wont work here

  # only useful for new records
  if atribs['id'].blank? && att[:some_atrib].to_f < something_blah
   true
  else
   false
  end
 end
end

class B < ActiveRecord::Base
 belongs_to :a

 validate :destruction_update # works both for create/update

 def destruction_update
  if self.some_atrib.to_f < something_blah
   self.mark_for_destruction
  end
 end

end

If on the other hand, you only need to check on the nested new records, :reject_if may do the job.

Adding dynamic ajax data to DataTable, a jQuery Table Plugin

via Axiom Zen

Recently on PedeDelivery I’ve been working with some loads of data to display on admin panels, and it can be pretty boring to make custom searches and sorts on the server side (as well as expensive up from some rates).

In order to counter this issues, I am using the DataTable jQuery plugin, which is able to deal with some huge amount of data, and is extremely featured and at the same time, customizable.

On a client that have to deal with server updating data IRT, reloading all the rows is very undesirable to happen all over again on a AJAX callback. So instead, lets just require from the server new content, in JSON format.

pd web app

The PROBLEM here are the arguments .fnAddData() is able to take: a Array OR a Array of Arrays. But I need to add class to the TD Element and still bind with some events! For this purpose I made the function tidy_up_row(), see below.

oh, cut the crap! Snippet/Solution

Data request is made periodically to the server, that returns a JSON array with all the new content (that’s server responsibility I am using Rails for the task).

  // Initialize with all ids from the server in a hash
  $orders = ( { 10 : true,
                     11: true
                     // and so on
                   } )

  function periodic_update_DataTable(){
    j.ajax({

        url: "painel_restaurante/sync_data",
        success:
          function(data, status, xhr){
            add_multiple_rows( data );
          },
        complete:
          function(a,b,c){
            setTimeout( periodic_update_DataTable, 30*1000)
          },
        dataType: 'json'
      }
    )
    return true;
  }

  function add_multiple_rows( arr_arr ){

    var added = new Array();
    var count = 0;

    for (var i=0; i < arr_arr.length; i++) {
      var id = arr_arr[i].pop();

      // only add order if not found in table
      if( !$orders[id] ){

        // see http://www.datatables.net/api
        var added_aoData =  j('.data_table').dataTable().fnAddData( arr_arr[i] )[0];

        var row = j('.data_table').dataTable().fnSettings().aoData[ added_aoData ].nTr;

        tidy_up_row(row, id);

        $orders[id] = true

        count++;
      }

    };

    console.log( 'Added rows', count );

  }

  function tidy_up_row(row, id){
    var jrow = j(row).attr('data-order-id', id);

    jrow.children('td:eq(0)').addClass('order_cell1').click( click_order_detail );
    jrow.children('td:eq(1)').addClass('order_cell2').click( click_order_detail );
    jrow.children('td:last').addClass('order_actions');
  }

j(document).ready(function(){

    // Initialize the table just the way you do normally. (in my case I18n to Portuguese-BR)
    j('table.data_table').dataTable({ // http://datatables.net/examples/basic_init/filter_only.html
      "aaSorting": [[ 0, "desc" ]],
      "iDisplayLength": 50,
      "bAutoWidth": true,
      "oLanguage": {
        "sLengthMenu": "_MENU_ entradas por página",
        "sZeroRecords": "Vazio!",
        "sInfo": "Exibindo de _START_ até _END_. Total de: _TOTAL_ entradas",
        "sInfoEmpty": "Nenhuma entrada",
        "sInfoFiltered": "(total de _MAX_ entradas)",
        'sSearch' : "Busca"
      }
    });

   setInterval(function(){ j("td.order_cell1").updatePrettyTime(); }, 30*1000);

  // Other inits ...
  })

Discussion

It is very important to notice that I haven’t done anything new, its just that, this exact mode of use I haven’t found anywhere else.

I see no downsides on this method, just go ftw! 🙂