Tuesday, January 27, 2009

Mootools $A function in PHP (with a little extra 'force')

As you may have seen I'm a big of the mootools js library. I really love their $ functions as the are pretty handy in mostly every situation.

But half of my programming time I'm working with the server-side mostly in PHP and sometimes there are situations where I would love to have this handy functions in my toolbox.

So here comes one of them the $A functions which acts the quite the same as in mootools and has one thing extra, the 'force' option.

function A($var, $force=false){
return ($force || !is_array($var) ? array($var) : $var);
}
It checks if the variable is an array if yes than it returns the unchanged variable if not it wraps the variable in a new array. The force option bypass this test and wraps the variable in a new array even if it is already an array. That's all, simple but powerful.

Usage:

//Without forcing
$var1 = 'hello world';
$var1 = A($var1);
//$var1 is wrapped => array('hello world')

$fruits = array('apple', 'banana', 'cherry');
$fruits = A($fruits);
//$fruits is unchanged => array('apple', 'banana', 'cherry')

//With forcing
$fruits = A($fruits, true);
//$fruits is wrapped => array(array('apple', 'banana', 'cherry'))
It helped me a lot and maybe it can help you too.

Thursday, January 22, 2009

Private Members in Mootools (I do it my way...)

A topic that keeps me busy for a while now is how can I achieve private members in a Mootools classes without rewriting the whole Class.js?

Today morning I stumpled upon Sean McArthurs and Dean Whites ideas to the topic, but I thought there must be a simpler way to achieve this.

My idea was to have two different class-objects, one is public and doesn't know anything about the private members, the second object has the first object as its prototype extended with all private members. The second object gets bind to all methods of the class. So you can use all private members within each method of the class, while you can't use it outside of the class. Sounds complicated? Maybe, but I don't know how to explain it simpler. Ask me in German and I think I can give you a simpler explanation.

Let's start with the class mutator:

Class.Mutators.Privates = function(self, privates){
//From "Javascript - The Good Parts" (Douglas Crockford)
var F = function () {};
F.prototype = self;
var privSelf = new F();

$extend(privSelf, privates);

for(var prop in self){
if($type(self[prop]) === 'function' && (prop !== 'parent' && prop !== 'parentOf')){
var parent = (self[prop]._parent_ || null);
self[prop] = self[prop].bind(privSelf);

if(parent !== null){
self[prop]._parent_ = parent;
}
}
}

delete self['Privates'];
for (var privProp in privates) {
delete self['Privates'][privProp];
}

privSelf.publicSelf = self;
};
That's all.
I will explain it step by step:

var F = function () {};
F.prototype = self;
var privSelf = new F();

As I explained above, making a new object as 'private object', that knows all private members, and has the 'public object' as prototype. So you can extend the class or 'public object' and your 'private object' can use the new members too, without extra work.


$extend(privSelf, privates);

Now extend the 'private object' with the private members.


for(var prop in self){
if($type(self[prop]) === 'function' && (prop !== 'parent' && prop !== 'parentOf')){
var parent = (self[prop]._parent_ || null);
self[prop] = self[prop].bind(privSelf);

if(parent !== null){
self[prop]._parent_ = parent;
}
}
}

Next loop through all properties of the 'public object' searching for methods we want to bind the 'private object' to. If we found one and it's not the mootools related methods 'parent' and 'parentOf' (they are needed for class extending), we cache its parent-function, bind the 'private object' to the public method and attach the parent function if the old function had one. We have to do this, or otherwise you can't use this.parent, when you extending a class.


delete self['Privates'];
for (var privProp in privates) {
delete self['Privates'][privProp];
}
So far so good, now do some cleanup.


privSelf.publicSelf = self;
This one is special. You need it to do chaining. As you can't return 'this' in your methods, remember you bound the 'private object' to all public methods, just returning 'this', will reveal all your private members. So I decide to attach the 'public object' to the 'private object'. If you want to return the class object for chaining, you can do something like 'return this.publicSelf'.

My Test-Case:
window.addEvent('domready', function(){
var Test0 = new Class({
method0: function(){
console.log('method0', this);
}
});

var Test1 = new Class({
Privates: {
'secret': 'secret',
'secretMethod': function(){
console.log('secretMethod', this);
}
},

Implements: [Options, Events],

Extends: Test0,

initialize: function(){
this.secret = 'test1-secret';
console.log('init', this);
},

publicMethod1: function(){
console.log('publicMethod1', this);
this.secretMethod();
this.instanceMethod();
this.implementedMethod();
},

setSecret: function(val){
this.secret = val;
},

getSecret: function(){
return this.secret;
}
});

var Test2 = new Class({
Extends: Test1,

Privates: {
'anotherSecret': 'secret2',
'secretObject': {
'prop1': 'save',
'prop2': 'and',
'prop3': 'sound'
}
},

initialize: function(){
this.anotherSecret = 'test2-secret';
},

publicMethod2: function(){
console.log('publicMethod2', this);
//Chaining without revealing your private members
return this.publicSelf;
},
publicMethod1: function(){
console.log('Test2.publicMethod1', this);
},

setSecret: function(val){
this.anotherSecret = val;
},

getSecret: function(){
return this.anotherSecret;
}
});

var t = new Test1();
console.log('t', t);
t.method0();

t.instanceMethod = function(){
console.log('instanceMethod', this);
};

Test1.implement({
'implementedMethod': function(){
console.log('implementedMethod', this);
}
});

t.implementedMethod();
t.instanceMethod();

var tt = new Test1();

t.setSecret('t');
tt.setSecret('tt');
console.log('t', t.getSecret());
console.log('tt', tt.getSecret(), 't', t.getSecret());

var t2 = new Test2();
console.log('t2', t2);
var secretThis = t2.publicMethod2();
console.log('secretThis', secretThis);
t2.publicMethod1();
t2.setSecret('t2 secret');
console.log('t2', t2.getSecret());

var t0 = new Test0();
console.log('t0', t0);
t0.method0();
});
I know the test looks rather complicated and unstructered, oh and you need firebug, or firebug lite or something, as I use 'console.log' all over the place. But you can see very well when you get the 'public object' and when the 'private object'.

Tell me what you think about it? Is it useful, or not? Are there any bugs in it?