How to make this nodeJs code synchronous using Meteor.wrapAsync? - javascript

I have some nodeJs code on my server that turns a cvs file into a JSON object. I'm trying to figure out how to utilize wrapAsync with my code, but I'm not too sure how to. Here is my attempt at it:
readFile: function(fileName){
var readFileToJsonSync = Meteor.wrapAsync(readFileToJson);
var result = readFileToJsonSync(fileName);
console.log(result);
},
});
if (Meteor.isServer){
var readFileToJson = function(fileName, cb){
var nodeFS = Meteor.npmRequire('node-fs');
var Converter = Meteor.npmRequire("csvtojson").Converter;
var fileStream = nodeFS.createReadStream("/Users/ray/Desktop/juju/upload/"+fileName,'utf8');
//new converter instance
var converter = new Converter({constructResult:true});
//end_parsed will be emitted once parsing finished
converter.on("end_parsed", function (jsonObj) {
return jsonObj ; //here is your result json object
});
//read from file
fileStream.pipe(converter);
}
}
And in the client:
Meteor.call("readFile", Meteor.user().emails[0].address+'/'+Session.get("file1"), function (error, result){
console.log(result);
});
When I run this code asynchronously, the proper output gets printed in the terminal, but undefined is printed out in the client console. This current attempt prints out nothing in both the server and client.

You are missing a return on your method. This may not fix your problem since you should have 2 client logs (1 from the method and 1 from the callback). Let me know.
Meteor.methods({
readFile: function(fileName){
var readFileToJsonSync = Meteor.wrapAsync(readFileToJson);
var result = readFileToJsonSync(fileName);
console.log(result);
return result;
}
});

Related

Expose result of method in Javascript

I have this method :
// Parse a whole file
fs.readFile("klv-file.klv", (err, file) => {
var KLVdata = KLV.parseKLVfile(file, options);
var packets = KLVdata.packets;
var nDropped = KLVdata.nDropped;
});
in a small Node app.
How to I get the variable packets outside this object?
It's actually quite simple. Define the variables outside the function, and set them inside. Like this:
// Define variables
var packets = null;
var nDropped = null;
// Parse a whole file
fs.readFile("klv-file.klv", (err, file) => {
var KLVdata = KLV.parseKLVfile(file, options);
packets = KLVdata.packets;
nDropped = KLVdata.nDropped;
});
function doSomething(){
// you can now use the variables anywhere
}
Also: make sure to only use the variables if they have been defined.
// Define variables
var packets = null;
var nDropped = null;
// Parse a whole file
fs.readFile("klv-file.klv", (err, file) => {
var KLVdata = KLV.parseKLVfile(file, options);
packets = KLVdata.packets;
nDropped = KLVdata.nDropped;
packetsReady(); // your callback function
});
function packetsReady(){
// should only be called once the packets are ready
processPackets(packets);
}
Simply pass it to a function
let packets;
let nDropped;
function processKLV(KLVdata){
packets = KLVdata.packets;
nDropped = KLVdata.nDropped;
}
// Parse a whole file
fs.readFile("klv-file.klv", (err, file) => {
processKLV(KLV.parseKLVfile(file, options));
});

How can I render a static HTML file with Handlebars on a Nodejs server?

