If I want to parse a text field in SSJS there are 2 main tools. The built in JavaScript code and the newly converted #Functions. Are the #Functions slower then using pure javascript? Or is there no real difference?
viewScope.put("length", tmpStr.length)
vs.
viewScope.put("length:, #Length(tmpStr))
All SSJS is parsed into an AST (abstract syntax tree) at runtime. In other words, your code just remains a String until the exact moment that it is executed, at which point a parser examines that String to syntactically identify what the code contains: which characters denote variables, which are operators, functions, etc. Once that parsing is complete, the runtime engine is able to run Java code that is a rough approximation of what the JavaScript code was designed to do.
This is why SSJS is always slower than the directly equivalent Java: if you just write your code in Java to begin with, then it's compiled into bytecode the moment you build your project, but perhaps more importantly, at runtime it doesn't have to "guess" what code to run by parsing a String... it just runs the Java code you already defined.
On the other hand, there's nothing about this process that significantly distinguishes the SSJS implementation of various #Functions from "native" JavaScript; given that #Length(tmpStr) is just a wrapper for tmpStr.length, it doesn't surprise me that Sven is seeing a difference in execution time given enough iterations. But if your goal is optimization, you'll gain far more improvement by moving all code from SSJS blocks to bean methods than you will by eschewing the convenience of #Functions in favor of native JavaScript, because even native JavaScript has to be parsed into an AST. In that sense, there is no fundamental difference between the two.
UPDATE: there's a slight caveat to the AST parsing mentioned at the beginning of this answer. By default, the XPages runtime caches up to 400 unique SSJS expressions (you can override this limit via the ibm.jscript.cachesize property in the server's xsp.properties file). So if an expression is encountered that matches exactly (including whitespace) one that is already cached, Domino doesn't have to construct a new AST for that expression; it just references the tree already in cache. This is a MRU ("most recently used") cache, so the more frequently the same expression is encountered, the more likely it is to remain in the cache. Regardless of whether the AST is cached, it still has to be evaluated against the current context, and some of the JavaScript wrapper objects do have additional overhead compared to what you'd likely use instead if you were just coding directly in Java (for instance, {} becomes an ObjectObject, which is similar to a HashMap, but has additional features that support closures, which are just wasted if you're not using closures anyway). But the primary performance implication of this AST cache is that, unlike in most development contexts, duplication of code can actually be a good thing, if only in the sense that using the same exact expression over and over again allows all but the first instance of each to skip the language parsing and jump straight to invocation.
For me it seems that the #Formula is not as fast as using SSJS-code.
You can easily test by yourself with some code like this one (reload page multiple times to get serious results):
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:label id="label1">
<xp:this.value>
<![CDATA[#{javascript:
var start = java.lang.System.currentTimeMillis();
var testString = "0123456789";
var dummy;
for( var i=0; i<100000; i++ ){
dummy = #Length( testString )
}
var stop = java.lang.System.currentTimeMillis();
stop - start + " ms"}]]>
</xp:this.value>
</xp:label>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:label id="label2">
<xp:this.value>
<![CDATA[#{javascript:
var start = java.lang.System.currentTimeMillis();
var testString = "0123456789";
var dummy;
for( var i=0; i<100000; i++ ){
dummy = testString.length;
}
var stop = java.lang.System.currentTimeMillis();
stop - start + " ms"}]]>
</xp:this.value>
</xp:label>
</xp:view>
I dont think #formula in SSJS is as fast as traditional #formula. One of the reasons would be that #formula is just another layer on top of SSJS functionality and therefore there is a bit more code to execute.
But that's just a wild guess.
As far as #DBLookup, I did some testing and found it was significantly slower then getting the view in SSJS, then doing a getDocumentByKey and getting fields from the found NotesDocument. At least 10 times slower when I looped four #DBLookups 100 times vs. getting the document 100 times and then getting the four fields.
Howard
tmpStr.length is browser native function (not in Javascript but code inside the browser and compiled)
#Length(tmpStr) is a javascript function, so it is interpreted by the browser
Related
In FileNet, using FEM or ACCE, it is possible to use JScript in order to set attributes' values to an object. The official IBM guide provides this example (for Strings):
importClass(Packages.com.filenet.api.property.Properties);
importClass(Packages.com.filenet.api.constants.RefreshMode);
function OnCustomProcess (CEObject)
{
CEObject.refresh();
CEObject.getProperties().putValue("DocumentTitle", "Test1");
CEObject.save(RefreshMode.REFRESH);
}
But is it possible to do the same thing for more complex objects? I'm referring, in particular, to StringList type. There are no examples on the web, and defining a JS-like array doesn't work.
It is definitely possible to do this for more complex objects. Most of it is just following the path you would follow using Java, but changing the name of the variable types to var. Therefore the code for setting the value of a multivalue string property is as follows:
importClass(Packages.com.filenet.api.constants.RefreshMode);
importClass(Packages.com.filenet.api.core.Factory);
function OnCustomProcess (CEObject)
{
CEObject.refresh();
var list = Factory.StringList.createList();
list.add("Value 1");
list.add("Value 2");
CEObject.getProperties().putObjectValue("TestMultiValueProperty1", list);
CEObject.save(RefreshMode.REFRESH);
}
I often use the putObjectValue() method instead of the putValue() method because JavaScript sometimes has problems determining which type safe version of the putValue() it should use.
For a lot of examples you could go to the Global Configuration > Data Design > Add-ons section in the domain tab of the ACCE. The pre- and post-import scripts of the different Add-ons contain a lot of relevant JavaScript code.
Is this: cy.get('[name=planSelect]').contains(dummyPlan)
equivalent to this: cy.get('[name=planSelect]').should('contain', dummyPlan)
And if so, which is preferred? The first is more of an implicit assertion, but it's shorter and cleaner to my mind.
Follow-up question: After looking around to see how best to select elements for e2e testing I found that the Cypress docs recommend using data-cy attributes. Is there a reason this would be better than just adding name attributes to the markup? Should name only be used for forms fields?
The result on your cypress test will be the same if the element with name=planSelect does not contain dummyPlan, that is, the test will fail at this point.
The difference between them is that in the first form, using contains(), you're actually trying to select an element, and the result of cy.get(...).contains() will yield this expected DOM element, allowing for further chaining of methods, like:
cy.get('[name=planSelect]').contains(dummyPlan).click();
In the second form you are making an explicit assertion to verify that dummyPlan exists within the other element, using the Chai chainer contain.
It is a subtle difference and the result is the same, but I would recommend you to use cy.get('[name=planSelect]').contains(dummyPlan) only in case you would like to chain some other method after contains, and use the second form if you want to explicitly assert that this element exists. Logically speaking, the first would represent a generic test failure (cypress tried to find an element that wasn't there) and the second represents an explicit assertion failure (element should contain dummyPlan but it does not).
As for your second question, name is a valid HTML attribute and using it for your tests can lead to confusion if the attribute is being used in its original function (to name input fields) or if the attribute is there just for testing purposes. I would recommend you to use cy-name as the documentation suggests because this way you avoid this ambiguity and make it clear that this attribute cy-name is only there for testing purposes.
Furhtermore, on some situations you might decide to strip all cy-name from your code before sending it to production (during the build process, using some webpack plugin, like string-replace-loader). You would not be able to do the same if using just name because you would also remove the required input name, if there was some inputs in your code.
Answer
.contains(selector, content) is the best selector; it retries
element selection AND allows text matching (not just <tag>
.class #id [attributes])
.should() is just an assertion and only the assertion is retried
(not the element selection)
.should('exist') is implied unless you specify your own -- this is how they allowed .should('not.exist')
Tangent
Browsers support XPath 1.0 which is a pretty cool but obscure way to make complex queries based on DOM tree traversal. There's a contains predicate function:
//*[ contains(normalize-space(.), 'The quick brown fox jumped over the lazy dog.') ]
[not(.//*[contains(normalize-space(.), 'The quick brown fox jumped over the lazy dog.') ])]
This searches from root of the document for any node that contains the text and doesn't contain a descendant node which contains the text.
You can test it in the console with the Chrome $x() shortcut or this polyfill (and helper):
getLowestDomNodesByText("The quick brown fox jumped over the lazy dog.")
function getLowestDomNodesByText (text) {
return x(`//*[contains(normalize-space(.), '${text}')][not(.//*[contains(normalize-space(.), '${text}') ])]`);
};
function x (expression) {
const results = new XPathEvaluator().evaluate(expression, document);
const nodes = [];
let node = null;
while (node = results.iterateNext()) {
nodes.push(node);
}
return nodes;
}
If you need even more performance, you can use a TreeWalker with NodeFilter.SHOW_TEXT as seen in this chrome extension I've worked on for a long time
I recommend to use contains after get then verify existence with should.
cy.get('[name=planSelect]').contains(dummyPlan, {matchCase: false}).should('exist')
I am modifiyng exisiting code which currently does
currentDocument.setValue("field", requestScope.variable);
This works well, but I now need to make it a little more dynamic so, I have the requestScope.variable stored as a string. I know I can do:
requestScope["variable"]
but this relies on me knowing which scope it is in. Given that the string could be requestScope.variable or even applicationScope.variable I think this route is closed to me.
I had also thought of splitting the string based on the . but think that there must be a more straightforward option.
How can I go about doing this?
Use ${javascript:'requestScope.variable'} to get the value of the scope variable which is defined as a string.
Your example would be
currentDocument.setValue("field", ${javascript:'requestScope.variable'});
${javascript:...} works here a preprocessor and inserts the result into the code.
Here is a complete example:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.beforePageLoad><![CDATA[#{javascript:
sessionScope.test = "this is the value of sessionScope.test";
viewScope.other = "this is the value of viewScope.other";
requestScope.myScopeVariable = "viewScope.other";
}]]></xp:this.beforePageLoad>
<xp:text
escape="true"
id="computedField1"
value="#{javascript:
var valueFromSessionScope = ${javascript: 'sessionScope.test'};
var valueFromViewScope = ${javascript: requestScope.myScopeVariable};
valueFromSessionScope + ' ... ' + valueFromViewScope;
}">
</xp:text>
</xp:view>
It shows as result:
this is the value of sessionScope.test ... this is the value of viewScope.other
Hmm, I've never seen requestScope["variable"] though that makes sense. I typically use something like requestScope.get("variable").
Also my scoped variables always get a prefix to indicate the scope. rsVariable, ssVariable, vsVariable, asVariable. If you did something like that then you could maybe inspect your variable name and pick the right scope. BUT the reason I did that is I had some kind of scope collision once. I forget the circumstances. But remember, XPages does a lot to "resolve the variable". I'm not fully sure how this "variableRolver" as we call it works in SSJS. But similar to how I had that problem once, there's likely someway to get a variable without knowing the scope.
Worse case, as long as your variables have unique names, remember that scoped variables are just Java HashMaps. So you can do something like : viewScope.containsKey("ssMyVariable") so see if it exists in that scope.
Just some quick thoughts.
How to clean SSJS (Server Side Javascript) in Domino server after someone used javascript prototype in a nsf?
Mark Roden discovered a huge weakness in XPages SSJS: (thanks to David Leedy for tell me about this and show me the article).
If you have the following SSJS code:
var dummyObj = {}
dummyObj.prototype.NAME = "Johann"
XPages SSJS doesn't care that you uses var (var means the variable must be local) and it makes dummyObj.NAME visible in the whole server with the value Johann. So if another nsf in the same server uses a var with the same name it inherits the whole prototype:
var dummyObj = {}
println(dummyObj.NAME) /*prints "Johann" */
This is a huge bug (one that makes unreliable XPages SSJS IMO). Even if you don't use prototype at all, if someone else in his application do something like this:
String.prototype.split = function(){ return "I broke this method" }
It will broke all applications in the same server that uses the innocent split().
So, the question is: if someone "by mistake" writes the following SSJS (XPages Server Side Javascript) in a NSF:
String.prototype.split = function(){ return "I broke this method" }
How can I fix String.prototype.split() to his original value?
As Mark Roden said, restarting HTTP task doesn't fix it.
///////////////////////////////////////////////////////////
Edit 1: Why I think this is a huge bug:
I'm a Javascript fan but IMHO #MarkyRoden has discovered a huge bug in SSJS. Shims and polyfills aren't really the main problem. Eval is known to be a bad practice but the prototype object is a fundamental element of basic Javascript. It's the standard and preferred way to add methods to Javascript classes, it's also needed for inheritance and all kind of OOP stuff. So you will need some kind of namespace at server level in order to avoid collisions. All this is really bad but the huge problem is that just a line of code in one application can broke all applications in a server. Yes, you can trust in your developers but one of them can write a bad line by mistake and also a Domino server can have hundreds of applications from different software vendors. Set the responsability in code reviews is not a reliable enought procedure. Maybe it's time to have a real javascript engine in SSJS, like V8, Spidermonkey, Chakra or Rhino. As a workaround, I'm thinking in something like Tommy Valand's idea with Rhino in SSJS.
Edit 2: It's even worse. You can do things like:
prototype.importPackage = null
or
prototype.Array = null
As you can see in #SvenHasselbach's article: http://hasselba.ch/blog/?p=1371
Edit 3: IBM: you told me I could use SSJS. COME ONE! PLEASE FIX THIS, it's AWFUL. Please let's officially report this issue to IBM.
You can reset the SSJS interpreter with the following Java code:
FacesContextExImpl fc = (FacesContextExImpl) FacesContextExImpl.getCurrentInstance();
UIViewRootEx2 uiRoot = (UIViewRootEx2) fc.getViewRoot();
JSContext jsContext = uiRoot.getJSInterpreter().getJSContext();
jsContext.getRegistry().init(jsContext);
This reinitializes the registry and all prototype functions.
EDIT: Changed the declaration of fc to the correct type.
EDIT 2:
Here is the SSJS version:
var uiRoot = facesContext.getViewRoot();
var jsContext = uiRoot.getJSInterpreter().getJSContext();
var reg = jsContext.getRegistry();
reg.init( jsContext );
Does I understand you correctly, that you want to clean up the SSJS interpreter to avoid a collision with your own prototype extension?
Just to clarify the answer above: This reinitializes the SSJS interpreter once. And only once.
You have to do this over and over again, because directly after reinitializing, another application on the server can overwrite the prototype functionality again. That's why this is not a real solution, it is an answer to your initial question.
It will have interessting consequences if another application will do the same while your code tries to use your extension...
try to do a Restart Task Http instead
tell http restart will not do a full restart of the http task
I am wondering if there is a way to ignore certain TypeScript errors upon compilation?
I basically have the same issues most people with large projects have around using the this keyword, and I don't want to put all my classes methods into the constructor.
So I have got an example like so:
TypeScript Example
Which seems to create perfectly valid JS and allows me to get around the this keyword issue, however as you can see in the example the typescript compiler tells me that I cannot compile that code as the keyword this is not valid within that scope. However I don't see why it is an error as it produces okay code.
So is there a way to tell it to ignore certain errors? I am sure given time there will be a nice way to manage the this keyword, but currently I find it pretty dire.
== Edit ==
(Do not read unless you care about context of this question and partial rant)
Just to add some context to all this to show that I'm not just some nut-job (I am sure a lot of you will still think I am) and that I have some good reasons why I want to be able to allow these errors to go through.
Here are some previous questions I have made which highlight some major problems (imo) with TypeScript current this implementation.
Using lawnchair with Typescript
Issue with child scoping of this in Typescript
https://typescript.codeplex.com/discussions/429350 (And some comments I make down the bottom)
The underlying problem I have is that I need to guarantee that all logic is within a consistent scope, I need to be able to access things within knockout, jQuery etc and the local instance of a class. I used to do this with the var self = this; within the class declaration in JavaScript and worked great. As mentioned in some of these previous questions I cannot do that now, so the only way I can guarantee the scope is to use lambda methods, and the only way I can define one of these as a method within a class is within the constructor, and this part is HEAVILY down to personal preference, but I find it horrific that people seem to think that using that syntax is classed as a recommended pattern and not just a work around.
I know TypeScript is in alpha phase and a lot will change, and I HOPE so much that we get some nicer way to deal with this but currently I either make everything a huge mess just to get typescript working (and this is within Hundreds of files which I'm migrating over to TypeScript ) or I just make the call that I know better than the compiler in this case (VERY DANGEROUS I KNOW) so I can keep my code nice and hopefully when a better pattern comes out for handling this I can migrate it then.
Also just on a side note I know a lot of people are loving the fact that TypeScript is embracing and trying to stay as close to the new JavaScript features and known syntax as possible which is great, but typescript is NOT the next version of JavaScript so I don't see a problem with adding some syntactic sugar to the language as people who want to use the latest and greatest official JavaScript implementation can still do so.
The author's specific issue with this seems to be solved but the question is posed about ignoring errors, and for those who end up here looking how to ignore errors:
If properly fixing the error or using more decent workarounds like already suggested here are not an option, as of TypeScript 2.6 (released on Oct 31, 2017), now there is a way to ignore all errors from a specific line using // #ts-ignore comments before the target line.
The mendtioned documentation is succinct enough, but to recap:
// #ts-ignore
const s : string = false
disables error reporting for this line.
However, this should only be used as a last resort when fixing the error or using hacks like (x as any) is much more trouble than losing all type checking for a line.
As for specifying certain errors, the current (mid-2018) state is discussed here, in Design Meeting Notes (2/16/2018) and further comments, which is basically
"no conclusion yet"
and strong opposition to introducing this fine tuning.
I think your question as posed is an XY problem. What you're going for is how can I ensure that some of my class methods are guaranteed to have a correct this context?
For that problem, I would propose this solution:
class LambdaMethods {
constructor(private message: string) {
this.DoSomething = this.DoSomething.bind(this);
}
public DoSomething() {
alert(this.message);
}
}
This has several benefits.
First, you're being explicit about what's going on. Most programmers are probably not going to understand the subtle semantics about what the difference between the member and method syntax are in terms of codegen.
Second, it makes it very clear, from looking at the constructor, which methods are going to have a guaranteed this context. Critically, from a performance, perspective, you don't want to write all your methods this way, just the ones that absolutely need it.
Finally, it preserves the OOP semantics of the class. You'll actually be able to use super.DoSomething from a derived class implementation of DoSomething.
I'm sure you're aware of the standard form of defining a function without the arrow notation. There's another TypeScript expression that generates the exact same code but without the compile error:
class LambdaMethods {
private message: string;
public DoSomething: () => void;
constructor(message: string) {
this.message = message;
this.DoSomething = () => { alert(this.message); };
}
}
So why is this legal and the other one isn't? Well according to the spec: an arrow function expression preserves the this of its enclosing context. So it preserves the meaning of this from the scope it was declared. But declaring a function at the class level this doesn't actually have a meaning.
Here's an example that's wrong for the exact same reason that might be more clear:
class LambdaMethods {
private message: string;
constructor(message: string) {
this.message = message;
}
var a = this.message; // can't do this
}
The way that initializer works by being combined with the constructor is an implementation detail that can't be relied upon. It could change.
I am sure given time there will be a nice way to manage the this keyword, but currently I find it pretty dire.
One of the high-level goals (that I love) in TypeScript is to extend the JavaScript language and work with it, not fight it. How this operates is tricky but worth learning.