Skip to content

Commit

Permalink
Merge pull request #1107 from NullVoxPopuli/NullVoxPopuli-patch-1
Browse files Browse the repository at this point in the history
Move recommendation of resourceFactory to better align with how TypeScript works
  • Loading branch information
NullVoxPopuli authored Jan 11, 2024
2 parents 699b112 + 1794976 commit 88a7b81
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 26 deletions.
17 changes: 11 additions & 6 deletions DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ Lastly, to support reactively changing the locale, we need to wrap the `resource
```js
import { resource, resourceFactory, use } from 'ember-resources';

const Clock = resourceFactory((locale = 'en-US') => {
function Clock(locale = 'en-US') {
return resource(({ on }) => {
let time = new TrackedObject({ current: new Date() });
let interval = setInterval(() => (time.current = new Date()), 1_000);
Expand All @@ -249,7 +249,9 @@ const Clock = resourceFactory((locale = 'en-US') => {

return () => formatter.format(time.current);
});
});
}

resourceFactory(Clock);
```

<details><summary>using &lt;template&gt;</summary>
Expand All @@ -258,7 +260,7 @@ const Clock = resourceFactory((locale = 'en-US') => {
// NOTE: this snippet has bugs and is incomplete, don't copy this (explained later)
import { resource, resourceFactory, use } from 'ember-resources';

const Clock = resourceFactory((locale = 'en-US') => {
function Clock(locale = 'en-US') {
return resource(({ on }) => {
let time = new TrackedObject({ current: new Date() });
let interval = setInterval(() => (time.current = new Date()), 1_000);
Expand All @@ -269,7 +271,9 @@ const Clock = resourceFactory((locale = 'en-US') => {

return () => formatter.format(time.current);
});
});
}

resourceFactory(Clock);

<template>
<time>{{Clock}}</time>
Expand Down Expand Up @@ -306,7 +310,7 @@ Supporting reactive argument changes from JS would require an arrow function to
```js
import { resource, resourceFactory, use } from 'ember-resources';

const Clock = resourceFactory((locale = 'en-US') => {
function Clock(locale = 'en-US') {
return resource(({ on }) => {
let currentLocale = locale;

Expand All @@ -323,7 +327,8 @@ if (typeof locale === 'function') {

return () => formatter.format(time.current);
});
});
}
resourceFactory(Clock):
```

and then usage in a class would look like:
Expand Down
32 changes: 18 additions & 14 deletions docs/docs/ember.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ const Clock = resource(/* ... */);
And then if your resource takes arguments:

```gjs
const Clock = resourceFactory((locale) => {
function Clock(locale) {
return resource(/* ... */);
});
}
resourceFactory(Clock)
<template>
{{Clock 'en-US'}}
Expand Down Expand Up @@ -167,13 +169,15 @@ class Demo {

This technique with using a function is nothing special to ember-resources, and can be used with any other data / class / etc as well.

Further, if multiple reactive arguments are needed with individual reactive behavior, you may instead decide to have your `resourceFactory` receive an object.
Further, if multiple reactive arguments are needed with individual reactive behavior, you may instead decide to have your wrapping function receive an object.

<details><summary>about resourceFactory</summary>

`resourceFactory` is a pass-through function purely for telling ember to
invoke the underlying resource immediately after invoking the `resourceFactory` function.

This is why we don't use its return value: it's the same as what you pass to it.

Without `resourceFactory`, ember would need extra internal changes to support primitives that
don't yet exist within the framework to, by convention, decide to _double-invoke_ the functions.

Expand Down Expand Up @@ -203,7 +207,7 @@ So when authoring a `Clock` that receives these types of function arguments, but
```js
import { resourceFactory } from 'ember-resources';

export const Clock = resourceFactory(( args ) => {
export function Clock(args) {
return resource(() => {
let { locale, timeZone } = args;

Expand All @@ -214,7 +218,9 @@ export const Clock = resourceFactory(( args ) => {

// ...
});
});
}

resourceFactory(Clock);
```

<details><summary>using functions for fine-grained reactivity</summary>
Expand Down Expand Up @@ -345,21 +351,19 @@ class Demo {

For TypeScript, you may have noticed that, if you're a library author, you may want to be concerned with supporting all usages of resources in all contexts, in which case, you may need to support overloaded function calls.

TypeScript does not support overloading anonymous functions, so we need to abstract the callback passed to `resourceFactory` into a named function, which we can then define overloads for.

Here is how the overloads for `Compiled`, the resource that represents a dynamically compiled component, provided by `ember-repl`, and used by https://limber.glimdown.com and https://tutorial.glimdown.com.

[compile/index.ts](https://github.com/NullVoxPopuli/limber/blob/main/packages/ember-repl/addon/src/browser/compile/index.ts)

```ts
// Additional types and APIs omitted for brevity
export function buildCompiler(markdownText: Input | (() => Input)): State;
export function buildCompiler(markdownText: Input | (() => Input), options?: Format): State;
export function buildCompiler(markdownText: Input | (() => Input), options?: () => Format): State;
export function buildCompiler(markdownText: Input | (() => Input), options?: ExtraOptions): State;
export function buildCompiler(markdownText: Input | (() => Input), options?: () => ExtraOptions): State;
export function Compiled(markdownText: Input | (() => Input)): State;
export function Compiled(markdownText: Input | (() => Input), options?: Format): State;
export function Compiled(markdownText: Input | (() => Input), options?: () => Format): State;
export function Compiled(markdownText: Input | (() => Input), options?: ExtraOptions): State;
export function Compiled(markdownText: Input | (() => Input), options?: () => ExtraOptions): State;

export function buildCompiler(
export function Compiled(
markdownText: Input | (() => Input),
maybeOptions?: Format | (() => Format) | ExtraOptions | (() => ExtraOptions)
): State {
Expand All @@ -383,7 +387,7 @@ export function buildCompiler(
});
}

export const Compiled = resourceFactory(buildCompiler) as typeof buildCompiler;
resourceFactory(Compiled)
```

When defining `Compiled` this way, we can be type-safe in a variety of situations.
Expand Down
5 changes: 3 additions & 2 deletions docs/docs/resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ In this example, the channel name that we're subscribing to is dynamic, and we w
```js
import { resourceFactory, resource, cell } from 'ember-resources';

const ChannelResource = resourceFactory((channelName) => {
function ChannelResource(channelName) {
return resource(({ on }) => {
const lastMessage = cell(null);

Expand All @@ -292,7 +292,8 @@ const ChannelResource = resourceFactory((channelName) => {
}
};
});
});
}
resourceFactory(ChannelResource);
```

To see this code in action, [checkout the live demo](https://limber.glimdown.com/edit?c=MQAgMglgtgRgpgJxAUQCYQC4HsEChcAicUWAdgM4YICGGEZIWAZiNSAEpzlYCuCAxnBAYAFrRAI4AB0nk4pDOWEihAIhKUJcQQpBQu5agHM4qrYIgA3OKkalWIAOpwYAZSz8A1nAwgAwmKkpHAANgB0%2BAAG0UYAVkohVnC40FI4vgDeWtx8ggBi1PzYCACeADTZvAJwFYIhISAAviBMCFhQIADkxPAIALSyVYLknQDc%2BAD0AFRTIAAKkhjytgHUQaEgqSHE8hi09PZMOCAwkpaYJSBTE7j8ZJqr6w0AvCAZuCAg5Dww5PwIEHgAC4QAAKfiBYIhABy1H0AEoQM8AHxvD6fEDbXz8aj1GCFTxKV4AbQAuuN0Z8JhMvtAeCFaEJJBZzqQjF8PN5fPpyIYTEopIh6OgcfUSpTMT5NgpEJZcUivj4AJIyhBykKg0GIlFojEYrGbWyvACytBEYTaPFIqFBptEFrWqHaWquIAAHAAGREAahAAEZxnrPgaQHwXiBIiIMBgpOQgdTyAB3ahSCBhVBwSwTFMQCYAEgyELWUNh%2Bka%2BYyEFQjUi%2BCDLR8ENBYfhEr1YVE8lBsjSFCEOp79zgYXiZC1rfrnw7KlIoNQtDYOvek4xopC%2BK85DCUBT4Nx64JSNRa43njnC7CpDhcHhE8njXhgYxjQqfo978fdYxiz49mXQbIY0DGMOAQVBJhSG1Y991PLcpB4cgRHAyCyjbUMKB%2BP4AWBMEoN1ScTwJIkQDJJ9634bZqAQFUljVXFQQgVV1U-etGglNjPjYtj8DuChfEeKFOByaoFUGXI4AKIocBKTUiyeUsbyPfCfwQewxOqTUsgYB8lP-T5eM0BlKCA3kQIVOoNVIekQhYiUDOxSENleATQjCb5fn%2BQE4HBRyYWvWy9TkqEwkA4CTE1Hk%2BUUpc0KMjATKityfFBSKQJY58AoxMgwgouA1h4KRNTwvTV18sIrXcrCvK1Mimkyz4VPsF0YvI%2B5fBkOAmAgAAPBVImJAsgtCBTGlJCNas%2BCAWFBOKEpAnK%2BEkXRnhWkArPqRESr1RqIwLDquu65poSwPQwq4cw4CSWwSh8SIJqaEBpmQP1rhAUI5Hw%2BsdsiPbJAO5oC1ms6FoQJaMBre6OOfJ8H3GWH8AAHiWKApAZJZkXRBH0EsL4MBKbZnlUGAcAzBAQT9KReu4RJUFGEApGoVB0DZcnJCgUZVAxvUMgyFyQiEoY1EFLBUdMRooYRiZsYxyXkdRxkMeiWsgA&format=glimdown)
Expand Down
6 changes: 4 additions & 2 deletions ember-resources/src/function-based/immediate-invocation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class ResourceInvokerManager {
* ```js
* import { resource, resourceFactory } from 'ember-resources';
*
* const RemoteData = resourceFactory((url) => {
* function RemoteData(url) {
* return resource(({ on }) => {
* let state = new TrackedObject({});
* let controller = new AbortController();
Expand All @@ -107,7 +107,9 @@ class ResourceInvokerManager {
*
* return state;
* })
* });
* }
*
* resourceFactory(RemoteData);
*
* <template>
* {{#let (RemoteData "http://....") as |state|}}
Expand Down
6 changes: 4 additions & 2 deletions ember-resources/src/type-tests/use.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const Clock = resource(({ on }) => {
return now;
});

const ParameterizedClock = resourceFactory((locale = 'en-US') => {
function ParameterizedClock(locale = 'en-US') {
return resource(({ use }) => {
let now = use(Clock);
let formatter = new Intl.DateTimeFormat(locale);
Expand All @@ -22,7 +22,9 @@ const ParameterizedClock = resourceFactory((locale = 'en-US') => {
return formatter.format(now.current);
};
});
});
}

resourceFactory(ParameterizedClock);

class DemoA {
@use stuck = StuckClock;
Expand Down

0 comments on commit 88a7b81

Please sign in to comment.