I have come across plenty of resources online for this but haven't been able to find one that is straight forward enough for me to understand.
At the moment, I have multiple massive <script> tags in an HTML document that has handlebars content. The server sends this HTML document to the client where the client then renders the page with data from an AJAX call. I'd like to move this entire process server-side so that all the server has to do is send a static file and re-render the page when data is updated. Data changes a few times per day - which is why it isn't hard coded in and I would like to run the handlebars compiler on the HTML document when data is updated.
Is it possible to simply put the HTML document with handlebars templating in <script> tags through a function to generate a new HTML file with data filled in?
Here is the code I have within my app.js file that is runned the Node server that does not do what I want it to:
function registerHelpers(callback){
Handlebars.registerHelper('equal', function(lvalue, rvalue, options) {
if (arguments.length < 3)
throw new Error("Handlebars Helper equal needs 2 parameters");
if( lvalue!=rvalue ) {
return options.inverse(this);
} else {
return options.fn(this);
}
});
Handlebars.registerHelper('trim', function(text) {
text = text.replace(/ /g, '');
return new Handlebars.SafeString(text);
});
callback();
}
function buildHomePage() {
var source = require(__dirname + '/public/home.handlebars');
var template = Handlebars.precompile(source);
var collection = db.get('datalist'); //Monk call to MongoDB
collection.find({}, function (err, docs){
var result = template(docs);
console.log(result)
var fs = require('fs');
fs.writeFile("test.html", result, function(err) {
if(err) {
console.log(err);
}
});
});
};
registerHelpers(buildHomePage);
The following can render handlebars to static html. Run node example.js. You may need to run npm install --save handlebars prior.
var fs = require('fs');
var Handlebars = require('handlebars');
function render(filename, data)
{
var source = fs.readFileSync(filename,'utf8').toString();
var template = Handlebars.compile(source);
var output = template(data);
return output;
}
var data = JSON.parse(fs.readFileSync("./data/strings.json", 'utf8'));
var result = render('./templates/somefile.html', data);
console.log(result);
If your handlebars templates are simple, with only string replacement, you can do this with underscore.js. Assume this example is named 'generate.js'
var fs = require('fs');
var _ = require('underscore');
_.templateSettings.interpolate = /\{\{(.+?)\}\}/g;
function render(filename, data)
{
var source = fs.readFileSync(filename,'utf8').toString();
var compiled = _.template(source);
return compiled(data);
}
var data = JSON.parse(fs.readFileSync("./data/strings.json", 'utf8'));
var result = render('./templates/somefile.html', data);
console.log(result);
Then run node generate.js to output the rendered template to the console. You may need to do npm install --save underscore prior.

nodejs decoded string from buffer returns multi line string

