Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

[Sanity Check] Defined object to setAttrs not working

1579113233

Edited 1579113439
John D.
Pro
Sheet Author
I'm refactoring my code to be more efficient (thanks again for the perspective Chris D and Scott C!) but I'm running into an issue that can be summed up in the immortal words of Jacobim Mugatu, "I feel like I'm taking crazy pills!"  I found this post for guidance, here , where Scott C suggests building an object variable to be used in a setAttrs() function call.  However, when I do this the attribute does not get set.  I believe I've gone through all permutations but to no avail.  If I pipe the object into the console I can see that the object is correctly constructed: {repeating_fluxattributes_-lr0cvx67yq2c3k7xf50_fluxenable: 0} Any suggestions are welcome!  Below is my code, the code that is failing is in bold. // *** Main Script *** // if (eventInfo.sourceType == "sheetworker") return; var repeating_source = eventInfo.sourceAttribute.substring(0, eventInfo.sourceAttribute.lastIndexOf('_')), sourceattr = eventInfo.sourceAttribute.split('_'), isFluxAttr = (sourceattr[1] == "fluxattributes" || sourceattr[1] == "itemfluxattributes") ? 1:0, activate = parseInt(eventInfo.newValue), getAttrsString = [repeating_source+"_attribute","CONST_SHOW_DEBUGGING_MESSAGES"]; if (isFluxAttr) getAttrsString.push(repeating_source+"_fluxenable"); getAttrs(getAttrsString, function(value) { var debug = parseInt(value.CONST_SHOW_DEBUGGING_MESSAGES), attribute = value[repeating_source+"_attribute"], fluxenable = parseInt(value[repeating_source+"_fluxenable"]); if (isFluxAttr && !fluxenable && activate) { var equipstate = {}; equipstate[repeating_source+"_fluxenable"] = 0; setAttrs(equipstate); return; } }); My ultimate goal here is to have a script run through all the attributes that will need to get set, build an object with those attributes and values, and then call settAttrs() with this object to write all the attributes in one call rather than 20-30+ individual calls.  This is just my first iteration, and failing miserably. :'(
1579118099

Edited 1579118373
GiGs
Pro
Sheet Author
API Scripter
Is this a sheet worker, that is being triggered by an API script? You cant use eventinfo reliably when scripts are involved. There's a bug where the event.attributes are not set properly. On a quick scan, this line will be the problem: activate = parseInt(eventInfo.newValue), since newValue doesnt get set properly when workers are triggered by API scripts. If thats the case, you need to use getAttrs to grab all the relevant attributes. There are ways to do that. If it's not the case, can you post your full sheet worker code (including the on(change) line, with a list of what the source attributes can be, and a general description of what the worker is meant to do. 
1579127567

Edited 1579127917
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Glad the advice in the other thread was helpful John, and glad you found my advice on constructing the setObj helpful as well. As GiGs said, I would avoid using the newValue/previousValue of the event info until it gets fixed. However, there are several places where you can optimize this code to do things in a more streamlined manner. With my example below, I'm assuming that this is always going to be triggered by a repeating attribute: if (eventInfo.sourceType == "sheetworker") return; var repeating_source, sourceattr, isFluxAttr,     getAttrsString = ["CONST_SHOW_DEBUGGING_MESSAGES"];          eventInfo.sourceAttribute.replace(/^(repeating_(.+?)_(.+?))_(.+)/,(match,wholeRepeat,sectionName,rowID,attributeName)=>{         repeating_source = wholeRepeat;         sourceattr = attributeName;         getAttrsString = [...getAttrsString,`${repeating_source}_attribute`,`${repeating_source}_${attributeName}`];         isFluxAttr = /fluxattributes/i.test(sectionName);         if(isFluxAttr){             getAttrsString.push(`${repeating_source}_fluxenable`);         }     }); getAttrs(getAttrsString, function(value) {     var debug = parseInt(value.CONST_SHOW_DEBUGGING_MESSAGES),         activate = parseInt(value[`${repeating_source}_${sourceAttr}`]),         attribute = value[`${repeating_source}_attribute`],         fluxenable = parseInt(value[`${repeating_source}_fluxenable`]),         equipstate = {};     if (isFluxAttr && !fluxenable && activate) {         equipstate[repeating_source+"_fluxenable"] = 0;     }     setAttrs(equipstate); }); I haven't tested that since it's out of context, but it should work. Note that I'm using the function argument for the second argument of .replace. This is passed the entire regex match, and each of the collection groups (denoted by wrapping them in parentheses). Using .replace in this way can combine a lot of string parsing and iteration into one expression. Given a sourceAttribute of repeating_fluxattribute_-jkjlKJoiu-adsfasdf_attribute  the arguments of that replace function will be: match: repeating_fluxattribute_-jkjlKJoiu-adsfasdf_attribute wholeRepeat: repeating_fluxattribute_-jkjlKJoiu-adsfasdf sectionName: fluxattribute rowID: -jkjlKJoiu-adsfasdf attributeName: attribute
1579387144

Edited 1579388494
John D.
Pro
Sheet Author
GiGs and Scott C, thank you both for your replies, suggestions and inquiries.  Apologies up front because I am an idiot!  After writing this entire manifesto of what should happen but does not..I figured out what my problem was.  The wrong variable...  *smashes head against table* However, Scott, your response did spark some questions.  The new value part of the replace() method, are those five variables (match,wholeRepeat,sectionName,rowID,attributeName).  Are they natively recognized in Roll20?  How in blazes did you define them otherwise? *mind in blown loop* Second question.  What’s the reason for referencing a variable using `${}` ?
1579427401

Edited 1579430684
GiGs
Pro
Sheet Author
API Scripter
I'll leave the first question to Scott because I don't understand that either, hehe. But I can handle the second part: this  `${repeating_source}_attribute` is called  template literal syntax, and is the same as writing this more traditional string notation: repeating_source + '_attribute' template literal syntax is more powerful and flexible than normal strng syntax. You start and end with backticks like this `, not the usual ' or ". Everything inside the backticks is kept, so you can include line breaks as you would in normal text, like `This is an example sentence. And here is a variable value: ${variable}.` The ${} is a placeholder, anything inside is processed as being outside the string and its return value is what appears in the string. So if you want a variable's value you can include that, without having to break up the string into separated sections, the way you do with traditional string syntax. But you can also include processing within the code block. `A CALCULATION. Weight: ${weight}lbs. Quantity: ${quantity}. Total Weight: ${weight * quantity}lbs.` You can include function calls. You can even have placeholders within code blocks inside placeholders, and some very complex calculations.  For roll20 purposes, though, one of the big benefits of this syntax is creating html-styled output for stuff printed to chat. It's a painful experience using traditional string syntax to build say a div or table, with various styles applied to different sections, and have actual values inserted in each row or section. It takes a lot of broken up string fragments to construct, but with a template literal, you just build a template (hence the name) as a single string with placeholders for the actual values that will be in the printout to chat. 
1579452332
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
GiGs' explanation of template literals is excellent, and better than I would have done! So, thanks for that GiGs. As for the .replace confusion, I'll try to clear that up. string.replace can be used in two ways. The first, and probably most common, is to just do a predefined replace like: 'Here's a Sentence we want to edit'.replace(/sentence/i,'string');// results in: 'Here's a sentence we want to edit' or 'Here's a sentence we want to completely change'.replace(/here's a sentence (.+)/i,'$1 this sentence.'); //results in: 'we want to completely change this sentence' But, what if we want to do some logic in that replace? For instance, in that second one, it'd really be better if we could properly capitalize the new start of the sentence. We could of course just manually do it, but that doesn't help if we want to use a generic pattern like I have above. This is what the second .replace syntax is for. Instead of using a string as the second argument, you can use a function call. The entire match, and each of the capturing groups are passed as arguments to the function. So, for our demonstration string replacement, we could do: 'Here's a sentence we want to completely change'.replace(/here's a sentence (.+)/i,(match,oldEnd)=>{     oldEnd = sentenceCase(oldEnd);//Note, this is a notional function whose purpose is to capitalize sentences correctly.     return `${oldEnd} this sentence.` }); //results in: 'We want to completely change this sentence' All that I've done in that function call is assign them names just like you would in a normal function call. So, my .replace in the post above is equivalent to:     eventInfo.sourceAttribute.replace(/^(repeating_(.+?)_(.+?))_(.+)/,function(match,wholeRepeat,sectionName,rowID,attributeName){         repeating_source = wholeRepeat;         sourceattr = attributeName;         getAttrsString = [...getAttrsString,`${repeating_source}_attribute`,`${repeating_source}_${attributeName}`];         isFluxAttr = /fluxattributes/i.test(sectionName);         if(isFluxAttr){             getAttrsString.push(`${repeating_source}_fluxenable`);         }     }); or     eventInfo.sourceAttribute.replace(/^(repeating_(.+?)_(.+?))_(.+)/,function(){         repeating_source = arguments[1];         sourceattr = arguments[4];         getAttrsString = [...getAttrsString,`${repeating_source}_attribute`,`${repeating_source}_${arguments[4]}`];         isFluxAttr = /fluxattributes/i.test(arguments[2]);         if(isFluxAttr){             getAttrsString.push(`${repeating_source}_fluxenable`);         }     }); Note that I would never recommend coding like the second option, but it illustrates the relationship between the names in the function definition and the arguments object nicely. Additionally, note that the return value is not strictly necessary. In my code in the post above, I don't return anything because we don't need a modified version of the string, we're just pulling data out of it. Hope that helps, Scott
1579454264

Edited 1579454275
GiGs
Pro
Sheet Author
API Scripter
Thanks Scott! I think the confusion probably stems not so much from the replace function, but the way you get return values from regex, and how capturing groups works. Thats something i havent learned yet, and should devote some attention to it at some point.
1579458391

Edited 1579458477
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Ah, that's actually pretty easy, and you'e right, I should have explained that. The capturing groups are passed in order from left to right. So a regex of /(lorem) (ipsum) (loqua)/ would give you the following arguments: Whole match: lorem ipsum loqua capture group 1: lorem capture group 2: ipsum capture group 3: loqua Additionally, the order is based on where they start, so a regex of /((lorem) (ipsum)) (loqua)/  would give you the following arguments: Whole match: lorem ipsum loqua group 1: lorem ipsum group 2: lorem group 3: ipsum group 4: loqua EDIT: For figuring out complicated regex's, I love regexr.com
1580104087

Edited 1580104173
John D.
Pro
Sheet Author
Scott, took some time to chew on this but I think I finally got it!  In theory, but I'm ready to experiment.  Very much appreciate you taking the time to explain this regex black magic.  ;) GiGs, thank you as well for taking the time to explain template literals!  I also read up on these and understand them a bit better, but I think I need some experimentation to really sort out where and how I can leverage these. Take care guys! PS: this is really good information.  Is there a best practices wiki this could go on before it gets buried?