Skip to content

Releases: orbitjs/ember-orbit

v0.17.3

10 Mar 14:02
Compare
Choose a tag to compare

This release improves upon the previous release which allowed source and strategy factories to conditionally return null or undefined to signal that they should be ignored by the coordinator factory. This approach was flawed in that the returned value still ends up in the application's container, and non-objects are not handled properly by ember when the container is destroyed. This could cause issues in test teardown for instance.

Instead of returning a custom value from create, it is now recommended that the default export of the source or strategy module be conditionally changed to a non-factory (e.g. null or undefined) to signal that it should be ignored. This avoids the call to lookup and thus prevents nullish values from entering the container.

For example, the following strategy will be conditionally included for all non-production builds:

// app/data-strategies/event-logging.js

import { EventLoggingStrategy } from '@orbit/coordinator';
import config from 'example/config/environment';

const factory = {
  create() {
    return new EventLoggingStrategy();
  }
};

// Conditionally include this strategy
export default config.environment !== 'production' ? factory : null;

Changelog

πŸš€ Enhancement

  • #401 Improve conditional creation of sources and strategies (@dgeb)

Committers: 1

v0.17.2

07 Mar 16:19
Compare
Choose a tag to compare

Changelog

Source and strategy factories can now return null or undefined from create(), in which case they will be ignored by the coordinator factory that typically instantiates them. This allows for conditional creation based upon an env setting or other cue.

πŸš€ Enhancement

  • #399 Allow for conditional creation of sources and strategies (@dgeb)

Committers: 1

v0.17.1...v0.17.2

v0.17.1

06 Mar 23:16
Compare
Choose a tag to compare

Changelog

πŸ› Bug Fix

πŸ“ Documentation

  • #393 Add 'Customizing validators' section to README (@dgeb)
  • #391 Fix broken doc links in the README (@bmoeskau)

🏠 Internal

Committers: 2

v0.17.0

01 Feb 14:17
Compare
Choose a tag to compare

This is the first production release of ember-orbit v0.17. Please review the Orbit docs for an overview of what's new in v0.17 of Orbit. In addition to understanding the changes that Orbit itself has undergone, it's important to understand the particular changes made to EO as you upgrade from v0.16.x.

Removal of implicit injections

In order to make EO compatible with Ember 4.0, implicit injections have been removed. Thus, the store service is no longer automatically injected into all controllers and routes. To access this service, you'll need to declare it explicitly:

import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class PlanetsRoute extends Route {
  @service store;

  async model() {
    return this.store.findRecords('planet');
  }
}

In addition, the standard injections (schema, keyMap, normalizer, and validatorFor) that each Orbit source relies upon are no longer injected automatically. As described in #367, you can use the applyStandardSourceInjections utility in your source factories as follows:

// /app/data-sources/remote.js
import SourceClass from '@orbit/jsonapi';
import { applyStandardSourceInjections } from 'ember-orbit';

export default {
  create(injections = {}) {
    applyStandardSourceInjections(injections);
    injections.name = 'remote';
    return new SourceClass(injections);
  }
};

This utility method is now used in all sources generated from blueprints with ember g data-source NAME.

Main bucket service

If you are using one or more buckets to store orbit state in browser storage, please see #382 to understand how to upgrade your bucket initializers for v0.17 compatibility.

Validators enabled by default

Orbit v0.17 now includes validators enabled by default in all sources. Validations are useful to ensure that your data matches its type expectations and that operations and query expressions are well formed. Of course, they also add some extra code and processing, which you may want to eliminate (perhaps always or perhaps only for production environments). You can disable validators across all sources by setting Orbit's skipValidatorService environment flag to true in config/environment:

module.exports = function (environment) {
  let ENV = {
    // ... other settings here
    orbit: {
      skipValidatorService: true
    }
  };

  return ENV;
};

If you want to use validators but extend them to include custom validators, you can override the standard validator service by generating your own data-validator service that passes custom arguments to buildRecordValidatorFor.

For instance, in order to provide a custom validator for an address type:

// app/services/data-validator.js

import { buildRecordValidatorFor } from '@orbit/records';

const validators = {
  address: (input) => {
    if (typeof input?.country !== 'string') {
      return [
        {
          validator: 'address',
          validation: 'country',
          description: 'is not a string',
          ref: input,
        },
      ];
    }
  },
};

