diff --git a/README.md b/README.md index 586a566..68dc278 100644 --- a/README.md +++ b/README.md @@ -6,22 +6,10 @@ This test application implements and showcases *a* solution for handling REST ne > `/resource/:id/sub_resource` -It does so with an [adapter mixin](./app/mixins/sub-resource-adapter.js) overriding the `buildURL()` method. +It does so with an [application adapter](./app/adapters/application.js) overriding the `buildURL()` method. ## Proposed API -> Make your sub-resource's adapter extends the mixin. - -In the sub-resource's adapter ([see example](./app/adapters/comment.js)): -```js -[...] -import SubResourceAdapterMixin from '../mixins/sub-resource-adapter'; - -export default ApplicationAdapter.extend(SubResourceAdapterMixin, { - [...] -}); -``` - > When using the adapter to query the backend, make sure to provide the `parentResource`. In a route fetching the index of the sub-resource ([see example](./app/routes/posts/detail/comments.js)): @@ -29,7 +17,7 @@ In a route fetching the index of the sub-resource ([see example](./app/routes/po [...] model() { let parentResource = this.modelFor('parent-route'); - return this.store.findAll('sub-resource', { adapterOptions: { parentResource } }); + return this.store.findSubAll(parentResource, 'sub-resource'); } [...] ``` @@ -39,7 +27,7 @@ In a route fetching a single item of the sub-resource ([see example](./app/route [...] model({ id }) { let parentResource = this.modelFor('parent-route'); - return this.store.findRecord('sub-resource', id, { adapterOptions: { parentResource } }); + return this.store.findSubRecord(parentResource, 'sub-resource', id); } [...] ``` @@ -117,7 +105,7 @@ By providing a reference to the parent resource, we are now able to build our su [...] model() { let post = this.modelFor('posts.detail'); - return this.store.findAll('comment', { adapterOptions: { parentResource: post } }); + return this.store.findSubAll(post, 'comment'); } [...] ``` diff --git a/app/adapters/application.js b/app/adapters/application.js index 28fb78c..23809d7 100644 --- a/app/adapters/application.js +++ b/app/adapters/application.js @@ -1,4 +1,16 @@ import DS from 'ember-data'; +import { get } from '@ember/object'; export default DS.RESTAdapter.extend({ + buildURL(modelName, id, snapshot) { + let url = this._super(...arguments); + let parentResource = get(snapshot, 'adapterOptions.parentResource') + if (parentResource) { + let { modelName: parentModelName } = parentResource.constructor; + let parentAdapter = this.store.adapterFor(parentModelName); + let parentUrl = parentAdapter.buildURL(parentModelName, parentResource.id); + return `${parentUrl}${url}`; + } + return url; + } }); diff --git a/app/adapters/comment.js b/app/adapters/comment.js deleted file mode 100644 index 07fd038..0000000 --- a/app/adapters/comment.js +++ /dev/null @@ -1,5 +0,0 @@ -import ApplicationAdapter from './application'; -import SubResourceAdapterMixin from '../mixins/sub-resource-adapter'; - -export default ApplicationAdapter.extend(SubResourceAdapterMixin, { -}); diff --git a/app/mixins/sub-resource-adapter.js b/app/mixins/sub-resource-adapter.js deleted file mode 100644 index 909a6a4..0000000 --- a/app/mixins/sub-resource-adapter.js +++ /dev/null @@ -1,17 +0,0 @@ -import Mixin from '@ember/object/mixin'; -import { get } from '@ember/object'; -import { assert } from '@ember/debug'; - -export default Mixin.create({ - buildURL(modelName, id, snapshot) { - assert('This is a subresource, a `snapshot.adapterOptions.parentResource:` must be provided.', get(snapshot, 'adapterOptions.parentResource')); - - let url = this._super(...arguments); - let { parentResource } = snapshot.adapterOptions; - let { modelName: parentModelName } = parentResource.constructor; - let parentAdapter = this.store.adapterFor(parentModelName); - let parentUrl = parentAdapter.buildURL(parentModelName, parentResource.id); - - return `${parentUrl}${url}`; - } -}); diff --git a/app/models/comment.js b/app/models/comment.js index aca82e7..ba80ea4 100644 --- a/app/models/comment.js +++ b/app/models/comment.js @@ -1,6 +1,5 @@ import DS from 'ember-data'; export default DS.Model.extend({ - content: DS.attr('string'), - post: DS.belongsTo('post') + content: DS.attr('string') }); diff --git a/app/models/post.js b/app/models/post.js index 172f8f6..38b7a7f 100644 --- a/app/models/post.js +++ b/app/models/post.js @@ -2,6 +2,5 @@ import DS from 'ember-data'; export default DS.Model.extend({ title: DS.attr('string'), - content: DS.attr('string'), - comments: DS.hasMany('comment') + content: DS.attr('string') }); diff --git a/app/routes/posts/detail/comments.js b/app/routes/posts/detail/comments.js index deaefb1..189f903 100644 --- a/app/routes/posts/detail/comments.js +++ b/app/routes/posts/detail/comments.js @@ -3,6 +3,6 @@ import Route from '@ember/routing/route'; export default Route.extend({ model() { let post = this.modelFor('posts.detail'); - return this.store.findAll('comment', { adapterOptions: { parentResource: post } }); + return this.store.findSubAll(post, 'comment'); } }); diff --git a/app/routes/posts/detail/comments/detail.js b/app/routes/posts/detail/comments/detail.js index 478b3e6..b4696d4 100644 --- a/app/routes/posts/detail/comments/detail.js +++ b/app/routes/posts/detail/comments/detail.js @@ -3,6 +3,6 @@ import Route from '@ember/routing/route'; export default Route.extend({ model({ comment_id }) { let post = this.modelFor('posts.detail'); - return this.store.findRecord('comment', comment_id, { adapterOptions: { parentResource: post } }); + return this.store.findSubRecord(post, 'comment', comment_id); } }); diff --git a/app/services/store.js b/app/services/store.js new file mode 100644 index 0000000..ea1405b --- /dev/null +++ b/app/services/store.js @@ -0,0 +1,15 @@ +import DS from "ember-data"; +import { set } from '@ember/object'; + +const { Store } = DS; + +export default Store.extend({ + findSubAll(parentResource, modelName, options = {}) { + set(options, 'parentResource', parentResource); + return this.findAll(modelName, options); + }, + findSubRecord(parentResource, modelName, id, options = {}) { + set(options, 'parentResource', parentResource); + return this.findRecord(modelName, id, options); + } +}); diff --git a/mock/db.json b/mock/db.json index 3acaef7..a656962 100644 --- a/mock/db.json +++ b/mock/db.json @@ -11,6 +11,11 @@ "id": 1, "content": "some comment", "post_id": 1 + }, + { + "id": 2, + "content": "some comment#", + "post_id": 2 } ] } diff --git a/tests/unit/services/store-test.js b/tests/unit/services/store-test.js new file mode 100644 index 0000000..16923a6 --- /dev/null +++ b/tests/unit/services/store-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Service | store', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let service = this.owner.lookup('service:store'); + assert.ok(service); + }); +});