I'm writing a function in Javascript that will verify the existence of a particular kind of file and if it does not exist, then it will copy the file from a known location in a git repository to the correct location.
To do this, I'm also using a function I wrote that verifies the existence of any file (only at certain paths that we've pre-defined). Also, file.exists is a function prebuilt in our IDE.
That function looks like this:
function verifyFileExistence(file, path, existState)
{
var result;
var logMessage;
var resultMessage;
if (existState == true)
{
logMessage = "Verify that \"\"" + file + "\"\" exists.";
result = (File.exists(path + file));
if (result)
{
resultMessage = "\"\"" + file + "\"\" exists.";
}
else
{
resultMessage = "\"\"" + file + "\"\" does not exist.";
}
}
else
{
logMessage = "Verify that \"\"" + file + "\"\" does not exist.";
result = (!File.exists(path + file ));
if (result)
{
resultMessage = "\"\"" + file + "\"\" does not exist.";
}
else
{
resultMessage = "\"\"" + file + "\"\" exists.";
}
}
resultVP(logMessage, resultMessage, result)
}
Side Note: Each of these functions will write results to a log file which is why the different result/log/message variables appear. I left them in because I think they help to show make the logic clear.
So far, my function to check for the specific file type looks something like this:
import {copyFile,verifyFileExistence} from 'Path\\to\\FileUtilityLibrary.js';
function verifyLoadFile(file, path, existState, inFile, outFile)
{
var exist;
exist = (verifyFileExistence(file, path, existState));
if (exist != true)
{
copyFile(inFile,outFile)
}
}
I feel like having this many parameters in the function is inefficient and that maybe there's a more efficient way of handling them. Can I somehow simply this or is this the best way to handle parameters when calling functions inside a function?
You can do one object, for example:
const object = {
file,
path,
existState,
inFile,
outFile
}
and handle only one parameter.
I'm going to assume you are using javascript ES6 with your import notation, that means you can use the spread operator to do this
import {copyFile,verifyFileExistence} from 'Path\\to\\FileUtilityLibrary.js';
function verifyLoadFile(inFile, outFile, ...fileParams)
{
var exist;
exist = (verifyFileExistence(...fileParams));
if (exist != true)
{
copyFile(inFile,outFile)
}
}
with fileParams being all the params you need to pass to your child function. With this notation you can have a variable number of parameters passed into this function.
As liskaandar's answer points out, an object would be the best way to reduce the number of parameters.
If you don't want to reference the object each time you use those variables, you can destructure the object to give you workable variables.
For example, if passing an object that looks like this:
const object = {
file,
path,
existsState,
infile,
outfile
}
you can destructure it within your verifyFileExistence(fileObject) function by doing the below:
var {file,
path,
existsState,
infile,
outfile} = fileObject;
You can then reference those objects like normal by calling the normal variable names like file and path again.
I thought in a way to optimize the code and this is what I would use in a scenario like yours:
// verifyLoadFile function only needs three params (repositoryPath, localPath, fileName) and return an object with the log result and message
function verifyLoadFile(repositoryPath, localPath, fileName){
var exists = (!File.exists(localPath+ fileName)); //Check if file exists
var logMessageHeader = "Verify that "; //Header of message log
var messageSuccess = "\"\" exists.";
var messageWarning = "\"\" does not exists.";
var resultMessage = "\"\"" +fileName; //Initialize resultMessage with redundant parth of message
if(exists){
resultMessage = resultMessage + messageSuccess;
} else {
resultMessage = resultMessage + messageWarning;
copyFile(repositoryPath+fileName, localPath+fileName);
}
return {
logMessage: resultMessage
resultMessage: logMessageHeader + resultMessage;
};
};
// This is a function that initialize the sync of repository
function syncRepository(typeOfRepository){
var repositoryConfig = getConfig(typeOfRepository); // In the example I get an array of objects or arrays that contains config needed
repositoryConfig.forEach((obj) => {
const {logMessage,resultMessage} = verifyLoadFile(obj.repositoryPath, obj.localPath, obj.fileName); //Retrieve log values for each file
resultVP(logMessage, resultMessage); //Log the result
});
}
In this way, you only need a 14-line function (verifyLoadFile) that verify if file exist, generate log messages and copy the file if it not exists, then only if needed log the result returned in each iteration
So i think, answering your initial question, that the best way to handle parameters in functions is optimize the code.
I hope it help you.
Related
I am building a small tool using Apps Script. I can't seem to get the global variable (URL) to update to include the docId.
What I expect
Run the App function, which first creates a Google Doc and sets the global variable for the documentId (docId). Second it calls myOnClickhandler, which should take the global url variable that includes the updated docId.
What is happening:
The variable 'url' in myClickHandler is not updated to the value of 'docId' set in the createProject function.
var docId = "";
var url = "https://docs.google.com/document/d/" + docId + "/";
function doGet() {
var output = HtmlService.createHtmlOutputFromFile('MyUI');
return output;
}
function createProject(test="test"){
const doc = DocumentApp.create(test);
docId = doc.getId();// <-- Update global docId variable. Doesn't seem to be working.
Logger.log("Doc ID is: " + docId);
}
function myOnClickHandler(){
const myList = ["Hello from the server", url]
Logger.log("URL is: " + url);
}
function app(){
createProject();
myOnClickHandler();
}
Probably a simple mistake on my part, but I am banging my head against the wall on this one. Screenshot attached.
In JavaScript, strings are immutable. Use an arrow function and a template literal instead of a static text string, like this:
let docId;
const getUrl_ = (docId) => `https://docs.google.com/document/d/${docId}/`;
function test() {
docId = 'testString';
const url = getUrl_(docId);
console.log(url);
}
I'm building a Discord bot which will allow users to create custom commands.
It works in this way, user enters "!addcommand !commandname:command value". The program will then split the string, and add !commandname:command value to a txt file. Whenever somebody types !commandname into discord, the bot will then output "command value" into chat.
The program is supposed to check if the new command exists whenever an if statement is triggered. However, this only seems to be checking the first time the program is run, which causes new commands to not be recognized unless the program is restarted.
Note:
Client.on listens to the channel, and contents are run every time someone says something in chat.
!addcommand appears to be functioning correctly, and I'm able to confirm the lines are being written to the file as intended.
I don't know what else to try.
Main file:
//Assume that requires etc are included
client.on('message', message => {
const pingCheck = message.content.charAt(0);
const commandCheck = message.content.split(" ");
if (pingCheck === "!") {
//Populates the list of custom commands. Must be done on every check, or new commands will not be recognized.
//Currently, this seems to only update once the service/program is restarted
var commandList = customCommands.returnPhrase();
//If the commandList object contains the correct key (stored in commandCheck[0]) such as !commandname, the bot will send the value "command value" as a string to the discord chat.
if (commandList.hasOwnProperty(commandCheck[0])) {
message.channel.send(commandList[commandCheck[0]]);
}
//If the key does not exist, the program then checks predefined commands. Other commands exist here, but for the purposes of this question I'll show only the !addcommand, which is used to create a new custom command.
else {
switch (commandCheck[0]) {
case "!addcommand":
//This checks that the command is formatted properly, "!commandname:commandvalue". If it does not start with ! or contain : somewhere in the string, it's probably an invalid format.
//Technically this still allows for a !:commandvalue format. I haven't implemented a check for this yet.
if (commandCheck[1].startsWith("!") && commandCheck[1].includes(":")) {
//While loop reconstructs the command key to be passed in, ignores slot 0 as this is the !addcommand
var gs = "";
var x = 1;
while (x < commandCheck.length) {
gs += gs +commandCheck[x] + " ";
x++;
}
gs = gs.slice(0,-1)+"\r\n"; //removes the last " " from the input string, and adds line-break
addCommands.addPhrase(gs);//passes reconstructed command to be added to commandlist.txt
message.channel.send("I have added " + commandCheck[1] + " to the command list.");
break;
}
default:
message.channel.send("I dont recognize that command.");
}
}
}
});
Module which adds commands:
const fs = require('fs');
var createCommand = {
addPhrase: function(x) {
fs.appendFile("commandlist.txt", x, function(err){
if(err) throw err;
console.log(err)
});
}
}
module.exports = createCommand;
Module which populates list of custom commands:
const fs = require('fs');
var commandFile = fs.readFileSync('commandlist.txt','utf8');
var dataSplit = commandFile.split("\r\n");
var readCommand = {
returnPhrase: function(){
var splitSplit = {};
var i = 0;
//populates splitSplit with keys and values based on text file
while (i<dataSplit.length){
var newKey = dataSplit[i].split(':');
splitSplit[newKey[0]] = newKey[1];
i++
};
return splitSplit;
},
};
module.exports = readCommand;
Better readability: https://repl.it/repls/DarkvioletDeafeningAutomaticparallelization
Expected: commandList is populated everytime if statement is triggered
Actual: commandList populates first time statement is triggered
You write to the file whenever a new command comes in, however you only read once from it when the server starts, so you won't keep track of changes (untill you restart the server which will read the file again). Now you could theoretically listen for filechanges and reload then, but that is overcomplicating things, the filesystem is not meant to achieve that. Instead, just keep your commands in an object and export some methods for adding / checking:
let commands = {};
// ¹
module.exports = {
addCommand(key, value) {
commands[key] = value;
// ²
},
getValue(key) {
return commands[key];
}
};
Now when you add a command, it directly gets added to the object, and that can then directly be read out.
Now as objects aren't persisted accross restarts, you will lose all commands then. But that is easy to fix: You could just reflect the object to a file whenever it updates, and then load it on every start. Instead of creating a custom format for that, I'd just use JSON. The code above can easily be extended:
// ¹
try {
commands = JSON.parse( fs.readFileSync("commands.txt") );
} catch(e) { /* ignore if file doesnt exist yet */ }
// ²
fs.writeFile("commands.txt", JSON.stringify(commands), err => {
if(err) console.error(err);
});
How I would write the bot:
const playerCommands = require("./commands.js");
const defaultCommands = {
addCommand(...pairs) {
let result = "";
for(const pair of pairs) {
const [name, value] = pair.split(":");
playerCommands.addCommand(name, value);
result += `${name} added with value ${value}\n`;
}
return result;
}
};
client.on('message', ({ content, channel }) => {
if(content[0] !== "!") return; // ignore non-commandd
const [commandName, ...args] = content.slice(1).split(" ");
if(defaultCommands[commandName])
return channel.send(defaultCommands[commandName](...args));
const value = playerCommands.getValue(commandName);
if(value) return channel.send(value);
return channel.send("Command not found!");
});
I have a javascript file I want to call. contents are below. When I tried calling the file, I keep getting a "no variable found with name: response" even though there is clearly a variable defined. The file executes fine within command-line using node so the javascript function is valid. Any thoughts? I attached the error message in a screenshot.
Javascript content in snippet below.
Karate script:
Scenario: Call JavaScript:
* def sample = read('classpath:reusable/gen-data.js')
* print someValue
function createTestData(sampleJson, fieldsToChange, numRecords) {
var testData = [];
for (var i = 0; i < numRecords; i++) {
var copy = JSON.parse(JSON.stringify(sampleJson));
fieldsToChange.forEach(function(fieldToChange) {
copy[fieldToChange] = copy[fieldToChange] + i;
});
testData.push(copy);
}
return {content: testData};
}
var testData = {
"country": "US",
"taskStatusCode" : "Closed",
"facilityCode" : "US_203532",
};
function getTestData() {
String testData = JSON.stringify(createTestData(testData, ["taskStatusCode", "facilityCode"], 1), null, 1);
console.log("all done getTestData()");
console.log("test data: \n" + testData);
return testData;
};
console.log("calling getTestData()");
getTestData();
I think this error is thrown when the JavaScript is not correct. For example in my case this JS file:
/* Set the custom authentication header */
function fn() {
var authToken = karate.get('authToken');
var out = {};
out['Auth-Token'] = authToken
return out;
}
This file will produce the "no variable found with name: response".
The reason is because "the right-hand-side (or contents of the *.js file if applicable) should begin with the function keyword." according to the karate docs (link).
Now by moving the comment and making the function keyword the first bit of text it works as expected:
function fn() {
/* Set the custom authentication header */
var authToken = karate.get('authToken');
var out = {};
out['Auth-Token'] = authToken
return out;
}
In the OP, the function keyword is the first thing in the file, but there is javascript outside the original function -- which I don't think is legal for karate syntax. In other words, everything has to be in the outer function.
My workaround was to use java instead of JavaScript.
I'm running a script on an apache webserver on a linux box. Based on the parameter I want to change the name of variable(or set it)
The idea is that humDev(lines 11 and 14) is named humDev21 for example. Where devId is the number 21 in this example.
My script looks like this:
function getHumDev(devId){
$.ajax({
async: false,
url: "/url" + devId,
success: function(result) {
var array = result["Device_Num_" + devId].states;
function objectFindByKey(array, key, value) {
for (var i = 0; i < array.length; i++) {
if (array[i][key] === value) {
humDev = array[i].value;
}
}
return humDev;
};
objectFindByKey(array, 'service', 'some');
}
});
};
If Im looking in the wrong direction, please do let me know. Maybe its bad practice what Im trying. The reason I want to have the object a unique name is because this function is called several times by another function, based on the content of an array. But when I have the humDev object named without the number suffix to make it unique, the content of the object is getting mixed up between the different calls.
I may be off base but I am making some assumptions based on what I understand of what you are trying to do.
First, you need to understand how to do file I/O in node.js. So lets start there:
var pathToFile, //set with file path string
fs = require('fs'), //require the file i/o module API
bunchOfHumDevs = {},
fileContents; //we'll cache those here for repeated use
fs.readFile(pathToFile, function(err, result) {
if (err) {
throw new Error(); //or however you want to handle errors
} else {
fileContents = JSON.parse(result); //assumes data stored as JSON
}
});
function getHumDev(devId) {
//first make sure we have fileContents, if not try again in 500ms
if (!fileContents) {
setTimeout(function() {
getHumDev(devId);
}, 500);
} else {
var array = fileContents["Device_Num_" + devId].states,
i = array.length,
//if 'service' and 'some' are variable, make them params of
//getHumDev()
while (i--) {
if (array[i]['service'] === 'some') {
//store uniquely named humDev entry
bunchOfHumDevs['humDev' + devId.toString()] = array[i].value;
break; //exit loop once a match is found
}
}
}
return null;
}
getHumDev(21);
assuming a match is found for the devId 21, bunchOfHumdevs will now have a property 'humDev21' that is the object (value?) in question. Also, the fileContents are now cached in the program so you don't have to reopen it every time you call the function.
I am just getting started with coding for FirefoxOS and am trying to get a list of files in a directory.
The idea is to find the name of each file and add it to the array (which works), but I want to return the populated array and this is where I come unstuck. It seems that the array gets populated during the function (as I can get it to spit out file names from it) but when I want to return it to another function it appears to be empty?
Here is the function in question:
function getImageFromDevice (){
var imageHolder = new Array();
var pics = navigator.getDeviceStorage('pictures');
// Let's browse all the images available
var cursor = pics.enumerate();
var imageList = new Array();
var count = 0;
cursor.onsuccess = function () {
var file = this.result;
console.log("File found: " + file.name);
count = count +1;
// Once we found a file we check if there are other results
if (!this.done) {
imageHolder[count] = file.name;
// Then we move to the next result, which call the cursor
// success with the next file as result.
this.continue();
}
console.log("file in array: "+ imageHolder[count]);
// this shows the filename
}
cursor.onerror = function () {
console.warn("No file found: " + this.error);
}
return imageHolder;
}
Thanks for your help!
Enumerating over pictures is an asynchronous call. Essentially what is happening in your code is this:
You are initiating an empty array
You are are telling firefox os to look for pictures on the device
Then in cursor.onsuccess you are telling firefox os to append to the array you have created WHEN it gets back the file. The important thing here is that this does not happen right away, it happens at some point in the future.
Then you are returning the empty array you have created. It's empty because the onsuccess function hasn't actually happened.
After some point in time the onsuccess function will be called. One way to wait until the array is full populated would be to add in a check after:
if (!this.done) {
imageHolder[count] = file.name;
this.continue();
}
else {
//do something with the fully populated array
}
But then of course your code has to go inside the getImageFromDevice function. You can also pass a callback function into the getImageFromDevice function.
See Getting a better understanding of callback functions in JavaScript
The problem is with the aSynchronous nature of the calls you are using.
You are returning (and probably using) the value of imageHolder when it's still empty - as calls to the "onsuccess" function are deferred calls, they happen later in time, whereas your function returns immediately, with the (yet empty) imageHolder value.
You should be doing in this case something along those lines:
function getImageFromDevice (callback){
...
cursor.onsuccess = function () {
...
if (!this.done) {
// next picture
imageHolder[count] = file.name;
this.continue();
} else {
// no more pictures, return with the results
console.log("operation finished:");
callback(imageHolder);
}
}
}
Or use Promises in your code to accomplish the same.
Use the above by e.g.:
getImageFromDevice(function(result) {
console.log(result.length+" pictures found!");
});