Skip to content
This repository has been archived by the owner on Jul 6, 2018. It is now read-only.

Commit

Permalink
implement DS.belongsTo/hasMany
Browse files Browse the repository at this point in the history
  • Loading branch information
dwickern committed Sep 4, 2017
1 parent 6a9bdfa commit 0100a8b
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 21 deletions.
74 changes: 55 additions & 19 deletions types/ember-data/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,30 @@ namespace DS {
* `DS.belongsTo` is used to define One-To-One and One-To-Many
* relationships on a [DS.Model](/api/data/classes/DS.Model.html).
*/
function belongsTo(modelName: string, options?: {}): Ember.ComputedProperty;
function belongsTo<T extends Model>(modelName: string, options: {
async: false,
inverse?: string | null,
polymorphic?: boolean
}): T;
function belongsTo<T extends Model>(modelName: string, options?: {
async?: true,
inverse?: string | null,
polymorphic?: boolean
}): DS.PromiseObject<T> & T;
/**
* `DS.hasMany` is used to define One-To-Many and Many-To-Many
* relationships on a [DS.Model](/api/data/classes/DS.Model.html).
*/
function hasMany(type: string, options?: {}): Ember.ComputedProperty;
function hasMany<T extends Model>(type: string, options: {
async: false,
inverse?: string | null,
polymorphic?: boolean
}): DS.ManyArray<T>;
function hasMany<T extends Model>(type: string, options?: {
async?: true,
inverse?: string | null,
polymorphic?: boolean
}): DS.PromiseManyArray<T>;
/**
* This method normalizes a modelName into the format Ember Data uses
* internally.
Expand Down Expand Up @@ -429,7 +447,7 @@ namespace DS {
/**
* Get the reference for the specified hasMany relationship.
*/
hasMany(name: string): HasManyReference;
hasMany(name: string): HasManyReference<any>;
/**
* Given a callback, iterates over each of the relationships in the model,
* invoking the callback with the name of each relationship and its relationship
Expand Down Expand Up @@ -537,15 +555,15 @@ namespace DS {
* may trigger a search on the server, whose results would be loaded
* into an instance of the `AdapterPopulatedRecordArray`.
*/
class AdapterPopulatedRecordArray extends RecordArray {
class AdapterPopulatedRecordArray<T> extends RecordArray<T> {
}
/**
* Represents a list of records whose membership is determined by the
* store. As records are created, loaded, or modified, the store
* evaluates them to determine if they should be part of the record
* array.
*/
class FilteredRecordArray extends RecordArray {
class FilteredRecordArray<T> extends RecordArray<T> {
/**
* The filterFunction is a function used to test records from the store to
* determine if they should be part of the record array.
Expand All @@ -559,7 +577,7 @@ namespace DS {
* `DS.RecordArray` or its subclasses will be returned by your application's store
* in response to queries.
*/
class RecordArray {
class RecordArray<T> {
/**
* The flag to signal a `RecordArray` is finished loading data.
*/
Expand All @@ -580,7 +598,7 @@ namespace DS {
/**
* Saves all of the records in the `RecordArray`.
*/
save(): PromiseArray;
save(): PromiseArray<T>;
}
/**
* A BelongsToReference is a low level API that allows users and
Expand Down Expand Up @@ -643,7 +661,7 @@ namespace DS {
* A HasManyReference is a low level API that allows users and addon
* author to perform meta-operations on a has-many relationship.
*/
class HasManyReference {
class HasManyReference<T> {
/**
* `ids()` returns an array of the record ids in this relationship.
*/
Expand All @@ -663,15 +681,15 @@ namespace DS {
* Data will treat the new data as the canonical value of this
* relationship on the backend.
*/
push(objectOrPromise: any[]|Promise<any>): ManyArray;
push(objectOrPromise: T[] | Promise<T[]>): ManyArray<T>;
/**
* `value()` sycronously returns the current value of the has-many
* relationship. Unlike `record.get('relationshipName')`, calling
* `value()` on a reference does not trigger a fetch if the async
* relationship is not yet loaded. If the relationship is not loaded
* it will always return `null`.
*/
value(): ManyArray;
value(): ManyArray<T>;
/**
* Loads the relationship if it is not already loaded. If the
* relationship is already loaded this method does not trigger a new
Expand Down Expand Up @@ -726,7 +744,8 @@ namespace DS {
* A `ManyArray` is a `MutableArray` that represents the contents of a has-many
* relationship.
*/
class ManyArray {
interface ManyArray<T> extends Ember.MutableArray<T> {}
class ManyArray<T> extends Ember.Object.extend(Ember.MutableArray as {}, Ember.Evented) {
/**
* The loading state of this array
*/
Expand All @@ -741,11 +760,15 @@ namespace DS {
* Ember Data will revisit the original links url to repopulate the
* relationship.
*/
reload(): any;
reload(): DS.PromiseArray<T>;
/**
* Saves all of the records in the `ManyArray`.
*/
save(): PromiseArray;
save(): PromiseArray<T>;
/**
* Create a child record within the owner
*/
createRecord(inputProperties?: {}): T;
}
/**
* A `PromiseArray` is an object that acts like both an `Ember.Array`
Expand All @@ -754,7 +777,8 @@ namespace DS {
* it easy to create data bindings with the `PromiseArray` that will be
* updated when the promise resolves.
*/
class PromiseArray {
interface PromiseArray<T> extends Ember.ArrayProxy<T>, Ember.PromiseProxyMixin<PromiseArray<T>> {}
class PromiseArray<T> {
}
/**
* A `PromiseObject` is an object that acts like both an `Ember.Object`
Expand All @@ -763,14 +787,26 @@ namespace DS {
* it easy to create data bindings with the `PromiseObject` that will
* be updated when the promise resolves.
*/
class PromiseObject {
interface PromiseObject<T> extends Ember.ObjectProxy, Ember.PromiseProxyMixin<PromiseObject<T> & T> {}
class PromiseObject<T> {
}
/**
* A PromiseManyArray is a PromiseArray that also proxies certain method calls
* to the underlying manyArray.
* Right now we proxy:
*/
class PromiseManyArray {
class PromiseManyArray<T extends DS.Model> extends PromiseArray<T> {
/**
* Reloads all of the records in the manyArray. If the manyArray
* holds a relationship that was originally fetched using a links url
* Ember Data will revisit the original links url to repopulate the
* relationship.
*/
reload(): DS.PromiseManyArray<T>;
/**
* Create a child record within the owner
*/
createRecord(inputProperties?: {}): T;
}
class SnapshotRecordArray {
/**
Expand Down Expand Up @@ -872,7 +908,7 @@ namespace DS {
* Create a new record in the current store. The properties passed
* to this method are set on the newly created record.
*/
createRecord(modelName: string, inputProperties: {}): Model;
createRecord(modelName: string, inputProperties?: {}): Model;
/**
* For symmetry, a record can be deleted via the store.
*/
Expand Down Expand Up @@ -922,7 +958,7 @@ namespace DS {
* This method returns a filtered array that contains all of the
* known records for a given type in the store.
*/
peekAll(modelName: string): RecordArray;
peekAll(modelName: string): RecordArray<any>;
/**
* This method unloads all records in the store.
* It schedules unloading to happen during the next run loop.
Expand Down Expand Up @@ -1509,7 +1545,7 @@ namespace DS {
/**
* This method is called when you call `query` on the store.
*/
query(store: Store, type: Model, query: {}, recordArray: AdapterPopulatedRecordArray): Promise<any>;
query(store: Store, type: Model, query: {}, recordArray: AdapterPopulatedRecordArray<any>): Promise<any>;
/**
* The `queryRecord()` method is invoked when the store is asked for a single
* record through a query object.
Expand Down
16 changes: 16 additions & 0 deletions types/ember-data/test/belongs-to.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import DS from 'ember-data';
import { assertType } from './lib/assert';

class Folder extends DS.Model {
name = DS.attr('string');
children = DS.hasMany<Folder>('folder', { inverse: 'parent' });
parent = DS.belongsTo<Folder>('folder', { inverse: 'children' });
}

const folder = Folder.create();
assertType<Folder>(folder.get('parent'));
assertType<string>(folder.get('parent').get('name'));
folder.get('parent').then(parent => {
assertType<Folder>(parent);
assertType<string>(parent.get('name'));
});
34 changes: 34 additions & 0 deletions types/ember-data/test/has-many.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import DS from 'ember-data';
import { assertType } from './lib/assert';

class Comment extends DS.Model {
text = DS.attr('string');
}

class BlogPost extends DS.Model {
title = DS.attr('string');
commentsAsync = DS.hasMany<Comment>('comment');
commentsSync = DS.hasMany<Comment>('comment', { async: false });
}

const post = BlogPost.create();

assertType<DS.PromiseArray<Comment>>(post.get('commentsSync').reload());
assertType<Comment>(post.get('commentsSync').createRecord());
assertType<Comment>(post.get('commentsSync').get('firstObject'));
assertType<string>(post.get('commentsSync').get('firstObject').get('text'));

assertType<DS.PromiseArray<Comment>>(post.get('commentsAsync').reload());
assertType<Comment>(post.get('commentsAsync').createRecord());
assertType<Comment>(post.get('commentsAsync').get('firstObject'));
assertType<string>(post.get('commentsAsync').get('firstObject').get('text'));
assertType<boolean>(post.get('commentsAsync').get('isFulfilled'));

post.get('commentsAsync').then(comments => {
assertType<Comment>(comments.get('firstObject'));
assertType<string>(comments.get('firstObject').get('text'));
});

class Polymorphic extends DS.Model {
paymentMethods = DS.hasMany('payment-method', { polymorphic: true });
}
5 changes: 4 additions & 1 deletion types/ember-data/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@
},
"files": [
"index.d.ts",
"test/lib/assert.ts",
"test/model.ts",
"test/adapter.ts",
"test/serializer.ts",
"test/transform.ts",
"test/relationships.ts",
"test/store.ts"
"test/store.ts",
"test/has-many.ts",
"test/belongs-to.ts"
]
}
3 changes: 2 additions & 1 deletion types/ember-data/tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// not sure what this means
"no-single-declare-module": false,
"object-literal-key-quotes": false,
"only-arrow-functions": false
"only-arrow-functions": false,
"no-empty-interface": false
}
}

0 comments on commit 0100a8b

Please sign in to comment.