-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce EloquentAttribute which manage known and dynamic attribute
- Loading branch information
Showing
11 changed files
with
529 additions
and
541 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { Eloquent } from './Eloquent'; | ||
export declare class EloquentAttribute { | ||
protected known: string[]; | ||
protected dynamic: { | ||
[key: string]: { | ||
name: string; | ||
getter: boolean; | ||
setter: boolean; | ||
accessor?: string; | ||
mutator?: string; | ||
}; | ||
}; | ||
constructor(model: Eloquent, prototype: any); | ||
protected createDynamicAttributeIfNeeded(property: string): void; | ||
isKnownAttribute(name: string | Symbol): boolean; | ||
buildKnownAttributes(model: Eloquent, prototype: any): void; | ||
findGettersAndSetters(prototype: any): void; | ||
findAccessorsAndMutators(prototype: any): void; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const Eloquent_1 = require("./Eloquent"); | ||
const lodash_1 = require("lodash"); | ||
const EloquentProxy_1 = require("./EloquentProxy"); | ||
class EloquentAttribute { | ||
constructor(model, prototype) { | ||
this.dynamic = {}; | ||
this.known = []; | ||
this.findGettersAndSetters(prototype); | ||
this.findAccessorsAndMutators(prototype); | ||
this.buildKnownAttributes(model, prototype); | ||
} | ||
createDynamicAttributeIfNeeded(property) { | ||
if (!this.dynamic[property]) { | ||
this.dynamic[property] = { | ||
name: property, | ||
getter: false, | ||
setter: false | ||
}; | ||
} | ||
} | ||
isKnownAttribute(name) { | ||
if (typeof name === 'symbol') { | ||
return true; | ||
} | ||
return this.known.indexOf(name) !== -1; | ||
} | ||
buildKnownAttributes(model, prototype) { | ||
this.known = Array.from(new Set(model['getReservedProperties']().concat(Object.getOwnPropertyNames(model), EloquentProxy_1.GET_FORWARD_TO_DRIVER_FUNCTIONS, EloquentProxy_1.GET_QUERY_FUNCTIONS, Object.getOwnPropertyNames(Eloquent_1.Eloquent.prototype), Object.getOwnPropertyNames(prototype)))); | ||
} | ||
findGettersAndSetters(prototype) { | ||
const descriptors = Object.getOwnPropertyDescriptors(prototype); | ||
for (const property in descriptors) { | ||
const getter = lodash_1.isFunction(descriptors[property].get); | ||
const setter = lodash_1.isFunction(descriptors[property].set); | ||
if (!getter && !setter) { | ||
continue; | ||
} | ||
this.createDynamicAttributeIfNeeded(property); | ||
this.dynamic[property].getter = getter; | ||
this.dynamic[property].setter = setter; | ||
} | ||
} | ||
findAccessorsAndMutators(prototype) { | ||
const names = Object.getOwnPropertyNames(prototype); | ||
const regex = new RegExp('^(get|set)([a-zA-z0-9_\\-]{1,})Attribute$', 'g'); | ||
names.forEach(name => { | ||
let match; | ||
while ((match = regex.exec(name)) != undefined) { | ||
// javascript RegExp has a bug when the match has length 0 | ||
// if (match.index === regex.lastIndex) { | ||
// ++regex.lastIndex | ||
// } | ||
const property = lodash_1.snakeCase(match[2]); | ||
this.createDynamicAttributeIfNeeded(property); | ||
if (match[1] === 'get') { | ||
this.dynamic[property].accessor = match[0]; | ||
} | ||
else { | ||
this.dynamic[property].mutator = match[0]; | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
exports.EloquentAttribute = EloquentAttribute; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
import 'jest'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
require("jest"); | ||
const najs_binding_1 = require("najs-binding"); | ||
const Eloquent_1 = require("../../lib/model/Eloquent"); | ||
const EloquentAttribute_1 = require("../../lib/model/EloquentAttribute"); | ||
const EloquentDriverProvider_1 = require("../../lib/drivers/EloquentDriverProvider"); | ||
const DummyDriver_1 = require("../../lib/drivers/DummyDriver"); | ||
const EloquentProxy_1 = require("../../lib/model/EloquentProxy"); | ||
EloquentDriverProvider_1.EloquentDriverProvider.register(DummyDriver_1.DummyDriver, 'dummy'); | ||
class Model extends Eloquent_1.Eloquent { | ||
get accessor() { | ||
return ''; | ||
} | ||
set mutator(value) { } | ||
getClassName() { | ||
return 'Model'; | ||
} | ||
modelMethod() { } | ||
} | ||
najs_binding_1.register(Model); | ||
class ChildModel extends Model { | ||
get child_accessor() { | ||
return ''; | ||
} | ||
set child_mutator(value) { } | ||
getClassName() { | ||
return 'ChildModel'; | ||
} | ||
childModelMethod() { } | ||
} | ||
najs_binding_1.register(ChildModel); | ||
const fakeModel = { | ||
getReservedProperties() { | ||
return []; | ||
} | ||
}; | ||
describe('EloquentAttribute', function () { | ||
describe('.findGettersAndSetters()', function () { | ||
it('finds all defined getters and put to accessors with type = getter', function () { | ||
class ClassEmpty { | ||
} | ||
const attribute = new EloquentAttribute_1.EloquentAttribute(fakeModel, {}); | ||
attribute.findGettersAndSetters(Object.getPrototypeOf(new ClassEmpty())); | ||
expect(attribute['dynamic']).toEqual({}); | ||
class Class { | ||
get a() { | ||
return ''; | ||
} | ||
set a(value) { } | ||
get b() { | ||
return ''; | ||
} | ||
} | ||
attribute.findGettersAndSetters(Object.getPrototypeOf(new Class())); | ||
expect(attribute['dynamic']).toEqual({ | ||
a: { name: 'a', getter: true, setter: true }, | ||
b: { name: 'b', getter: true, setter: false } | ||
}); | ||
}); | ||
}); | ||
describe('.findAccessorsAndMutators()', function () { | ||
it('finds all defined getters and put to accessors with type = getter', function () { | ||
class ClassEmpty { | ||
} | ||
const attribute = new EloquentAttribute_1.EloquentAttribute(fakeModel, {}); | ||
attribute.findAccessorsAndMutators(Object.getPrototypeOf(new ClassEmpty())); | ||
expect(attribute['dynamic']).toEqual({}); | ||
class Class { | ||
get a() { | ||
return ''; | ||
} | ||
getAAttribute() { } | ||
getFirstNameAttribute() { } | ||
getWrongFormat() { } | ||
set b(value) { } | ||
setBAttribute() { } | ||
setWrongFormat() { } | ||
setDoublegetDoubleAttribute() { } | ||
get c() { | ||
return ''; | ||
} | ||
set c(value) { } | ||
getCAttribute() { } | ||
setCAttribute() { } | ||
} | ||
attribute.findGettersAndSetters(Object.getPrototypeOf(new Class())); | ||
attribute.findAccessorsAndMutators(Object.getPrototypeOf(new Class())); | ||
expect(attribute['dynamic']).toEqual({ | ||
a: { name: 'a', getter: true, setter: false, accessor: 'getAAttribute' }, | ||
b: { name: 'b', getter: false, setter: true, mutator: 'setBAttribute' }, | ||
c: { name: 'c', getter: true, setter: true, accessor: 'getCAttribute', mutator: 'setCAttribute' }, | ||
first_name: { name: 'first_name', getter: false, setter: false, accessor: 'getFirstNameAttribute' }, | ||
doubleget_double: { | ||
name: 'doubleget_double', | ||
getter: false, | ||
setter: false, | ||
mutator: 'setDoublegetDoubleAttribute' | ||
} | ||
}); | ||
}); | ||
}); | ||
describe('protected .buildKnownAttributes()', function () { | ||
const attribute = new EloquentAttribute_1.EloquentAttribute(fakeModel, {}); | ||
attribute.buildKnownAttributes(new Model(), Model.prototype); | ||
it('merges reserved properties defined in .getReservedProperties() of model and driver', function () { | ||
const props = new Model()['getReservedProperties'](); | ||
for (const name of props) { | ||
expect(attribute['known'].indexOf(name) !== -1).toBe(true); | ||
} | ||
}); | ||
it('merges properties defined Eloquent.prototype', function () { | ||
const props = Object.getOwnPropertyNames(Model.prototype); | ||
for (const name of props) { | ||
expect(attribute['known'].indexOf(name) !== -1).toBe(true); | ||
} | ||
}); | ||
it('merges properties defined in model', function () { | ||
const props = ['accessor', 'mutator', 'modelMethod']; | ||
for (const name of props) { | ||
expect(attribute['known'].indexOf(name) !== -1).toBe(true); | ||
} | ||
// warning: props defined in model is not included in list | ||
expect(attribute['known'].indexOf('props') === -1).toBe(true); | ||
}); | ||
it('merges properties defined in child model', function () { | ||
const childAttribute = new EloquentAttribute_1.EloquentAttribute(new ChildModel(), ChildModel.prototype); | ||
const props = ['child_accessor', 'child_mutator', 'childModelMethod']; | ||
for (const name of props) { | ||
expect(childAttribute['known'].indexOf(name) !== -1).toBe(true); | ||
} | ||
// warning: props defined in model is not included in list | ||
expect(childAttribute['known'].indexOf('child_props') === -1).toBe(true); | ||
}); | ||
it('merges properties defined GET_FORWARD_TO_DRIVER_FUNCTIONS', function () { | ||
const props = EloquentProxy_1.GET_FORWARD_TO_DRIVER_FUNCTIONS; | ||
for (const name of props) { | ||
expect(attribute['known'].indexOf(name) !== -1).toBe(true); | ||
} | ||
}); | ||
it('merges properties defined GET_QUERY_FUNCTIONS', function () { | ||
const props = EloquentProxy_1.GET_QUERY_FUNCTIONS; | ||
for (const name of props) { | ||
expect(attribute['known'].indexOf(name) !== -1).toBe(true); | ||
} | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.