I'm trying to get a better understanding of how the publication / subscription model works.
Specifically I'm referring to this step in the tutorial.
if (Meteor.isServer) {
Meteor.publish('tasks', function tasksPublication() {
return Tasks.find();
});
}
The name tasksPublication isn't used anywhere later on in the tutorial.
Looking at the documentation here the name doesn't seem to be needed.
Does it make any difference to name a publication function?
Using named functions is indeed not needed when publishing, all these work:
Meteor.publish('tasks', function publishAllTasks() { return Tasks.find() });
Meteor.publish('tasks', function() { return Tasks.find() });
Meteor.publish('tasks', () => Tasks.find());
(The third line features an Arrow function)
There is no raw difference in functionality, and choosing between the three mostly boils down to taste.
However, there is one one thing that only the first statement (named function expression) does: the name appears in stack traces when an uncaught exception occurs.
For example let's say you make a typo when writing Tasks and write Waffles instead:
// File: server/index.js
Meteor.publish('tasks', function publishAllTasks() { return Waffles.find() });
// or:
Meteor.publish('tasks', () => Waffles.find());
Here is the stack trace in the first case (I removed timestamps)
Exception from sub tasks id egG3xJuLTLFvH4jLT ReferenceError: Waffles is not defined
at Subscription.publishAllTasks [as _handler] (server/index.js:4:10)
(some boring stuff)
Stack trace in the second case:
Exception from sub tasks id u4rKBFH78uTBEoys2 ReferenceError: Waffles is not defined
at Subscription._handler (server/index.js:4:10)
(more boring stuff)
In the first case the function name appears clearly.
The file name and line still appear at the end of the line.
So it could be helpful if you are crawling through your logs to find all uncaught exceptions that originated from / passed through publishAllTasks.
It won't help a lot when debugging though since you still have the file name and line and finding the faulty function is just a matter of opening that file.
So unless you have specific log-crawling needs don't bother and go for whichever you prefer.
Related
I'm using 'soft-assert' library (soft-assert library) to apply assertion on my test steps without stopping the test if any of them fail.
According to the documentation, all soft-assert is verified at the end of the test by using softAssertAll() command. And this works very well. However, if nothing fails, I don't see any explicit message in my test result as when I use expect command.
So, I'm trying to apply expect over the softAssertAll() command, as it's seen below, but I'm getting the error message:
"expected { Object (userInvocationStack, specWindow, ...) } to be a function"
What I'm trying to do:
expect(cy.softAssertAll()).not.throw(Error)
Does anyone know how can I do this or solve the error in the image below?
Thanks in advance.
See the Chai example for throw
var badFn = function () { throw new TypeError('Illegal salmon!'); };
expect(badFn).to.throw();
Note you pass in the function name without invoking it. I think this allows chai to wrap the function invocation in a try-catch and gracefully report the failure.
You can't do the same with a Cypress custom command as it will not raise errors in the same way as badFn above. Internally it swallows any error and sets the state of the test to "failed".
You could reasonably expect this to work
expect(jsonAssertion.softAssertAll).not.throw()
however there's an internal error in jsonAssertion that seems to be related to the this reference inside it's class.
TypeError: Cannot read properties of undefined (reading '_softThrowJsonDiffArray')
To fix, use an arrow function
const testSoftAssertAll = () => jsonAssertion.softAssertAll();
expect(testSoftAssertAll).not.throw()
or shorter
expect(() => jsonAssertion.softAssertAll()).not.throw()
Check the diff array
This is cleaner and clearer
// expect all soft-assert to be passing
expect(jsonAssertion.jsonDiffArray).to.eq(undefined)
// expect first soft-assert to fail with a certain message
expect(jsonAssertion.jsonDiffArray[0].error.message).to.contain('Illegal salmon')
I have such code:
A.js
module.exports = function() {
/*
Here I need to find out the path of module in which this function was called.
*/
};
B.js
var A = require('./A.js');
A();
C.js
var A = require('./A.js');
A();
Is it possible to find out from which file function of module A is called? I know that I can pass __filename param:
var A = require('./A.js');
A(__filename);
but maybe there is another way without passing any params to A()?
Well, it is possible, but you really should not do this. You can examine the error stack to get the calling file path like this:
function getCallingFile(n) {
// Regular expression used to extract the file path
var exp = / \(([^:]+)(:[^\)]+)?\)$/;
// Extract line from error stack
var line = new Error().stack.split('\n')[1 + n];
// Try to find path in that line
var m = exp.exec(line);
if(m) {
return m[1];
} else {
return null;
}
}
The parameter n means how many levels of the stack should be skipped, in your example it should be 1.
Why shouldn't you do this? Because
the exact format of err.stack has not been specified formally,
this function will fail if you call it from native code,
it is not what error stacks have been designed for and
it enforces a specific directory which might cause problems if you ever refactor your code.
If I understand your question properly, then the answer is No. A function in Javascript does not know the filename of its caller in any standard way that is documented in a specification. There are some hacks using the exception stack trace, but it is not recommended to use that for a variety of reasons.
You don't explain why you're trying to do this, but in general a function should not change its behavior based on who the caller is. Rather a function should have a defined behavior based on the arguments passed to it. If you want a different behavior when called from B.js and from C.js, then you should specify some argument when calling it in each circumstance that indicates what the behavior should be or create different functions that can be called to generate the different behaviors.
I'm attempting to use Blockly to do a "make your own game" sort of thing, and for the most part it works. However, when trying to run code generated (by Blockly's own pre-defined function generators) by declaring a function and calling it, I consistently get told that the function isn't defined, no matter what it is or what it contains.
I'm grabbing and running the code like so:
var code = Blockly.JavaScript.workspaceToCode();
try{
eval(code);
} catch (e){
alert(e);
}
Which is how the demos provide on Blockly generate code. I've also echoed the code out elsewhere in the page and it looks right to me:
function whatINameIt() {
//code I give it
}
//rest of code
Is this something to do with how eval works? The only thing I can think of is that for some reason it's "evaluating" the function code but not adding it as something callable. If that's the case, is there an alternate way I should run the code string Blockly gives me?
Maybe you are creating an infinite loop. To solve it, you will have to add the following lines as the documentation of Blockly says:
window.LoopTrap = 1000;
Blockly.JavaScript.INFINITE_LOOP_TRAP = 'if(--window.LoopTrap == 0) throw "Infinite loop.";\n';
var code = Blockly.JavaScript.workspaceToCode(workspace);
Also, if you have created custom Blocks, as it seems in your question, make sure that you are returning the code that you are creating in all of them. If you do not return it, the workspace will not know that these Blocks want to do whatever.
It would be great to help you if you provide the Blocks code that you are creating/using and the code that you are retrieving from your other function.
To make debugging easier, I'm capturing all of the console logs in Chrome so that users who submit a feedback entry will also submit all of the logs to our server. When someone encounters a problem in production, I can first and foremost get them back to work so that I can then sit down and more thoroughly go through all of the logs to determine the root cause of whatever issue the user encountered in production.
The technique I use to capture the logs involves overriding console.log so that all text entered in the first argument gets stored in an array while simultaneously invoking the legacy function so that I can still see the logs in the console too.
The problem is when there's the occasional uncaught exception. These aren't included in the uploaded logs, so it's not always clear what caused the problem. So I tried overriding ReferenceError by writing a JavaScript function that takes a function as an argument, then returns a new function that does stuff with it, like storing data in a variable, and then invoking the legacy function as the last step:
function overrideException(legacyFn) {
/** arguments for original fn **/
return function() {
var args = [];
args[0] = arguments[0];
// pass in as arguments to original function and store result to
// prove we overrode the ReferenceError
output = ">> " + legacyFn.apply(this, args).stack;
return legacyFn.apply(this, arguments);
}
}
To test the overrideException function, I ran the following code on the console:
ReferenceError = overrideException(ReferenceError);
Afterwards, I tested the returned function, the new ReferenceError, by manually throwing a ReferenceError:
throw new ReferenceError("YES!! IT WORKS! HAHAHA!");
The resulting output on the console is:
ReferenceError: YES!! IT WORKS! HAHAHA!
And checking the global variable output from the overrideException function shows that it did indeed run:
output
">> ReferenceError: YES!! IT WORKS! HAHAHA!
at ReferenceError (<anonymous>)
at new <anonymous> (<anonymous>:18:35)
at <anonymous>:2:7
at Object.InjectedScript._evaluateOn (<anonymous>:562:39)
at Object.InjectedScript._evaluateAndWrap (<anonymous>:521:52)
at Object.InjectedScript.evaluate (<anonymous>:440:21)"
Now, here's where things start to fall apart. In our code, we're not going to know when an uncaught exception occurs, so I tested it by attempting to run a function that doesn't exist:
ttt();
Which results in:
ReferenceError: ttt is not defined
However, unlike the case where we explicitly throw an error, in this case, the function doesn't fire, and we're left with only the legacy functionality. The contents of the variable output is the same as in the first test.
So the question seems to be this: How do we override the ReferenceError functionality that the JavaScript engine uses to throw errors so that it's the same one we use when we throw a ReferenceError?
Keep in mind that my problem is limited only to Chrome at this time; I'm building a Chrome Packaged app.
I have done quite a bit of research for the same reason: I wanted to log errors and report them.
"Overriding" a native type (whether ReferenceError, String, or Array) is not possible.
Chrome binds these before any Javascript is run, so redefining window.ReferenceError has no effect.
You can extend ReferenceError with something like ReferenceError.prototype.extension = function() { return 0; }, or even override toString (for consistency, try it on the page, not the Dev Tools).
That doesn't help you much.
But not to worry....
(1) Use window.onerror to get file name, 1-indexed line number, and 0-indexed position of uncaught errors, as well as the error itself.
var errorData = [];
onerror = function(message, file, line, position, error) {
errorData.push({message:message, file:file, line:line, position:position, error:error});
};
See the fiddle for an example. Since the OP was Chrome-specific, this has only been tested to work in Chrome.
(2) Because of improvements to (1), this is no longer necessary, but I leave this second technique here for completeness, and since onerror is not guaranteed to work for all errors on all browsers. You will also sometimes see the following:
var errors = [];
function protectedFunction(f) {
return function() {
try {
f.apply(this, arguments);
} catch(e) {
errors.push(e);
throw e;
}
};
}
setTimeout = protectedFunction(setTimeout);
setInterval = protectedFunction(setInterval);
etc...
FYI, all this is very similar to what has been done in the Google Closure Compiler library, in goog.debug, created during Gmail development with the intent of doing exactly this. Of particular interest is goog.debug.ErrorHandler and goog.debug.ErrorReporter.
My code seems to bring about some kind of quantum duel state in chrome, invoking a universe where functions turn into undefined upon being pushed to an array... spooky
I'm building an array of functions using the following code, in order to callback chain them together using this function https://gist.github.com/3609831
console.log "creating stack"
ids = (id for id of #s3) # an array of integers
stack = [callback]
console.log stack
for ssid of #s3
new_func = ((cb) => #idb.load_s3(ssids.shift(),cb))
console.log new_func
stack.push new_func
console.log stack
console.log "stack done"
What's bizarre is that although it actually seems to work; all the function calls with the right arguments seem to be happening, it also get this in the console (with my formatting and comments)
> creating stack
# callback added to array correctly
> [function () { return _this.start(true); }]
# function generated correctly
> function (cb) { return _this.idb.load_s3(ssids.shift(), cb); }
# but the function doesn't get added to the array !!!!!!!
> [function () { return _this.start(true); }, undefined × 1]
> stack done
# and the undefined from the array can't be executed of course, verified my line number
> Uncaught TypeError: undefined is not a function
which seems to mean that although it worked... it also didn't work... because... the new_func turns into undefined upon being pushed to the array...
WTF???
Anyone got any clues as to what might be happening here?
I'm using chrome v21
...
EDIT: fyi the #idb.load_s3 function IS in scope and I know it's working because it loads data from indexedDB which doesn't load if any part of this is broken, (and because the callback does fire).
...
EDIT2: here's the javascript, nothing very different or odd about it, just harder to read
var id, ids, new_func, ssid, stack,
_this = this;
console.log("creating stack");
ids = (function() {
var _results;
_results = [];
for (id in this.s3) {
_results.push(id);
}
return _results;
}).call(this);
stack = [callback];
console.log(stack);
for (ssid in this.s3) {
new_func = (function(cb) {
return _this.idb.load_s3(ssids.shift(), cb);
});
console.log(new_func);
stack.push(new_func);
console.log(stack);
}
console.log("stack done");
...
EDIT 3: Just realised that part of the problem is because Chrome's console is presumably asynchronous so it seems the last item is popped off the stack (in code not shown here) AFTER the call to console.log but BEFORE console.log has actually output the array contents!!! So when I don't call the function (see referenced gist) to execute the stack then the rest seems to work fine.
HOWEVER this doesn't solve the mystery of the exception complaining about undefined not being a function (like it's trying to pop undefined from the array and execute it)... still weird!
...
EDIT #4:
load_s3 function is here: https://github.com/gnatters/neuroanatomist/blob/master/app/assets/jax/models/asset_loader.js.coffee.erb#L118
the real life version of the code being questioned here is in the same file in load_everything at #L145
I worked it out! There are two parts to the problem which look like the same thing. Firstly, as in edit 3, the array looks like it contains an undefined item in the place where the function was pushed. However this would seem to be a quirk (bug?) in chrome's asynchronous console, whereby the function is removed from the array in the time between the console.log call and the actual logging of the array.
The second part of the problem was also deceptive in appearance, and sorry I didn't think this was so important when asking the question. In the linked gist (which has now be revised) the function was trying to pop and execute an extra function from the array which of course amounts to trying to all undefined. Not an easy one to spot...
All is well. Thanks for playing ;)