export default {
  create() {
    return buildRecordValidatorFor({ validators });
  },
};

This custom validator service will be injected into all your orbit sources via applyStandardSourceInjections, as described above.

Model-aware normalizers

A new model-aware normalizer has been introduced to normalize record field and identity data across EO. This normalizer is based on the new StandardRecordNormalizer from @orbit/records.

With this normalizer, you can now use a number of convenient forms to provide a record's identity:

// type / id
store.query((q) => q.findRecord({ type: 'planet', id: '123' }));

// type / key / value
store.query((q) => q.findRecord({ type: 'planet', key: 'remoteId', value: 'abc' }));

// EO Model
store.query((q) => q.findRecord(earth));

And you can express a record's fields in the standard Orbit expanded form:

let [earth, mars] = await store.update((t) => [
  // flattened model-aware fields
  t.addRecord({
    type: 'planet',
    name: 'Earth',
    moons: [theMoon]
  }),

  // expanded form
  t.addRecord({
    type: 'planet',
    attributes: {
      name: 'Jupiter'
    },
    relationships: {
      moons: {
        data: [io, europa]
      }
    }
  })
]);

Goodbye ArrayProxy

Ember's ArrayProxy was used in v0.16 to represent "live" resultsets whose contents could update to reflect changes in a source's cache. This has been replaced in v0.17 with an orbit specific concept called a LiveQuery, which has an extended form in EO that includes Glimmer-based reactivity.

Every LiveQuery has a value property that can be used to access the raw result, which will be a native JS array of Models (if a collection), a single Model, or undefined. LiveQuery results are also iterable and can be used directly in templates (with no need to reference .value).

For instance, a component may fetch a LiveQuery from the store's cache:

this.planets = store.cache.liveQuery((q) => q.findRecords('planet'));
console.log(planets.value)  // [earth, mars]
console.log(planets.length) // 2

And its template could use those results just as before:

