module.exports won't export object with function - javascript

I've made a ModulesManager to manage all my modules, so I don't have a huge list of requirements in the top of my "server.js", but every time I'm trying to access the manager's methods, my server crash and throw an error which is saying that my manager method isn't a function.
TypeError: moduleManager.sqlString is not a function
I'm not really sure what I'm doing wrong here, this might be a stupid error, I've tried to look online for an answer but everyone is saying something different and nothing work.
Modules Manager (manager.js):
const sqlSetup = require("./sqlSetup.js");
const sqlSafeString = require("./sqlString.js");
function Manager(){
this.sqlString = function(query){
return sqlSafeString.getSqlSafeString(query);
},
this.sql = function(){
return sqlSetup;
}
}
module.exports = Manager;
Module SQL (sqlSetup.js):
const SqlString = require('sqlstring');
function getSqlSafeString(query){
//format query to fit in MySQL
var format = SqlString.escape(query);
return format;
}
module.exports = getSqlSafeString;
This is a test for my Travis build that I'm trying to make, the module manager path is good, the problem is really in the "ModuleManager.js" which I don't understand...
require('dotenv').config();
const Discord = require("discord.js");
const client = new Discord.Client();
const token = process.env.MOISTY;
const moduleManager = require("../modules/manager.js");
const assert = require("assert");
console.log("MAKE SURE MOISTY IS OFFLINE!");
client.login(token);
client.on('ready', () => {
//confirm login
console.log("MOISTY:200");
//Assert
assert.deepEqual(moduleManager.sqlString("sample text"), "sample test");
//terminate the session
process.exit();
});
I'm not very used to module exports, so this might be an easy question...

