Reputation: 919
solution: nope. you can't do it. chain commands with && and be done with it.
tl;dr: in random.function("blah").check()
, how do I make it ignore check()
if function("blah")
returns false? Or is this even possible?
The background:
Okay, so I'm horrid at code. But I'm trying to procrastinate by coding things that would make my life marginally easier. And my marginally, I mean microscopically. So basically, my entire project is based around objects and plugins. (plugins being additional .js files that contain an object. for example, afk.js looks something like this:
global.afk = function(){};
afk.check = function(){ ... };
etc, etc. And by loading that js file, I get access to the afk object and all the function therein. Probably not efficient, but it works and it makes figuring out what functions are doing what easier.
Back to the procrastination bit. Because I don't know what plugins are loaded, in some of the shared core .js files, there are these conditional statements: if (plugin.has("afk")) { afk.check(); };
However, I got bored and made plugin.has()
return the actual object instead of 'true', so I could do:
plugin.has("afk").check();
which works as expected. Unless there is no afk plugin, which throws the obvious error of "false has no method check".
So that's that. Any ideas? Also, if you have any qualms with anything, let me know. I know I do a lot of things wrong/inefficiently, and it's nice to fix them when I figure it out.
Upvotes: 7
Views: 3092
Reputation: 919
So this is what I came up with, and eventually ended up using.
Wrather than create a dummy wrapper, I decided to load all the
plugins into the main wrapper. So now, afk is plg.afk, as with
all the other plugins. That allowed me to write this wrapper:
global.& = function(a,b) {
var x,y,z;if (!a) return false; //if nothing, return false.
if (a.indexOf(".") < 0) { //if value has no "."
if (plg["data"][a]) { x = ["data",a]; } //use data function if exists
else { x = ["basic",a]; } //else assume it's a basic function
} else { //or if it does contain "." (such as my example, afk.check)
x = a.split(".") //define the plugin, and the function as an array.
}
y = x[0],z = x[1]; //define the plugin,function as variables.
if (!plg[y]) { return false; } //return if plugin doesn't exist.
if (!plg[y][z]) { return false; } //return if function doesn't exist.
if (!b) { plg[y][z](); } //if no variable, call raw function
return plg[y]z[](b); //return function, passing an optional variable.
}
So this works! I can call $("afk.check"); And it essentially does:
if (plugin.has("afk") && afk.hasOwnProperty("check")) { afk.check(); };
:D
And I can pass variables., and use a shortcut for basic functions.
For example, if I call $("boot");
then it checks data for a boot function, and when it doesn't find one, reverts to basic.boot();, and calls the right function. And I can call data functions and pass variables such as in the case of saving data, $("save",data);
Feel free to let me know if this is a stupid idea! But so far it's working for me <3
Upvotes: 0
Reputation: 105886
Actually you solved the problem already yourself, at least almost:
This is for any number of functions doing any number of things. I was thinking of switching
plugin.has()
to doplugin.has("afk","check");
or something similar, so instead of chaining the functions, it's just in one function. But passing variables to the check function would get tricky.
Well, there are basically two ways for this:
plugin_with_afk.has("afk","check").check(100); // "check" twice
plugin_with_afk.has("afk","check")(100); // "check" not twice
The first way is actually rather simple: simply return this
if the plugin has "afk", and otherwise return an object that has a no-op method check
:
Plugin.prototype.has = function(str, member){
if(this.things.indexOf(str) > -1) {
return this;
} else {
var tmp = {}
tmp[member] = function(){}
return tmp;
}
}
plugin_with_afk.has("afk","check").check(1); // has afk, check is there
plugin_without_afk.has("afk","check").check(2); // has no afk, check is no-op
plugin_without_afk.has("nope","check").check(3); // has nope, check is there
This has also the great advantage that you don't need to use any function wrapper and so on. However, you have to specify the used function twice, which would result in ReferenceError
if you accidentally used another name:
plugin_with_afk.has("afk","check").test(1); // oh oh
So what can you do against this? Create a simple function wrapper:
PluginWithFuncRet.prototype.has = function(str,member){
var self = this;
if(this.things.indexOf(str) > -1) {
return function(){
return self[member].apply(self, arguments);
}
} else {
return function(){};
}
}
plugin_with_afk.has("afk","check")(4); // calls check()
plugin_without_afk.has("afk","check")(5);// no-op
And that's it. Note that you should actually rename has
to use_if
or something similar, since it's now doing many things more than actually checking whether a component is there.
var PluginBase = function(name, things){
this.name = name;
this.things = things;
}
/// First variant
var Plugin = function() {
this.constructor.apply(this, arguments);
}
Plugin.prototype.constructor = PluginBase;
Plugin.prototype.has = function(str,member){
if(this.things.indexOf(str) > -1) {
return this;
} else {
var tmp = {}
tmp[member] = function(){}
return tmp;
}
}
var plugin_with_afk = new Plugin("with afk", ["afk"]);
plugin_with_afk.check = function(val){
console.log("hi", val, this.name);
};
var plugin_without_afk = new Plugin("w/o afk", ["nope"]);
plugin_without_afk.check = function(val){
console.log("nope", val, this.name);
}
/// First variant demo
plugin_with_afk.has("afk","check").check(1)
plugin_without_afk.has("afk","check").check(2)
plugin_without_afk.has("nope","check").check(3)
/// Alternative
var PluginWithFuncRet = function(){
this.constructor.apply(this, arguments);
}
PluginWithFuncRet.prototype.constructor = PluginBase;
PluginWithFuncRet.prototype.has = function(str,member){
var self = this;
if(this.things.indexOf(str) > -1) {
return function(){
return self[member].apply(self,arguments);
}
} else {
return function(){}
}
}
plugin_with_afk = new PluginWithFuncRet("with afk",["afk"]);
plugin_with_afk.check = function(val){
console.log("Hi",val,this.name);
}
plugin_without_afk = new PluginWithFuncRet("w/o afk",["nope"]);
plugin_without_afk.check = function(val){
console.log("Nope",val,this.name);
}
/// Alternative demo
plugin_with_afk.has("afk","check")(4);
plugin_without_afk.has("afk","check")(5);
plugin_without_afk.has("nope","check")(6);
Result:
hi 1 with afk nope 3 w/o afk Hi 4 with afk Nope 6 w/o afk
Upvotes: 0
Reputation: 339816
Within current ECMAScript specifications, turning .check()
into a no-op (and any other method that might exist on the non-existent plugin) is impossible since you cannot as you've found portably access undefined properties of undefined objects.
The proposed "Direct Proxies" ECMAScript API would appear to solve this, but it's not standardised yet. I also suspect that it's not very efficient to proxy your code this way since you would (AIUI) have to make your .has()
function return a trapping proxy object which then has to do real work for every "missed" function call. It would be far better IMHO to simply ensure that the conditional code is never run at all.
So, other than the &&
method proposed elsewhere, all I can offer is this:
plugin.if("afk", function() {
// put conditional code here
});
Upvotes: 1
Reputation: 1748
Oh, hello. Node-proxy looks promising. I don't actually know anything about it, but it sounds like it's supposed to let you do exactly this kind of thing.
Upvotes: 0
Reputation: 1748
It won't work in all browsers, but I think you could create a dummy class that uses __noSuchMethod__
to always return itself instead of throwing an error when a nonexistent method is called. See this question.
I do not know what browsers this will work in, and my general impression is "not many".
Upvotes: 0
Reputation: 1117
A short and dirty way to make this work would be to use the && operator.
var x = {};
x.afk && x.afk();
x.afk = function () { console.log("I exist!"); };
x.afk && x.afk();
Not your optimal wide range solution though.
Upvotes: 1
Reputation: 27323
So jQuery does it a bit like this:
$(plugin)
-> returns a collection like object
.has("afk")
-> filters the internal collection, and returns the collection
.check()
-> action executed on this collection
So let's make a small example:
function Wrapper(ele) {
// make our collection
this.coll = [ ele ];
// function to filter
this.has = function(filter) {
this.coll = this.coll.filter(function(e) {
return e === filter;
});
// we return ourself for chaining
return this;
};
// function to execute on ourself
this.check = function() {
// returns actual value
return this.coll.length;
};
}
var $ = function(ele) {
// wrapper around the `new` syntax
return new Wrapper(ele);
};
console.log($('jan').has('jan').check()); // gives 1
console.log($('pietje').has('jan').check()); // gives 0
Cool thing is you can now extend your Wrapper
object from external files by extending the prototype:
Wrapper.prototype.hasNot = function(filter) {
/* do magic */
return this;
}
Upvotes: 1
Reputation: 49919
The returned object should have that function, otherwise you get an error. What you can do is make a base Object type, which is always returned. Than when you return it, it has an empty function and will not create an error.
Something like:
var baseObj = function(){
this.afk = function(){
return this;
}
this.check = function(){
return false;
}
};
var advObj = new baseObj();
advObj.afk = function(){
// extended func
return new baseObj();
}
var a = new advObj();
a.afk("test").check();
Upvotes: 1