I've been looking around for a way to do this but can't seem to find anything, I have different configuration objects that I need to save as a text in variables for some processing later on, here is a sample:
object:
args.config.config = {
next: null,
final:[],
delimiter: '~', header: false,
step: function (row) {
var item = {
'line_code': row.data[0][0],
'order': row.data[0][1]
}
args.config.config.final.push(item);
},
complete: function (result) {
console.log('Reading data completed. Processing.');
return args.config.config.next(null, args.config.config.final);
},
error: function () {
console.log('There was an error parsing');
}'
}
I need to save this as a string, so something like:
args.config.config = "{object goes here}";
Without putting everything on one giant line or adding break line characters as this will be parsed later to be used in a config, and that will mess things up, any ideas?
UPDATE:
So changing them into text may not be the best solution, these configs will be stored in a mongo database, so it may take them as is (I have not tried it yet).
One of the other problems I was running into was that in the config object I had this:
final.push(item)
and
return next(null, final)
Which will be defined in another file using the config object:
other file:
exports.parse = function(args, next){//next is what I need to call in the config
var final = []; //this is the final referred to in the config object
....
Baby.parse(data, args.config)
}
So the return next(null, final) and final.push(result) have to refer the the var / function in the new file, but I have no idea how to get that to work, that't why I had to add a final array in the config object and a null next function, then assign it like so:
exports.parse = function(args, next){
args.config.next = next;
....
Baby.parse(data, args.config)
}
the object was calling it with the ugly line:
return args.config.config.next(null, args.config.config.final);
If anyone has a way around this, it would be much appreciated.
If you use JSON.stringify with a "replacer" function and
JSON.parse with a "reviver" function along with new Function(), you can do it:
I'm not sure I'm following the second (updated) question you have. Once the object is parsed back into an object, why can't you just initialize the next and final properties to valid objects before calling any of the object's methods? You can even add tests into that method that checks for the existence of final and next before returning anything.
var myObj = {
next: null,
final:[],
delimiter: '~',
header: false,
step: function (row) {
var item = {
'line_code': row.data[0][0],
'order': row.data[0][1]
};
args.config.config.final.push(item);
},
complete: function (result) {
console.log('Reading data completed. Processing.');
return args.config.config.next(null, args.config.config.final);
},
error: function () {
console.log('There was an error parsing');
}
};
// Stringify the object using a replacer function that will explicitly
// turn functions into strings
var myObjString = JSON.stringify(myObj, function(key, val) {
return (typeof val === 'function') ? '' + val : val;
});
// Now, parse back into an object with a reviver function to
// test for function values and create new functions from them:
var obj = JSON.parse(myObjString, function(key, val){
// Make sure the current value is not null (is a string)
// and that the first characters are "function"
if(typeof val === "string" && val.indexOf('function') === 0){
// Isolate the argument names list
var start = val.indexOf("(") + 1;
var end = val.indexOf(")");
var argListString = val.substring(start,end).split(",");
// Isolate the body of the function
var body = val.substr(val.indexOf("{"), val.length - end + 1);
// Construct a new function using the argument names and body
// stored in the string:
return new Function(argListString, body);
} else {
// Non-function property, just return the value
return val;
}
}
);
// Test the method:
obj.error(); // 'There was an error parsing' is written to console.
// Examine the object:
console.log(obj);
Related
I've been looking around for a way to do this but can't seem to find anything, I have different configuration objects that I need to save as a text in variables for some processing later on, here is a sample:
object:
args.config.config = {
next: null,
final:[],
delimiter: '~', header: false,
step: function (row) {
var item = {
'line_code': row.data[0][0],
'order': row.data[0][1]
}
args.config.config.final.push(item);
},
complete: function (result) {
console.log('Reading data completed. Processing.');
return args.config.config.next(null, args.config.config.final);
},
error: function () {
console.log('There was an error parsing');
}'
}
I need to save this as a string, so something like:
args.config.config = "{object goes here}";
Without putting everything on one giant line or adding break line characters as this will be parsed later to be used in a config, and that will mess things up, any ideas?
UPDATE:
So changing them into text may not be the best solution, these configs will be stored in a mongo database, so it may take them as is (I have not tried it yet).
One of the other problems I was running into was that in the config object I had this:
final.push(item)
and
return next(null, final)
Which will be defined in another file using the config object:
other file:
exports.parse = function(args, next){//next is what I need to call in the config
var final = []; //this is the final referred to in the config object
....
Baby.parse(data, args.config)
}
So the return next(null, final) and final.push(result) have to refer the the var / function in the new file, but I have no idea how to get that to work, that't why I had to add a final array in the config object and a null next function, then assign it like so:
exports.parse = function(args, next){
args.config.next = next;
....
Baby.parse(data, args.config)
}
the object was calling it with the ugly line:
return args.config.config.next(null, args.config.config.final);
If anyone has a way around this, it would be much appreciated.
If you use JSON.stringify with a "replacer" function and
JSON.parse with a "reviver" function along with new Function(), you can do it:
I'm not sure I'm following the second (updated) question you have. Once the object is parsed back into an object, why can't you just initialize the next and final properties to valid objects before calling any of the object's methods? You can even add tests into that method that checks for the existence of final and next before returning anything.
var myObj = {
next: null,
final:[],
delimiter: '~',
header: false,
step: function (row) {
var item = {
'line_code': row.data[0][0],
'order': row.data[0][1]
};
args.config.config.final.push(item);
},
complete: function (result) {
console.log('Reading data completed. Processing.');
return args.config.config.next(null, args.config.config.final);
},
error: function () {
console.log('There was an error parsing');
}
};
// Stringify the object using a replacer function that will explicitly
// turn functions into strings
var myObjString = JSON.stringify(myObj, function(key, val) {
return (typeof val === 'function') ? '' + val : val;
});
// Now, parse back into an object with a reviver function to
// test for function values and create new functions from them:
var obj = JSON.parse(myObjString, function(key, val){
// Make sure the current value is not null (is a string)
// and that the first characters are "function"
if(typeof val === "string" && val.indexOf('function') === 0){
// Isolate the argument names list
var start = val.indexOf("(") + 1;
var end = val.indexOf(")");
var argListString = val.substring(start,end).split(",");
// Isolate the body of the function
var body = val.substr(val.indexOf("{"), val.length - end + 1);
// Construct a new function using the argument names and body
// stored in the string:
return new Function(argListString, body);
} else {
// Non-function property, just return the value
return val;
}
}
);
// Test the method:
obj.error(); // 'There was an error parsing' is written to console.
// Examine the object:
console.log(obj);
I've added the first function superscriptDesignation to this code and now want to call it on the items in teamDesignations. Doing
return superscriptDesignation(teamDesignations);
gives me an error in the console that .replace is not defined. How can I add my function superscriptDesignation to the teamDesignations?
JS
var superscriptDesignation = function(designation) {
return designation.replace(/(®)/ig, "<sup>®</sup>").replace(/(™)/ig, "<sup> </sup>");
};
var getTeamDesignations = function(profile) {
//Designations for a single team member
var teamDesignations = [];
if (profile.team_members) {
teamDesignations = _.chain(profile.team_members)
.filter(_.compose(_.isArray, _.property('team_member_designations')))
.map(_.property('team_member_designations'))
.flatten()
.uniq()
.value();
}
return teamDesignations;
};
What this makes me think is that teamDesignations is not a string when it's being passed to superScriptDesignations. .replace() is only a method on the String prototype.
I'm not familiar with .uniq() and .flatten(), but is the result of that method chain a string? Either way, it's all in an if statement, so if that is not met teamDesignations could be an empty array when it's passed to superScriptDesignation.
You can either modify the replace() method in the first function to handle arrays, or put in some error handling to ensure that the parameter is a string. i.e:
var superscriptDesignation = function(designation) {
if(typeof(designation === 'string)) {
//... do your stuff
}
};
My app receives a base64 encoded value that is also encrypted. The data can come in a few different ways so I wanted to create chain-able methods to keep the code clean and modular.
I want to be able write: decryptionChain.decodeBase64(b64Value).stringToBuffer().finallyDecrypt();
When I run the code, the last property method "finallyDecrypt" returns as undefined.
Why is the "finallyDecrypt" method coming back as undefined? The rest all works and if I run encryptionChain.decodeBase64(b64Value).stringToBuffer() I get back the Buffer I expect. It is only when the finallyDecrypt is chained in that I error out.
Here is the code:
function decrypt(encrypted) {
var decipher = crypto.createDecipheriv(algorithm, password, iv);
decipher.setAuthTag(encrypted.tag);
var dec = decipher.update(encrypted.content, 'hex', 'utf8');
dec += decipher.final('utf8');
return dec;
}
var decryptionChain = {
currentValue:"",
decodeBase64: function (encryptedValue){
this.currentValue = new Buffer(encryptedValue.toString(), "base64");
return this;
},
stringToBuffer: function() {
if (this.currentValue) {
myBuffer = JSON.parse(this.currentValue, function (key, value) {
return value && value.type === 'Buffer'
? new Buffer(value.data)
: value;
});
}
return myBuffer;
},
finallyDecrypt : function(myBuffer){
if(myBuffer){
decrypt(myBuffer);
}
return this;
}
};
Chaining works by returning this from each method (which points back to the decryptionChain object).
stringToBuffer, however, returns myBuffer, so you're trying to call a method named finallyDecrypt on that buffer (which doesn't have that method, hence the error).
If you want it to work with chaining, use something similar to how you're dealing with currentValue:
stringToBuffer : function() {
...
this.myBuffer = myBuffer;
return this;
},
finallyDecrypt : function() {
if (this.myBuffer) {
...
}
}
The stringToBuffer() function returns a Buffer (or it may return nothing if this.currentValue is falsey). The function you're trying to call is not in Buffer, it's in decryptionChain, so when you try to call a non-existing function on Buffer, it's telling you it's undefined.
Notice in decodeBase64(), you store this.currentValue and then return this. That's why the next chain works, because the decryptionChain object is being returned.
So stringToBuffer() could store it's result in this.bufferedResult, and then stringToBuffer() could return this.
In javascript using an object parameter is my preferred way of working with functions. To check that a function has the required parameters I either (Solution 1) loop through all the object parameters properties and throw an error or (Solution 2) wait until a required property is needed and throw an error. Solution two seems efficient but I have to throws in multiple places in the function. Solution 1 seems pragmatic but should probably be a reusable piece of code. Is there another solution I should be looking at?
You can actually do this
var propsNeeded = ["prop1", "prop2", "blah", "blah", "blah"],
obj = {
prop1: "Hi"
}
function hasRequiredProperties(props, obj){
return Object.keys(obj).sort().join() == propsNeeded.sort().join();
}
console.log(hasRequiredProperties(propsNeeded, obj)); // false
You can check for single properties like
function hasProperty(propName, obj){
return obj.hasOwnProperty(propName);
}
For consistency I would create require method and use it always when some property is required.
var require = function (key, object) {
if (typeof object[key] === 'undefined') {
throw new Error('Required property ' + key + ' is undefined');
}
};
I would test if required property exists as soon as I'm certain that property is needed. Like this:
var example = function (args) {
require('alwaysRequired', args);
// some code here which uses property alwaysRequired
if (args.something) {
require('sometimesRequired', args);
// some code here which uses property sometimesRequired
}
};
Using #Amit's answer I'd probably add a method to Object itself:
Object.prototype.hasAllProperties = function(props, fire){
var result = Object.keys(this).sort().join() == propsNeeded.sort().join();
if (fire && !result){
throw new Error('Object does not define all properties');
}
return result;
}
and in your function:
function someFunction(myObject){
var objComplete = myObject.hasAllProperties(["prop1", "prop2", "prop3"], false);
}
Update:
After noticing the problem with #Amit's original answer, here's what I suggest:
Object.prototype.hasAllProperties = function(props, fire){
var result = true;
$(props).each(function(i, e){
if (!this.hasOwnProperty(e) ) {
result = false;
return false;
}
});
if (fire && !result){
throw new Error('Object does not define all properties');
}
return result;
}
This is just a general case of checking for presence of keys on a object, which can be done easily enough with
requiredParams.every(function(prop) { return prop in paramObj; })
It almost reads like natural language. "Taking the required parameters, is EVERY one of them IN the parameter object?".
Just wrap this in function checkParams(paramObj, requiredParams) for easy re-use.
More generally, this is the problem of asking if one list (in this case the list of required parameters) is included in another list (the keys on the params object). So we can write a general routine for list inclusion:
function listIncluded(list1, list2) {
return list1.every(function(e) { return list2.indexOf(e) !== -1; });
}
Then our parameter-checking becomes
function checkParams(paramObj, requiredParams) {
return listIncluded(requiredParams, Object.keys(paramObj));
}
If you want to know if object has at least some properties you can use this function without third parameter:
function hasRequiredProperties(propsNeeded, obj, strict) {
if (strict) return Object.keys(obj).sort().join() == propsNeeded.sort().join();
for (var i in propsNeeded ) {
if (!obj.hasOwnProperty(propsNeeded[i])) return false;
}
return true;
};
Example:
options = {url: {
protocol: 'https:',
hostname: 'encrypted.google.com',
port: '80'
}
};
propsNeeded = ['protocol', 'hostname'];
hasRequiredProperties(propsNeeded, options.url); // true
hasRequiredProperties(propsNeeded, options.url, true); // false
I'm tring to get some json to contain a function so that the option in the plugin gets the returned value of the function that was in the json string.
The set up is as such:
default.txt
[
{
"element":"a[href$='.jpg']",
"options":{
"label":"test"
}
},{
"element":"a#hover",
"options":{
"label":(function(ele){ return 'test'; })()
}
}
]
and the plug-in is
(function($) {
function defined(obj){return typeof(obj)!=='undefined';}
function evaluate(ele, obj) {
if(typeof obj === 'function') {
//alert('is function');
obj = obj(ele);
}else{
try{
//alert('thinking it may be a function still');
obj = eval("("+obj+"("+ele+"));");
//obj = (obj)(ele);
}catch(err){
//Handle errors here
//alert('not function');
}
}
return obj;
}
function debug(message) {
console.debug(message);
}
$.set = function(options){
var settings = $.extend({}, {}, options);
if(defined(settings.actions)){
$.each(settings.actions, function(index, value) {
$(value.element).do_action(defined(value.options)?value.options:null);
});
}
}
$.fn.do_action = function(options) {
// Add event handler to all matching elements
return this.each(function() {
var ele = $(this);
var settings = $.extend({}, $.fn.do_action.defaults, options);
var label= evaluate(ele, settings.label);
var message = "label:'" + label + "'";
debug('Tracking ' + action + ' ' + message);
});
};
$.fn.do_action.defaults = {
label : function(ele) { return ele.attr('href'); }
};
}(jQuery));
and the control is
$.getJSON('default.txt' , function(data){
$.set({
actions:data // this si where we pull in the options
});
});
I seems to work if I put in an alert inside the anonymous function, so from
"label":(function(ele){ return 'test'; })()
to
"label":(function(ele){ alert('test') })()
but after that I can't seem to get it to print right in the console as I only get the message
Tracking label:'(function(ele){ return 'test'; })
[EDIT]
Please stop saying that "you should not put code in json". If you work for Google and are their top level programmers getting paid the big super bucks then I'll listen to the why's, but I'm sorry if it's good for them, it'll work for me. Don't know what else to say, I already understand the point of why you should avoid it which I aim to do as well. There are times when you just can't and why i supect Google does the same.
Your label member is not a function; it's a value returned by a function. The final () at the end means "execute this function immediate whenever the value of label is requested and provide its return value as the value of label".
That said, if you must include functions in your JSON object (and from what you've said, you do) your best bet is to stringify-and-eval your functions. You should also be able to use raw functions in your JSON, like {foo : (function(){ ... }) }, though I've personally had a tricky time getting this right (but that's just me -- if you can get it right, go for it).
(As an aside, it's not possible to stringify functions from existing objects, but if you're getting your functions as text from user/programmer input, that's not an issue.)