Loving Git

I suppose this must be the zillionth post about how Git is so cool, but I’ll tell you what I love about it:

  1. Ultra fast commits. Coming from subversion, typing commit and getting a prompt back instantly is so surprising that you double check the first few times.
  2. Branching! God, you don’t realize how much you miss it until you have fast branching and merging. Without the pain of creating cps, just a simple git checkout -b branch_name and git merge branch_name and everything just works.
  3. The above was the reason I tried git in the first place. mobME’s an SVN shop and we do the usual trunk, tags, branches dance. When the trunk shapes up to be stable though I can’t seem to do anything on it. I can fork off a new branch in SVN but that’s too painful to even think about. What do I do now? git clone it, create a branch, and do regular git svn rebases and dcommits.
  4. Oh did I mention bidirectional SVN support? Without which I wouldn’t/couldn’t have switched no matter how much I wanted to try this cool new thing.

But it’s great and it really changes the way you think about VCS. Notice something? I didn’t even talk about distributed source control, and that’s coz even when I use git like I use SVN – committing to a central repo at the end and pulling changes from it, it’s brilliant. I’d definitely want to explore cool stuff like github soon for personal use (some gracious soul gave me an invite some time back).

An Orkut Application via a JSON API

I talked about delegating rendering in Symfony for creating a JSON API. Now here’s a consumer: an Orkut opensocial gadget:


