Is there a way to differentiate between false and undefined in a dust template? - dust.js

Is there any way to differentiate between false and undefined in a dust template? I believe:
{^myVariable}
Hello
{/myVariable}
would print Hello if myVariable is undefined right?

As you've noticed, Dust doesn't compare on truthiness, but on "emptiness", so you can't specifically check for a variable's falsiness.
You can use the {#eq} comparator instead, but you have to be careful with what you compare. You can't blindly use the Javascript keyword undefined, because Dust will just read it as a reference to the variable undefined (which is probably safe, but could give you a bug). So this is OK:
{#eq key=myVariable value="undefined" type="string"}myVariable is undefined{/eq}
{#eq key=myVariable value=somethingImSureDoesntExist}myVariable is undefined{/eq}
But you can't test the reverse, since using type="boolean" will cast both key and value
{#eq key=myVariable value="false" type="boolean"}
myVariable might be 0, undefined, null, false, etc...
{/eq}
{#eq key=myVariable value=somethingInContextIKnowIsAlwaysFalse}
This is sloppy because you have to include a dummy "false" in your context, but it works!
{/eq}
So if you really need to test === false, you should write a quick helper:
dust.helpers.isFalse = function(chunk, context, bodies, params) {
return context.resolve(params.key) === false;
}
And use it like:
{#isFalse key=myVariable}
myVariable is exactly false!
{:else}
myVariable is something else
{/isFalse}
All this said, you will probably be much happier if you allow Dust to use its emptiness checks instead, by not requiring your templates to care about the difference between undefined and false.

Related

Attempt to call global 'this' (a nil value)

I can see that similar questions has been asked, however I'm not really that familiar with lua coding.
I'm trying to fix an old World of Warcraft vanilla addon, to run in the Classic client.
The code is as follows:
function FHH_OnLoad()
this:RegisterEvent("PLAYER_ENTERING_WORLD");
this:RegisterEvent("UPDATE_MOUSEOVER_UNIT");
-- Register Slash Commands
SLASH_FHH1 = "/huntershelper";
SLASH_FHH2 = "/hh";
SlashCmdList["FHH"] = function(msg)
FHH_ChatCommandHandler(msg);
end
local version = GetAddOnMetadata("GFW_HuntersHelper", "Version");
GFWUtils.Print("Fizzwidget Hunter's Helper "..version.." initialized!");
end
And it throws the following errors;
Message: Interface\AddOns\GFW_HuntersHelper\HuntersHelper.lua:27: attempt to index global 'this' (a nil value)
Time: Tue Jun 30 09:25:14 2020
Count: 1
Stack: Interface\AddOns\GFW_HuntersHelper\HuntersHelper.lua:27: attempt to index global 'this' (a nil value)
Interface\AddOns\GFW_HuntersHelper\HuntersHelper.lua:27: in function `FHH_OnLoad'
[string "*:OnLoad"]:1: in function <[string "*:OnLoad"]:1>
Locals: (*temporary) = nil
(*temporary) = nil
(*temporary) = nil
(*temporary) = nil
(*temporary) = nil
(*temporary) = "attempt to index global 'this' (a nil value)"
I've tried playing around with the "this" statement but I'm not really sure what to do, and thought I'd see if anyone of you bright people in here would know what's going on
If the addon in question is quite old, then at some point in the past (2010?) the Addon API has moved from global variables to locals.
Frames are defined in XML files, like the one you posted in the comment:
<Frame name="HuntersHelperFrame">
<Scripts>
<OnLoad>FHH_OnLoad();</OnLoad>
<OnEvent>
FHH_OnEvent(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
</OnEvent>
</Scripts>
</Frame>
The elements in <Scripts> are actually called as functions with their content as function body. They are called with some arguments. You can find out what arguments using World of Warcraft API as a reference. It's not official, but it's the closest thing to reference manual there is.
For now, you are interested in Widget handlers.
Now then, the first steps you should take are:
Change the XML:
<Frame name="HuntersHelperFrame">
<Scripts>
<OnLoad>FHH_OnLoad(self)</OnLoad>
<OnEvent>
FHH_OnEvent(self, event, ...)
</OnEvent>
</Scripts>
</Frame>
Change the lua to reflect that:
function FHH_OnLoad(self)
self:RegisterEvent("PLAYER_ENTERING_WORLD")
-- and so on, change all occurrences of `this` to `self`
-- or simply name the first argument `this` instead of `self`:
-- function FHH_OnLoad(this)
end
-- Change all of the functions:
function FHH_OnEvent(self, event, ...)
-- function body
end
Depending on the size of the addon it might become a big chunk of work. Sadly, that's not the end to it; be wary as the script could rely directly on the presence of global variables and do some tricks.
I guess you could try to work around it with tricks like local this = self and similar, but that might not be applicable in all cases, and may cause some problems due to how framework may parse the XML.
One final note; the API changed a lot over the years and you will most likely run into even more problems. Good luck!

Google Dart : How does .where() function work?

var fruits = ['apples', 'oranges', 'bananas'];
fruits[0]; // apples
fruits.add('pears');
fruits.length == 4;
fruits.where((f) => f.startsWith('a')).toList();
The example in the documentation shows the above.
I dont really understand the documentation of the method either.
https://api.dartlang.org/stable/1.21.1/dart-collection/IterableMixin/where.html
I currently see a lambda function as a parameter inside where, with where having the argument f. What is f though? Im a bit confused.
It would be great if I could see a working example. As it stands now I dont really get it. I dont know how it works or what it really does apart from that it acts as some sort of filter.
Is an anonymous function and f is the parameter it accepts
(f) => f.startsWith('a')
where(...) calls that passed function for each element in fruits and returns an iterable that only emits the values where the function returned true
where(...) is lazy, therefore the iteration and call of the passed function will only happen when the result is actually accessed, like with .toList().
DartPad example
update
"anonymous" means the function has no name in contrary to a named function like
myFilter(f) => f.startsWith('a');
main() {
fruits.where(myFilter).toList();
}
also
myFilter(f) => f.startsWith('a');
is just a shorter form of
myFilter(f) {
return f.startsWith('a');
}

Is there a way to do logical OR between two {#eq} conditionals?

I am looking for a way to do logical OR between two {#eq} conditionals. Can I do it in some straightforward way?
For illustration, consider example below. I am iterating over result array. If grp argument is equal to all, I don't want to filter what goes to the page. If it is not equal to all, then I want to filter the array elements by group property being equal to grp. The condition would be expressed as (grp == "all" || group == grp) in JS. Currently, without any way to do OR between the two conditions, I have to repeat the BODY of the loop -- going against the DRY principle.
{#result grp=grp}
{#eq key=grp value="all"}
BODY
{:else}
{#eq key=group value=grp}
BODY
{/eq}
{/eq}
{/result}
The special {#any} helper allows you to express logical OR. (There is also a {#none} helper for logical NAND.)
{#result grp=grp}
{#select}
{#eq key=grp value="all"/}
{#eq key=group value=grp/}
{#any}BODY{/any}
{/select}
{/result}
If you need to do something more complex than that-- for example, if BODY is dependent on the value of grp and group-- you can always write a custom Dust helper to move your logic into Javascript. Here's a short example of what that would look like, and you can read further in the documentation on context helpers.
{
"filter": function(chunk, context, bodies, params) {
var group = context.resolve(params.group);
var grp = context.resolve(params.grp);
if (grp == "all" || group == grp) {
chunk = chunk.render(bodies.block, context.push({
somethingNewBasedOff: "the value of group and grp"
}));
}
return chunk;
}
}
And then you'd use it like:
{#result grp=grp}
{#filter group=group grp=grp}
The filter test passed. The new data I added to the context is {somethingNewBasedOff}
{/filter}
{/result}

what is the difference between chunk.write and chunk.render

I saw this,
chunk = chunk.write("<li>").render(bodies.block, context.push(items[i])).write("</li>\n");
Before seeing this code, i thought, render as something similar to flush, and write as something similar to "write in buffer", naturally leading to a code like below.
for loop
chunk.write("something")
end for loop
chunck.render();
But, as you can see in the first code, render is coming in between the writes. Can somebody explain the difference between these two functions.
#JAiro:
After reading your answer i tried the below code:
temaplate: You have {render} {write}
data:
{
"name": "Mick",
"render": function(c,ct,b){
chunk.render("Rendered {~n}");
},
write:function(c,ct,b){
chunk.write("Written {~n}")
}
}
Expected output:
you have Rendered
Written {~n}
Please note the {~n} after the word "Rendered" is interpreted but the {~n} after "Written" is not interpreted.
But the actual output is not same as the expected output.
Could you post a jsfiddle, that will help me in understanding.
The actual output is an empty string, which also indicate that there could be an error in the code.
The chunk.write method writes strings directly to the buffer.
On the other hand, chunk.render resolves variables contained in its argument and then writes the resulting string to the buffer.
You don't have to override the write and render function in the context.
Let me show you how it works.
Template
Hello {name}!, how are you?
Dust compiles the template to convert them in javascript. After compiling that template you are going to obtain something like:
return chk.write("Hello ").reference(ctx.get("name"), ctx, "h").write("! how are you?");
As you can see for "Hello" and "how are you?", dust uses chunk.write because it knows what it should print. However, dust doesn't know the value of {name} until it gets the context (JSON).
For that reason, it uses chunk.reference, because it will have to resolve the value of the variable name in the future. Dust is going to obtain the value of name from the JSON data.
You can read more about dust.js here:
http://linkedin.github.com/dustjs/wiki
And you can see working examples and try yours here:
http://linkedin.github.com/dustjs/test/test.html

Coffeescript ||= analogue?

I'm primarily a Rails developer, and so in whipping up a little script for my company's Hubot instance, I was hoping to accomplish the following:
robot.brain.data.contacts ||= {}
Or, only make this new hash if it doesn't already exist. The idea being that I want to have a contacts array added dynamically through the script so I don't have to modify Hubot's source, and I obviously don't want to overwrite any contacts I add to it.
Question: is there a quick little construct like the Rails ||= that I can use in Coffeescript to achieve the above goal?
Cheers.
You can use ?= for conditional assignment:
speed ?= 75
The ? is the "Existential Operator" in CoffeeScript, so it will test for existence (not truthiness):
if (typeof speed === "undefined" || speed === null) speed = 75;
The resulting JS is a bit different in your case, though, because you are testing an object property, not just a variable, so robot.brain.data.contacts ?= {} results in the following:
var _base, _ref;
if ((_ref = (_base = robot.brain.data).contacts) != null) {
_ref;
} else {
_base.contacts = {};
};
More info: http://jashkenas.github.com/coffee-script/
I personally use or= instead of ?= mainly because that's what I call ||= (or-equal) when I use it in Ruby.
robot.brain.data.contacts or= {}
The difference being that or= short-circuits when robot.brain.data.contacts is not null, whereas ?= tests for null and only sets robot.brain.data.contacts to {} if not null.
See the compiled difference.
As mentioned in another post, neither method checks for the existence of robot, robot.brain or robot.brain.data, but neither does the Ruby equivalent.
Edit:
Also, in CoffeeScript or= and ||= compile to the same JS.
?= will assign a variable if it's null or undefined.
Use it like speed ?= 25
It's called the existential operator in Coffeescript and is ?=, http://coffeescript.org/. Quoting below:
The Existential Operator
It's a little difficult to check for the existence of a variable in
JavaScript. if (variable) comes close, but fails for zero, the empty
string, and false. CoffeeScript's existential operator ? returns true
unless a variable is null or undefined, which makes it analogous to
Ruby's nil?
It can also be used for safer conditional assignment than ||=
provides, for cases where you may be handling numbers or strings.
The Coco dialect of CoffeeScript, http://github.com/satyr/coco , supports the array and object autovivification operators # and ##:
robot#brain#data#contacts.foo = 1
compiles to - granted, hairy-looking -
var _ref, _ref2;
((_ref = (_ref2 = robot.brain || (robot.brain = {})).data || (_ref2.data = {})).contacts || (_ref.contacts = {})).foo = 1;
which ensures that each step of the way, robot.brain, brain.data, data.contacts actually exists.
Of course you might just want the actual conditional assignment operator (which, according to the above answers, also exists in CoffeeScript):
robot.brain.data.contacts ?= {}
that compiles to
var _ref;
(_ref = robot.brain.data).contacts == null && (_ref.contacts = {});
a ||= b means if a exists, do nothing. If a doesn't exist, make it equal to b.
Example1:
a = undefined;
console.log(a ||= "some_string") //prints some_string
Example2:
a = 10
console.log(a ||= "some_string") //prints 10

Resources