Let's say that I have two files named dbPerson.js and dbCar.js both with their own methods like:
dbPerson.js:
module.exports = {
getAll(callback){}
}
dbCar.js:
module.exports = {
getOne(callback){}
}
And then I have another file named db.js that would import those files:
const dbPerson=require('./dbPerson');
const dbCar=require('./dbCar');
And ultimately I have a main file which imports db.js:
const db=require('./db');
I want to be able to call the methods by using db.getAll() and db.getOne(), but this way I would need to use db.dbPerson.getAll() and db.dbCar.getOne(), is there a way so I don't need to do that?
UPDATE: I found that the answer below helps me but thanks for everyone who tried to understand what I meant, next time I'm gonna try to be more clear!
There are few ways to do it. Which path you take should depend on your requirements.
First let's assume you want to merge these two objects(dbCar and dbPerson). Then you can do it simply using the object spread syntax.
// db.js
module.exports = {
...dbPerson,
...dbCar,
}
Note that doing this way, conflicting properties of dbPerson will be overridden by the properties of dbCar. And by the naming, it does not make sense to merge these two objects (What would you call something that is a Person and also a Car?).
Another approach is to use a proxy object to forward the calls.
// db.js
module.exports = {
getAll: dbPerson.getAll,
getOne: dbCar.getOne,
}
You would need need to export those functions from your db.js file too:
//db.js
const dbPerson=require('./dbPerson');
const dbCar=require('./dbCar');
module.exports = {
getAll: dbPerson.getAll,
getOne: dbCar.getOne
}
You say:
I want to be able to call the methods by using db.getAll() and db.getOne(), but this way I would need to use db.dbPerson.getAll() and db.dbCar.getOne(), is there a way so I don't need to do that?
Is there a reason that this would be a problem?
As far as I can see - you are talking about two different things, cars and people.
Doing db.dbCar.getOne() makes sense to me, if I did db.getOne(), I wouldn't be sure whether I'm getting one car or one person.
You could just rename the methods in your export, like:
module.exports {
getAllPersons: dbPerson.getAll,
getOneCar: dbCar.getOne,
}
Related
I am trying to require in some classes in Node, but I require them in through a single file which holds an object of all the individual requires. Like so -
File: /classes/controller.js
module.exports = class Controller {}
File: /classes/model.js
module.exports = class Model {}
File: /classes/classes.js
module.exports = {
Controller: require('./controller.js'),
Model: require('./model.js')
}
Then in app.js
const classes = require('./classes/classes.js');
....code.....
....more code.....
const someController = new classes['Controller'];
The thing is, this works just fine - but only for the first file that requires in the 'classes' variable. Any subsequent files that need to also require 'classes' in, ends up holding only an empty object inside of the 'classes' variable (and no, the actual variable name does not matter, as I tried using a different variable name in the second file but it still wound up storing an empty object). For instance, I often need my Controller to implement a Model class.
So once app.js calls controller.js, inside of that -
File: /classes/controller.js
const classes = require('./classes.js');
module.exports = class Controller {
constructor(var) {}
someMethod() {
....code....
const someModel = classes['Model'];
}
}
I can call a 'console.log(classes)', and it will print out '{}', but again this is only if it is done inside of 'controller.js' and only after being called from inside of 'app.js'. It will print out all of the classes as expected if the 'console.log()' is done either inside of 'app.js', or if 'controller.js' is called directly instead of through 'app.js'. Which is how I know where/how the error is occurring. The specific error being thrown is -
classes.Model is not a constructor
So that is where my question/problem comes in, because as I said I have called other objects in multiple files without ever running into this problem. So it must have something to do with how classes are implemented, correct? And although I do not understand the specifics of how it works, I roughly know that when a file is required in, in Node, after that it uses that same reference for any subsequent files - correct? So I was thinking it must somehow be the interplay of those two things, but I have no idea after that fact nor if I am even correct in assuming that is the problem.
You have a circular dependency of require() statements. This causes one of the require() to just return an empty object. You can't do that.
Inside of controller.js, you have require('./classes.js');.
Inside of classes.js, you have require('./controller.js').
So, each refers to the other which is what causes the circular dependency.
You will need to restructure the way code is laid out into files so you don't have this. Sometimes, the simplest fix is to just combine a couple functions into one file rather than having them all in separate files that are vulnerable to this issue. I don't quite understand your overall design to know what best to suggest in this case.
If nobody else besides controller.js is really going to use classes.js, then you can just move that code into controller.js (where you don't need to require in controller.js any more).
I am trying to set a global variable (NOT A GLOBAL ENV VARIABLE) in Cypress.io. I would like to abstract a url that I will use over and over again AND something that I can use in multiple files (not just one).
Also, I do not want to set it as baseurl. I already have that set and I want to leave that alone.
Can anyone help me with this?
this can be done more easily than that :
in your cypress.json add
"env": {
"YourVarName":"YourVarValue"
}
And in your code you can access to your var with :
const myGolabalVar = Cypress.env("YourVarName")
That's all you need
I recently found the answer in another blog on github linked here: https://github.com/cypress-io/cypress/issues/1121
But the answer in this blog was answered my Brian Mann with...
"TL;DR - just use modules, not globals.
Cypress is just JavaScript. All of the underlying principles about structuring files apply to Cypress as it would your own application files.
In this case, you keep mentioning variables. Variables are things defined in a particular file and are never global. This means it's not possible for them to be shared. Variables are accessible based on the local scope in which they are defined.
In order to make them global you have to attach them to a global object: window. However, there's no reason to do this, Cypress automatically has module support built in. This enables you to import functions into each spec file, thus making them way more organized and obvious than using globals.
We have recipes of this here: https://docs.cypress.io/examples/examples/recipes.html#Node-Modules"
I hope this helps someone else who is on Stackoverflow for this answer!
You can use the cypress.json file and set enviornment values there. If you do not want to use .env values, then you create any constants file you like and import it in to via the cypress index.js file.
Create a constants file,
e.g., cypress/constants.js
with a json object inside:
export const constants = {
VALUEA: 'my text for value A',
VALUEB: 'example value b'
}
in support/index.js
add a reference to your constants.js file:
import '../constants';
This will automatically import the constants dictionary into all of your spec files.
All you have to do to reference your constants in your spec files, is to use "constants.VALUEA"
for example:
cy.get('#button-label-id).should('contain.text', constants.VALUEB);
You can create a variable and a task in plugins/index.ts
// global variable
let counter = 0;
// you can then write a task to fetch this value
// For more info, visit https://on.cypress.io/plugins-api
module.exports = (on, config) => {
require("#cypress/code-coverage/task")(on, config);
// custom tasks
on("task", {
getCounter() {
counter++;
console.log("new counter value: ", counter);
return counter;
}
};
// reset this value before every test run
on('before:run', (details) => {
counter = 0;
return details;
});
});
Use this in your test cases as
cy.task("getCounter").then((counter) => {
console.log("counter", counter);
});
I'm getting rather confused as to if something is possible or not.
I create a module that contains the following:
export function logText(){
console.log('some text');
}
export class Example {
constructor(){
logText();
}
}
The intent is for the user to call new Example to start off the module logic.
import { logText, Example } from 'example';
// Do some magic here to modify the functionality of logText
new Example();
Is it possible for the end user to modify logText?
It would make sense for there to be a way for users to do something like this, or they'd have to take the entire module into their own repo just to make small functionality tweaks.
I frequently see repos with lots of functions exported that are useless without the users having to remake almost all the functionality manually, making it rather pointless to do. One good example is this repo whre theuy even call the exported functions their 'API'. In that example these are rather pointless exports and at worse would just cause issues if someone tried to use them in conjunction with the main function. But if you could modify them and have them still run then it would make sense to me.
Given this:
import { logText, Example } from 'example';
Is it possible for the end user to modify logText?
Since you aren't being very specific about what you mean by "modify logText", I'll go through several options:
Can you reassign some other function to the variable logText?
No. You cannot do that. When you use import, it creates a variable that is const and cannot be assigned to. Even if it wasn't const, it's just a local symbol that wouldn't affect the other module's use of its logText() anyway. The import mechanism is designed this way on purpose. A loader of your module is not supposed to be able to replace internal implementation pieces of the module that weren't specifically designed to be replaced.
Can you modify the code inside of the logText function from outside of the module that contains it?
No, you cannot. The code within modules lives inside it's own function scope which gives it privacy. You cannot modify code within that module from outside the module.
Can you replace the logText() function inside the module such that the implementation of Example inside that class will use your logText() function?
No, you cannot do that from outside the module. You would have to actually modify the module's code itself or someone would have to design the Example interface to have a replaceable or modifiable logText() function that the Example object used.
For example, logText() could be made a method on Example and then you could override it with your own implementation which would cause Example's implementation to use your override.
Code in the module that you do not modify:
export class Example {
constructor(){
this.logText();
}
logText() {
console.log('some text');
}
}
Code doing the import:
import { Example } from 'example';
class MyExample extends Example {
constructor() {
super();
}
logText() {
console.log("my own text");
}
}
let o = new MyExample();
Can you create your own version of logText and use it locally?
Sure, you can do that.
function myLogText() {
do your own thing
}
And, you could even NOT import logText so that you could use the symbol name logText() locally if you wanted. But, that won't affect what Example does at all.
Are there ways to design the example module so that that logText() can be easily replaced.
Yes, there are lots of ways to do that. I showed one above that makes logText a method that can be overriden. It could also be passed as an optional argument to the Example constructor.
There could even be an exported object that allowed the caller to replace properties on that object. For example:
export const api = {
logText: function logText(){
console.log('some text');
}
};
export class Example {
constructor(){
api.logText();
}
}
Then, use it like this:
import { api, Example } from 'example';
api.logText = function() {
console.log('my Text');
};
I would generally not recommend this because it sets you up for usage conflicts between multiple users of the same module where each one tries to modify it globally in ways that conflict with each other. The subclassing model (mentioned above) lets each use of the module customize in its own way without conflicting with other usages of the module.
Is it possible for the end user to modify logText?
No, that's not possible, import bindings are immutable, and function objects are basically immutable wrt the code they contain.
It would make sense for there to be a way for users to do something like this, or they'd have to take the entire module into their own repo just to make small functionality tweaks.
Why not make the log function an optional argument in the constructor? Usually when something is variable it becomes a parameter.
export class Example {
constructor(log=logText){
log();
}
}
There are 2 things that I'm very confused about.
What is the advantage of using any of ES6 class or Object literals.
And where should I use any of them?
Some of the examples that I'm trying out are mentioned below. Please let me know when to use particular way of implementation and when not to.
Class Example 1:
// auth.js
class Auth {
login(req, res) {...}
signup(req, res) {...}
}
module.exports = new Auth();
// index.js
const auth = require('auth');
Class Example 2:
// auth.js
class Auth {
login(req, res) {...}
signup(req, res) {...}
}
module.exports = Auth;
// index.js
const Auth = require('auth');
const auth = new Auth();
Object Literal Example:
// auth.js
module.exports = {
login: (req, res) => {...},
signup: (req, res) => {...}
};
// index.js
const auth = require('auth');
What I think from reading about them is that:
Class Example 1:
You can not create more than 1 object. Because a module is only executed once. So, on every import you will get the same object. Something similar to singleton. (Correct me here if I misunderstood it)
You will not be able to access the static methods of the class because you are only exporting the object of the class.
Class Example 2:
If you have a class that contains nothing but helper methods and the object does not have any state, It makes no sense creating object of this class all the time. So, in case of helper classes, this should not be used.
Object Literal Example:
You can not do inheritance.
Same object will be passed around on every require. (Correct me if I'm wrong here as well)
Please help me understand these concepts, what I'm missing out, what I've misunderstood and what should be used when and where. I'll be very grateful for your help.
Feel free to edit the question, if you think I made a mistake somewhere.
Class Example 1: You can not create more than 1 object. Because a module is only executed once. So, on every import you will get the same object. Something similar to singleton.
Correct. This is an antipattern therefore. Do not use it. class syntax is no replacement for object literals.
You will not be able to access the static methods of the class because you are only exporting the object of the class.
Theoretically you can do auth.constructor.… but that's no good.
Class Example 2: If you have a class that contains nothing but helper methods and the object does not have any state, It makes no sense creating object of this class all the time. So, in case of helper classes, this should not be used.
Correct. Use a simple object literal instead, or even better: multiple named exports instead of "utility objects".
Object Literal Example: You can not do inheritance.
You still can use Object.create to do inheritance, or parasitic inheritance, or really anything.
Same object will be passed around on every require.
Correct, but that's not a disadvantage. If your object contains state, you should've used a class instead.
If your class has got a constructor, you can build several objects from this class threw :
var Panier= require('./panier');
var panier1 = new Panier(13, 12, 25);
var panier2 = new Panier(1, 32, 569);
Of course your Panier would be defined in the file Panier.js located in the same directory :
module.exports = class Panier
{
constructor(code, qte, prix)
{
this.codeArticle = code;
this.qteArticle = qte;
this.prixArticle = prix;
}
getCode()
{
return this.codeArticle;
}
getQte()
{
return this.qteArticle;
}
getPrix()
{
return this.prixArticle;
}
}
So I'm writing a whole bunch of vendor-specific files in node which all have a similar controller pattern, so it makes sense for me to cut them out and put into a common file.
You can see my common controller file here: https://gist.github.com/081a04073656bf28f46b
Now when I use them in my multiple modules, each consecutively loaded module is overwriting the first. This is because the file is only required once and passed dynamically through to each module on load (this allows me to add extra modules and these modules are able to add their own routes, for example). You can see an example module here: https://gist.github.com/2382bf93298e0fc58599
You can see here on line 53 I've realised that we need to create a seperate instance every time, so I've tried to create a new instance by copying the standardControllers object into a new object, then initialising the new object. This has zero impact on the code, and the code behaves in exactly the same way.
Any ideas guys? I'm in a bit of a jam with this one!
First thing I'd do is try to make things simpler and reduce coupling by invoking the single responsibility principle, et al.
http://www.codinghorror.com/blog/2007/03/curlys-law-do-one-thing.html
Put those Schemas into their own files, eg
models/client.js
models/assistant.js
models/contact.js
I've also found that embedded docs + mongoose is generally a PITA. I'd probably promote all those to top level docs.
You don't need to enclose your object's keys in quotes.
routes = {
list: function() {} // no quotes is aok
}
Also 'list' in typical REST apps is called 'index'. Anyway.
Ok, I'd break this up differently. Since you're requiring stuff from the index.js file in the middleware, they become tightly coupled, which is bad. in fact, I think I'd rewrite this whole thing so it was tidier. Sorry.
I'd probably replace your 'middleware' file with an express-resource controller
https://github.com/visionmedia/express-resource (built by author of express). This is a good framework for restful controllers, such as what you're building. The auto-loader is really sweet.
You may also want to look at: http://mcavage.github.com/node-restify/ It's new, I haven't tried it out, but I've heard good things.
Since what you're building is basically an automated mongoose-crud system, with optional overriding, I'd create an express-resource controller as your base
/controllers/base_controller.js
and it might look like
var BaseController = function() {} // BaseController constructor
BaseController.prototype.index = function() {
// copy from your middleware
}
BaseController.prototype.show = function() {
// copy from your middleware
}
BaseController.prototype.create = function() {
// copy from your middleware
}
// etc
module.exports = BaseController
Then I'd do something like:
/controllers/some_resource_controller.js
which might look something like:
var BaseController = require('./base_controller')
var NewResourceController = function() {
// Apply BaseController constructor (i.e. call super())
BaseController.apply(this, arguments)
}
NewResourceController.prototype = new Base()
NewResourceController.prototype.create = function() {
// custom create method goes here
}
module.exports = NewResourceController
Then to use it, you can do:
var user = app.resource(myResourceName, new ResourceController());
…inside some loop which sets myResourceName to be whatever crud you're trying to set up.
Here's some links for you to read:
http://tobyho.com/2011/11/11/js-object-inheritance/
http://yehudakatz.com/2011/08/12/understanding-prototypes-in-javascript/
Also, it sounds like you're not writing tests. Write tests.
http://www.codinghorror.com/blog/2006/07/i-pity-the-fool-who-doesnt-write-unit-tests.html