Reason: Error: Unbound value not__ for bs-jest - reason

Trying to test a binding of lit-html method html
open Jest;
let write = () => LitHtml.html("<div></div>");
open Expect;
describe("LitHtml", () =>
test("#html", () =>
expect(() =>
write()
) |> not_ |> toThrow
)
);
I am told this cryptic error:
Error: Unbound value not__
Hint: Did you mean not_?
Jest.Expect.plainPartial('a) => Jest.Expect.invertedPartial('a)
But clearly wrote not_ as it suggests, not not__.
My attempted binding:
[#bs.module "LitHtml"] [#bs.val]
external html: string => Js.nullable(string) = "html";
let html = htmlStr => html(htmlStr) |> Js.Nullable.toOption;
Thanks for any assistance. Just getting started with ReasonML ;)

Seems like this is caused by a largely undocumented change in Reason 3.3.4. I think it might hide in PR #2197.
not is a keyword in OCaml, which is why Expect.not_ is named such as it is in the first place. And this change seems to "mangle" (ie. translate) not in Reason into not_ in OCaml, and then not_ to not__ and so on.
So the solution is simply to replace all instances of not_ in your code with not. OR you can update bs-jest to 0.4.7 where I've added not__ as an alias to not_, so you can use either not or not_.

Related

How to String matchAll in Reason?

