Skip to content

Commit 34b9cae

Browse files
committed
refactor(kitsu-core): pass camelCase and pluralisation options as arguments to serialise
BREAKING CHANGE: serialise.apply[{ camel, resCase, plural}, [ model, data, method ]) is no longer neccessary. New syntax is serialise(model, data, method, { camelCaseTypes: camel, pluralTypes: plural}).
1 parent a73ae66 commit 34b9cae

2 files changed

Lines changed: 163 additions & 79 deletions

File tree

packages/kitsu-core/src/serialise/index.js

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,21 +91,39 @@ function hasID (node) {
9191
*
9292
* @param {string} model Request model
9393
* @param {Object} obj The data
94-
* @param {string} method Request type
94+
* @param {string} method Request type (PATCH, POST, DELETE)
95+
* @param {Object} options Optional configuration for camelCase and pluralisation handling
96+
* @param {Function} options.camelCaseTypes Convert library-entries and library_entries to libraryEntries (default no conversion). To use parameter, import camel from kitsu-core
97+
* @param {Function} options.pluralTypes Pluralise types (default no pluralisation). To use parameter, import pluralize (or another pluralisation npm package)
9598
* @returns {Object} The serialised data
9699
*
97-
* @example <caption>Due to its usage in kitsu, it **MUST** be called with **this** set in 6.0.x</caption>
98-
* import { serialise, camel, kebab } from 'kitsu-core'
99-
* import plural from 'pluralize'
100+
* @example <caption>Setting camelCaseTypes and pluralTypes options (example shows options used by `kitsu` by default)</caption>
101+
* import { serialise, camel } from 'kitsu-core'
102+
* import pluralize from 'pluralize'
103+
*
104+
* const model = 'anime'
105+
* const obj = { id: '1', slug: 'shirobako' }
106+
*
107+
* // { data: { id: '1', type: 'anime', attributes: { slug: 'shirobako' } } }
108+
* const output = serialise(model, obj, 'PATCH', { camelCaseTypes: camel, pluralTypes: pluralize })
109+
*
110+
* @example <caption>Basic usage (no case conversion or pluralisation)</caption>
111+
* import { serialise } from 'kitsu-core'
112+
*
113+
* const model = 'anime'
114+
* const obj = { id: '1', slug: 'shirobako' }
100115
*
101-
* const output = serialise.apply({ camel, resCase: kebab, plural }, [ model, obj, 'PATCH' ])
116+
* // { data: { id: '1', type: 'anime', attributes: { slug: 'shirobako' } } }
117+
* const output = serialise(model, obj, 'PATCH')
102118
*/
103-
export function serialise (model, obj = {}, method = 'POST') {
119+
export function serialise (model, obj = {}, method = 'POST', options = {}) {
104120
try {
121+
if (!options.camelCaseTypes) options.camelCaseTypes = s => s
122+
if (!options.pluralTypes) options.pluralTypes = s => s
105123
// Delete relationship to-one (data: null) or to-many (data: [])
106124
if (obj === null || (Array.isArray(obj) && obj.length === 0)) return { data: obj }
107125

108-
const type = this.plural ? this.plural(this.camel(model)) : this.camel(model)
126+
const type = options.pluralTypes(options.camelCaseTypes(model))
109127
let data = { type }
110128

111129
isValid(obj, method, type)
@@ -114,7 +132,7 @@ export function serialise (model, obj = {}, method = 'POST') {
114132

115133
for (const key in obj) {
116134
const node = obj[key]
117-
const nodeType = this.plural ? this.plural(this.camel(key)) : this.camel(key)
135+
const nodeType = options.pluralTypes(options.camelCaseTypes(key))
118136
// 1. Skip null nodes, 2. Only grab objects, 3. Filter to only serialise relationable objects
119137
if (node !== null && node.constructor === Object && hasID(node)) {
120138
data = serialiseObject(node, nodeType, key, data, method)

packages/kitsu-core/src/serialise/index.spec.js

Lines changed: 137 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,70 @@
11
import plural from 'pluralize'
2-
import { camel, kebab, snake } from '../'
2+
import { camel } from '../'
33
import { serialise } from './'
44

5-
// Mock being run from the Kitsu Class:
6-
// serialise.call(this, [...args]) is used to pass constructor options
7-
const skip = s => s
8-
const serial = {
9-
// camelCaseTypes: false resourceCase: kebab pluralize: false
10-
kebab: serialise.bind({ camel: skip, resCase: kebab, plural: skip }),
11-
// camelCaseTypes: false resourceCase: kebab pluralize: true
12-
kebabPlural: serialise.bind({ camel: skip, resCase: kebab, plural }),
13-
// camelCaseTypes: false resourceCase: none pluralize: false
14-
none: serialise.bind({ camel: skip, resCase: skip, plural: skip }),
15-
// camelCaseTypes: false resourceCase: none pluralize: true
16-
nonePlural: serialise.bind({ camel: skip, resCase: skip, plural }),
17-
// camelCaseTypes: false resourceCase: snake pluralize: false
18-
snake: serialise.bind({ camel: skip, resCase: snake, plural: skip }),
19-
// camelCaseTypes: false resourceCase: snake pluralize: true
20-
snakePlural: serialise.bind({ camel: skip, resCase: snake, plural: skip }),
21-
// camelCaseTypes: true resourceCase: kebab pluralize: false
22-
camelKebab: serialise.bind({ camel, resCase: kebab, plural: skip }),
23-
// camelCaseTypes: true resourceCase: kebab pluralize: true DEFAULT
24-
camelKebabPlural: serialise.bind({ camel, resCase: kebab, plural }),
25-
// camelCaseTypes: true resourceCase: none pluralize: false
26-
camelNone: serialise.bind({ camel, resCase: skip, plural: skip }),
27-
// camelCaseTypes: true resourceCase: none pluralize: true
28-
camelNonePlural: serialise.bind({ camel, resCase: skip, plural }),
29-
// camelCaseTypes: true resourceCase: snake pluralize: false
30-
camelSnake: serialise.bind({ camel, resCase: snake, plural: skip }),
31-
// camelCaseTypes: true resourceCase: snake pluralize: true
32-
camelSnakePlural: serialise.bind({ camel, resCase: snake, plural })
33-
}
34-
355
describe('kitsu-core', () => {
366
describe('serialise', () => {
37-
it('serialises to a JSON API compliant object', () => {
7+
it('accepts camelCaseTypes as an option (default)', () => {
8+
expect.assertions(1)
9+
const input = serialise('library-entries', { id: '1' }, undefined)
10+
expect(input).toEqual({
11+
data: {
12+
id: '1',
13+
type: 'library-entries'
14+
}
15+
})
16+
})
17+
18+
it('accepts camelCaseTypes as an option (value set)', () => {
3819
expect.assertions(1)
39-
const input = serial.camelKebabPlural('libraryEntries', {
40-
ratingTwenty: 20
20+
const input = serialise('library-entries', { id: '1' }, undefined, {
21+
camelCaseTypes: camel
4122
})
23+
expect(input).toEqual({
24+
data: {
25+
id: '1',
26+
type: 'libraryEntries'
27+
}
28+
})
29+
})
30+
31+
it('accepts pluralTypes as an option (default)', () => {
32+
expect.assertions(1)
33+
const input = serialise('libraryEntry', { id: '1' }, undefined)
34+
expect(input).toEqual({
35+
data: {
36+
id: '1',
37+
type: 'libraryEntry'
38+
}
39+
})
40+
})
41+
42+
it('accepts pluralTypes as an option (value set)', () => {
43+
expect.assertions(1)
44+
const input = serialise('libraryEntry', { id: '1' }, undefined, {
45+
pluralTypes: plural
46+
})
47+
expect(input).toEqual({
48+
data: {
49+
id: '1',
50+
type: 'libraryEntries'
51+
}
52+
})
53+
})
54+
55+
it('serialises to a JSON API compliant object', () => {
56+
expect.assertions(1)
57+
const input = serialise(
58+
'libraryEntries',
59+
{
60+
ratingTwenty: 20
61+
},
62+
undefined,
63+
{
64+
camelCaseTypes: camel,
65+
pluralTypes: plural
66+
}
67+
)
4268
expect(input).toEqual({
4369
data: {
4470
attributes: {
@@ -51,11 +77,19 @@ describe('kitsu-core', () => {
5177

5278
it('serialises JSON API relationships', () => {
5379
expect.assertions(1)
54-
const input = serial.camelKebabPlural('libraryEntries', {
55-
user: {
56-
id: '2'
80+
const input = serialise(
81+
'libraryEntries',
82+
{
83+
user: {
84+
id: '2'
85+
}
86+
},
87+
undefined,
88+
{
89+
camelCaseTypes: camel,
90+
pluralTypes: plural
5791
}
58-
})
92+
)
5993
expect(input).toEqual({
6094
data: {
6195
relationships: {
@@ -73,17 +107,25 @@ describe('kitsu-core', () => {
73107

74108
it('serialises JSON API array relationships', () => {
75109
expect.assertions(1)
76-
const input = serial.camelKebabPlural('libraryEntries', {
77-
user: [
78-
{
79-
id: '2',
80-
type: 'users'
81-
},
82-
{
83-
id: '3'
84-
}
85-
]
86-
})
110+
const input = serialise(
111+
'libraryEntries',
112+
{
113+
user: [
114+
{
115+
id: '2',
116+
type: 'users'
117+
},
118+
{
119+
id: '3'
120+
}
121+
]
122+
},
123+
undefined,
124+
{
125+
camelCaseTypes: camel,
126+
pluralTypes: plural
127+
}
128+
)
87129
expect(input).toEqual({
88130
data: {
89131
relationships: {
@@ -107,10 +149,18 @@ describe('kitsu-core', () => {
107149

108150
it('serialises JSON API with a client-generated ID', () => {
109151
expect.assertions(1)
110-
const input = serial.camelKebabPlural('libraryEntries', {
111-
id: '123456789',
112-
ratingTwenty: 20
113-
})
152+
const input = serialise(
153+
'libraryEntries',
154+
{
155+
id: '123456789',
156+
ratingTwenty: 20
157+
},
158+
undefined,
159+
{
160+
camelCaseTypes: camel,
161+
pluralTypes: plural
162+
}
163+
)
114164
expect(input).toEqual({
115165
data: {
116166
id: '123456789',
@@ -124,9 +174,17 @@ describe('kitsu-core', () => {
124174

125175
it('pluralises type', () => {
126176
expect.assertions(1)
127-
const input = serial.camelKebabPlural('libraryEntry', {
128-
rating: '1'
129-
})
177+
const input = serialise(
178+
'libraryEntry',
179+
{
180+
rating: '1'
181+
},
182+
undefined,
183+
{
184+
camelCaseTypes: camel,
185+
pluralTypes: plural
186+
}
187+
)
130188
expect(input).toEqual({
131189
data: {
132190
type: 'libraryEntries',
@@ -139,9 +197,17 @@ describe('kitsu-core', () => {
139197

140198
it('does not pluralise mass nouns', () => {
141199
expect.assertions(1)
142-
const input = serial.camelKebabPlural('anime', {
143-
slug: 'Cowboy Bebop 2'
144-
})
200+
const input = serialise(
201+
'anime',
202+
{
203+
slug: 'Cowboy Bebop 2'
204+
},
205+
undefined,
206+
{
207+
camelCaseTypes: camel,
208+
pluralTypes: plural
209+
}
210+
)
145211
expect(input).toEqual({
146212
data: {
147213
type: 'anime',
@@ -154,7 +220,7 @@ describe('kitsu-core', () => {
154220

155221
it('does not pluralise type', () => {
156222
expect.assertions(1)
157-
const input = serial.none('libraryEntry', {
223+
const input = serialise('libraryEntry', {
158224
rating: '1'
159225
})
160226
expect(input).toEqual({
@@ -169,25 +235,25 @@ describe('kitsu-core', () => {
169235

170236
it('throws an error if obj is missing', () => {
171237
expect.assertions(1)
172-
expect(() => serial.camelKebabPlural('post'))
238+
expect(() => serialise('post'))
173239
.toThrowError('POST requires a JSON object body')
174240
})
175241

176242
it('throws an error if obj is not an Object', () => {
177243
expect.assertions(1)
178-
expect(() => serial.camelKebabPlural('post', 'id: 1', 'DELETE'))
244+
expect(() => serialise('post', 'id: 1', 'DELETE'))
179245
.toThrowError('DELETE requires a JSON object body')
180246
})
181247

182248
it('throws an error when missing ID', () => {
183249
expect.assertions(1)
184-
expect(() => serial.camelKebabPlural('user', { theme: 'dark' }, 'PATCH'))
185-
.toThrowError('PATCH requires an ID for the users type')
250+
expect(() => serialise('user', { theme: 'dark' }, 'PATCH'))
251+
.toThrowError('PATCH requires an ID for the user type')
186252
})
187253

188254
it('serialises strings/numbers/booleans into attributes', () => {
189255
expect.assertions(1)
190-
const input = serial.none('resourceModel', {
256+
const input = serialise('resourceModel', {
191257
string: 'shark',
192258
number: 1,
193259
boolean: true
@@ -206,7 +272,7 @@ describe('kitsu-core', () => {
206272

207273
it('serialises bare objects into attributes', () => {
208274
expect.assertions(1)
209-
const input = serial.none('resourceModel', {
275+
const input = serialise('resourceModel', {
210276
object: {
211277
string: 'shark'
212278
},
@@ -227,7 +293,7 @@ describe('kitsu-core', () => {
227293

228294
it('serialises type objects into relationships', () => {
229295
expect.assertions(1)
230-
const input = serial.none('resourceModel', {
296+
const input = serialise('resourceModel', {
231297
object: {
232298
id: '1',
233299
type: 'relationshipModel'
@@ -250,7 +316,7 @@ describe('kitsu-core', () => {
250316

251317
it('serialises bare arrays into attributes', () => {
252318
expect.assertions(1)
253-
const input = serial.none('resourceModel', {
319+
const input = serialise('resourceModel', {
254320
array: [ 0 ],
255321
deepArray: [ [ 0 ] ],
256322
arrayObject: [ { string: 'shark' } ],
@@ -271,7 +337,7 @@ describe('kitsu-core', () => {
271337

272338
it('serialises type arrays into relationships', () => {
273339
expect.assertions(1)
274-
const input = serial.none('resourceModel', {
340+
const input = serialise('resourceModel', {
275341
array: [ {
276342
id: '1',
277343
type: 'relationshipModel'
@@ -294,15 +360,15 @@ describe('kitsu-core', () => {
294360

295361
it('serialises relationship clearing (to-one)', () => {
296362
expect.assertions(1)
297-
const input = serial.none('resourceModel', null)
363+
const input = serialise('resourceModel', null)
298364
expect(input).toEqual({
299365
data: null
300366
})
301367
})
302368

303369
it('serialises relationship clearing (to-many)', () => {
304370
expect.assertions(1)
305-
const input = serial.none('resourceModel', [])
371+
const input = serialise('resourceModel', [])
306372
expect(input).toEqual({
307373
data: []
308374
})

0 commit comments

Comments
 (0)