Skip to content

Commit fcb8658

Browse files
Require @orbit decorator to inject orbit services (#436)
* Use @orbit decorator to inject orbit services * Rename services * Pass owner to setupOrbit * Fix order * Update package.json * Simplify decorator * Update rollup.config.mjs * Update README.md * Update README.md
1 parent c12142f commit fcb8658

File tree

21 files changed

+198
-166
lines changed

21 files changed

+198
-166
lines changed

README.md

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,10 @@ In a new vite based app, we can use `import.meta.glob` to grab all the things we
8888
to register and pass them to `setupOrbit. You may want to set this up in your application route's `beforeModel` hook.
8989

9090
```ts
91+
import { getOwner } from "@ember/owner";
92+
import type Owner from "@ember/owner";
9193
import Route from "@ember/routing/route";
92-
import { service } from "@ember/service";
93-
import { setupOrbit, type Store } from "ember-orbit";
94+
import { orbit, setupOrbit, type Store } from "ember-orbit";
9495
import type Coordinator from "@orbit/coordinator";
9596

9697
const dataModels = import.meta.glob("../data-models/*.{js,ts}", {
@@ -104,11 +105,14 @@ const dataStrategies = import.meta.glob("../data-strategies/*.{js,ts}", {
104105
});
105106

106107
export default class ApplicationRoute extends Route {
107-
@service declare dataCoordinator: Coordinator;
108-
@service declare store: Store;
108+
@orbit declare dataCoordinator: Coordinator;
109+
@orbit declare store: Store;
109110

110111
async beforeModel() {
112+
const owner = getOwner(this) as Owner;
113+
111114
setupOrbit(
115+
owner,
112116
{
113117
...dataModels,
114118
...dataSources,
@@ -138,24 +142,6 @@ must be handled in each individual source.
138142
In a classic ember-cli app, we are not able to use `import.meta.glob` out of the box yet.
139143
Until ember-cli is updated to support this, you will need to install [ember-classic-import-meta-glob](https://github.com/NullVoxPopuli/ember-classic-import-meta-glob) to get it to work.
140144

141-
### Registering services
142-
143-
In your `app/app.ts` you will need to register all the services as well.
144-
It should be something like this:
145-
146-
```ts
147-
import emberOrbitRegistry from "ember-orbit/registry.ts";
148-
149-
// ...
150-
151-
modules = {
152-
// if the app is using ember-strict-application-resolver
153-
...emberOrbitRegistry(),
154-
// or if using ember-resolver
155-
...emberOrbitRegistry("name-of-app"),
156-
};
157-
```
158-
159145
## Usage
160146

161147
EO creates the following directories by default:
@@ -188,6 +174,20 @@ EO installs the following services by default:
188174
and local IDs for scenarios in which a server does not accept client-generated
189175
IDs.
190176

177+
### Orbit Decorator Usage
178+
179+
The `@orbit` decorator allows you to inject orbit services that are registered in `orbitRegistry.services` instead of using Ember's `@service` decorator. This decouples your services from Ember's owner system.
180+
181+
```ts
182+
import { orbit } from "ember-orbit";
183+
184+
class MyComponent {
185+
@orbit declare dataCoordinator;
186+
187+
@orbit declare store;
188+
}
189+
```
190+
191191
### Defining models
192192

193193
Models are used to access the underlying data in an EO `Store`.
@@ -633,9 +633,10 @@ enable the coordinator. Let's do this in our application route's `beforeModel`
633633
hook (in `app/routes/application.js`):
634634

635635
```ts
636+
import { getOwner } from "@ember/owner";
637+
import type Owner from "@ember/owner";
636638
import Route from "@ember/routing/route";
637-
import { service } from "@ember/service";
638-
import { setupOrbit, type Store } from "ember-orbit";
639+
import { orbit, setupOrbit, type Store } from "ember-orbit";
639640
import type Coordinator from "@orbit/coordinator";
640641

641642
const dataModels = import.meta.glob("../data-models/*.{js,ts}", {
@@ -649,11 +650,14 @@ const dataStrategies = import.meta.glob("../data-strategies/*.{js,ts}", {
649650
});
650651

651652
export default class ApplicationRoute extends Route {
652-
@service declare dataCoordinator: Coordinator;
653-
@service declare store: Store;
653+
@orbit declare dataCoordinator: Coordinator;
654+
@orbit declare store: Store;
654655

655656
async beforeModel() {
657+
const owner = getOwner(this) as Owner;
658+
656659
setupOrbit(
660+
owner,
657661
{
658662
...dataModels,
659663
...dataSources,

package.json

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,7 @@
117117
"version": 2,
118118
"type": "addon",
119119
"main": "addon-main.cjs",
120-
"app-js": {
121-
"./services/data-coordinator.js": "./dist/_app_/services/data-coordinator.js",
122-
"./services/data-key-map.js": "./dist/_app_/services/data-key-map.js",
123-
"./services/data-normalizer.js": "./dist/_app_/services/data-normalizer.js",
124-
"./services/data-schema.js": "./dist/_app_/services/data-schema.js",
125-
"./services/data-validator.js": "./dist/_app_/services/data-validator.js",
126-
"./services/store.js": "./dist/_app_/services/store.js"
127-
}
120+
"app-js": {}
128121
},
129122
"imports": {
130123
"#src/*": "./src/*"

rollup.config.mjs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,7 @@ export default {
2525
// up your addon's public API. Also make sure your package.json#exports
2626
// is aligned to the config here.
2727
// See https://github.com/embroider-build/embroider/blob/main/docs/v2-faq.md#how-can-i-define-the-public-exports-of-my-addon
28-
addon.publicEntrypoints([
29-
'index.js',
30-
'registry.js',
31-
'services/**/*.js',
32-
'test-support/**/*.js',
33-
]),
28+
addon.publicEntrypoints(['index.js', 'test-support/**/*.js']),
3429

3530
// These are the modules that should get reexported into the traditional
3631
// "app" tree. Things in here should also be in publicEntrypoints above, but

src/-private/decorators/orbit.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { orbitRegistry } from '../utils/orbit-registry.ts';
2+
3+
function getDescriptor(serviceName: string) {
4+
return {
5+
get() {
6+
const services = orbitRegistry.services;
7+
const service = services[serviceName as keyof typeof services];
8+
9+
if (service === undefined) {
10+
throw new Error(
11+
`No orbit service named '${serviceName}' was found. Available services: ${Object.keys(services).join(', ')}`,
12+
);
13+
}
14+
15+
return service;
16+
},
17+
configurable: true,
18+
enumerable: true,
19+
};
20+
}
21+
22+
/**
23+
* Decorator that injects orbit services from the orbitRegistry
24+
* This provides an alternative to Ember's @service decorator that is decoupled from Ember's owner.
25+
*
26+
* Usage:
27+
* - @orbit declare dataCoordinator;
28+
* - @orbit('keyMap') declare myKeyMap;
29+
*/
30+
export function orbit(
31+
nameOrTarget?: unknown,
32+
propertyKey?: string | symbol,
33+
): any {
34+
// Direct decorator usage: @orbit
35+
if (propertyKey !== undefined) {
36+
return getDescriptor(String(propertyKey));
37+
}
38+
39+
// Factory decorator usage: @orbit() or @orbit('name')
40+
const name = nameOrTarget as string | undefined;
41+
return function (_target: any, propertyKey: string | symbol) {
42+
return getDescriptor(name ?? String(propertyKey));
43+
};
44+
}

src/-private/factories/coordinator-factory.ts

Lines changed: 0 additions & 43 deletions
This file was deleted.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { orbitRegistry } from '../utils/orbit-registry.ts';
2+
import { Coordinator, type CoordinatorOptions } from '@orbit/coordinator';
3+
4+
type CoordinatorInjections = {
5+
sourceNames?: string[];
6+
strategyNames?: string[];
7+
} & CoordinatorOptions;
8+
9+
export default {
10+
create(): Coordinator {
11+
const injections: CoordinatorInjections = {};
12+
13+
const sourceNames = Object.keys(orbitRegistry.registrations.sources);
14+
injections.sources = sourceNames
15+
.map((name) => {
16+
return orbitRegistry.registrations.sources[name];
17+
})
18+
.filter((source) => !!source);
19+
20+
const strategyNames = Object.keys(orbitRegistry.registrations.strategies);
21+
injections.strategies = strategyNames
22+
.map((name) => {
23+
return orbitRegistry.registrations.strategies[name];
24+
})
25+
.filter((strategy) => !!strategy);
26+
27+
return new Coordinator(injections);
28+
},
29+
};

src/-private/services/data-normalizer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import { orbitRegistry } from '../utils/orbit-registry.ts';
66

77
export default {
88
create(injections: ModelRecordNormalizerSettings): ModelAwareNormalizer {
9-
injections.keyMap = orbitRegistry.services.keyMap;
10-
injections.schema = orbitRegistry.services.schema;
9+
injections.keyMap = orbitRegistry.services.dataKeyMap;
10+
injections.schema = orbitRegistry.services.dataSchema;
1111

1212
return new ModelAwareNormalizer(injections);
1313
},

src/-private/factories/store-factory.ts renamed to src/-private/services/store.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Store, { type StoreSettings } from '../store.ts';
22
import type {
33
ModelAwareQueryBuilder,
44
ModelAwareTransformBuilder,
5-
} from '../utils/model-aware-types';
5+
} from '../utils/model-aware-types.ts';
66
import { orbitRegistry } from '../utils/orbit-registry.ts';
77
import type { RequestOptions } from '@orbit/data';
88
import type MemorySource from '@orbit/memory';

0 commit comments

Comments
 (0)