1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | 'use strict';
|
8 | const should = require('./init.js');
|
9 |
|
10 | const jdb = require('../');
|
11 | const ModelBuilder = jdb.ModelBuilder;
|
12 | const DataSource = jdb.DataSource;
|
13 | const Memory = require('../lib/connectors/memory');
|
14 |
|
15 | const modelBuilder = new ModelBuilder();
|
16 | const mixins = modelBuilder.mixins;
|
17 |
|
18 | function timestamps(Model, options) {
|
19 | Model.defineProperty('createdAt', {type: Date});
|
20 | Model.defineProperty('updatedAt', {type: Date});
|
21 |
|
22 | const originalBeforeSave = Model.beforeSave;
|
23 | Model.beforeSave = function(next, data) {
|
24 | Model.applyTimestamps(data, this.isNewRecord());
|
25 | if (data.createdAt) {
|
26 | this.createdAt = data.createdAt;
|
27 | }
|
28 | if (data.updatedAt) {
|
29 | this.updatedAt = data.updatedAt;
|
30 | }
|
31 | if (originalBeforeSave) {
|
32 | originalBeforeSave.apply(this, arguments);
|
33 | } else {
|
34 | next();
|
35 | }
|
36 | };
|
37 |
|
38 | Model.applyTimestamps = function(data, creation) {
|
39 | data.updatedAt = new Date();
|
40 | if (creation) {
|
41 | data.createdAt = data.updatedAt;
|
42 | }
|
43 | };
|
44 | }
|
45 |
|
46 | mixins.define('TimeStamp', timestamps);
|
47 |
|
48 | describe('Model class', function() {
|
49 | it('should define mixins', function() {
|
50 | mixins.define('Example', function(Model, options) {
|
51 | Model.prototype.example = function() {
|
52 | return options;
|
53 | };
|
54 | });
|
55 | mixins.define('Demo', function(Model, options) {
|
56 | Model.demoMixin = options.value;
|
57 | });
|
58 | mixins.define('Multi', function(Model, options) {
|
59 | Model.multiMixin = Model.multiMixin || {};
|
60 | Model.multiMixin[options.key] = options.value;
|
61 | });
|
62 | });
|
63 |
|
64 | it('should apply a mixin class', function() {
|
65 | const Address = modelBuilder.define('Address', {
|
66 | street: {type: 'string', required: true},
|
67 | city: {type: 'string', required: true},
|
68 | });
|
69 |
|
70 | const memory = new DataSource('mem', {connector: Memory}, modelBuilder);
|
71 | const Item = memory.createModel('Item', {name: 'string'}, {
|
72 | mixins: {Address: true},
|
73 | });
|
74 |
|
75 | const properties = Item.definition.properties;
|
76 |
|
77 | properties.street.should.eql({type: String, required: true});
|
78 | properties.city.should.eql({type: String, required: true});
|
79 | });
|
80 |
|
81 | it('should fail to apply an undefined mixin class', function() {
|
82 | const memory = new DataSource('mem', {connector: Memory}, modelBuilder);
|
83 | function applyMixin() {
|
84 | memory.createModel('Item', {name: 'string'}, {
|
85 | mixins: {UndefinedMixin: true},
|
86 | });
|
87 | }
|
88 | should.throws(applyMixin, 'failed to apply undefined mixin class');
|
89 | });
|
90 |
|
91 | it('should apply mixins', function(done) {
|
92 | const memory = new DataSource('mem', {connector: Memory}, modelBuilder);
|
93 | const Item = memory.createModel('Item', {name: 'string'}, {
|
94 | mixins: {
|
95 | TimeStamp: true,
|
96 | Demo: {value: true},
|
97 | Multi: [
|
98 | {key: 'foo', value: 'bar'},
|
99 | {key: 'fox', value: 'baz'},
|
100 | ],
|
101 | },
|
102 | });
|
103 |
|
104 | Item.mixin('Example', {foo: 'bar'});
|
105 |
|
106 | Item.demoMixin.should.be.true;
|
107 |
|
108 | Item.multiMixin.foo.should.equal('bar');
|
109 | Item.multiMixin.fox.should.equal('baz');
|
110 |
|
111 | const properties = Item.definition.properties;
|
112 | properties.createdAt.should.eql({type: Date});
|
113 | properties.updatedAt.should.eql({type: Date});
|
114 |
|
115 | Item.create({name: 'Item 1'}, function(err, inst) {
|
116 | inst.createdAt.should.be.a.date;
|
117 | inst.updatedAt.should.be.a.date;
|
118 | inst.example().should.eql({foo: 'bar'});
|
119 | done();
|
120 | });
|
121 | });
|
122 |
|
123 | it('should fail to apply undefined mixin', function() {
|
124 | const memory = new DataSource('mem', {connector: Memory}, modelBuilder);
|
125 | const Item = memory.createModel('Item', {name: 'string'});
|
126 |
|
127 | function applyMixin() {
|
128 | Item.mixin('UndefinedMixin', {foo: 'bar'});
|
129 | }
|
130 | should.throws(applyMixin, 'failed to apply undefined mixin');
|
131 | });
|
132 |
|
133 | describe('#mixin()', function() {
|
134 | let Person, Author, Address;
|
135 |
|
136 | beforeEach(function() {
|
137 | Address = modelBuilder.define('Address', {
|
138 | street: {type: 'string', required: true},
|
139 | city: {type: 'string', required: true},
|
140 | });
|
141 | const memory = new DataSource('mem', {connector: Memory}, modelBuilder);
|
142 | Person = memory.createModel('Person', {name: 'string'});
|
143 | Author = memory.createModel('Author', {name: 'string'});
|
144 | });
|
145 |
|
146 | it('should register mixin class into _mixins', function() {
|
147 | Person.mixin(Address);
|
148 | Person._mixins.should.containEql(Address);
|
149 | });
|
150 |
|
151 | it('should NOT share mixins registry', function() {
|
152 | Person.mixin(Address);
|
153 | Author._mixins.should.not.containEql(Address);
|
154 | });
|
155 |
|
156 | it('should able to mixin same class', function() {
|
157 | Person.mixin(Address);
|
158 | Author.mixin(Address);
|
159 | Author._mixins.should.containEql(Address);
|
160 | });
|
161 | });
|
162 | });
|