UNPKG

4.11 kBJavaScriptView Raw
1import Ember from 'ember';
2import DS from 'ember-data';
3
4
5const {
6 InvalidError,
7 errorsHashToArray,
8 RESTAdapter
9} = DS;
10
11const {
12 pluralize,
13 decamelize,
14 underscore
15} = Ember.String;
16
17/**
18 @module ember-data
19*/
20
21/**
22 The ActiveModelAdapter is a subclass of the RESTAdapter designed to integrate
23 with a JSON API that uses an underscored naming convention instead of camelCasing.
24 It has been designed to work out of the box with the
25 [active\_model\_serializers](http://github.com/rails-api/active_model_serializers)
26 Ruby gem. This Adapter expects specific settings using ActiveModel::Serializers,
27 `embed :ids, embed_in_root: true` which sideloads the records.
28
29 This adapter extends the DS.RESTAdapter by making consistent use of the camelization,
30 decamelization and pluralization methods to normalize the serialized JSON into a
31 format that is compatible with a conventional Rails backend and Ember Data.
32
33 ## JSON Structure
34
35 The ActiveModelAdapter expects the JSON returned from your server to follow
36 the REST adapter conventions substituting underscored keys for camelcased ones.
37
38 Unlike the DS.RESTAdapter, async relationship keys must be the singular form
39 of the relationship name, followed by "_id" for DS.belongsTo relationships,
40 or "_ids" for DS.hasMany relationships.
41
42 ### Conventional Names
43
44 Attribute names in your JSON payload should be the underscored versions of
45 the attributes in your Ember.js models.
46
47 For example, if you have a `Person` model:
48
49 ```js
50 App.FamousPerson = DS.Model.extend({
51 firstName: DS.attr('string'),
52 lastName: DS.attr('string'),
53 occupation: DS.attr('string')
54 });
55 ```
56
57 The JSON returned should look like this:
58
59 ```js
60 {
61 "famous_person": {
62 "id": 1,
63 "first_name": "Barack",
64 "last_name": "Obama",
65 "occupation": "President"
66 }
67 }
68 ```
69
70 Let's imagine that `Occupation` is just another model:
71
72 ```js
73 App.Person = DS.Model.extend({
74 firstName: DS.attr('string'),
75 lastName: DS.attr('string'),
76 occupation: DS.belongsTo('occupation')
77 });
78
79 App.Occupation = DS.Model.extend({
80 name: DS.attr('string'),
81 salary: DS.attr('number'),
82 people: DS.hasMany('person')
83 });
84 ```
85
86 The JSON needed to avoid extra server calls, should look like this:
87
88 ```js
89 {
90 "people": [{
91 "id": 1,
92 "first_name": "Barack",
93 "last_name": "Obama",
94 "occupation_id": 1
95 }],
96
97 "occupations": [{
98 "id": 1,
99 "name": "President",
100 "salary": 100000,
101 "person_ids": [1]
102 }]
103 }
104 ```
105
106 @class ActiveModelAdapter
107 @constructor
108 @namespace DS
109 @extends DS.RESTAdapter
110**/
111
112const ActiveModelAdapter = RESTAdapter.extend({
113 defaultSerializer: '-active-model',
114 /**
115 The ActiveModelAdapter overrides the `pathForType` method to build
116 underscored URLs by decamelizing and pluralizing the object type name.
117
118 ```js
119 this.pathForType("famousPerson");
120 //=> "famous_people"
121 ```
122
123 @method pathForType
124 @param {String} modelName
125 @return String
126 */
127 pathForType: function(modelName) {
128 var decamelized = decamelize(modelName);
129 var underscored = underscore(decamelized);
130 return pluralize(underscored);
131 },
132
133 /**
134 The ActiveModelAdapter overrides the `handleResponse` method
135 to format errors passed to a DS.InvalidError for all
136 422 Unprocessable Entity responses.
137
138 A 422 HTTP response from the server generally implies that the request
139 was well formed but the API was unable to process it because the
140 content was not semantically correct or meaningful per the API.
141
142 For more information on 422 HTTP Error code see 11.2 WebDAV RFC 4918
143 https://tools.ietf.org/html/rfc4918#section-11.2
144
145 @method ajaxError
146 @param {Object} jqXHR
147 @return error
148 */
149 handleResponse: function(status, headers, payload) {
150 if (this.isInvalid(status, headers, payload)) {
151 let errors = errorsHashToArray(payload.errors);
152
153 return new InvalidError(errors);
154 } else {
155 return this._super(...arguments);
156 }
157 }
158});
159
160export default ActiveModelAdapter;