There are multiple mistakes in your code.
module.exports = getSqlSafeString; sets the export to getSqlSafeString.
When you do require("./sqlString.js") you will get the value that was assigned to the exports so in your case the getSqlSafeString.
So if you want to access that function you would write:
const getSqlSafeString = require("./sqlString.js");
//...
return getSqlSafeString(query);`
module.exports = Manager; exports the function Manager but not an object of the type Manager, so moduleManager.sqlString would be equal to Manager.sqlString. If you only want to group the functions in one object, then using a constructor would not make much sense here anyway, so you should write it that way:
module.exports = {
sqlString : function(query){
return sqlSafeString.getSqlSafeString(query)
},
sql : function(){
return sqlSetup
}
};
If you really want to do create on Object of the type Manager then you need to write:
module.exports = new Manager

For you to be able to require something from module A at module B, you have to export that something at module A. You export it assigning it to module.exports.
Yout problem is you didn't export the Manager at the Manager module.
You could simple export Manager, but, because you seem to be using an instance of Manager:
const moduleManager = require("../modules/manager.js");
...
moduleManager.sqlString("sample text")
Then you must export an instance. Add to manager.js:
module.exports = new Manager();

You are putting the functions on an instance, ie this.sqlString is a property named sqlString on an instance of Manager. But you are not using Manager as a constructor, ie not doing new Manager() or in your case new moduleManager()
You can change your import code to like below
const moduleManager = new require("../modules/manager.js")();
Or change the export to:
module.exports = new Manager();
Then moduleManager will be an instance of Manager and will be able to use moduleManager.sqlString

Related

Editing an XML document

I am new to JavaScript and need the ability to create, edit and export an XML document on the server side. I have seen different options on the Internet, but they do not suit me.
It seems that I found one suitable option with processing my XML file into JSON, and then back and then export it through another plugin, but maybe there is some way to make it easier?
Thanks!
I recently came across a similar problem. The solution turned out to be very simple. It is to use XML-Writer
In your project folder, first install it via the console
npm install xml-writer
Next, first import it and create a new file to parse what's going on here:
var XMLWriter = require ('xml-writer');
xw = new XMLWriter;
xw.startDocument ();
xw.startElement ('root');
xw.writeAttribute ('foo', 'value');
xw.text ('Some content');
xw.endDocument ();
console.log (xw.toString ());
You can find more information here and at the bottom of the page see the different code for each item. In this way, you can create, edit and export xml files. Good luck and if something is not clear, write!
Additional
You will need also fs module
const fs = require("fs")
const xmlParser = require("xml2json")
const formatXml = require("xml-formatter")
Completed code:
const fs = require("fs")
const xmlParser = require("xml2json")
const formatXml = require("xml-formatter")
var XMLWriter = require('xml-writer');
xw = new XMLWriter;
xw.startDocument();
xw.startElement('root');
xw.startElement('man');
xw.writeElement('name', 'Sergio');
xw.writeElement('adult', 'no');
xw.endElement();
xw.startElement('item');
xw.writeElement('name', 'phone');
xw.writeElement('price', '305.77');
xw.endElement();
xw.endDocument();
const stringifiedXmlObj = JSON.stringify(xmlObj)
const finalXml = xmlParser.toXml(stringifiedXmlObj)
fs.writeFile("./datax.xml", formatXml(finalXml, { collapseContent: true }), function (err, result) {
if (err) {
console.log("Error")
} else {
console.log("Xml file successfully updated.")
}
})
})

Can't save/create files using Store.js

So I wanted to save a file on the client storage using Store.js.
I can change the date using store.set and i can log it to console to see the change, but then it's supposed to be saved in app data where it's not created.
I tried to get the Path where it's being saved and it's :
C:\Users\USER\AppData\Roaming\stoma2/Categories.json
I noticed that there is a "/" so I tried :
C:\Users\USER\AppData\Roaming\stoma2\Categories.json
and :
C:/Users/USER/AppData/Roaming/stoma2/Categories.json
But all 3 of them didn't work.
This is my Store.js :
const fs = require('browserify-fs');
var fs2 = require('filereader'),Fs2 = new fs2();
const electron = window.require('electron');
const path = require('path');
class Store {
constructor(opts) {
// Renderer process has to get `app` module via `remote`, whereas the main process can get it directly
// app.getPath('userData') will return a string of the user's app data directory path.
//const userDataPath = (electron.app || electron.remote.app).getPath('userData');
var userDataPath = (electron.app || electron.remote.app).getPath('userData');
for(var i=0;i<userDataPath.length;i++){
if(userDataPath.charAt(i)=="\\"){
userDataPath = userDataPath.replace("\\","/");
}
}
// We'll use the `configName` property to set the file name and path.join to bring it all together as a string
this.path = path.join(userDataPath, opts.configName + '.json');
this.data = parseDataFile(this.path, opts.defaults);
console.log(this.path);
}
// This will just return the property on the `data` object
get(key) {
return this.data[key];
}
// ...and this will set it
set(key, val) {
this.data[key] = val;
// Wait, I thought using the node.js' synchronous APIs was bad form?
// We're not writing a server so there's not nearly the same IO demand on the process
// Also if we used an async API and our app was quit before the asynchronous write had a chance to complete,
// we might lose that data. Note that in a real app, we would try/catch this.
fs.writeFile(this.path, JSON.stringify(this.data));
}
}
function parseDataFile(filePath, data) {
// We'll try/catch it in case the file doesn't exist yet, which will be the case on the first application run.
// `fs.readFileSync` will return a JSON string which we then parse into a Javascript object
try {
return JSON.parse(Fs2.readAsDataURL(new File(filePath)));
} catch(error) {
// if there was some kind of error, return the passed in defaults instead.
return data;
}
}
// expose the class
export default Store;
There might be a probleme fith js.writeFile() (well that's the source of probleme).
and this is my call :
//creation
const storeDefCat = new Store({
configName: "Categories",
defaults: require("../data/DefaultCategorie.json")
})
//call for the save
storeDefCat.set('Pizza',{id:0,path:storeDefCat.get('Pizza').path});
For now if possible,I might need to find another way to save the file.
And i tried : fs : It doesn't work for me for some reason (I get strange errors that they don't want to be fixed..) .
If anyone has an Idea then please I would be grateful.
So I managed to fix the probleme, Why fs was sending me errors about undefined functions?Why file wasn't getting created ? It has NOTHING to do with the code it self, but the imports...
To clearify, I was using :
const fs = require('fs');
And the solution is to make it like :
const fs = window.require('fs');
Just adding window. fixed all the problems .Since it's my first time using electron I wasn't used to import from the window but it seems it's necessary.And more over...There was no posts saying this is the fix.

Why is a variable assigned 'this' in module.export function in javascript

I am trying to understand the following code taken from a service created in a feathersjs app.
// Initializes the `users` service on path `/users`
const createService = require('feathers-knex');
const createModel = require('../../models/users.model');
const hooks = require('./users.hooks');
const filters = require('./users.filters');
module.exports = function () {
const app = this;
const Model = createModel(app);
const paginate = app.get('paginate');
const options = {
name: 'users',
Model,
paginate
};
// Initialize our service with any options it requires
app.use('/users', createService(options));
// Get our initialized service so that we can register hooks and filters
const service = app.service('users');
service.hooks(hooks);
if (service.filter) {
service.filter(filters);
}
};
This file is then imported as follows:
// many requires..
const feathers = require('feathers');
// more requires
const services = require('./services');
// other requires
const app = feathers();
Can someone explain as to what does the line
const app = this
do in the code that is creating the service?
It assigns the value of this to a variable (well, a constant) with a name that more clearly describes the value than this does. It is designed to make the code easier for maintainers to understand.

How to mock a function using rewirejs and chai-spies in order to test it?

tl;dr
I am trying to test an express app using mocha, chai, chai-spies and rewire.
In particular, what I am trying to do is to mock a function that exists in a module and use a chai spy instead.
My set-up
I have a module called db.js which exports a saveUser() method
db.js
module.exports.saveUser = (user) => {
// saves user to database
};
The db module is required by app.js module
app.js
const db = require('./db');
module.exports.handleSignUp = (email, password) => {
// create user object
let user = {
email: email,
password: password
};
// save user to database
db.saveUser(user); // <-- I want want to mock this in my test !!
};
Finally in my test file app.test.js I have the following
app.test.js
const chai = require('chai')
, spies = require('chai-spies')
, rewire = require('rewire');
chai.use(spies);
const expect = chai.expect;
// Mock the db.saveUser method within app.js
let app = rewire('./app');
let dbMock = {
saveUser: chai.spy()
};
app.__set__('db', dbMock);
// Perform the test
it('should call saveUser', () => {
let email = 'someone#example.com'
, password = '123456';
// run the method we want to test
app.handleSignUp(email, password);
// assert that the spy is called
expect(dbMock.saveUser).to.be.spy; // <--- this test passes
expect(dbMock.saveUser).to.have.been.called(); // <--- this test fails
});
My problem
My problem is that my test for ensuring that the spy is called by app.handleSignUp fails as follows
AssertionError: expected { Spy } to have been called at Context.it (spies/app.test.js:25:40)
I sense that I am doing something wrong but I am stuck at the moment. Any help is appreciated, thank you
Finally, I figured out what the problem was. From rewire github page:
Limitations
Using const It's not possible to rewire const (see #79). This can
probably be solved with proxies someday but requires further research.
So, changing const db = require('./db'); to let db = require('./db'); in app.js made all test pass.
A better solution
However, since changing all const declarations to let in order to test an application with spies is a cumbersome, the following approach seems to be better:
We can require our db module in app.js as a const as we did, but instead of creating the spy and overwriting the const variable:
let dbMock = {
saveUser: chai.spy()
};
app.__set__('db', dbMock);
we may use rewire's getter method to import the db module in our app.test.js file, and then mock the saveUser() method using our spy (that is to mutate one of the const variable's properties; since objects in JS are passed by reference, getting and mutating the db object within app.test.js module is also mutates the same object within app.js module)
const db = app.__get__('db');
db.saveUser = chai.spy()
Finally, we can expect that the mutated db.saveUser (i.e our spy) will be called
expect(db.saveUser).to.have.been.called();
To sum up, both the db.js and app.js will not be changed, but the test file should now looks like the following:
const chai = require('chai')
, spies = require('chai-spies')
, rewire = require('rewire');
chai.use(spies);
let expect = chai.expect;
// Fetch the app object
let app = rewire('./app');
// Use the getter to read the const db object and mutate its saveUser property
const db = app.__get__('db');
db.saveUser = chai.spy()
// Finally perform the test using the mocked
it('should call saveUser', () => {
let email = 'someone#example.com'
, password = '123456';
// run the method we want to test
app.handleSignUp(email, password);
expect(db.saveUser).to.have.been.called(); // <--- now passes!!!
});

Common logging for node, express application -- best practice?

I'm working on an node.js application with several dozen modules and using bunyan for logging (JSON output, multiple configurable streams). I've been looking for good examples of how to implement a instance across all the modules, but haven't seen what appears to be a really clean example I can learn from.
Below illustrates an approach that works, but seems quite inelegant (ugly) to me. I'm new to node & commonjs javascript in general, so looking for recommendations on how to improve it.
module: ./lib/logger
// load config file (would like this to be passed in to the constructor)
nconf.file({ file: fileConfig});
var logSetting = nconf.get('log');
// instantiate the logger
var Bunyan = require('bunyan');
var log = new Bunyan({
name: logSetting.name,
streams : [
{ stream : process.stdout,
level : logSetting.stdoutLevel},
{ path : logSetting.logfile,
level : logSetting.logfileLevel}
],
serializers : Bunyan.stdSerializers
});
function Logger() {
};
Logger.prototype.info = function info(e) { log.info(e) };
Logger.prototype.debug = function debug(e) { log.debug(e) };
Logger.prototype.trace = function trace(e) { log.trace(e) };
Logger.prototype.error = function error(e) { log.error(e) };
Logger.prototype.warn = function warn(e) { log.warn(e) };
module.exports = Logger;
module: main app
// create the logger
var logger = require('./lib/logger)
var log = new logger();
// note: would like to pass in options --> new logger(options)
module: any project module using logger
// open the logger (new, rely on singleton...)
var logger = require('./lib/logger');
var log = new logger();
or view the gist
any recommendations?
EDIT:
I've modified the constructor, making the singleton pattern explicit (rather than implicit as part of the 'require' behaviour.
var log = null;
function Logger(option) {
// make the singleton pattern explicit
if (!Logger.log) {
Logger.log = this;
}
return Logger.log;
};
and then changed the initialization to take an options parameter
// initialize the logger
Logger.prototype.init = function init(options) {
log = new Bunyan({
name: options.name,
streams : [
{ stream : process.stdout,
level : options.stdoutLevel},
{ path : options.logfile,
level : options.logfileLevel}
],
serializers : Bunyan.stdSerializers
});
};
Singleton pattern in nodejs - is it needed?
Actually, singleton is perhaps not needed in Node's environment. All you need to do is to create a logger in a separate file say, logger.js:
var bunyan = require("bunyan"); // Bunyan dependency
var logger = bunyan.createLogger({name: "myLogger"});
module.exports = logger;
Then, retrieve this logger from another module:
var logger = require("./logger");
logger.info("Anything you like");
if you are using express with node.js then you can try this.
By default, logging is disabled in Express. You have to do certain stuff to get logs working for your app. For access logs, we need to use the Logger middleware; for error logs we will use Forever.Hope it will help you..
Here is a good example How to Logging Access and Errors in node.js

Categories