What I'm trying to do is the following:
Check if a record with a filter criteria exists
If it does, do nothing
If it does not, create it with some default settings.
Now I could do it with 2 queries:
function ensureDocumentExists(connection, criteria, defaults) {
return r.table('tbl')
.filter(criteria)
.coerceTo('array') // please correct me if there's a better way
.run(connection)
.then(([record]) => {
if (record) {
return Promise.resolve() // Record exists, we are good
} else {
return r.table('tbl') // Record is not there we create it
.insert(defaults)
.run(connection)
}
})
}
But the fact that r.branch and r.replace exists, suggest me that this would be possible in a single run. Is it? I was thinking something like this:
function ensureDocumentExists(connection, criteria, defaults) {
return r.table('tbl')
.filter(criteria)
.replace(doc => r.branch(
r.exists(doc), // If doc exists (I'm just making this up)
doc, // Don't touch it
defaults // Else create defaults
)).run(connection)
}
But I'm not sure if replace is the right method for this, and also no idea how to check if the given row exists.
Figured it out:
function ensureDocumentExists(connection, criteria, defaults) {
return r.table('tbl')
.filter(criteria)
.isEmpty() // isEmpty for the rescue
.do(empty => r.branch(
empty, // equivalent of if(empty)
r.table('tbl').insert(defaults), // insert defaults
null // else return whatever
).run(connection)
})
}
Related
I'm quite new to cypress, but I am wondering if there is some way to end a command chain conditionally? I know that conditional testing should be avoided in cypress, but I want try this out anyway.
What I've tried
I tried to solve it by passing in the chain to a custom command, but it doesn't seem to work.
Cypress.Commands.add('ifExists', { prevSubject: true }, (subject: object, element: string) => {
cy.get('body').then(($body) => {
if ($body.find(element).length) {
cy.wrap(object).end();
} else {
// something else
}
})
})
and also
Cypress.Commands.add('ifExists', { prevSubject: true }, (subject: JQuery<HTMLBodyElement>, element: string) => {
cy.wrap(subject).then(($body) => {
if ($body.find(element).length) {
return cy.wrap(subject);
} else {
return cy.wrap(subject).end();
}
})
})
And just a clarification on what I want from this command. I want to be able to add it to a chain like this:
cy.get('body > #specialThing').ifExists().then((thing) => ...)
or this:
cy.ifExists('body > #specialThing').then((thing) => ...)
All help and advice is appreciated!
I'd check out the Cypress documentation on conditional testing, in particular the part about element existence. The tl;dr is you just need to search for a more general element that yields something that you can then assert on.
y.get('body')
.then(($body) => {
// synchronously query from body
// to find which element was created
if ($body.find('#specialThing').length) {
// input was found, do something else here
return 'input'
}
// else assume it was textarea
return 'textarea'
})
.then((selector) => {
// selector is a string that represents
// the selector we could use to find it
cy.get(selector).type(`found the element by selector ${selector}`)
})
You can adapt the jQuery OR operator shown here Function to check if element exist in cypress.
You cannot it call using the cy.get('body > #specialThing').ifExists()... because the cy.get() will fail if the element isn't there, so this implies your custom command must be a parent command (no prevSubject).
Cypress.Commands.add('ifExists', { prevSubject: false }, (selector: string) => {
const orSelector = `${selector}, body`; // note comma, if "selector" fails return body
return Cypress.$(orSelector);
})
cy.ifExists('#specialThing').then((thing) => ...)
You may find it more useful to return null rather than body so that you can test the result more easily,
Cypress.Commands.add('ifExists', { prevSubject: false }, (selector: string) => {
const result = Cypress.$(selector);
return result.length ? cy.wrap(result) : null;
})
cy.ifExists('#specialThing').then((thing) => if (thing) ... else ... )
Warning this will inevitably lead to flaky tests. There is no retry in this command.
See Element existence
You cannot do conditional testing on the DOM unless you are either:
Server side rendering with no asynchronous JavaScript.
Using client side JavaScript that only ever does synchronous rendering.
It is crucial that you understand how your application works else you will write flaky tests.
Using cy.get('body').then(...) does not give you retry on the element you want to test conditionally.
I want the user to have the option to add more stepTypes, stepCodes and properties. He can add an stepCode with an existing StepType, or with a different stepType, so, the object would like similar to this:
You see? In the stepType called 'guide', I have 2 stepCodes (G019, G040). In the stepType called 'success', I have just one (S003), and so on. Since I'm newbie with js and even more with objects, I'd like you guys to help me creating a function that checks if the stepType already exists, and then adds another stepCode to it (with its properties). And, if it doesn't exist yet, I want this function to create this new stepType, with the stepCode and its properties.
Currently, my code looks like this:
const checkStep = () => {
if (!Object.keys(procedures).length) {
let proc =
{[key]:
{
[stepType]: {
[stepCode]: {
[language]: stepText,
timeout,
nextInstruction,
}
}
}
}
setProcedures(proc)
}
else{
Object.entries(procedures).forEach((p, k) =>{
...
})
}
}
I call this function everytime the user clicks the "Add another step" button. The first part checks if the object already exists, and, if it doesn't, it creates the object with its key and so on (this part is working). What I don't know how to do is the ELSE part. I think we have to check if the stepType already exists in the object called procedures, but I don't know how to do it. I don't know how to put the stepCode and properties inside the existing object(procedures) either. Maybe I create a variable and do like: setProcedures (...procedures, variable). I don't want to lose the content I have in the procedure, just to add more content to it in the way I explained you.
P.S.: All the variables (stepType, stepCode, language, stepText, timeout, nextInstruction) are an useState. When the user writes anything in the input text field, I set the specific variable with the e.target.value.
Thank you.
You can do the loop on the each keys and if it matches then add to existing data or create a new stepType and add.
var newStepType = "test", stepCode="test1", language ="en", stepText="hello", timeout=9, nextInstruction="new ins";
Object.keys(procedure.DOF0014).forEach(key => {
//if newStepType matches insert stepCode. eg: stepType is "guide"
if(key === newStepType) {
procedure.DOF0014[key] = { ...procedure.DOF0014[key], ...{[stepCode]: {[language]: stepText,timeout,nextInstruction}}}
}else{
procedure.DOF0014 = {...procedure.DOF0014, ...{[newStepType]:{[stepCode]: {[language]: stepText,timeout,nextInstruction}}}};
}
});
Try this. I didnt tested code. But hope it works. I am sharing the idea how to do.
Object.keys(procedure).forEach(codes => {
Object.keys(procedure[codes]).forEach(key => {
if(key === newStepType) {
procedure[codes][key] = { ...procedure[codes][key], ...{[stepCode]: {[language]: stepText,timeout,nextInstruction}}}
}else{
procedure[codes] = {...procedure[codes], ...{[newStepType]:{[stepCode]: {[language]: stepText,timeout,nextInstruction}}}};
}
});
})
In one of Vue my components I have code that looks like this
<li class="comment" v-for="comment in comments">
...
</li>
And my computed method
computed: {
comments() {
// All filter UI elements are associated with a filter method
// and when the user interacts with an element of the UI, filtersToApply gets populated with
// the associated method. Not too important right now,
// it just checks whether user interacted with the UI or not, so it could essentially be a boolean. But I plan to make use of the functionality at a later time.
const filtersToApply = this.filtersToApply();
// if user hasn't interacted with the filter UI, just return the comments
// else, apply filter(s) to comments
if (filtersToApply.length === 0) {
return this.$store.getters.comments;
} else {
return this.applyFilters(this.$store.getters.comments);
}
}
Ultimately, I want to do something like this:
// Apply the filters to the comments, one by one
applyFilters(comment) {
return this.filterByX()
.then(this.filterByY)
.then(this.filterByZ)
....
}
where the filter methods look like
filterByX(comments) {
return new Promise((resolve, reject) => {
.......
resolve(comments)
})
}
How could I make this work? And is it a good pattern?
Thanks to #wostex for pointing me in the right direction. Here's what I did (I'm using the lodash library to implement pipe):
Changed applyFilters function to
applyFilters(filters) {
const filteredComments = _.flow(filters)
return filteredComments(this.$store.getters.comments);
}
In the computed comments method I'm passing the array of filters to applyFilters which is the only other change I made.
const filtersToApply = this.filtersToApply();
if (filtersToApply.length === 0) {
return this.$store.getters.comments;
} else {
return this.applyFilters(filtersToApply);
}
If I remove an item using localStorage.removeItem("key"); is there any way I can check if the removal was successful or not? Something like a callback or promise?
Right now I am doing like this:
if(localStorage.getItem("key"))
localStorage.removeItem("key");
else
console.log("Error");
Is this the right way to do it or could this be done in a "better" way?
The removeItem() call does not return any indication of failure1 of any sort (excerpt taken from a recent editor's draft of the W3C web storage specification, with my emphasis):
The removeItem(key) method must cause the key/value pair with the given key to be removed from the list associated with the object, if it exists. If no item with that key exists, the method must do nothing.
Hence, the only way to tell if the key was an actual removal (as opposed to the "do nothing" action in the second sentence above) is to check it first.
You should almost certainly use explicit checking of the getItem() return value against null (with === of course) but that doesn't change the fact that you can't detect failure with removeItem() itself1.
Of course, if you're worried about peppering your code with these snippets, you're quite able to define a function to do the heavy lifting for you, something like:
function removeExistingItem(key) {
if (localStorage.getItem(key) === null)
return false;
localStorage.removeItem(key);
return true;
}
and then call it with the more succinct:
if (! removeExistingItem("key"))
console.log("Error");
1 This is based on "failure" being defined as the key not existing (as seems to be the definition you're using in the question). In reality, removeItem() cannot fail simply because it will do nothing in the case where the item does not exist (see inset above).
A more accurate check will be as below, else if the value of the key was ""(empty string) then it will fail
if (localStorage.getItem("key") !== null) {
localStorage.removeItem("key");
} else {
console.log("Error");
}
Storage.getItem() will return null if the key is not found
I ran into this same issue today [06-23-2019], and adjusted #paxdiablo's answer to add a few more fail-safes to the solution. I really hope my version helps someone else save the time and headache I went through:
/* Conditional Function supplements localStorage.removeItem(...) to return a [Boolean] 'success' or 'failure' value. [BEGIN] */
if (typeof removeLocalStorageItem !== 'function')
{
function removeLocalStorageItem(key)
{
if (typeof (Storage) !== 'undefined')
{
if (localStorage.getItem(key) === null)
{
return false;
};
localStorage.removeItem(key);
if (localStorage.getItem(key) === null)
{
return true;
}
else
{
return false;
};
}
else
{
return false;
};
};
};
/* Conditional Function supplements localStorage.removeItem(...) to return a [Boolean] 'success' or 'failure' value. [END] */
I am creating a module that takes in several complicated JSON files and would like some code to give the user feedback if certain elements are absent.
Below is the way I am doing it now, but I cannot help to think there must be a cleaner, less hacky way.
var _und = require("underscore");
//this function takes a list of required attributes and ensures they are present
var check_req_attr = function(config, req_attr, callback) {
var config_attr = Object.keys(config);
var absent_attr = _und.difference(req_attr, config_attr); //slightly hacky code that checks to ensure config has correct vars
if (absent_attr.length !== 0) {
throw Error("missing following attributes from config:" + absent_attr);
} else {
callback();
};
};
It just feels...dirty. If there is no real elegant way to do it, I would be open to critiques on my code. Thanks!
Parse the JSON to JS.
var data = JSON.parse(theJson);
Use something like:
function hasKey(obj, key) {
return typeof obj[key] !== 'undefined';
};
function hasKeys(obj, keys) {
for (var i = 1, len = keys.length; i < len; i++) {
if (!hasKey(obj, keys[i])) {
return false;
};
};
return true;
};
Now you can simply do:
if (hasKeys(data, ["firstKey", "secondKey", "thirdKey"]) {
console.log("valid");
};
This should be the way to do it, using every and has:
if (_und.every(req_attr, function(attr) {
return _und.has(config, attr);
}))
throw new Error();
In a native environment, you would just use the in operator:
req_attr.every(function(attr){ return attr in config; })
I think your solution is actually quite elegant! No need for an anonymous function, and the loop (which must happen at some point, obviously) neatly abstracted away with difference.
Two suggestions:
I'd give the function a synchronous signature. No callback argument. There can't be any reason to go async if you honor the function signature (i.e. basing your answer on config and req_attr only).
I'd change the function to return the missing properties (attributes is wrong term). You could also add a requireProperties function that uses this "check" function that would throw if a property was missing. This allows for different kind of uses.
Why don't you try with something like:
obj = JSON.parse(json);
and then check
if(obj.YourProperty == undefined){
//do something..
}
Hope i understood your question.. It should work with complicated JSON files too.. Good luck ;)
You could also use the in operator (requiredAttr in obj):
function objHasAllRequiredAttrs(obj, attrNames) {
return attrNames.reduce(function(memo, attrName) {
return memo && (attrName in obj);
}, true);
}
objHasAllRequiredAttrs({foo:1}, ['foo']); // => true
objHasAllRequiredAttrs({bar:1}, ['foo']); // => false