Thursday, October 6, 2011

safeCall new and improved.

jQuery Project page

Some other day in the not so distant past, I found a need to have my safeCall plugin also be a safeGet as well. and this task was extremely simple. all the code had been written already for it in the safeCall function. I just had to stop it from calling it at the end and have that be the return. one other thing I noticed jQuery frowned on my attaching my mSafeCall and gSafeCall directly to the jQuery object so instead I created the safe namespace in jQuery so I took off the safe from both of them. and added the gets. and I borrowed a concept that stands at the root of jQuery; the ability to chain.

lets use the following js object for examples it shows just about everything we need it to show:

var TestObj = {
 funcRoute: {
  CallMe: function(msg) { alert(msg); },
  CallMe2: function() { 
   return { 
    NestedCall: function(msg) { 
     alert(msg); 
    },
    NestedGet: "I'm way down here!"
   }
  }
 },
 varRoute: {
  VarA: 5,
  VarB: "Hello world"
 }
};

note: the following four functions return a Safe object. so to get the return value/ value of the get you must call the object returned's value member. More on this later.

$.safe.mCall( Context, FuncPath, [...]);
this function listed above calls a function nested inside a javascript object. Starting from the Context. this is formally $.mSafeCall. the parameters are;

Context: this parameter is the starting point of the path and it is also the "this" for that function.

FuncPath: this parameter is the path to the function. starting from the context that you passed in.

Additional Params:  any additional params that you pass into the function will be passed into the function you are calling.

example: 

$.safe.mCall(TestObj,'funcRoute.CallMe','hi');

the call above will be equivalent to calling TestObj.funcRoute.CallMe('hi') with the exception that the safecall will not break if TestObj, funcRoute, or CallMe haven't been intitialized.


$.safe.gCall( Context, FuncPath, [...]);
this function listed above calls a function nested inside a javascript object. Starting from the window object. this is formally $.gSafeCall. the parameters are;

Context: this parameter is the "this" for that function.

FuncPath: this parameter is the path to the function. starting from the window object.

Additional Params:  any additional params that you pass into the function will be passed into the function you are calling.

example: 

$.safe.gCall(null,'TestObj.funcRoute.CallMe','hi');


Onward to the new functionality.


$.safe.mGet(Context,Path);
this function listed above just get's the value at the end of the selected path.

Context: this parameter is the starting point that the path uses to find what you are talking about.

Path: this parameter is the path starting from the context to follow to get the value from.

example:

$.safe.mGet(TestObj,'varRoute.VarA').value;


$.safe.gGet(Path);
this function preforms a get but rather then starting from the context. it starts from the window object.

Path: this parameter is the path to the object that you are getting.

example:

$.safe.gGet('TestObj.varRoute.VarA').value;


the previous four functions all return a Safe Object. and what a safe object is is an object that lets you do your chaining. and it also lets you get the value returned or see's if it successfully did what it was intending to do. the members of a Safe Object are

Value: this is the value returned by the function or got from the getter.

isValid: this is a boolean that tells you if it found the object correctly or in the case of the function if it was a function at the end of the path. that it ran.

along with those it also has 2 chaining functions.

call(funcPath,[...]);
this function preforms a call that path starts from the value in the value field. or does nothing if the previous call was not valid. also returns a Safe object for further chaining.

funcPath: this parameter is the path to the function that you are calling.

Additional Parameters: these are the parameters for the function that your are trying to call.

example:

$.safe.mCall(TestObj,'funcRoute.CallMe2').call('NestedCall','hi');



get(Path);
this function preforms a get with a path starting from the value in the value field of the Safe object. or does nothing if the previous call was not valid. also returns a Safe object for further chaining.

Path: this parameter is the path to the value it's trying to get starting from the value of the previous function.

example:

$.safe.mCall(TestObj,'funcRoute.CallMe2').get('NestedGet');


And last but not least. the code:

// Aaron Gresham Aaron@Tomrel.com
// 10/6/2011
// Function to safely call another function, get a value nested in a complex object or chained on to another function

//the variable that holds it
var safe;

