I created a function that should check if a string correspond to an url format:
const test = (str) => {
const t = new RegExp(
'^(https?:\\/\\/)?' +
'(www\\.)' +
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|)' +
'(\\#[-a-z\\d_]*)?$',
'i',
);
return t.test(str);
};
console.log(test('http://demo.com')); //expect true
console.log(test('http://ww.demo.com')); //expect false
For each console.log() i wrote the expected value, in both cases i got false. In the last case false is ok, but in the first i should get true. How to fix the regex?
Even if this answer is a bit too much for this Problem, it illustrates the problem: Even if it might be possible to create a regexp to check the url, it is much simpler and more robust to parse the URL and "create a real Object", on/with which the overall test can be decomposed to a number of smaller tests.
So probably the builtin URL constructor of modern browsers may help you here (link1, link 2).
One approach to test you url might look like this:
function testURL (urlstring) {
var errors = [];
try {
var url = new URL(urlstring);
if (!/https/.test(url.protocol)) {
errors.push('wrong protocol');
}
//more tests here
} catch(err) {
//something went really wrong
//log the error here
} finally {
return errors;
}
}if (testURL('mr.bean').length == 0) { runSomething(); }
Related
I have a string:
string = "abc_test_dashboard.json";
The value of string could vary like:
"tes.test.dashboard.json"
"jhgwefj-gfjh.widget.json"
The last "dashboard.json" and "widget.json" is static and could be either of them depending on a condition.
Basically I'm trying to identify if its "dashboard" or "widget" from the string.
I want to do stuff based on:
if ("dashboard.json") {//do some stuff}
else { // do something else
}
I also just realized that I may have multiple files with same name, and hence I may end up getting (1), (2) suffixes i.e: "abc_test_dashboard(1).json", "abc_test_dashboard(2).json". is there any way to test these kind of scenarios?
Thanks
You can do it with
if(string.endsWith('dashboard.json')) {
}
if(string.endsWith('widget.json')) {
}
Also you can use regex if you want (in case your target browsers do not support endsWith);
if (/widget\.json$/.test('widget.json')) {
}
Using regex you can even extract the initial portion of the file;
var widgetInfo = 'asd.widget.json'.match(/^(.*)widget\.json$/)
if (widgetInfo) {
console.log(widgetInfo[1]) // will print `asd.`
}
// similar code to check for `dashboard.json`
EDIT:
In the case you commented you can use the following regex; /^(.*)widget(\(.+\))?\.json$/. It will match strings in the forms of randomstring.widget.json and randomstring.widget(1).json, but not randomstring.widget().json
If you don't mind RegEx, you can use the String match() method as in the below:
function checkStrEnd(str) {
if (str.match(/dashboard\.json$/)) {
console.log('Do dashboard stuff');
} else if (str.match(/widget\.json$/)) {
console.log('Do widget stuff');
} else {
console.log('Do something else');
}
}
checkStrEnd('tes.test.dashboard.json'); // 'Do dashboard stuff'
checkStrEnd('jhgwefj-gfjh.widget.json'); // 'Do widget stuff'
checkStrEnd('random string'); // 'Do something else'
you can use includes to see if the string exists within the string
let arr = ["tes.test.dashboard.json", "jhgwefj-gfjh.widget.json"]
arr.forEach(item => {
if (item.includes('dashboard.json')) {
console.log('dasboard')
} else if (item.includes('widget.json')) {
console.log('widget')
}
})
Currently, my if/else statement does not work correctly as it never goes to the else portion of my code. The node app takes in an argument (process.argv[3]) and uses that to pick the API to call. process.argv[4] is used to specify what to search (example "Yesterday") and works correctly if argument is provided. However, I want to have a default search if user leaves that argument blank. I'm unsure of why it never goes to the else portion of the code.
I'm new to programming so I'm sure this is stupid error on my part, but I've tried rewritting the statement and same issue. Any help would be greatly appreciated.
function getSpotifySongInfo() {
//4th node argument is reserved for the song user wants to select
var query = process.argv[3];
if (query !== "") {
//could make this less repeating code by passing the song as a parameter?
spotifyClient.search({ type: 'track', query: query, limit: 1 }, function (err, data) {
if (!err) {
console.log("=============Artist==Track==Album==PreviewURL=============================");
console.log("Artist: " + data.tracks.items[0].artists[0].name);
console.log("Track: " + data.tracks.items[0].name);
console.log("Album: " + data.tracks.items[0].name);
console.log("Preview URL: " + data.tracks.items[0].preview_url);
} else {
console.log(err);
}
});
} else {
//need to make this specific for Ace of Base. For some reason it's not changing the query to reflect default song. I've tried commenting this portion out and just testing w/ a simple console.log("test") and nothing...
query = 'The Sign';
spotifyClient.search({ type: 'track', query: query, limit: 1 }, function (err, data) {
if (!err) {
console.log("=============Artist==Track==Album==PreviewURL=============================");
console.log("Artist: " + data.tracks.items[0].artists[0].name);
console.log("Track: " + data.tracks.items[0].name);
console.log("Album: " + data.tracks.items[0].name);
console.log("Preview URL: " + data.tracks.items[0].preview_url);
} else {
console.log(err);
}
});
}
}
if (query !== "") is a bad test and probably doesn't do what you want. For example:
var query = undefined;
query !== ""
// true
query = null
query !== ""
// true
You are testing for a very specific thing — an empty string — which you probably aren't getting as an argument to your function.
A better way that leads to a lot less code is to assign a value to query if none exists. You can do some thing like:
if (!query) {
query = 'The Sign'
}
Then you don't need the if/else code at all. A quick and easy way to do this is:
var query = process.argv[3] || 'The Sign'
This will either assign the value of process.argv[3] or, if that value is falsy, you'll get the default. This is a very common pattern in Javascript.
I am trying to create a generic document update handler.
I am using:
function(doc, req) {var field = req.query.field; var value =
req.query.value; var message = 'set '+field+' to '+value; doc[field] =
value; return [doc, message]; }
This works ok with simple json but not with a nested object such as
"abc":{"ax":"one", "by":"two" ...}
my curl command is:
curl -X PUT 'http://127.0.0.1:5984/db/_design/updatehandler/_update/inplace/id?field=abc.ax&value=three'
The result is a new field is created and the existing abc:{ax:one} is left
untouched.
With a simpler example:
if I have: "xyz":"five"
curl -X PUT 'http://127.0.0.1:5984/db/_design/updatehandler/_update/inplace/id?field=xyz&value=ten'
... works correctly.
I have not yet tried the generic process on "pqr":[s, t, u] yet but I guess
this may require a different design modification as well.
Ideally one wants something that works in at least the abovementioned three
cases if possible, as long as it is not too complex for it not to be worth
the effort.
Could someone possibly kindly help here or refer me to some javascript examples please.
Many thanks.
John
function (doc, req) {
function merge(nDoc,oDoc ) {
for (var f in nDoc) {
var tmpNewDoc = nDoc[f],
tmpDoc = oDoc[f];
var type = typeof(tmpNewDoc);
if (type === 'object' && tmpNewDoc.length === undefined && tmpDoc !== undefined) merge(tmpNewDoc, tmpDoc);
else oDoc[f] = tmpNewDoc;
}
}
if (!doc) {
return [null, toJSON({
error: 'not_found',
reason: 'No document were found with the specified ID or an incorrect method was used.'
})];
}
try {
var newDoc = JSON.parse(req.body);
merge(newDoc, doc);
}
catch (e) {
return [null, ToJSON({
error: 'bad_request',
reason: 'Invalid json or processing error'
})];
}
return [doc, toJSON({
doc: doc,
ok: true
})];
}"
}
Simply pass the new document to this handler. It will merge the new values to it (warning, the arrays will be overwrite). If you also want to merge array, you can either use a third party library or build your own recursive merge function.
I want to log objects using log4javascript. For example consider the following code:
function LogObject() {
var blah = {
one: 42,
two: "486"
};
logger.Info(blah);
Assuming that logger is instance of log4javascript logger that is properly set up:
var logger = log4javascript.getLogger("InternalLogger");
var ajaxAppender = new log4javascript.AjaxAppender(url),
jsonLayout = new log4javascript.JsonLayout(false, false);
ajaxAppender.setLayout(jsonLayout);
ajaxAppender.addHeader("Content-Type", "application/json");
logger.addAppender(ajaxAppender);
I am expecting the result to the following: request payload contains array of messages first of which is my object serialized into JSON. What I see is array of messages first of which has string "Object object" (like toString() method was invoked). How can I achieve that?
JsonLayout formats the logging event (which includes log level, timestamp and logger name in addition to the log message(s)) as JSON rather than the log message, which is pretty much assumed to be a string. The reason for this is to avoid a dependency on a JSON library for older browsers; generating JSON for the simple, known data that JsonLayout deals with is no problem without a JSON library but handling arbitrary objects definitely requires one.
The workaround I'd suggest is simply to format the message before you pass it to the logging call:
logger.info( JSON.stringify(blah) );
We were following #Tim Down's suggestion
logger.info( JSON.stringify(blah) );
But we had performance issues since the JSON.stringify happens before logger.info is called, therefore it will always happen even if the logging level is set to ignore this log.
In order to work around this I wrote a new lazy layout so that the stringification only happens if the log is actually output. In order to be more flexible it also alows passing a function, in which case it outputs the result of running said function.
Usage:
logger.trace("Received ", widget, " which has ", () => countFrimbles(widget), ' frimbles');
Implementation:
function LazyFormatLayout() { }
LazyFormatLayout.prototype = new log4javascript.Layout();
LazyFormatLayout.prototype.format = function (loggingEvent) {
var time = loggingEvent.timeStamp.toTimeString().split(/\s/)[0];
var head = time + ' ' + loggingEvent.logger.name + ' [' + loggingEvent.level.name + '] - ';
var body = loggingEvent.messages.map(function (arg) {
try {
switch (typeof (arg)) {
case 'function':
return arg();
case 'object':
return JSON.stringify(arg);
}
}
catch (e) {
return '<<error while logging: ' + e.stack + '>>';
}
return arg;
}).join('');
if (!loggingEvent.exception)
return head + body;
return head + body + ' ==> Exception: ' + loggingEvent.exception.stack;
}
LazyFormatLayout.prototype.ignoresThrowable = function () { return false; };
LazyFormatLayout.prototype.toString = function () { return "LazyFormatLayout"; };
Question is somewhat dated, but a simple google search turned up this question and there seems to be a build-in way to log objects:
var log = log4javascript.getDefaultLogger();
log.info("log following object",{ data:5, text:"bla" });
output
12:49:43 INFO - log following object {
data: 5,
text: bla
}
JavaScript developers who have spent time in languages like C often miss the ability to use certain types of introspection, like logging line numbers, and what method the current method was invoked from. Well if you're using V8 (Chrome, Node.js) you can employ the following.
Object.defineProperty(global, '__stack', {
get: function(){
var orig = Error.prepareStackTrace;
Error.prepareStackTrace = function(_, stack){ return stack; };
var err = new Error;
Error.captureStackTrace(err, arguments.callee);
var stack = err.stack;
Error.prepareStackTrace = orig;
return stack;
}
});
Object.defineProperty(global, '__line', {
get: function(){
return __stack[1].getLineNumber();
}
});
console.log(__line);
The above will log 19.
Combined with arguments.callee.caller you can get closer to the type of useful logging you get in C via macros.
The problem with the accepted answer, IMO, is that when you want to print something you might be using a logger, and when that is the case, using the accepted solution will always print the same line :)
Some minor changes will help avoiding such a case!
In our case, we're using Winston for logging, so the code looks like this (pay attention to the code-comments below):
/**
* Use CallSite to extract filename and number, for more info read: https://v8.dev/docs/stack-trace-api#customizing-stack-traces
* #returns {string} filename and line number separated by a colon
*/
const getFileNameAndLineNumber = () => {
const oldStackTrace = Error.prepareStackTrace;
try {
// eslint-disable-next-line handle-callback-err
Error.prepareStackTrace = (err, structuredStackTrace) => structuredStackTrace;
Error.captureStackTrace(this);
// in this example I needed to "peel" the first CallSites in order to get to the caller we're looking for
// in your code, the number of stacks depends on the levels of abstractions you're using
// in my code I'm stripping frames that come from logger module and winston (node_module)
const callSite = this.stack.find(line => line.getFileName().indexOf('/logger/') < 0 && line.getFileName().indexOf('/node_modules/') < 0);
return callSite.getFileName() + ':' + callSite.getLineNumber();
} finally {
Error.prepareStackTrace = oldStackTrace;
}
};