Wordle Delicious
Tuesday, June 24, 2008
I just visualized my delicious tags using wordle, and they look good! Thought I’ll share! Note: they use Java instead of Flash for these effects, interesting!
Tuesday, June 24, 2008
I just visualized my delicious tags using wordle, and they look good! Thought I’ll share! Note: they use Java instead of Flash for these effects, interesting!
Friday, June 13, 2008
So there was interesting and possibly useful problem that I wanted to tackle. Get a landline STD BSNL number and find out the location and state. As you can see, it’s a simple mapping problem with the map being available from here (just leave everything blank and hit search). The idea was to parse that html file, extract the good bits and return an array of hashes. Since the easiest language to do this is Ruby I chose to use that.
The implementation itself is just bits of activesupport and hpricot magic (nothing to write home about), but after I did the simple ruby client, I thought about writing up a webserver frontend for it. In the ruby world, deployments a bitch (maybe phusion passenger - now that it has support for rack - will change this). The ideal way for a simple script like this to be deployed is just to write a single page PHP file. How to call the ruby function though?
Enter thrift [2]. I’ve always wanted to play around with it and this seemed a perfect opportunity. The magic with thrift is that it works with a service definition that defines the functions to be used and auto-generates most of the interface code. You just have to write it’s implementation.
First you have to make a service definition like this:
service STDService {
map<string, string> lookup(1:string lookup)
}
Then run the thrift generator:
thrift --gen rb -php std.thrift
This will create two folders in the source tree: gen-rb and gen-php which contains stubs for client and server implementations of the thrift interface. A lot of the boilerplate code is written for you so you don’t have to bother, but you do have to write the implementation at the client and server end.
This is how the client bit looks like (in PHP):
function lookup_number($number) {
$transport = new TBufferedTransport(new TSocket('localhost', 9090));
$protocol = new TBinaryProtocol($transport);
$client = new STDServiceClient($protocol);
$transport->open();
$info = $client->lookup($number);
$transport->close();
return $info;
}
The relevant server bit is this:
class STDServiceHandler
def initialize
@std_parser = STDParser.instance
end
def lookup(number)
@std_parser.query(number)
end
end
handler = STDServiceHandler.new()
processor = STDService::Processor.new(handler)
transport = TServerSocket.new(9090)
transportFactory = TBufferedTransportFactory.new()
server = TSimpleServer.new(processor, transport, transportFactory)
puts "Starting the STDService server..."
server.serve()
puts "Done"
The original files are here and here.
It’s nice to see how it works: I fire up Apache, point to my PHP file and once I submit the form, all the interaction happens behind the scenes automatically. Thrift makes it easy to call one language from the other.
India STD identifier is up at googlecode.
A quick addendum on how you get started with Thrift:
./configure make and make installSunday, June 8, 2008
The UNIX philosophy has always been to tie in small tools and make a bigger whole. During the course of my programming and sysadmin carer, I’ve found these tools to be invaluable, and I’ve often noticed that many people don’t know about them:
This binds functionality of the readline library to applications which doesn’t have native readline bindings. Ever wanted the bash-shell Ctrl+R reverse history search in another REPL? Just start the program with Rlwrap like so (this is for Jan’s really nice php-shell):
rlwrap php-shell
This sort of a thing makes working with the otherwise clumsy php-shell a breeze.
Often you want to run programs as a daemon but they themselves don’t have any sort of functionality like that. Daemonize comes to the rescue. (For ruby programs that I write I use the similarly named but much more capable daemons gem). Use this like so:
daemonize tail -f log/apache>log > /home/vishnu/.log
Redir is another gem. It redirects transparently TCP/IP traffic from one port to another IP and port. Use this for IP masking and redirect:
redir --lport=81 --cport=80
Dtach is again great at what it does. It’s like daemonize, but it allows you to detach and attach from a running process. I’ve written about this before.
Monday, May 12, 2008
![]()
Please enter all letters having a cat below. Enough said.
Tuesday, April 29, 2008
I’m off to release an internal XML conversion script. I began using Hpricot, but after hearing good things about LibXML, decided to switch to that. Here’s a benchmark:
real 0m11.692s
user 0m11.497s
sys 0m0.188s
… for Hpricot versus…
real 0m6.441s
user 0m6.258s
sys 0m0.177s
… for LibXML
So that’s almost twice as fast. Nice to know.
Thursday, April 10, 2008
This is for people who still use the old Symfony 0.6.2.
Get the script from here and execute it so:
sh ./symfony_sandbox.sh
This is largely plagiarized from the create quickstart script from the Symfony SVN but changed to make the pake install work.
Wednesday, April 9, 2008
First the URL: Kup.in is a fast URL redirection service run off Google’s new App Engine.
This was mostly a test project than anything else, just to gauge how easy App Engine is to start up and get going (very easy!). I’ll post in detail later, but here are bulleted observations:
appcfg.py update . which is uber cool.import _md5 error which I suspect again is my Macports). I didn’t need templates here.Speaking of which, it’s 3 in the morn here and I’ve got office tomorrow!
Sunday, April 6, 2008
So here’s a piece of code which I think you guys will like. It’s a URL call with timeout function in PHP.
/* This works only for simple URIs
Returns:
array(headers, body): if everything's fine.
TIMED OUT if it times out.
UNABLE TO OPEN if we can't connect to host.
*/
private function file_get_contents_with_timeout($url, $read_timeout = 5, $connection_timeout = 5) {
$url_parts = parse_url($url);
$host = $url_parts['host'];
$get = $url_parts['path'] . '?' . $url_parts['query'];
$fp = fsockopen($host, 80, $errno, $errstr, $connection_timeout);
if (!$fp) {
return "UNABLE TO OPEN";
} else {
$out = "GET $get HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
stream_set_timeout($fp, $read_timeout);
$result = stream_get_contents($fp);
$divider = strpos($result, "\r\n\r\n");
$headers = substr($result, 0, $divider);
$body = substr($result, $divider, strlen($result));
$info = stream_get_meta_data($fp);
fclose($fp);
if ($info['timed_out']) {
return 'TIMED OUT';
} else {
return array('headers' => $headers, 'body' => $body);
}
}
}
Yup, a bit crude, but hey it works for me! (Oh, and it probably needs PHP5). Feel free to take the core logic out and wrap it up in exceptions and what have you.
Why did I have to write this contraption? Ah, the joys of the Indian SMS scene. Let me take you through a tour: Fastalerts is a bulk SMS solution for end users and resellers. The web frontend is written in PHP using Symfony (an older version, 0.6). The API is written in plain PHP and it connects to a SOAP-based (using nuSOAP) SMS sending solution.
This is a comment I have on top of the new backend code which sums up all the complexity:
/*
A note on message sending.
These actions have to happen as transactions:
* Calling the GATEWAY.
* Entering data into LOGS.
* Reducing CREDITS.
ONLY if:
* User has "enough" credits.
* Input is valid (numbers and mesage)
* User has valid credentials
AND we have to handle:
* Gateway timeouts
* Gateway errors
* CDMA senderid correction.
ALSO:
If the user doesn't have enough credits to send the entirety of numbers,
messages are sent until his credits are exhausted.
AND:
A separate status message is returned for each of these conditions.
BUT WE ENSURE:
That everything is logged appropriately:
* All successfully sent messages are logged and credits reduced.
* In every other case, credits are NOT reduced (we are customer friendly).
* When gateway times out or errors out, the messages are logged.
*/
Interested people should note that there is a lot more you can add on to this: dynamic gateway switches (automatic failover), regular gateway tests, more backend support etc. but this is the bare minimum that’s needed for the backend to work. If you spend some time thinking about the problem, you’ll come to the realization that this bit:
AND we have to handle:
* Gateway timeouts
* Gateway errors
means that we have to handle gateways that time out and never return a response. Most of the SMPP providers in India use something called NowSMS to manage their backend connectivity to the operator. In short: we connect to a vendor via his common gateway, he routes it (depending on destination) to multiple operators. NowSMS exposes a simple HTTP service (a URL call in other words) to add SMS to the vendor queue and it’s this service that’s preferred by a majority of the good vendors. However, HTTP traffic being what it is, a monolithic backend in PHP will stall if the request doesn’t return a response. Hence that file_get_contents_with_timeout.
Note: this is hardly an ideal solution (that would be a separate daemon). But without adding moving parts, this simple function should increase the reliability of our backend.
Tuesday, March 25, 2008
Here’s a sneak peek of the Mobshare broadcast widget I’ve been working on. If you look to the right and below, you can see that in action live. You’ll see the last ten pics I’ve uploaded and direct from the widget you can subscribe to my feed or send me an SMS.
The widget is the first released code which uses our internal API. While the API itself is undocumented [which hopefully will soon change], it’s public and it’s JSON (which makes implementing Javascript widgets incredibly easy).
How do you get your own widget? You can’t do that easily right now (the pleasures of cutting-edge code eh?), but paste this code somewhere:
<iframe width="150" height="150" src="http://mobshare.in/user/:mobshare_id:/broadcast/widget"></iframe>
…and change :mobshare_id: to your Mobshare ID.
Try it out and let me know if you like it!
Tags: broadcast • cool • javascript • json • mobshare.in • widget
Tuesday, March 18, 2008
I suppose this must be the zillionth post about how Git is so cool, but I’ll tell you what I love about it:
cps, just a simple git checkout -b branch_name and git merge branch_name and everything just works.git clone it, create a branch, and do regular git svn rebases and dcommits.Tags: development • git • source control • subversion • sysadmin • vcs
Saturday, March 8, 2008
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.
Tags: api • application • development • javascript • json • opensocial • orkut • web
Thursday, March 6, 2008
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!
Tags: api • cool • development • json • jsonp • PHP • symfony • web
Tuesday, March 4, 2008
Most people use GNU screen (or equivalent) to daemonize processes. There is a better alternative called dtach which should become the definitive way to run a process in the background as a daemon.
Installing dtach is easy. If you’re on gentoo, it is a simple:
$ emerge dtach
dtach --help provides the following output:
Usage: dtach -a <socket> <options>
dtach -A <socket> <options> <command...>
dtach -c <socket> <options> <command...>
dtach -n <socket> <options> <command...>
Modes:
-a Attach to the specified socket.
-A Attach to the specified socket, or create it if it
does not exist, running the specified command.
-c Create a new socket and run the specified command.
-n Create a new socket and run the specified command detached.
This basically means that you start dtach like so:
$ dtach -c ~/rtorrent/.socket/dtach rtorrent
The command-shortcut Ctrl+\ detaches the running command and runs it in the background. To re-attach, just enter:
$ dtach -a ~/rtorrent/.socket/dtach
dtach hasn’t got much more functionality, but that’s what makes it superb. Tiny and functional. As you can see, I’m using dtach to run a Seedbox
Tags: *nix • applications • sysadmin
Sunday, February 24, 2008
Virtual hosting and infrastructure is coming up in a big way, and Amazon’s EC2 is leading the pack. However, there doesn’t exist a comprehensive & provably scalable system for deploying applications to such a stack yet: the components are in place or being developed (SimpleDB or equivalent, EC2 and S3) but the crucial connecting layer is missing. Several attempts are on to fill the gap.
Until it becomes a reality (and perhaps even then), a great intermediate tool is Slicehost’s xen-based virtual machines: “slices”, that allow for a quick snapshot from any particular slice and the creation of a new slice from that snapshot.
The way it works is a bit cumbersome now (and they really should expose this feature more), but the idea is simple:
I plan to use this for factory-like instances across MobME’s infrastructure.
Tags: ec2 • hosting • puppet • slicehost • sysadmin • virtual
Monday, February 11, 2008
I’ve recently switched to a new role at MobME, and here are my initial impressions and a few lessons learnt in the past few months. This is a rather free-flowing account, but I want to write this down and codify before I forget.
Rule uno: people aren’t resources. There’s a constant management-level question I hear: “How many resources can you allocate to a particular product?” or “How much time and resources do you need to solve this problem?”. I don’t answer such questions at all, or when I do, I talk about the people I have in my team and their skills and limitations. I’ve strongly felt that no organization can grow without taking care of its people, and especially for small startups, to make attrition low and happiness high, this is a concern that should be addressed from the top. Rephrase these questions this way: “Can person X solve this problem?” and “Can person X and Y be reallocated to this task?”.
Following on, every person is special: they have different skills, learning speeds and communication capabilities. Every good person can improve, and they should constantly be pushed to do that. Nobody should be idle and static.
And nobody should do make-work. It’s not necessary because there are always real problems to solve. If anybody is picking up a new language or skill, after a few tutorial-level sessions and a max of two days, he should jump into the new role and start fixing bugs and solving real problems.
Good processes make a good team. At MobME, I’m implementing a few things I learnt while working at Uzanto: Daily End-Of-Day reports for the team, SCRUM in the morning, and constant communication via a small office and approachable managers. The work atmosphere is relaxed, and people are given time to learn. There’s no fixed time for lunch or a quick smoke. There’s an attendance register and people are held accountable for their work and leave.
At the end of the day, Work is Everything™. If there’s a deadline to be met, do or die, we meet it. People remain late in the office (till 1 at night is the current MobME record) and finish work before they go.
What you do when you’re not in office is none of anybody else’s business. Although as a rule, work hard, party hard.
When you lead a technical team, if you have problems you use technology to solve them. This is obvious, but I’ve so often seen this overlooked. You can’t find a way for developers to collaborate? Find an effective source control method. Project management? Bug tracking? Time tracking? A caveat: too many tools is a mess. Find effective and simple ones. We use Beanstalk and Lighthouse along with Google Apps for documents and mail.
As a corollary to the above, not every problem is technical. Instead of inventing one, try simplifying the product or the spec or going back to the drawing board. If it’s too much of a hassle technically, it’s the product pushing back at you, resisting crappy things being done to it. Hear it out and redo things. Redoing things the right way is a good thing. Take time out, fix stuff and it won’t bite you in the ass the next time.
Another gem: keep internal mail to a minimum. When everybody is in the same office, shout out to him (or take him outside) instead of firing off a mail thread. It’ll solve problems so much more efficiently. Having the management & technical wing in nearby offices means that for us, most problems are solved in a week. No network cabling in office? A quick discussion with my operations lead and I get work done. No firing off complaint mails.
Communication is key. Let everybody know your worries. Everybody includes the people under you. A lot of the time, they’ll end up finding a better solution.
The primary role of a technical lead as I understand it is to get work done. It’s not to go off to conferences evangelizing the product (although it’s a good thing to do). Meet the deadlines your boss sets for you. Make sure your people work hard and they learn lots of stuff in the process. Keep an eye out for new technology. Document your processes. When you find problems, fix it and ensure it doesn’t happen again. Get good infrastructure. Communicate problems with the management.
Everybody in sales, marketing, etc. should know more than a little bit of tech. Increase technical awareness and make the process transparent so anybody can ask questions no matter how stupid it is. Often a completely new way of thinking about a problem brings in new technical insights too.
When there’s a problem, criticize. This is hard to do initially, but good people realize that your criticism is for the work and not the person. If they still have issues after you talk it out, ask them politely to leave or toe the line. When you find people improving and processes becoming smoother after you’ve talked it out with the team, it’s a real nice feeling.
And at the end of the day, make people happy and be happy yourself. Which of the lot, is the hardest.
Tags: me outside • mobme • musings
Sunday, February 3, 2008
This was my talk for BarcampKerala2. Decent event, although I had to leave post-noon.
Tags: barcampkerala • barcampkerala2 • me • mobme • mobshare
Sunday, January 27, 2008
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.
Tags: code • development • snippet • web
Tuesday, January 15, 2008
Like previous code snippets, this one is mostly all code and no explanation.
A brief primer: you’ve often seen self-labeling input boxes, the ones that display a label inside in gray text, and when you click replaces with an empty input element using Javascript. This is a simple class to help you achieve that effect:
SelfLabelingBox = {
initialize: function(box_id, message) {
this.setup_behavior(box_id, message);
},
setup_behavior: function(box_id, message) {
box_id = $(box_id);
if(box_id) {
if(box_id.value == '') {
box_id.value = message;
box_id.addClassName('inactive');
}
Event.observe(box_id, 'focus', function() {
if(box_id.value == message) {
box_id.value = '';
box_id.removeClassName('inactive');
}
});
Event.observe(box_id, 'blur', function() {
if(box_id.value == '') {
box_id.addClassName('inactive');
box_id.value = message;
}
});
}
}
};
SetupDefaultInputs = {
initialize: function() {
Event.observe(document, 'dom:loaded', this.setup_default_inputs);
},
setup_default_inputs: function() {
SelfLabelingBox.initialize('search_text', 'Search');
}
};
Until next time!
Tags: code • javascript • prototype • snippet
Saturday, January 12, 2008
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.
Tags: development • javascript • validation • web
Saturday, January 12, 2008
You might often want to connect to different databases using ActiveRecord. Here’s how you do it:
#DB definitions:
class DatabaseCurrent < ActiveRecord::Base
self.abstract_class = true
establish_connection settings['database']
end
class DatabaseOld < ActiveRecord::Base
self.abstract_class = true
establish_connection settings['database2']
end
#Model definitions (current):
class Video < DatabaseCurrent
belongs_to :user
set_table_name :videos
end
class Photo < DatabaseCurrent
belongs_to :user
set_table_name :photos
end
class User < DatabaseCurrent
has_many :videos
has_many :photos
set_table_name :user
end
class Tag < DatabaseCurrent
set_table_name :tag
end
#Model definitions (old):
class FileDB < DatabaseOld
set_table_name :file
end
Pretty easy. The only thing to note is that you should set_table_name, otherwise, AR chokes up. Also, often you want to directly play directly with SQL. Unfortunately, ActiveRecord::Base.execute doesn’t work any longer (coz it doesn’t have a connection), but you can do it this way:
DatabaseCurrent.connection.execute("SQL")
That’s the tutorial for the day!
Tags: activerecord • development • rails • ruby • rubyonrails • tutorial • web
Tuesday, December 18, 2007
I recently registered for the TOEFL again. Anal-retentive ETS only stocks its TOEFL scores for 2 years. Why, may I ask, does my English deteriorate so much after a couple years of a perfect score? Money-loving land lubbers if you ask me… but that is a topic for another day.
Here’s what you’re presented when you seek to create a username and password at the TOEFL site to register for an exam:
Important User Name and Password Information User names must be between 6 and 16 characters long and may contain both letters and numbers. The user name may NOT contain any special characters like !, $, #, % or +. Passwords must be a minimum of 8 characters in length. The maximum is 16 characters. For added security, they must also contain three of the following four character categories: English uppercase characters (A through Z) English lowercase characters (a through z) Numeric characters (0 through 9) Special characters (for example, !, $, #, %) Examples of allowable passwords are: Urt#5489 RT@GR125 UIrty452 H!tTh3M@rK Your password is case-sensitive. For example, "H!tTh3M@rK" is not equal to "h!TtH3m@Rk". Only English characters are allowed and your password cannot contain your user name, first/given name, or last/family name.
Let’s see: A username must be between six and sixteen chars long. Check. Cannot contain special chars. Check. Passwords must be a min of 8 chars long. Check. Max is 16… Check. They must contain three of four character categories…. what is this my bank vault?
Or think of this another way: a reading comprehension test even before the exam starts. Can we applaud and bring the roof down already?
Wednesday, November 28, 2007
An easy way to deploy JAR/JAD content on all phones with GPRS is to create a WML site and provide a link to the JAR file that you have. [As an aside, the whole J2ME scene is so fragmented that it sucks completely. Let’s hope Android sorts it out.]
Here’s how you go about setting up that in Lighttpd:
Add these mimetypes (to lighttpd.conf or mime-types.conf):
mimetype.assign += ( ".wml" => "text/vnd.wap.wml", ".wmlc" => "application/vnd.wap.wmlc", ".wmls" => "text/vnd.wap.vmlscript", ".vmlsc" => "application/vnd.wap.wmlscriptc", ".wbmp" => "image/vnd.wap.wbmp", ".jar" => "application/java-archive", ".jad" => "text/vnd.sun.j2me.app-descriptor" )
Add the index.wml file as a valid DirectoryIndex:
server.indexfiles += ("index.wml")
Make this .wml file and upload it somewhere (replace JARCLIENT with your .jar file, and JADCLIENT with your .jad file):
<?xml version="1.0"?> <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml"> <wml> <card title="Download"> <p>Download the Jar file:</p> <p> <a href="JARCLIENT.jar">Download</a> </p> <p> <a href="JADCLIENT.jad">Download (JAD)</a> </p> </card> </wml>
Restart your server, and that should be it! If you’re using Apache, the steps are similar.
Tags: deployment • development • j2me • jar • sysadmin • wap
Saturday, November 24, 2007
(Edit: was a liveblog, now over!)
Hey so I’m at BarcampKerala and this is a liveblog. Random impressions:
Tags: barcamp • barcampkerala • gosu • n3wton
Saturday, November 24, 2007
For BarcampKerala, I made a small 2D game in Ruby and Gosu, which is a clone of a popular flash-based game by the same name.
I call it Gosu-N3wton, and I had great fun developing this game. Go check it out! (The code is opensource)
Tags: barcamp • barcampkerala • development • game • gosu • ruby
Wednesday, November 14, 2007
A lot has been said about OpenSocial and Facebook, this is an attempt to say a bit more. As usual, a free-flowing list of observations:
Tags: development • facebook • opensocial • web
Tuesday, November 13, 2007
Stay, says Viragor,
I want you near me.
Nay, breathes Matilda,
Tho I can’t bear to leave thee.
His hair she clasps with twists and turns,
Hers he touches softly, kissing in burns.
‘Twill be months, years yet we meet,
But fleet should be our hearts be, sweet.
And then she’s gone, an orphan left,
Between people near but dead.
Stolen moments, sweet but misery,
Slowly turned dear, restless and finicky.
Oft-shaded heart and steady work’s a blanket,
A poor salve that, uproars many a racket.
Bent, slippery, tested and hurt,
But so doth love be stronger for worth.
A true refrain then, to ease parted hearts,
His heart and hers may beat to stars:
Come, says Viragor,
Been aching to hold thee.
Love, smiles Matilda,
When have we been free?
Monday, November 12, 2007
Kenney had this great idea about having a BarCampKerala over here and it’s got me really excited. I’ve been to BarCamps in Delhi and one in Hyderabad, and it’s always been an amazing place to talk to good people. I’m pretty sure I’ll find loads of techies in Thiruvananthapuram to contribute and because of the event I’ve already reconnected with some great people I know. Raj from Linuxense is chipping in, and so are many others I’ve yet to meet.
It’s going to happen Nov 24, and we haven’t decided on the venue yet it’s at the UST Zenith hall (next to Coffee Beanz) at Technopark. It should be easy enough to find. Most probably it’s going to be somewhere in or near Technopark.
In case you don’t know what a BarCamp is, it’s an unconference (yes cool word) which anybody can organize and anyone can come and speak on a first-come-first-speak basis. What I love about BarCamps are the discussions on the fringes of talks and the great people that come to them.
Gimme a call (+91.9846087017) if you’re somewhere in the vicinity and want to contribute!
Other assorted stuff:
Tags: barcamp • barcampkerala
Monday, November 12, 2007
I saw Nalu Pennungal today. Before I forget a lot of the film, here’s some random impressions:
I took the shorts as they came to me and didn’t think about any connecting thread then. Looking back, one thing which is perhaps common is that all the stories are about women who did the ‘right thing’ and somehow is punished because of it. Kunzhupennu who found a husband but is still branded a prostitute, Kanyaka who married but didn’t find happiness, Chinnu Amma who sacrificed progeny for being faithful, and the eternal virgin who sacrificed for her sister and so couldn’t marry. And still that punishment isn’t so severe that they aren’t able to lead a semi-normal life or as in the case of Chinnu Amma, look back upon the incident with a bit of humor.
It’s a good movie. Not a must watch, but it does make you think.
Saturday, October 27, 2007
There was a bit of a downtime for this site because my MySQL binary logs and lighty logs had temporarily filled up my 10G slice. That’s fixed now but I haven’t found a permanent solution (this has happened before). I suppose I should whip up a cron job that runs every week to do some housekeeping.
Also while I was at it, I upgraded to the latest version of Wordpress. This was the easiest upgrade ever. I got a notification from the WP console that a new version is available and I used svn to switch to that tag:
vysnu@slicey ~/public $ svn sw ↵
http://svn.automattic.com/wordpress/tags/2.3.1/
U wp-login.php
U wp-includes/default-filters.php
U wp-includes/bookmark.php
U wp-includes/query.php
A wp-includes/wlwmanifest.xml
U wp-includes/formatting.php
U wp-includes/taxonomy.php
A wp-includes/images/wlw
A wp-includes/images/wlw/WpComments.png
A wp-includes/images/wlw/wp-icon.png
A wp-includes/images/wlw/WpIcon.png
A wp-includes/images/wlw/wp-watermark.png
A wp-includes/images/wlw/WpWatermark.png
A wp-includes/images/wlw/wp-comments.png
U wp-includes/post.php
U wp-includes/version.php
U wp-includes/general-template.php
U wp-includes/canonical.php
U wp-includes/pluggable.php
U wp-includes/widgets.php
U wp-includes/functions.php
U wp-includes/script-loader.php
U wp-mail.php
U wp-admin/includes/template.php
U wp-admin/includes/upload.php
U wp-admin/includes/upgrade.php
U wp-admin/edit-post-rows.php
U wp-admin/import/wp-cat2tag.php
U wp-admin/import/utw.php
U wp-admin/import/mt.php
U wp-admin/link-import.php
U wp-admin/link.php
Updated to revision 6294.
See? That’s it. I think my complaints regarding hard WP upgrades are finally over. If you’re not maintaining your WP release via SVN, you should.
Edit: There’s scope to make a plugin to automate this process. Just an upgrade link that runs this svn command in your root folder and redirects to /wp-admin/upgrade.php after that. That’ll make it a no-shell no-brainer to update. Of course, you’ve got to install via SVN first, but an admin could do that.
Sunday, October 21, 2007
I’m loving web.py for developing tiny web apps. Here’s how to get it going on a Mac:
sudo port -v install python2.4 py-setuptools mysql5
#coz macports insists on adding a '5' suffix to all mysql5 tools.
sudo ln -s /opt/local/bin/mysql_config5 /opt/local/bin/mysql_config
sudo easy_install web.py cheetah markdown MySQL-python DBUtils
Not the twenty odd steps that’s on the web.py page (that even includes installing and configuring postgresql - bizarre!).
What I love about web.py is that this small bit of code:
#code.py
import web
urls = (
'/', 'index')
class index:
def GET(self):
print "Hello, world!"
if __name__ == "__main__": web.run(urls, globals())
… and running python code.py in a terminal gets you a working dev environment. Even camping doesn’t feel this tiny.
Edit: the performance numbers for this simple action are also very impressive:
time httperf --client=0/1 --server=localhost --port=8080 --uri=/ --send-buffer=4096 --recv-buffer=16384 --num-conns=10000 --rate=500
httperf --client=0/1 --server=localhost --port=8080 --uri=/ --rate=500 --send-buffer=4096 --recv-buffer=16384 --num-conns=10000 --num-calls=1
Maximum connect burst length: 17
Total: connections 10000 requests 10000 replies 10000 test-duration 20.000 s
Connection rate: 500.0 conn/s (2.0 ms/conn, <=24 concurrent connections)
Connection time [ms]: min 0.1 avg 1.8 max 2987.4 median 0.5 stddev 42.2
Connection time [ms]: connect 0.7
Connection length [replies/conn]: 1.000
Request rate: 500.0 req/s (2.0 ms/req)
Request size [B]: 60.0
Reply rate [replies/s]: min 499.8 avg 500.0 max 500.0 stddev 0.1 (4 samples)
Reply time [ms]: response 1.0 transfer 0.0
Reply size [B]: header 108.0 content 14.0 footer 2.0 (total 124.0)
Reply status: 1xx=0 2xx=10000 3xx=0 4xx=0 5xx=0
CPU time [s]: user 3.75 system 9.17 (user 18.7% system 45.8% total 64.6%)
Net I/O: 88.9 KB/s (0.7*10^6 bps)
Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0
httperf --client=0/1 --server=localhost --port=8080 --uri=/ --send-buffer=409 3.75s user 9.17s system 64% cpu 20.028 total
That’s 10K connections and more than 500req/s in a 20 sec timeframe. Really good.
Tags: development • mac • python • web • web.py
Friday, October 19, 2007
I tried out the new Ubuntu Gutsy today on my mom’s Compaq Presario V3000 (didn’t install, just tried out the LiveCD) and found it to be a really good revamp from the last version. Now WiFi works out of the box, the battery indicator works perfectly, and there’s a bluetooth screen for managing bluetooth devices. The new desktop effects are also pretty cool: I switched from ‘normal’ to ‘extreme’ and found em windows wobbling all over the place. The inbuilt virtual desktop is also smooth and not distracting. While the fonts look a bit too rough, I found web pages very readable in Firefox, and the color theme is also pretty easy on the eyes (although I’d prefer a more colorful default theme). I love the Ubuntu Terminal, it’s much better than the default Mac one.
I can cautiously say that Ubuntu has gotten to the point where a web programmer (a non ASP type) can easily ditch Windows for Linux and not pay the premium for a Mac. There are things the Mac still does better and places where the Mac experience feels a lot more polished, but Ubuntu is closing the gap very fast.
Monday, October 15, 2007
These are a set of informal observations made while watching my little niece Ganga (she’s about 4 years old) draw a picture on the computer.
The whole incident made me really curious about kids & computers. How do you introduce programming for example? Squeak? _why has something called hacketyhack as well. I’ll be keeping an eye on this in the future coz watching kids use computers is really interesting because of the way they instinctively search for new features.
Sunday, October 14, 2007
I watched Chak De India yesterday and found the movie disturbing. I’d rather India had lost at the end but still come home to accolades for playing well. Focusing on victory as the singular goal seems to be so wrong. I also thought the movie took a rather unsubtle dig at the media’s reaction to loss and its treatment of a muslim player. [The movie itself was mediocre with Sharukh’s overacting and tripe dialogues; however surprisingly broad: everything from cricket’s unequaled popularity to sexual undertones between the coach and players].