I'm trying to replicate what I would do in javascript with matchAll()
const names = [
...withoutSlashes.matchAll(/(?<=Pos\. \d+ \- )(.*?)(?=","Importe)/g),
];
I see Reason has Js.String.match but I can't find the matchAll. I guess it's because matchAll is a newer ecmascript.
Any hint on which would be a good way to do a performant matchAll? or is there a specific Reason feature that I'm missing?
Based on the accepted answer I wanted to add a version that is following ReScript conventions. [#bs.send.pipe] is discouraged, and the ReScript language officially recommends the pipe-first operator (-> instead of |>).
like this:
[#bs.send]
external matchAll: (string, Js.Re.t) => Js.Array.array_like(array(string)) =
"matchAll";
let matches: array(string) =
matchAll("abc", [%re "/[a-c]/g"])->Js.Array.from;
You can bind to it yourself. The biggest problem with it is that it returns an iterator, which we also don't have bindings for. But we can use Js.Array.array_like('a) and then convert it to an array using Js.Array.from:
[#bs.send.pipe: string]
external matchAll: Js.Re.t => Js.Array.array_like(array(string)) = "matchAll";
let matches = "abc" |> matchAll([%re "/[a-c]/g"]) |> Js.Array.from;

How to port following hook to reasonml

I have following custom hook
function useConstant(fn) {
const ref = React.useRef()
if (!ref.current) {
ref.current = fn()
}
return ref.current
}
and it seems quite hard to port this to reasonml, I have to use type cast twice, what's the ideal way?
external toAny: 'a => 'b = "%identity";
external toBool: 'a => bool = "%identity";
let useConstant = (fn: unit => 'a) => {
let ref: React.Ref.t('a) = toAny(React.useRef());
if (!toBool(React.Ref.current(ref))) {
React.Ref.setCurrent(ref, fn());
};
React.Ref.current(ref);
};
If I understand the purpose of the hook correctly, it's really just a reimplementation of React.useMemo. But for the sake of learning, here's an implementation that should work.
let useLazy = (fn: unit => 'a): 'a => {
let ref = React.useRef(None);
switch (React.Ref.current(ref)) {
| Some(value) => value
| None =>
let value = fn();
React.Ref.setCurrent(ref, Some(value));
value;
};
};
It uses the option type, which is specifically designed for cases like this. If there's no value, we represent that using options None value, and if there is a value we use Some. Instead of using if with JavaScript's semantically unclear concept of truthiness, we pattern match on the option using switch to find that it's None and the value needs to be computed, or Some to get at the value.
The use of option and pattern matching is really common in Reason code, so it's one you should really try to understand using the links provided above for more details if needed.
Note that you could also have used Lazy for this. But that's far less commonly used and therefore also much less useful to learn.

Focusing an input field in a React component – getting type error trying to create a ref

I have a React component that contains a text <input> element. When the component is mounted, I want the text cursor to be set in the input field, i.e. I want the text input element to have the focus.
In a “traditional” JavaScript React component, I would get the input field's DOM element through a ref and then call its focus method.
I've read this documentation that explains how to use refs in Reason-React: https://github.com/reasonml/reason-react/blob/master/docs/react-ref.md
Alas, the code sample contained on this page is for refs of custom component, it only mentions that it also works on React DOM elements.
So I've tried to convert the example code to a React DOM element, here is what I've tried so far:
type state = {
text: string,
inputElement: ref(option(Dom.element))
};
let valueFromEvent = (evt) : string => (
evt
|> ReactEventRe.Form.target
|> ReactDOMRe.domElementToObj
)##value;
let component = ReasonReact.reducerComponent("EditTodoField");
let setInputElement = (theRef, {ReasonReact.state}) =>
state.inputElement := Js.Nullable.to_opt(theRef);
let make = (~initialText, ~onSubmit, _) => {
...component,
initialState: () => {text: initialText, inputElement: ref(None)},
reducer: (newText, state) => ReasonReact.Update({...state, text: newText}),
render: ({state: {text}, reduce, handle}) =>
<input
value=text
_type="text"
ref=(handle(setInputElement))
placeholder="Todo description"
onChange=(reduce((evt) => valueFromEvent(evt)))
onKeyDown=(
(evt) =>
if (ReactEventRe.Keyboard.key(evt) == "Enter") {
onSubmit(text);
(reduce(() => ""))()
} else if (ReactEventRe.Keyboard.key(evt) == "Escape") {
onSubmit(initialText);
(reduce(() => ""))()
}
)
/>
};
The error message I get is this:
We've found a bug for you!
/Users/pahund/git/todo-list-reason-react/src/EditTodoField.re 21:11-35
19 ┆ value=text
20 ┆ _type="text"
21 ┆ ref=(handle(setInputElement))
22 ┆ placeholder="Todo description"
23 ┆ onChange=(reduce((evt) => valueFromEvent(evt)))
This has type:
ReasonReact.Callback.t(Js.Nullable.t(Dom.element)) (defined as
(Js.Nullable.t(Dom.element)) => unit)
But somewhere wanted:
(Js.null(Dom.element)) => unit
The incompatible parts:
Js.Nullable.t(Dom.element) (defined as Js.nullable(Dom.element))
vs
Js.null(Dom.element)
I know that the problem probably lies within how I define the type of the state at the beginning of the code, it's different for DOM elements than it is for custom components.
What would be the correct type definition here to fix the bug?
The full project can be found here on GitHub: https://github.com/pahund/todo-list-reason-react/tree/ref-problem
I think your reason-react dependency is out of date. refs were changed from Js.null(Dom.element) to Js.nullable(Dom.element) in 0.3.0. See https://github.com/reasonml/reason-react/blob/master/HISTORY.md#030
If for some reason you can't or refuse to upgrade, you can just use Js.Null.to_opt instead though :)
(Also, if you do upgrade, you can use Js.toOption as a nice shortcut in place of Js.Nullable.to_opt)

missing context in coffeescript => operator

App.WebNotificationComponent = Em.Component.extend
subscriptionQueue: null
connection: (->
App.StompConnection.create(subscriptionQueue: #get('subscriptionQueue'))
).property('subscriptionQueue')
willDestroy: ->
#get('connection').destroy()
App.AlertsIndexAlertWebNotificationComponent = App.WebNotificationComponent.extend
subscriptionQueue: "notifications"
didInsertElement: ->
#get('connection').onMessage = ((message) ->
result = JSON.parse(message.body)
App.AlertNotification.store.find('alert_notification', result.id).then (notification) =>
debugger
).bind(#get('targetObject'))
#get('connection').connect()
At the debugger breakpoint, I am no longer able to access message or result.
Is there anything that I am doing wrong or another way to do this ?
You can't access those values in the debugger because you didn't close over them, so they're not available in the closure. If you use them in the closure, they'll be available. Javascript has to know that you're going to use a variable in order to save it in the closed scope.
In other words, this will work as expected:
result = JSON.parse(message.body)
App.AlertNotification.store.find('alert_notification', result.id).then (notification) =>
console.log(message)
console.log(result)

Getting an "Odd number of elements in hash assignment" error

I'm getting the following error when running a Perl script
Odd number of elements in hash assignment at GenerateInterchangeFromIntegrationManifest.pl line 197.
{
"Change list" : "0"
}
This is the script:
my %labelFieldMap = (IUItemName => convertIuItemName,
Changelist => sub {},
IUItemLevel => createNormalConvert('iuItemLevel'),
ContactPOC => \&convertContacts,
Cspec => \&convertCspec,
IsNew => createBooleanConvert('isNew'),
Submitter => createNormalConvert('submitter'),
LabelType => createNormalConvert('type'),
Revision => createNestedConvert('component', 'revision'),
RevisionName => sub {},
ComponentBaseName => createNestedConvert('component', 'baseName'),
Version => createNestedConvert('component', 'version'),
PLMapping => createNormalConvert('plMapping'),
BidMapping => createNormalConvert('bidMapping'),
ClientId => createNormalConvert('clientId'),
Path => \&convertPath,
ExtendedData => \&convertExtendedData);
Can any one help me resolve this issue?
There are several subroutine calls in assignment to the hash that could be returning lists with an even number of elements (which would make the list count odd overall, and also change which data is keys and which values from that point in the list on, which is probably worse for you). As Dallaylaen has pointed out in comments, this could simply be a line which returns "nothing", return; which will evaluate to empty list (), i.e. an even length of 0, in list context. All the subroutine calls in the question code will be evaluated in list context.
I would suggest a simple debug technique:
Comment out all the lines with a function call, that should remove the warning.
Then add back a few at a time and re-test.
When the warning re-appears, you will have isolated the problem to one of a few subroutines.
Repeat until you know which one.
Then investigate that call to see how you might fix it.