//closure function
(function()
{
 //the function for safe. returns a new Safe object
 safe = function()
 {
  return new Safe();
 }
 
 //the Safe Function
 var Safe = function()
 {
  //the final return value (default to window)
  this.value = window;
  //the member that says weither or not it succeeded.
  this.isValid = true;
 }
 
 //the Prototype for Safe mapped to safe.fn simular to jQuery.fn
 safe.fn = Safe.prototype = 
 {
  //Internal get function. Internal use only
  //this function acts the same as the old _safecall except it doesn't call it at the end.
  _get: function()
  {
   //initialize this to false. if succeed it will become true.
   this.isValid = false;
   if (arguments.length >= 3) {
    //pull out all the  parameters.  and sets them to local values
    var args = Array.prototype.slice.call(arguments);
    var runGlobal = args.shift();
    var context = args.shift();
    var handler = args.shift();
    
    //make sure Handler is a string.
    if (typeof handler == 'string') {
     var partArry = handler.split('.');
     //start at window if told to run global
     if (runGlobal) {
      handler = window;
      //if it's  global and the first element is window you can remove it from the path.
      if (partArry[0] == 'window') {
       partArry.shift();
      }
     }
     else {
      handler = context;
     }
     //Transverse the path intill you reach your destination or reach a null
     while (partArry.length > 0 && handler) {
      handler = handler[partArry[0]];
      partArry.shift();
     }
    }
    //if length is 0 it found the end. and succeeds.
    if (partArry.length == 0) {
     this.value = handler;
     this.isValid = true
    }
   }
   //return this to maintain chainability.
   return this;
  },
  _call: function()
  {
   //call the get on the function
   Safe.prototype._get.apply(this,arguments);
   //pull out the parameters into local variables.
   var args = Array.prototype.slice.call(arguments);
   var runGlobal = args.shift();
   var context = args.shift();
   var handler = args.shift();
   //if the _get returned foul no point in trying to run.
   if(this.isValid)
   {
    //is it a function
    if(typeof(this.value) == 'function')
    {
     //then run it.
     this.value = this.value.apply(context,args)
    }
    else
    {
     //otherwise it fails
     this.isValid = false;
     this.value = null;
    }
   }
   //return this to maintain chainability.
   return this;
  },
  //function that does the chaining. is automatically member call.
  //params are function you are trying to call as a string. and then the parameters for that function.
  call: function()
  {
   var args = Array.prototype.slice.call(arguments);
   //only continue to try if the previous function succeeded.
   if(this.isValid)
   {
    //context should be the value already in the object
    args.splice(0, 0, this.value);
    //should automatically be member call
    args.splice(0, 0, false);
    //call the internal call that does all the heavy lifting.
    Safe.prototype._call.apply(this,args);
   }
   //return this for chaining.
   return this;
  },
  //function that does chaining. is automatically member call.
  //Param is the member you are trying to get.
  get: function()
  {
   var args = Array.prototype.slice.call(arguments);
   if(this.isValid)
   {
    //this starts where the other one stopped
    args.splice(0, 0, this.value);
    //automatically member function
    args.splice(0, 0, false);
    //call the get
    Safe.prototype._get.apply(this,args);
   }
   //return this for chaining.
   return this;
  }
 };
}());
//function that starts off the call. was the original $.mSafeCall (member SafeCall)
// params are:
// Context. the this for the function and where the call starts.
// function name String that is the path to the function.
// additional params the function needs.
safe.mCall = function()
{
 var args = Array.prototype.slice.call(arguments);
 args.splice(0, 0, arguments[0] == null);
 var tempSafe = safe();
 return tempSafe._call.apply(tempSafe, args);
}
//function that starts off the call. was the original $.gSafeCall (global SafeCall)
// params are:
// Context. the this for the function.
// function name String that is the path to the function.
// additional params the function needs.
safe.gCall = function()
{
 var args = Array.prototype.slice.call(arguments);
 args.splice(0,0,true);
    var tempSafe = safe();
 return tempSafe._call.apply(tempSafe, args);
}
// function that gets a value starting from the context.
// params are:
// Context this is the starting point of the function.
// member path from the context.
safe.mGet = function()
{
 var args = Array.prototype.slice.call(arguments);
 // isGlobal should be false inless the context is null
 args.splice(0, 0, arguments[0] == null);
 // create the object and call the get.
 var tempSafe = safe();
 return tempSafe._get.apply(tempSafe, args);
}
// function that gets a value starting from the window.
// params are:
// member path.
safe.gGet = function()
{
 var args = Array.prototype.slice.call(arguments);
 //context should be null
 args.splice(0,0,null);
 // is global is true.
 args.splice(0,0,true);
 // create the object and apply the get.
 var tempSafe = safe();
 return tempSafe._get.apply(tempSafe, args);
}
// hook into jQuery. if exists.
if($)
{
 $.safe = safe;
 //safe = null;
}

No comments:

Post a Comment