MobshareOrkutAPI = {

	api_url: 'http://api.mobshare/api.php',
	cache_time: 0, //0 to disable

	makeCachedRequest: function(url, callback, params, refreshInterval) {
	  var ts = new Date().getTime();
	  var sep = "?";
	  if (refreshInterval && refreshInterval > 0) {
	    ts = Math.floor(ts / (refreshInterval * 1000));
	  }
	  if (url.indexOf("?") > -1) {
	    sep = "&";
	  }
	  url = [ url, sep, "nocache=", ts ].join("");
	  gadgets.io.makeRequest(url, callback, params);
	},

	call: function(module, action, params, callback) {
		var options = {};
		options[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.JSON;
		this.makeCachedRequest(this.api_url + '/' + module + '/' + action + '?' + params, callback, options, this.cache_time);
	}

};

makeCachedRequest has been plaigarized from the Opensocial documentation and it’s very useful to bust the cache for requests. Also, notice this line for setting the content-type to JSON:


options[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.JSON;

This is how we access that API, a code fragment:


...
authenticate: function(alias, mobile_no, password) {
	MobshareOrkutAPI.call('user', 'authenticate',
		'alias=' + encodeURIComponent(alias) + '&mobile_no=' + encodeURIComponent(mobile_no) +
			'&password=' + encodeURIComponent(password),
 		MyOrkutApp.login
	);
}
...
login: function(orkut_response) {
	response = orkut_response.data;
	data_success = response['success'];
	data_error = response['error'];

	if(data_success) {
		html = '<h2>Login Successful!</h2>';
		html += '<p>Welcome: ' + data_success.name + '!</p>';
	} else if(data_error) {
		html = '<h2>Login Unsuccessful!</h2>';
		html += '<p>' + data_error + '!</p>';
	}

	document.getElementById('mobshare_login').innerHTML += html;
},
...

Note that orkut_response.data is automatically set by Orkut because you passed in the JSON content type; it parses the data received and creates a javascript object. Cool, ain’t it? It’s very easy to create a proper interactive Opensocial app this way.

Delegate Rendering in Symfony

Warning: pretty advanced Symfony ahead: if you’re not familiar with the framework, this wouldn’t make sense.

I recently developed a bare-bones API for Mobshare (it’s not yet live), and to keep everything clean, I abstracted away the rendering bit from the controller to an external class. It ended up being a sweet solution, so here it is!

I wanted this to be a JSONP API (the major use case would be a JS client, and parsing XML etc. via JS is a pain. Besides JSON is much shorter over the wire). I didn’t want to rewrite a lot of code: checking for a callback parameter and wrapping the returned string around the JSON output was just begging to be refactored away. So here it is, a generic Symfony JSON API wrapper class:


<?php

class JSONPAPI {

	const CALLBACK_PARAMETER = 'callback';

	var $status;
	var $data;
	var $callback;


	public function __construct($status, $data) {
		$this->status = $status;
		$this->data = $data;

		$callback_parameter_value = $this->getCurrentAction()->getRequestParameter(self::CALLBACK_PARAMETER);
		if($callback_parameter_value)
			$this->callback = $callback_parameter_value;
	}

	public function render() {
		$render_text = json_encode(array($this->status => $this->data));

		if($this->callback)
			$render_text = $this->callback . '(' . $render_text . ');';

		$this->setJavascriptHeaders();
		return $this->getCurrentAction()->renderText($render_text);
	}

	//hack to get the current action
	private function getCurrentAction() {
		return sfContext::getInstance()->getActionStack()->getLastEntry()->getActionInstance();
	}

	private function setJavascriptHeaders() {
		sfContext::getInstance()->getResponse()->setParameter('Content-Type', 'application/javascript', 'symfony/response/http/headers');
	}
}

The bits of magic here are the getCurrentAction() function and the render() call. It works on one very simple idea: everything in Symfony can be accessed from the sfContext::getInstance() object, you just need to dig deep enough.

Once you write this boiler-plate code, using it is very elegant. First, subclass it for your API:


<?php

require_once('JSONPAPI.class.php');

class MobshareAPI extends JSONPAPI {


}

And then, use it like so within your controller:


<?php
class userActions extends sfActions
{

	public function executeAuthenticate() {
		$valid_user = UserPeer::authenticate($this->getRequestParameter('alias'),
			$this->getRequestParameter('mobile_no'), $this->getRequestParameter('password'));
		if($valid_user instanceOf User) {
			$success = new MobshareAPI('success', $valid_user->toArray());
			return $success->render();
		} else {
			$error = new MobshareAPI('error', 'Authentication failed: Alias, mobile number or password invalid.');
			return $error->render();
		}
	}

}

Note: the rendering has been delegated to the $success and $error MobshareAPI objects. This allows for a really maintainable API. Adding functionality is much simpler since you don’t have to worry about the boilerplate.

You end up calling the API like this:


http://api.mobshare/user/authenticate?alias=vish&password=xxx&callback=handler

and you get back data which looks like this:


handler({"success":{"alias":"vish","name":"Vishnu Gopal","photo_mini": ...);

Note that callback handling is done entirely by the API and the controller needn’t worry about this parameter at all!

Mouse Wheel Handling in Prototype/Javascript

Here’s how to handle mouse wheel events in prototype, code heavily borrowed from other places on the net, but brought together and made unobtrusive:


/* Mouse Wheel */

Object.extend(Event, {
	wheel:function (event){
		var delta = 0;
		if (!event) event = window.event;
		if (event.wheelDelta) {
			delta = event.wheelDelta/120;
			if (window.opera) delta = -delta;
		} else if (event.detail) { delta = -event.detail/3;	}
		return Math.round(delta); //Safari Round
	}
});

SetupMouseWheel = {
	initialize: function() {
		Event.observe(document, 'dom:loaded', this.setup_mouse_wheel);
	},

	setup_mouse_wheel: function() {
		Event.observe($('element'), "mousewheel", function(e) { SetupMouseWheel.handleDiv(e) }, false);
		Event.observe($('element'), "DOMMouseScroll", function(e) { SetupMouseWheel.handleDiv(e) }, false); // Firefox
	},

	handleDiv: function(e) {
		direction = Event.wheel(e) < 0 ? leftSeek : rightSeek;
		console.log(direction); //handle scroll
		console.log(Event.wheel(e));
		direction(); //call leftSeek or rightSeek depending on direction.
	}
};

The problem with this code though, and the reason I haven’t found a productive use for this yet is that the browser always scrolls the viewport when it extends beyond screen dimensions. So you have your scroll handler and the browser’s scrolling viewport playing hanky-panky, and it’s not a good sight to see. For applications which always stays within the browser window though, this is useful code.

Simple Javascript Validation

I hunted around for a good Javascript class to use for validation and finally clobbered together this from various sources on the net. This should be extremely useful for anyone doing client-side validation.


Validate = {
	forMinLength: function(whatYouTyped, length_min) {
		var fieldset = whatYouTyped.parentNode;
		var txt = whatYouTyped.value;
		if (txt.length >= length_min) {
			fieldset.className = "valid";
		} else {
			fieldset.className = "invalid";
		}
	},

	forMaxLength: function(whatYouTyped, length_max) {
		var fieldset = whatYouTyped.parentNode;
		var txt = whatYouTyped.value;
		if (txt.length < = length_max) {
			fieldset.className = "valid";
		} else {
			fieldset.className = "invalid";
		}
	},

	forLength: function(whatYouTyped, length) {
		var fieldset = whatYouTyped.parentNode;
		var txt = whatYouTyped.value;
		if (txt.length == length) {
			fieldset.className = "valid";
		} else {
			fieldset.className = "invalid";
		}
	},

	asEmail: function(whatYouTyped) {
		var fieldset = whatYouTyped.parentNode;
		var txt = whatYouTyped.value;
		if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(txt)) {
			fieldset.className = "valid";
		} else {
			fieldset.className = "invalid";
		}
	},

	asNumber: function(whatYouTyped) {
		var fieldset = whatYouTyped.parentNode;
		var txt = whatYouTyped.value;
		if (/^\d+$/.test(txt)) {
			fieldset.className = "valid";
		} else {
			fieldset.className = "invalid";
		}
	},

	asEqualTo: function(whatYouTyped, whatYouMatchWith) {
		var fieldset = whatYouTyped.parentNode;
		var txt = whatYouTyped.value;
		var expect = whatYouMatchWith.value;
		if (txt == expect && txt != "") {
			fieldset.className = "valid";
		} else {
			fieldset.className = "invalid";
		}
	},

	asNotEqualTovalue: function(whatYouTyped, whatYouMatchWith) {
		var fieldset = whatYouTyped.parentNode;
		var txt = whatYouTyped.value;
		var expect = whatYouMatchWith;
		if (txt != expect) {
			fieldset.className = "valid";
		} else {
			fieldset.className = "invalid";
		}
	},

	withRegex: function(whatYouTyped, regex) {
		var fieldset = whatYouTyped.parentNode;
		txt = whatYouTyped.value;
		if (regex.test(txt)) {
			fieldset.className = "valid";
		} else {
			fieldset.className = "invalid";
		}
	}

};

How do you use it? This is how:

HTML:


<form name="signup" id="signup">
<label for="alias">Alias <sup>*</sup></label>
<fieldset>
<input type="text" name="alias" id="alias" value="" />
</fieldset>
<br />
<label for="mobile_no">Mobile Number <sup>*</sup></label>
<fieldset>
<input type="text" name="mobile_no" id="mobile_no" />
</fieldset>
<br />
[..]
</form>

CSS (different images for fieldset.valid and fieldset.invalid):


fieldset.valid {
	background:transparent url(/images/bg-fieldset-valid.gif) no-repeat 194px 0px;
}

fieldset.invalid {
	background:transparent url(/images/bg-fieldset-invalid.gif) no-repeat 194px 0px;
}

And I hate onclick and onkeyup, so here’s some unobtrusive Javascript to tie it all in:


SignupValidation = {
	initialize: function() {
		Event.observe(document, "dom:loaded", this.setup_validation)
	},

	setup_validation: function() {
		if($('signup_body')) {
			Event.observe($('alias'), 'keyup', function() {
				Validate.withRegex($('alias'), /^[A-Za-z]+[A-Za-z0-9]{4,}$/);
			});
			Event.observe($('mobile_no'), 'keyup', function() {
				Validate.withRegex($('mobile_no'), /^9[0-9]{9,9}$/)
			});
		}
	}
};

SignupValidation.initialize();

That’s it. The idea (for those who can’t bother to grok code) is that a fieldset changes its class from valid to invalid depending on the status of the field, this is then reflected in the styling.

AsktheCSSGuy was of much help.