Skip to content

Commit b16f3ef

Browse files
committed
allow to explixitly set keyCase.from and .to
1 parent da0552e commit b16f3ef

File tree

10 files changed

+48
-41
lines changed

10 files changed

+48
-41
lines changed

src/model.ts

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,19 @@ import { cloneDeep } from "./util/clonedeep"
3939
import { nonenumerable } from "./util/decorators"
4040
import { IncludeScopeHash } from "./util/include-directive"
4141

42+
export interface KeyCase {
43+
from: "dash" | "camel" | "snake"
44+
to: "dash" | "camel" | "snake"
45+
}
46+
4247
export interface ModelConfiguration {
4348
baseUrl: string
4449
apiNamespace: string
4550
jsonapiType: string
4651
endpoint: string
4752
jwt: string
4853
jwtStorage: string | false
49-
camelizeKeys: boolean
50-
letterCase: string
54+
keyCase: KeyCase
5155
strictAttributes: boolean
5256
logger: ILogger
5357
}
@@ -129,8 +133,7 @@ export class JSORMBase {
129133
static endpoint: string
130134
static isBaseClass: boolean
131135
static jwt?: string
132-
static camelizeKeys: boolean = true
133-
static letterCase: string = "underscore"
136+
static keyCase: KeyCase = { from: "snake", to: "camel" }
134137
static strictAttributes: boolean = false
135138
static logger: ILogger = defaultLogger
136139

@@ -467,9 +470,7 @@ export class JSORMBase {
467470
if (attrs.hasOwnProperty(key)) {
468471
let attributeName = key
469472

470-
if (this.klass.camelizeKeys) {
471-
attributeName = this.klass.deserializeKey(key)
472-
}
473+
attributeName = this.klass.deserializeKey(key)
473474

474475
if (key === "id" || this.klass.attributeList[attributeName]) {
475476
;(<any>this)[attributeName] = attrs[key]
@@ -695,14 +696,31 @@ export class JSORMBase {
695696
}
696697

697698
static serializeKey(key: string): string {
698-
if (this.letterCase == "dasherized") {
699-
return dasherize(underscore(key))
699+
switch (this.keyCase.from) {
700+
case "dash": {
701+
return dasherize(underscore(key))
702+
}
703+
case "snake": {
704+
return underscore(key)
705+
}
706+
case "camel": {
707+
return camelize(underscore(key), false)
708+
}
700709
}
701-
return underscore(key)
702710
}
703711

704712
static deserializeKey(key: string): string {
705-
return camelize(underscore(key), false)
713+
switch (this.keyCase.to) {
714+
case "dash": {
715+
return dasherize(underscore(key))
716+
}
717+
case "snake": {
718+
return underscore(key)
719+
}
720+
case "camel": {
721+
return camelize(underscore(key), false)
722+
}
723+
}
706724
}
707725

708726
async destroy(): Promise<boolean> {

src/util/deserialize.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { JsonapiTypeRegistry } from "../jsonapi-type-registry"
22
import { JSORMBase } from "../model"
3-
import { camelize, underscore } from "inflected"
43
import {
54
IncludeDirective,
65
IncludeScopeHash,
@@ -238,15 +237,7 @@ class Deserializer {
238237
) {
239238
for (const key in relationships) {
240239
if (relationships.hasOwnProperty(key)) {
241-
let relationName = key
242-
243-
if (instance.klass.camelizeKeys) {
244-
if (instance.klass.letterCase === "dasherized") {
245-
relationName = camelize(underscore(key), false)
246-
} else {
247-
relationName = camelize(key, false)
248-
}
249-
}
240+
let relationName = instance.klass.deserializeKey(key)
250241

251242
if (instance.klass.attributeList[relationName]) {
252243
const relationData = relationships[key].data

src/util/validation-errors.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,7 @@ export class ValidationErrors {
4040
errorsAccumulator: Record<string, string>,
4141
meta: JsonapiErrorMeta
4242
) {
43-
let attribute = meta.attribute
44-
45-
if (this.model.klass.camelizeKeys) {
46-
attribute = this.model.klass.deserializeKey(attribute)
47-
}
43+
let attribute = this.model.klass.deserializeKey(meta.attribute)
4844

4945
errorsAccumulator[attribute] = meta.message
5046
}

test/fixtures.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ export class PersonWithExtraAttr extends Person {
3030
extraThing: string
3131
}
3232

33-
@Model({ camelizeKeys: false })
33+
@Model({ keyCase: { from: "snake", to: "snake" } })
3434
export class PersonWithoutCamelizedKeys extends Person {
3535
@Attr first_name: string
3636
}
3737

38-
@Model({ letterCase: "dasherized" })
38+
@Model({ keyCase: { from: "dash", to: "camel" } })
3939
export class PersonWithDasherizedKeys extends Person {}
4040

4141
@Model({
@@ -70,7 +70,7 @@ export const NonFictionAuthor = Author.extend({
7070
static: {
7171
endpoint: "/v1/non_fiction_authors",
7272
jsonapiType: "non_fiction_authors",
73-
camelizeKeys: false
73+
keyCase: { from: "snake", to: "snake" }
7474
},
7575

7676
attrs: {

test/integration/relations.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ describe("Relations", () => {
100100
})
101101
})
102102

103-
describe("when camelizeKeys is false", () => {
103+
describe("when keyCase is snake_case", () => {
104104
beforeEach(() => {
105105
fetchMock.get(
106106
"http://example.com/api/v1/non_fiction_authors/1?include=books,multi_words",
@@ -110,7 +110,7 @@ describe("Relations", () => {
110110

111111
afterEach(fetchMock.restore)
112112

113-
it("Doesn't convert relationships to snake_case if camelization is off", async () => {
113+
it("Doesn't convert relationships to snake_case if keyCase.to is snake is off", async () => {
114114
const data = await resultData(
115115
NonFictionAuthor.includes(["books", "multi_words"]).find(1)
116116
)

test/integration/validations.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,21 +122,21 @@ describe("validations", () => {
122122
})
123123
})
124124

125-
describe("when camelizeKeys is false", () => {
125+
describe("when keyCase.to is snake", () => {
126126
beforeEach(() => {
127-
instance.klass.camelizeKeys = false
127+
instance.klass.keyCase.to = "snake"
128128
})
129129

130130
afterEach(() => {
131-
instance.klass.camelizeKeys = true
131+
instance.klass.keyCase.to = "camel"
132132
})
133133

134134
it("does not camelize the error keys", async () => {
135135
await instance.save({ with: { books: "genre" } })
136136

137137
expect(instance.errors).to.deep.equal({
138138
first_name: "cannot be blank",
139-
"last-name": "cannot be blank"
139+
last_name: "cannot be blank"
140140
})
141141
})
142142
})

test/unit/decorators.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ describe("Decorators", () => {
3535

3636
it("preserves defaults for unspecified items", () => {
3737
expect(TestModel.baseUrl).to.eq("http://please-set-a-base-url.com")
38-
expect(TestModel.camelizeKeys).to.be.true
38+
expect(TestModel.keyCase.from).to.eq("snake")
39+
expect(TestModel.keyCase.to).to.eq("camel")
3940
})
4041

4142
it("correctly assigns options", () => {

test/unit/model-attributes.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ describe("Model attributes", () => {
2525
expect(person.firstName).to.eq("Joe")
2626
})
2727

28-
it("does not camlize underscored strings if camelization is disabled", () => {
28+
it("does not camlize underscored strings if keys.to is snake", () => {
2929
const person = new PersonWithoutCamelizedKeys({ first_name: "Joe" })
3030
expect(person.firstName).to.eq(undefined)
3131
expect(person.first_name).to.eq("Joe")

test/unit/model-class-typings.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ describe("Model Class static attributes typings", () => {
4444
const endpoint: string = RootClass.endpoint
4545
const jwt: string | undefined = RootClass.jwt
4646
const jwtLocalStorage: string | false = RootClass.jwtLocalStorage
47-
const camelizeKeys: boolean = RootClass.camelizeKeys
47+
const keyCase: KeyCase = RootClass.keyCase
4848
const strictAttributes: boolean = RootClass.strictAttributes
4949
})
5050
})

test/unit/model.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,8 @@ describe("Model", () => {
446446

447447
it("preserves defaults for unspecified items", () => {
448448
expect(MyModel.baseUrl).to.eq("http://please-set-a-base-url.com")
449-
expect(MyModel.camelizeKeys).to.be.true
449+
expect(MyModel.keyCase.from).to.eq("snake")
450+
expect(MyModel.keyCase.to).to.eq("camel")
450451
})
451452

452453
it("correctly assigns options", () => {
@@ -817,8 +818,8 @@ describe("Model", () => {
817818
})
818819
})
819820

820-
describe('when previously persisted, dirty, then persisted again', () => {
821-
it('is no longer dirty', () => {
821+
describe("when previously persisted, dirty, then persisted again", () => {
822+
it("is no longer dirty", () => {
822823
const instance = new Author({ id: 1 })
823824
instance.firstName = "foo"
824825
instance.isPersisted = true

0 commit comments

Comments
 (0)