{{#each this.planets as |planet|}}
  <p>{{planet.name}}</p>
{{/each}}

A model's hasMany relationships use live queries internally, although accessing the field returns the value of the LiveQuery directly. Thus, planet.moons will now return a native array of Models. There's no longer a need to access planet.moons.content to get this array (as in v0.16).

$-prefixed Model methods and properties

In order to avoid conflicts with standard user-provided Model fields, all of EO's standard properties and methods on Model are now prefixed with a $ (see #342). Thus, instead of planet.getData(), call planet.$getData(). Instead of planet.identity, access planet.$identity.

The old methods and properties will continue to work but have been deprecated.

Updates to Models, Stores, and Caches

There has been a lot of confusion in the past over mutations made to model fields, how and when they apply to their associated cache and store, and how they flow through Orbit sources. Much of this confusion has been due to models reflecting state immediately and synchronously via their fields, but the store requiring async mutations to have data flow through to other sources. With all these factors, the meaning and timing of simply setting planet.name = 'Earth' becomes very tricky to understand.

In order to simplify both these important issues, some fundamental relationships in EO have been been changed. Models are always now associated only with a Cache and no longer directly reference a Store. Any updates applied directly to models are applied to their associated cache. Thus planet.name = 'Earth' is a synchronous assignment which will immediately be reflected when accessing planet.name. So how do changes to models ever make it back to the store and flow through to other sources?

Orbit itself has been updated in v0.17 to synchronously track changes in forks. Thus, when changes are made to forked caches, they are tracked and can be merged back to a base source / store.

To enforce this flow, only forked caches are able to be updated directly. This provides protection against data loss, since changes to caches do not articipate in Orbit's data flows. An exception is made for forks because the changes are tracked and applied back to stores via merge. If you want to override these protections and update a non-forked cache, you can set cache.allowUpdates = true, but know that those updates will never leave the cache.

Let's wrap this up with some simple rules:

  • If you want to apply a series of synchronous changes, say while handling user input in a form, then fork a base store and then update the fork directly, through its cache or models. Call merge to apply those changes back to the base store.

  • If you want to apply changes directly to the store and have those changes flow to other orbit sources, call one of the async update methods directly on the store (addRecord, updateRecord, updateRecordFields, removeRecord, or update).

More details can be found in the Updating Data section of the readme.


If you have any questions or problems updating your EO applications, please reach out on gitter.


Changelog (for v0.17.0, since v0.17.0-beta.23)

🏠 Internal

  • #390 Update all orbit packages ^v0.17 (@dgeb)

Committers: 1

v0.17.0-beta.23

31 Jan 15:55
Compare
Choose a tag to compare
v0.17.0-beta.23 Pre-release
Pre-release

Changelog

πŸš€ Enhancement

  • #389 Introduce Cache support for fork/merge/rebase/reset (@dgeb)
  • #388 Allow custom source settings to be applied via Store#fork (@dgeb)

Committers: 1

v0.17.0-beta.22

30 Jan 14:31
Compare
Choose a tag to compare
v0.17.0-beta.22 Pre-release
Pre-release

Changelog

πŸš€ Enhancement

  • #386 Skip all validations by default when skipValidatorService = true (@dgeb)

🏠 Internal

Committers: 1

v0.17.0-beta.21

25 Jan 17:47
Compare
Choose a tag to compare
v0.17.0-beta.21 Pre-release
Pre-release

Changelog

πŸš€ Enhancement

  • #385 Introduce Store#updateRecordFields (@dgeb)

Committers: 1

v0.17.0-beta.20

24 Jan 18:05
Compare
Choose a tag to compare
v0.17.0-beta.20 Pre-release
Pre-release

Changelog

πŸš€ Enhancement

  • #384 Introduce content as a deprecated synonym for value on LiveQuery (@dgeb)

Committers: 1

v0.17.0-beta.19...v0.17.0-beta.20

v0.17.0-beta.19

23 Jan 18:31
Compare
Choose a tag to compare
v0.17.0-beta.19 Pre-release
Pre-release

This release finishes the work started in beta.18 to eliminate implicit injections from ember-orbit. #382 addresses the issue of implicit injections used by buckets. As noted in that PR, please update your bucket initializers to follow the following form:

import BucketFactory from '../data-buckets/main';

export function initialize(application) {
  const orbitConfig = application.resolveRegistration('ember-orbit:config');

  // Register this bucket as the main bucket service so that it can be injected
  // into sources via `applyStandardSourceInjections`.
  //
  // IMPORTANT: If your app has more than one bucket, only register one as the
  // main bucket service.
  application.register(`service:${orbitConfig.services.bucket}`, BucketFactory);
}

export default {
  name: 'main-bucket-initializer',
  after: 'ember-orbit-config',
  initialize,
};

Changelog

πŸ’₯ Breaking Change

  • #382 Introduce main bucket service to allow for bucket injections (@dgeb)

🏠 Internal

  • #381 Update orbit packages ^0.17.0-beta.26 (@dgeb)

Committers: 1

v0.17.0-beta.18

06 Jan 23:29
Compare
Choose a tag to compare
v0.17.0-beta.18 Pre-release
Pre-release

This release includes a couple breaking changes to bring compatibility with ember v4.x:

  • Support has been dropped for ember < v3.24 (see #373)
  • EO no longer uses implicit injections, which were deprecated by ember prior to v4.

As discussed in #367, a new utility method has been added, applyStandardSourceInjections, which can be invoked to apply standard injections (schema, keyMap, normalizer, and validatorFor) to a source during construction. Alternatively, these injections can also be applied on a per-source basis.

This should be considered a breaking change because applyStandardSourceInjections must now be invoked for previously defined sources other than the store.

For instance:

// /app/data-sources/remote.js
import SourceClass from '@orbit/jsonapi';
import { applyStandardSourceInjections } from 'ember-orbit';

export default {
  create(injections = {}) {
    applyStandardSourceInjections(injections);
    injections.name = 'remote';
    return new SourceClass(injections);
  }
};

Please update your existing source factories as shown above!

The new blueprints are structured in this way, which should cover new source factories.

πŸ’₯ Breaking Change

  • #373 Update ember v3.25.0...v4.1.0 (@dgeb)
  • #367 [BREAKING] Replace deprecated implicit injections with explicit injections (@dgeb)

πŸš€ Enhancement

  • #377 Expose store on Cache (@dgeb)
  • #372 Use camelize from @orbit/serializers instead of @ember/string (@dgeb)

🏠 Internal

Committers: 1