I have a buffer which I want to decode so that I can treat it as an object.
I have the below setup
var StringDecoder = require('string_decoder').StringDecoder;
var req = http.request(reqOptions, function(res) {
...
var decoder = new StringDecoder('utf8');
res.on('data', function(chunk) {
var textChunk = decoder.write(chunk);
console.log(textChunk);
});
});
textChunk returns - note how this is on multiple lines.
{"aaa":true,"bbb":true, "cc
c":true, "ddd":true, "eee":true, "f
ff":true, "ggg":true}
so when I try and convert it to an object
JSON.parse(textChunk)
I get Unexpected end of input
How can I return my string as one line so that I can then treat it as an object.
{"aaa":true,"bbb":true, "ccc":true, "ddd":true, "eee":true, "fff":true, "ggg":true}
You need to create a string variable to concatenate all the chunks in, and then use your decoder at the end. Something like this:
var text = '';
res.on('data', function(chunk) {
text += chunk;
});
res.on('end', function() {
var decoder = new StringDecoder('utf8');
var result = decoder.write(text);
// Do something with the result
});
Try request.js
npm install request -g --save
And pipe the result instead, MUCH simpler trust me
The whole http.request / response lines would be replaced by
res.pipe(url) with the result being a nice object returned to parse or consume by a service, factory, or service

How to inflate part of string

While building a NNTP client in NodeJS, I have encountered the following problem. When calling the XZVER command, the first data I receive from the socket connection contains both a string and an inflated string;
224 compressed data follows (zlib version 1.2.3.3)
^*�u�#����`*�Ti���d���x�;i�R��ɵC���eT�����U'�|/S�0���� rd�
z�t]2���t�bb�3ѥ��>�͊0�ܵ��b&b����<1/ �C�<[<��d���:��VW̡��gBBim�$p#I>5�cZ�*ψ%��u}i�k�j
�u�t���8�K��`>��im
When I split this string and try to inflate it like this;
lines = chunk.toString().split('\r\n');
response = lines.shift();
zlib.inflate(new Buffer(lines.shift()), function (error, data) {
console.log(arguments);
callback();
});
I receive the following error;
[Error: invalid code lengths set] errno: -3, code: 'Z_DATA_ERROR'
Any help is welcome, I am kinda stuck here :(
UPDATE
After implementing the answer of mscdex, the whole function looks like this;
var util = require('util'),
zlib = require('zlib'),
Transform = require('stream').Transform;
function CompressedStream () {
var self = this;
this._transform = function (chunk, encoding, callback) {
var response = chunk.toString(),
crlfidx = response.indexOf('\r\n');
response = response.substring(0, crlfidx);
console.log(response);
zlib.gunzip(chunk.slice(crlfidx + 2), function (error, data) {
console.log(arguments);
callback();
});
};
Transform.call(this/*, { objectMode: true } */);
};
util.inherits(CompressedStream, Transform);
module.exports = CompressedStream;
You should probably avoid using split() in case those two bytes are in the raw data. You might try something like this instead:
var response = chunk.toString(),
crlfidx = response.indexOf('\r\n');
// should probably check crlfidx > -1 here ...
response = response.substring(0, crlfidx);
zlib.inflate(chunk.slice(crlfidx + 2), function (error, data) {
console.log(arguments);
callback();
});
However if you're doing this inside a 'data' event handler, you should be aware that you may not get the data you expect in a single chunk. Specifically you could get a CRLF split between chunks or you could get multiple responses in a single chunk.
It seems that my chunks were incorrectly encoded. By removing socket.setEncoding('utf8');, the problem was solved.

How to change emscripten browser input method from window.prompt to something more sensible?

I have a C++ function which once called consumes input from stdin. Exporting this function to javascript using emscripten causes calls to window.prompt.
Interacting with browser prompt is really tedious task. First of all you can paste only one line at time. Secondly the only way to indicate EOF is by pressing 'cancel'. Last but not least the only way (in case of my function) to make it stop asking user for input by window.prompt is by checking the checkbox preventing more prompts to pop up.
For me the best input method would be reading some blob. I know I can hack library.js but I see some problems:
Reading blob is asynchronous.
To read a blob, first you have to open a file user has to select first.
I don't really know how to prevent my function from reading this blob forever - there is no checkbox like with window.prompt and I'm not sure if spotting EOF will stop it if it didn't in window.prompt case (only checking a checkbox helped).
The best solution would be some kind of callback but I would like to see sime hints from more experienced users.
A way would be to use the Emscripten Filesystem API, for example by calling FS.init in the Module preRun function, passing a custom function as the standard input.
var Module = {
preRun: function() {
function stdin() {
// Return ASCII code of character, or null if no input
}
var stdout = null; // Keep as default
var stderr = null; // Keep as default
FS.init(stdin, stdout, stderr);
}
};
The function is quite low-level: is must deal with one character at a time. To read some data from a blob, you could do something like:
var data = new Int8Array([1,2,3,4,5]);
var blob = new Blob([array], {type: 'application/octet-binary'});
var reader = new FileReader();
var result;
reader.addEventListener("loadend", function() {
result = new Int8Array(reader.result);
});
var i = 0;
var Module = {
preRun: function() {
function stdin() {
if (if < result.byteLength {
var code = result[i];
++i;
return code;
} else {
return null;
}
}
var stdout = null; // Keep as default
var stderr = null; // Keep as default
FS.init(stdin, stdout, stderr);
}
};
Note (as you have hinted), due to the asynchronous nature of the reader, there could be a race condition: the reader must have loaded before you can expect the data at the standard input. You might need to implement some mechanism to avoid this in a real case. Depending on your exact requirements, you could make it so the Emscripten program doesn't actually call main() until you have the data:
var fileRead = false;
var initialised = false;
var result;
var array = new Int8Array([1,2,3,4,5]);
var blob = new Blob([array], {type: 'application/octet-binary'});
var reader = new FileReader();
reader.addEventListener("loadend", function() {
result = new Int8Array(reader.result);
fileRead = true;
runIfCan();
});
reader.readAsArrayBuffer(blob);
var i = 0;
var Module = {
preRun: function() {
function stdin() {
if (i < result.byteLength)
{
var code = result[i];
++i;
return code;
} else{
return null;
}
}
var stdout = null;
var stderr = null;
FS.init(stdin, stdout, stderr);
initialised = true;
runIfCan();
},
noInitialRun: true
};
function runIfCan() {
if (fileRead && initialised) {
// Module.run() doesn't seem to work here
Module.callMain();
}
}
Note: this is a version of my answer at Providing stdin to an emscripten HTML program? , but with focus on the standard input, and adding parts about passing data from a Blob.
From what I understand you could try the following:
Implement selecting a file in Javascript and access it via Javascript Blob interface.
Allocate some memory in Emscripten
var buf = Module._malloc( blob.size );
Write the content of your Blob into the returned memory location from Javascript.
Module.HEAPU8.set( new Uint8Array(blob), buf );
Pass that memory location to a second Emscripten compiled function, which then processes the file content and
Deallocate allocated memory.
Module._free( buf );
Best to read the wiki first.

Categories

Resources