javascript closures and returning array elements - javascript

I'm trying to learn javascript to work with node.js and obviously don't quite get closures.
I'm try to read a file, line by line, parse the line and put the result into an array and return the array. Here's what I have (doesn't quite work):
var fs = require('fs'), Lazy = require('lazy');
function parseMyFile (filename) {
var myArray= [];
var lazy = new Lazy (fs.createReadStream(filename));
lazy
.lines
.map(function(line){
var parts = line.toString().split('|');
var item = {
bucket: parts[1],
uri: parts[2].substring(2),
token: parts[0],
fileDate: parts[3]
};
myArray.push (item);
});
console.log(myArray); // empty
return myArray;
};
var myItems = parseMyFile ('Tokens.csv');
I'm sure this has something to do with closures, just not quite getting it. Any help would be appreciated.
Thanks!

It's a lazy list. It's a wrapper around asynchronous behavior. You're trying to examine the list before it's been filled in, so of course it doesn't work.
The problem has nothing to do with closures. It's all about asynchronous behavior.
I don't see anything in that lazy list code that allows a generic "when finished" callback.

Related

Alternative to eval() in node script

I am working on a script that runs during our build process in Jenkins right before npm install. My issue is that I need to download a JavaScript file from an external resource and read a variable from it.
unzipper.on('extract', () => {
const content = fs.readFileSync(`${outputDir}/js/en.js`, 'utf8');
eval(content); // Less smellier alternative?
if (obj) {
const str = JSON.stringify(obj);
fs.writeFileSync(`${outputDir}/public/data.json`, str);
} else {
throw 'Variable `obj` not found';
}
});
I know that "eval is evil", but any suggested alternatives I've found online don't seem to work.
I have tried different variations of new Function(obj)(), but Node seems to exit the script after (the if-case never runs).
Ideas?
Since node.js provides the API to talk to the V8 runner directly, it might be a good idea to use it. Basically, it's the API used by node's require under the hood.
Assuming the js file in question contains the variable obj we're interested in, we do the following:
read the code from the file
append ; obj to the code to make sure it's the last expression it evaluates
pass the code to V8 for evaluation
grab the return value (which is our obj):
const fs = require('fs'),
vm = require('vm');
const code = fs.readFileSync('path-to-js-file', 'utf8');
const obj = vm.runInNewContext(code + ';obj');
This answer is heavily based on #georg's comments, but since it helped me I'll provide it as an alternative answer.
Explanation in the comments.
let content = fs.readFileSync(`${outputDir}/js/en.js`, 'utf8');
content += '; module.exports=obj'; // Export "obj" variable
fs.writeFileSync(`${outputDir}/temp`, content); // Create a temporary file
const obj = require(`${outputDir}/temp`); // Import the variable from the temporary file
fs.unlinkSync(`${outputDir}/temp`); // Remove the temporary file

Combine two $firebaseArrays into single object using javascript or angularfire

I'm working in Angular (angularfire) trying to combine two firebaseArrays into a single object, but the asynchronous nature of the return calls is making it difficult. I would like to do something simple like:
$scope.itemStats = $firebaseStorage.getArray('item-stats');
$scope.stats = $firebaseStorage.getArray('stats');
$scope.newArray = $scope.itemStats.concat($scope.stats);
The code for the getArray function is:
getArray: function(key) {
var dbRef = ref.child(key);
return $firebaseArray(dbRef);
}
It's possible I'm querying the data wrong in the first place, and could grab both at the same time which would also solve my problem (aka, I'm asking the wrong question).
Which approach should I use, and how do I solve this?
You can use the $loaded which will return a promise resolved when loading the array is done.
An example from the documentation
var list = $firebaseArray(ref);
list.$loaded()
.then(function(x) {
// now "list" is loaded
})

Builder design pattern in javascript

Need to construct a custom JSON file after parsing XML. To cut the long story short, have an XML file from which must be created a JSON file, but not all information from XML must be present in JSON. It must be done using only JS and Node.js. To parse XML I used xmldom and fs.
Now the part I am stuck at. To create a JSON file I can't use code snipped already available on the Web, because there are very few examples with Node.js and because I need to omit many things from XML. Looping also is not a possibility, since XMLDOM is recursive. Right now I use this code to do it:
var builder = function() {
this.meta = {};
}
builder.atRoot = function(library) {
this.meta["!name"] = library;
this.meta["!define"] = {};
this.define = this.meta["!define"];
}
builder.atNamespace = function(name) {
this.namespace = {};
this.namespace["!category"] = "namespace";
this.namespace["!description"] = "";
this.define[name] = this.namespace;
}
builder.atDescription = function(method){
this.class = {};
this.class["!Accordion"] = method;
this.class["!AccordionSection"] = method;
this.class["!ApplicationHeader"] = method;
....
}
// and so on
builder.toJson = function() {
var s = JSON.stringify(this.meta);
console.log(s);
}
But it doesn't work. Now, may be I simply don't understand how builder works, or it is the wrong way. Any ideas on what is wrong, or additional explanation of builder design pattern will be much appreciated.
Many thanks in advance.
Builder usually implies recursion. I don't fully understand what do you need, but it seems that you need to create a whitelist of attribute names and just recursively copy properties into new object, then serialize it as JSON.

Node.js make initialized object available in all modules

I have an initialized object that I initialized in app.js file and I would like to make this initialized object is available in all modules. How could I do that? Passing this object to every modules is one way to do and I'm wondering if I'm missing anything or there should be done in difference ways?
I saw mongoose actually support default connection, which I need to init in app.js one time and anywhere in other modules, I can just simply use it without requiring passing it around. Is there any I can do the same like this?
I also checked global object doc from node.js http://nodejs.org/api/globals.html, and wondering I should use global for issue.
Thanks
A little advice:
You should only very rarely need to use a global. If you think you need one, you probably don't.
Singletons are usually an anti-pattern in Node.js, but sometimes (logging, config) they will get the job done just fine.
Passing something around is sometimes a useful and worthwhile pattern.
Here's an example of how you might use a singleton for logging:
lib/logger.js
var bunyan = require('bunyan'),
mixIn = require('mout/object/mixIn'),
// add some default options here...
defaults = {},
// singleton
logger,
createLogger = function createLogger(options) {
var opts;
if (logger) {
return logger;
}
opts = mixIn({}, defaults, options);
logger = bunyan.createLogger(opts);
return logger;
};
module.exports = createLogger;
lib/module.js
var logger = require('./logger.js'),
log = logger();
log.info('Something happened.');
Hope that helps.
The solution, as you suggest is to add the object as a property to the global object. However, I would recommend against doing this and placing the object in its own module that is required from every other module that needs it. You will gain benefits later on in several ways. For one, it is always explicit where this object comes from and where it is initialized. You will never have a situation where you try to use the object before it is initialized (assuming that the module that defines it also initializes it). Also, this will help make your code more testable,
There are multiple solutions to the problem, depends upon how large your application is. The two solutions that you have mentioned are the most obvious ones. I would rather go for the third which is based on re-architecturing your code. The solution that I am providing looks alot like the executor pattern.
First create actions which require your common module that are in this particular form -
var Action_One = function(commonItems) {
this.commonItems = commonItems;
};
Action_One.prototype.execute = function() {
//..blah blah
//Your action specific code
};
var Action_Two = function(commonItems) {
this.commonItems = commonItems;
};
Action_Two.prototype.execute = function() {
//..blah blah
//Your action_two specific code
};
Now create an action initializer which will programmatically initialize your actions like this -
var ActionInitializer = function(commonItems) {
this.commonItems = commonItems;
};
ActionInitializer.prototype.init = function(Action) {
var obj = new Action(this.commonItems);
return obj;
};
Next step is to create an action executor -
//You can create a more complex executor using `Async` lib or something else
var Executor = function(ActionInitializer, commonItems) {
this.initializer = new ActionInitializer(commonItems);
this.actions = [];
};
//Use this to add an action to the executor
Executor.prototype.add = function(action) {
var result = this.initializer.init(action);
this.actions.push(result);
};
//Executes all the actions
Executor.prototype.executeAll = function() {
var result = [];
for (var i = this.action.length - 1; i >= 0; i--) {
result[i] = this.action[i].execute();
}
this.action = []
return result;
};
The idea was to decouple every module so that there is only one module Executor in this case which is dependent on the common properties. Now lets see how it would work -
var commonProperties = {a:1, b:2};
//Pass the action initilizer class and the common property object to just this one module
var e = new Executor(ActionInitializer, commonProperties);
e.add(Action_One);
e.add(Action_Two);
e.executeAll();
console.log(e.results);
This way your program will be cleaner and more scalable. Shoot questions if it's not clear. Happy coding!

Lazy Load External Javascript Files

I am trying to write a javascript class that loads script files as they are needed. I have most of this working. It is possible to use the library with the following Syntax:
var scriptResource = new ScriptResource('location/of/my/script.js');
scriptResource.call('methodName', arg1, arg2);
I would like to add some additional syntactic sugar so you could write
var scriptResource = new ScriptResource('location/of/my/script.js');
scriptResource.methodName(arg1, arg2);
I'm almost certain that this isnt possible but there may be an inventive solution. I guess what there need to be is some sort of methodCall event. SO the following could work
ScriptResource = function(scriptLocation)
{
this.onMethodCall = function(methodName)
{
this.call(arguments);
}
}
This code is obviously very incomplete but I hope it gives an idea of what I am trying to do
Is something like this even remotely possible?
There is a non standard method, __noSuchMethod__ in Firefox that does what you're looking for
have a look at
https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/noSuchMethod
so you could define
obj.__noSuchMethod__ = function( id, args ) {
this[id].apply( this, args );
}
If the set of method names is limited, then you could generate those methods:
var methods = ["foo", "bar", "baz"];
for (var i=0; i<methods.length; i++) {
var method_name = methods[i];
WildCardMethodHandler[method_name] = function () {
this.handleAllMethods(method_name);
};
}
edit: Posted this answer before the question changed dramatically.
An intermediary solution might be to have syntax such as:
var extObj = ScriptResource('location/of/my/script.js');
extObj('methodname')(arg1,arg2);
the code might look like this:
function ScriptResource(file) {
return function(method) {
loadExternalScript(file);
return window[method];
}
}
All kinds of assumptions in the code above, which I'd let you figure out yourself. The most interesting, IMHO, is - in your original implementation - how do you get the proxyied method to run synchronously and return a value? AFAIK you can only load external scripts asynchronously and handle them with an "onload" callback.

Categories