Skip to content

Instantly share code, notes, and snippets.

@bmeck
Created June 9, 2010 19:26
Show Gist options
  • Save bmeck/432045 to your computer and use it in GitHub Desktop.
Save bmeck/432045 to your computer and use it in GitHub Desktop.
var sys=require("sys");
//and so the evil begins...
function Interface() {
//BE SURE TO UPDATE compile -> var a = ...
var $this={}
, _tags = {}
, _properties = {}
, _arguments = []
//adds an argument, higher priority than a private variable
$this.addArgument=function(argumentName,position,defaultValue) {
if(!_arguments[position]) {
_arguments[position]={
name:argumentName
,value:defaultValue
};
}
else {
throw "argument already exists at that index"
}
}
$this.addProperty=function(propertyName,propertyDescriptor) {
_properties[propertyName]=propertyDescriptor;
}
$this.tagProperty=function(propertyName,tagName) {
if(tagName in _tags) {
_tags[tagName].push(propertyName);
}
else {
_tags[tagName]=[propertyName];
}
}
$this.getTag=function(tagName) {
return _tags[tagName];
}
//Generate a JS function for this Interface, loses native and closure based values
$this.compile=function(
excludeTags//dont include properties with these flags (dictionary) {flag:true}
,allowExtension/*can put on other objects/can use constructor (boolean) */
,privateProperties/*closure based variables, hidden beyond this...should we return this? (dictionary) */
) {
var a={
//arguments is used as the this variable, sneak sneak
// {"this":x,"src":compileString}
"arguments":{src:""}
, a:undefined//dont let them just get a anyway...
//from compile
, excludTags:undefined
, allowExtensions:undefined
, privateProperties:undefined
//from Interface
, $this:undefined
, _tags:undefined
, _properties:undefined
, _arguments:undefined
,
}
{//scope our data dealing ways
var compilingProperties={};
for(var propertyName in _properties) {
var valid=true;
for(var exclude in excludeTags) {
if(exclude in _tags && _tags[exclude].indexOf(propertyName)!==-1) {
valid=false;
break;
}
}
if(valid) {
compilingProperties[propertyName]=_properties[propertyName];
}
}
var compileSourceBody="return function("
var scopedArgs=[]
for(var i=0;i<_arguments.length;i++) {
var value=_arguments[i];
scopedArgs[i]=value.name;
}
compileSourceBody+=scopedArgs.join(",")+"){";
for(var i=0;i<_arguments.length;i++) {
var value=_arguments[i];
compileSourceBody+="if(typeof("+value.name+")==='undefined')"+value.name+"="+value.value+";"
}
for(var propertyName in compilingProperties) {
var propertyDescriptor=compilingProperties[propertyName];
var value=propertyDescriptor.value
if(value) {
compileSourceBody+="this["+JSON.stringify(propertyName)+"]=("+value+");";
}
}
for(var propertyName in privateProperties) {
a[propertyName]=privateProperties[propertyName];
}
compileSourceBody+=";return this;}"
compileSourceBody="arguments=(function(){return function(){"
+(allowExtension
?""
:"if(this!==(function(){return this;})()){throw new Error('not-extensible');};"
)
+compileSourceBody
+".apply(this!==(function(){return this;})()?this:{},arguments)}}).call(arguments);"
sys.puts("src::",compileSourceBody)
a.arguments.src=compileSourceBody
}
with(a) {
(eval(
arguments["src"]
))
}
return a["arguments"];
}
//Generate a JS string for this interface, loses all closures / native functions
$this.exportable=function(
excludeTags//dont include properties with these flags (dictionary) {flag:true}
,allowExtension/*can put on other objects/can use constructor (boolean) */
,privateProperties/*closure based variables, hidden beyond this...should we return this? (dictionary) */
) {
var compilingProperties={};
for(var propertyName in _properties) {
var valid=true;
for(var exclude in excludeTags) {
if(exclude in _tags && _tags[exclude].indexOf(propertyName)!==-1) {
valid=false;
break;
}
}
if(valid) {
compilingProperties[propertyName]=_properties[propertyName];
}
}
var compileSourceBody=""
var scopedArgs=[]
for(var i=0;i<_arguments.length;i++) {
var value=_arguments[i];
scopedArgs[i]=value.name;
}
for(var i=0;i<_arguments.length;i++) {
var value=_arguments[i];
compileSourceBody+="if(typeof("+value.name+")==='undefined')"+value.name+"="+value.value+";"
}
for(var propertyName in privateProperties) {
compileSourceBody+="var "+propertyName+"="+privateProperties[propertyName]+";";
}
for(var propertyName in compilingProperties) {
var propertyDescriptor=compilingProperties[propertyName];
var value=propertyDescriptor.value
if(value) {
compileSourceBody+="this["+JSON.stringify(propertyName)+"]=("+value+");";
}
}
compileSourceBody+=";return this;}"
compileSourceBody="function("
+(allowExtension
?""
:"){if(this!==(function(){return this;})()){throw new Error('not-extensible');}; return function("
)
+scopedArgs.join(",")+"){"
+compileSourceBody
+(allowExtension
?""
:"}.apply(this!==(function(){return this;})()?this:{},arguments)}"
)
+""
//sys.puts("exptsrc::",compileSourceBody)
return compileSourceBody
}
return $this;
}
var Car=Interface();
//Car.addArgument("_wheels",0,4)
var _wheels=22;
Car.addProperty("wheels",{value:function(){return _wheels;}});
Car.addProperty("fly",{value:true});
Car.tagProperty("fly","todo")
var VW=Car.compile(
//exclude tags
{
//todo:true
},true,{
_wheels:3
}
)
sys.puts(
"EXPORTABLE:::"
,Car.exportable({},true,{_wheels:4})
)
sys.puts(
"INSPECTING:::"
,sys.inspect(VW.toString())
,sys.inspect(VW())
,sys.inspect(VW().wheels())
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment