Reading Files Asynchronously on Meteor - asynchronous

I am very new to Meteor so if my apologies if this is a dumb question:
How do you read a file from the filesystem on the server side, and get those contents displayed on the client side. Doing it synchonously isn't a problem because I can return it right away but as I understand from reading that defeats the purpose of Meteor so I am trying to do it asynchronously. However I dont know how to connect the client/server in this fashion. Publish/Subscribe seem to only be for db collections and not filesystem files. Any guidance you can give me would be very much appreciated.
if (Meteor.isClient) {
Template.body.helpers({
myfile: function(){
return file_contents;
}
});
}
if (Meteor.isServer) {
Meteor.methods({
myfile: function(){
var fs = Npm.require("fs");
fs.readFile('/opt/SI/SIAgent/conf/myfile','utf-8', function (err, data) {
file_contents = data;
});
}
});
}

You have to use Meteor.wrapAsync to turn Node.JS fs.readFile into synchronous-looking code so that you can return the result within the Meteor method.
var fs=Npm.require("fs");
fsReadFileSync=Meteor.wrapAsync(fs.readFile,fs);
Meteor.methods({
getMyFileContent:function(){
return fsReadFileSync("/opt/SI/SIAgent/conf/myfile","utf-8");
}
});

Related

Meteor reading data from a file stored on server

I want to show some data points on the client side, and I want to get the latitude, longitude and summary from a file stored on the server.
I have read a lot of posts saying to use papaParse using Meteor methods but I am not able to make it work.
Can you guys point me to right direction, my questions are:
In which folder to should I store a .txt, .csv or .json file in Meteor?
How to access it from the client and return the read data to client for display.
You can put your static files into private folder on the server and get them through Assets.
For exmaple, you have a data.json file in your private folder.
Method to get this data:
Meteor.methods({
getData() {
return JSON.parse(Assets.getText('data.json'));
}
});
You can now call this method on the client:
Meteor.call('getData', function(err, res) {
console.log(res);
});
UPD
Ok, how to display it.
Meteor.call runs async, so we will use reactivity to update our view on result.
Here is how we can display data on ourData template.
<template name="ourData">
<!-- Here you may want to use #each or whatever -->
<p>{{ourData}}</p>
</template>
Template.ourData.onCreated(function() {
this.ourData = new ReactiveVar();
Meteor.call('getData', (err, res) => {
if (err) {
console.error(err);
} else {
// Putting data in reactive var
this.ourData.set(res);
}
});
});
Template.ourData.helpers({
ourData: function() {
// Helper will automatically rerun on method res
return Template.instance().ourData.get();
}
});
reactive-var package required or you can also use Session.

Meteor: How to count some data in collection?

I've a problem to count some data in a collection. This code is not working for me. Can anyone help me?
Client code:
Template.count_status.helpers({
countcategory: function(){
return Profil.find({
status: 'Available',
category: { $in: ['PTR', 'KOM'] },
}).count();
}
});
Server Code:
Meteor.methods({
countcategory: function () {
return Profil.find().count();
}
});
Make sure you have subscribed to "profil" collection(client side). And it is published in the server code. I presume that you are not using pub/sub functions.
Make sure autopublish package is added to your package (for client side code mentioned to work).
You are not using Server code pasted above.
You can call server method using below code:
Template.count_status.helpers({
countcategory: function(){
Meteor.call('countcategory', function(err,res){
if(err){
//do something with err
}
else{
//do something with result
}
});
}
});

Meteor Server Latency

I feel like I'm doing something wrong because my results seem to go against the very nature of Meteor's pitch of simulating client/sever interactions for speed. When I do any sort of database update using Meteor.call() the app has to wait for the round trip to the server, often resulting in a slow response or the user hitting the button twice. I just want to make sure I'm doing this correctly. Here's what I'm doing:
Client:
Template.shot.events({
'change #shot-status-select': function (event, template) {
var new_status = $(event.target).val();
var shot_id = Session.get('current_shot_id');
Meteor.call('setShotStatus', shot_id, new_status, function (error, result) {
if (result) {
feedbackSuccess('Status changed to <b>'+new_status+'</b>');
} else {
feedbackError('Status change failed');
console.log(error);
}
});
},
});
And Server:
...
'setShotStatus': function(shot_id, status) {
var result = Shots.update({'_id': shot_id}, {$set: {'status': status}});
if (result) {
return true;
} else {
return false;
}
},
There are a couple of things going on here that are preventing your method from being latency compensated (it's making the complete round trip to the server).
First, if you execute a Meteor.call on the client with a callback, it will always wait for the result from the server. Unfortunately, you can't just write it synchronously because a call will always return undefined on the client, and you need the returned result.
If you really want the result of the stub, you'd need to rewrite it like this:
var args = [shot_id, new_status];
var result = Meteor.apply('setShotStatus', args, {returnStubValue: true});
if (result)
feedbackSuccess('Status changed to <b>'+new_status+'</b>');
Note you should wrap the call in a try/catch if errors are likely. Also note that the client and server return values will not always match in the general case, so use this technique with that in mind.
Next, your method definition needs to be in a shared location for both the client and the server code (putting it somewhere under lib or in a package are good choices). If the client doesn't have the method code, it can't simulate it.
Recommended reading:
How to return value on Meteor.call() in client?
Introduction to Latency Compensation
The "Latency Compensation" articles at the Discover Meteor Encyclopedia
Thank you, David. Your answer got me on the right track, but I think there are a couple of nuggets in there that seemed too much to discuss in a comment. The main thing I found was this:
The challenge of getting back to the "Meteor zero-latency" promise was as simple as moving all of my "server" methods to the lib directory.
Literally, no code changes. After making the methods accessible to both client and server, Meteor did all of the heavy lifting of executing first on the client, then checking the result with the server result.
David's answer said that using a callback will always wait for a result from the server. I found that to be partly true, in that it will asynchronously wait for a result. Though depending on the accessibility of your methods, it could be a result from the client that you experience, not a round-trip from the server. Not using the callback will always return undefined, thus result will not work in the given example
Lastly, I moved truly private logic to the server only directory for security reasons.
Here's the code result:
client/shot.js
Template.shot.events({
'change #shot-status-select': function (event, template) {
var new_status = $(event.target).val();
var shot_id = Session.get('current_shot_id');
Meteor.call('setShotStatus', shot_id, new_status, function (error, result) {
if (!(result)) {
feedbackError('Status change failed');
console.log(error);
}
});
},
});
lib/methods.js
Meteor.methods({
'setShotStatus': function(shot_id, status) {
var result = Shots.update({'_id': shot_id}, {$set: {'status': status}});
if (result) {
return true;
} else {
return false;
}
},
});

Can I publish only Collections object in meteor?

On the introducing article of DDP, I read that anything can be published, but I've read somewhere ( for example in this Stackoverflow comment Publish arbitrary data and automatically update HTML ) that only Collections can be published.
So where is the truth? If we can publish other things than Collections, I would liek to see an example as I can't find one so far.
From the docs: http://docs.meteor.com/#meteor_publish
Publish functions can return a Collection.Cursor, in which case Meteor will publish that cursor's documents. You can also return an array of Collection.Cursors, in which case Meteor will publish all of the cursors.
So at the moment you can only return a Collection via a cursor (result of a Collection.find()).
To return other data you need to hack into the sockjs stream (the socket library meteor uses to communicate to the server). Be aware this does not guarantee compatibility with future versions of meteor. Sockjs is the library used for meteor to communicate between the server (the wire)
from Publish arbitrary data and automatically update HTML*
client side js
sc = new Meteor._Stream('/sockjs');
sc.on('message', function(payload) {
var msg = JSON.parse(payload);
Session.set('a_random_message', JSON.stringify(msg.data));
});
Template.hello.greeting = function () {
return Session.get('a_random_message');
};
server side js
ss = new Meteor._StreamServer();
ss.register(function (socket) {
var data = {socket: socket.id, connected: new Date()}
var msg = {msg: 'data', data: data};
// Send message to all sockets (which will be set in the Session a_random_message of the client
_.each(ss.all_sockets(), function(socket) {
socket.send(JSON.stringify(msg));
});
});
You can also look at Meteor Streams too. See below.
assume you have added meteor streams via atmosphere - mrt add streams
sc = new Meteor.Stream('hello');
if(Meteor.isServer) {
Meteor.setInterval(function() {
sc.emit('a_random_message', 'Random Message: ' + Random.id());
}, 2000);
Meteor.permissions.read(function() { return true });
}
if(Meteor.isClient) {
sc.on('a_random_message', function(message) {
Session.set('a_random_message', message);
});
Template.hello.greeting = function () {
return Session.get('a_random_message');
};
}

dynamically add files to the public folder and getting the refreshed results in meteor

I'm playing around with the idea of using Meteor as a backend for creating a static html generator (with an admin UI).
I want that, when triggered, a new file will be created on the public folder and instantly reviewed by the author on an iframe embed in the html.
The file gets created but two side effects happen:
The server is restarted.
The file is cached - so the user can't see that the change took place.
Any ideas?
if (Meteor.is_client) {
Template.hello.events = {
'click input' : function () {
Meteor.call('makeFile', 'filename.html', function(error, result){
alert(result);
});
//window.location = '/filename.txt';
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
};
}
if (Meteor.is_server) {
var fs = __meteor_bootstrap__.require('fs');
Meteor.startup(function () {
// code to run on server at startup
});
Meteor.methods({
'makeFile': function(fileName) {
/*
fs.unlink("public/"+fileName, function (err) {
if (err) throw err;
console.log('successfully deleted ');
});
*/
fs.writeFile("public/"+fileName, "<html><body><h1>test</h1></body></html>", function(err) {
if(err) {
console.log(err);
} else {
console.log("The file was saved! "+ fileName);
}
});
return fileName;
}
});
}
I think the meteor server restarts whenever you change any file in a meteor subdirectory. So to prevent this don't write your files within the meteor app directory, make a created_files directory one level up or something. This is a good idea anyway, to separate the generated files from the code that's generating them.
Easiest way to show the content in the admin interface is probably to write it to the database (as opposed to trying to watch for directory changes). No sense re-inventing the wheel, since Meteor is designed to watch for db updates.
Create a collection (at the top, so it's on client & server):
GeneratedFiles = new Meteor.Collection("generated_files");
Then assign the file contents to a variable before writing them and in your fs.writeFile callback:
if(err) {
console.log(err);
} else {
GeneratedFiles.insert({name: fileName, contents: fileContents});
console.log("The file was saved! "+ fileName);
}
And then auto-publish the file contents to template in the client code (not sure why you want to use an iframe, seems like a div would work fine. But either way would probably work).
Hope that helps. And good luck, I think meteor is a great framework for this kind of thing. Most of the admin interfaces I use are still entirely synchronous and just annoying to work with.

Resources