Skip to content

Commit a58b63c

Browse files
authored
Implement clone method (#97)
* Add clone method * Update dist * Add docs about cloning * Move attribute setters/getters to the final so methods won't override them * Update dist * Bump minor
1 parent aac5225 commit a58b63c

File tree

9 files changed

+397
-44
lines changed

9 files changed

+397
-44
lines changed

.prettierignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
/dist
2+
/README.md
3+
/CHANGELOG.md

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 1.7.0 - 2019-09-14
2+
Enhancements:
3+
* Add method to clone structures
4+
15
## 1.6.0 - 2019-08-27
26
Enhancements:
37
* Allow custom error class to static mode

dist/structure.js

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ return /******/ (function(modules) { // webpackBootstrap
9797
attributeDescriptorFor = _require2.attributeDescriptorFor,
9898
attributesDescriptorFor = _require2.attributesDescriptorFor;
9999

100+
var Cloning = __webpack_require__(39);
101+
100102
var define = Object.defineProperty;
101103

102104
function attributesDecorator(schema) {
@@ -118,8 +120,6 @@ return /******/ (function(modules) { // webpackBootstrap
118120
}
119121
});
120122

121-
define(WrapperClass, 'buildStrict', StrictMode.buildStrictDescriptorFor(WrapperClass, schemaOptions));
122-
123123
if (WrapperClass[SCHEMA]) {
124124
schema = Object.assign({}, WrapperClass[SCHEMA], schema);
125125
}
@@ -138,14 +138,18 @@ return /******/ (function(modules) { // webpackBootstrap
138138

139139
define(WrapperClass.prototype, 'attributes', attributesDescriptorFor(schema));
140140

141-
Object.keys(schema).forEach(function (attr) {
142-
define(WrapperClass.prototype, attr, attributeDescriptorFor(attr, schema));
143-
});
144-
145141
define(WrapperClass.prototype, 'validate', Validation.descriptorFor(schema));
146142

147143
define(WrapperClass.prototype, 'toJSON', Serialization.descriptor);
148144

145+
define(WrapperClass, 'buildStrict', StrictMode.buildStrictDescriptorFor(WrapperClass, schemaOptions));
146+
147+
define(WrapperClass.prototype, 'clone', Cloning.buildCloneDescriptorFor(WrapperClass));
148+
149+
Object.keys(schema).forEach(function (attr) {
150+
define(WrapperClass.prototype, attr, attributeDescriptorFor(attr, schema));
151+
});
152+
149153
return WrapperClass;
150154
};
151155
}
@@ -1329,6 +1333,36 @@ return /******/ (function(modules) { // webpackBootstrap
13291333
return attributes;
13301334
}
13311335

1336+
/***/ },
1337+
/* 39 */
1338+
/***/ function(module, exports) {
1339+
1340+
"use strict";
1341+
1342+
exports.buildCloneDescriptorFor = function buildCloneDescriptorFor(StructureClass) {
1343+
return {
1344+
configurable: true,
1345+
value: function clone() {
1346+
var overwrites = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1347+
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1348+
var strict = options.strict;
1349+
1350+
1351+
var newAttributes = Object.assign({}, this.attributes, overwrites);
1352+
1353+
var cloneInstance = void 0;
1354+
1355+
if (strict) {
1356+
cloneInstance = StructureClass.buildStrict(newAttributes);
1357+
} else {
1358+
cloneInstance = new StructureClass(newAttributes);
1359+
}
1360+
1361+
return cloneInstance;
1362+
}
1363+
};
1364+
};
1365+
13321366
/***/ }
13331367
/******/ ])
13341368
});

docs/SUMMARY.md

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,28 @@
11
# Table of contents
22

3-
* [Introduction](../README.md)
4-
* [Schema concept](schema-concept/README.md)
5-
* [Shorthand and complete type descriptor](schema-concept/shorthand-and-complete-type-descriptor.md)
6-
* [Circular reference](schema-concept/circular-references-and-dynamic-types.md)
7-
* [Nullable attributes](schema-concept/nullable-attributes.md)
8-
* [Coercion](coercion/README.md)
9-
* [Primitive type coercion](coercion/primitive-type-coercion.md)
10-
* [Arrays coercion](coercion/arrays-and-array-subclasses.md)
11-
* [Generic coercion](coercion/generic-coercion.md)
12-
* [Recursive coercion](coercion/recursive-coercion.md)
13-
* [Observations](coercion/observations.md)
14-
* [Validation](validation/README.md)
15-
* [String validations](validation/string-validations.md)
16-
* [Number validations](validation/number-validations.md)
17-
* [Boolean validations](validation/boolean-validations.md)
18-
* [Date validations](validation/date-validations.md)
19-
* [Array validations](validation/array-validations.md)
20-
* [Attribute reference](validation/attribute-reference.md)
21-
* [Nested validations](validation/nested-validations.md)
22-
* [Validate raw data](validation/validate-raw-data.md)
23-
* [Strict mode](strict-mode.md)
24-
* [Serialization](serialization.md)
25-
* [Contributing](../contributing.md)
26-
* [License](../license.md)
27-
* [GitHub](https://github.com/talyssonoc/structure)
3+
- [Introduction](../README.md)
4+
- [Schema concept](schema-concept/README.md)
5+
- [Shorthand and complete type descriptor](schema-concept/shorthand-and-complete-type-descriptor.md)
6+
- [Circular reference](schema-concept/circular-references-and-dynamic-types.md)
7+
- [Nullable attributes](schema-concept/nullable-attributes.md)
8+
- [Coercion](coercion/README.md)
9+
- [Primitive type coercion](coercion/primitive-type-coercion.md)
10+
- [Arrays coercion](coercion/arrays-and-array-subclasses.md)
11+
- [Generic coercion](coercion/generic-coercion.md)
12+
- [Recursive coercion](coercion/recursive-coercion.md)
13+
- [Observations](coercion/observations.md)
14+
- [Validation](validation/README.md)
15+
- [String validations](validation/string-validations.md)
16+
- [Number validations](validation/number-validations.md)
17+
- [Boolean validations](validation/boolean-validations.md)
18+
- [Date validations](validation/date-validations.md)
19+
- [Array validations](validation/array-validations.md)
20+
- [Attribute reference](validation/attribute-reference.md)
21+
- [Nested validations](validation/nested-validations.md)
22+
- [Validate raw data](validation/validate-raw-data.md)
23+
- [Strict mode](strict-mode.md)
24+
- [Cloning an instance](cloning.md)
25+
- [Serialization](serialization.md)
26+
- [Contributing](../contributing.md)
27+
- [License](../license.md)
28+
- [GitHub](https://github.com/talyssonoc/structure)

docs/cloning.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Cloning an instance
2+
3+
Structure adds a method `#clone` in order to be able to create a **shallow** copy of an instance. This methods accepts an optional overwrite object that permits you to overwrite some attributes of the copy.
4+
5+
```js
6+
const { attributes } = require('structure');
7+
8+
const User = attributes({
9+
name: String,
10+
})(class User {});
11+
12+
const user = new User({
13+
name: 'Me',
14+
});
15+
16+
const cloneUserWithNoOverwrite = user.clone(); // User { name: 'Me }
17+
18+
const cloneWithOverwrite = user.clone({ name: 'Myself' }); // User { name: 'Myself' }
19+
```
20+
21+
If the structure has a nested structure inside of it, the `#clone` method **will not** clone it but just point the new instance to the old value of the nested attribute.
22+
23+
```js
24+
const { attributes } = require('structure');
25+
26+
const Book = attributes({
27+
name: String,
28+
})(class Book {});
29+
30+
const User = attributes({
31+
name: String,
32+
favoriteBook: Book,
33+
})(class User {});
34+
35+
const user = new User({
36+
name: 'Me',
37+
favoriteBook: new Book({ name: 'The Silmarillion' }),
38+
});
39+
40+
const cloneUserWithNoOverwrite = user.clone();
41+
cloneUserWithNoOverwrite.favoriteBook === user.favoriteBook; // true, it was not cloned
42+
43+
const cloneWithOverwrite = user.clone({
44+
favoriteBook: { name: 'The Lord of the Rings' },
45+
});
46+
cloneWithOverwrite.favoriteBook === user.favoriteBook; // false, it was **replaced** with the new value
47+
cloneWithOverwrite.favoriteBook; // Book { name: 'The Lord of the Rings' }
48+
```
49+
50+
## Strict mode
51+
52+
When cloning an instance, you can clone it in [strict mode](strict-mode.md) as well, so if the resulting clone is invalid it throws an error. To do that, pass a second argument to the `#clone` method with the option `strict` as `true`.
53+
54+
```js
55+
const { attributes } = require('structure');
56+
57+
const User = attributes({
58+
name: {
59+
type: String,
60+
required: true,
61+
},
62+
age: Number,
63+
})(class User {});
64+
65+
const user = new User({
66+
name: 'Me',
67+
});
68+
69+
const clonedUser = user.clone(
70+
{ name: null },
71+
{ strict: true } // strict mode option
72+
);
73+
74+
// Error: Invalid Attributes
75+
// details: [
76+
// { message: '"name" is required', path: 'name' }
77+
// ]
78+
```

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "structure",
3-
"version": "1.6.0",
3+
"version": "1.7.0",
44
"description": "A simple schema/attributes library built on top of modern JavaScript",
55
"main": "src/index.js",
66
"browser": "dist/structure.js",

src/attributes/decorator.js

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const {
99
attributeDescriptorFor,
1010
attributesDescriptorFor,
1111
} = require('./descriptors');
12+
const Cloning = require('../cloning');
1213

1314
const define = Object.defineProperty;
1415

@@ -29,11 +30,6 @@ function attributesDecorator(schema, schemaOptions = {}) {
2930
},
3031
});
3132

32-
define(WrapperClass, 'buildStrict', StrictMode.buildStrictDescriptorFor(
33-
WrapperClass,
34-
schemaOptions
35-
));
36-
3733
if (WrapperClass[SCHEMA]) {
3834
schema = Object.assign({}, WrapperClass[SCHEMA], schema);
3935
}
@@ -54,19 +50,28 @@ function attributesDecorator(schema, schemaOptions = {}) {
5450
schema
5551
));
5652

53+
define(WrapperClass.prototype, 'validate', Validation.descriptorFor(
54+
schema
55+
));
56+
57+
define(WrapperClass.prototype, 'toJSON', Serialization.descriptor);
58+
59+
define(WrapperClass, 'buildStrict', StrictMode.buildStrictDescriptorFor(
60+
WrapperClass,
61+
schemaOptions
62+
));
63+
64+
define(WrapperClass.prototype, 'clone', Cloning.buildCloneDescriptorFor(
65+
WrapperClass
66+
));
67+
5768
Object.keys(schema).forEach((attr) => {
5869
define(WrapperClass.prototype, attr, attributeDescriptorFor(
5970
attr,
6071
schema
6172
));
6273
});
6374

64-
define(WrapperClass.prototype, 'validate', Validation.descriptorFor(
65-
schema
66-
));
67-
68-
define(WrapperClass.prototype, 'toJSON', Serialization.descriptor);
69-
7075
return WrapperClass;
7176
};
7277
}

src/cloning/index.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
exports.buildCloneDescriptorFor = function buildCloneDescriptorFor(
2+
StructureClass
3+
) {
4+
return {
5+
configurable: true,
6+
value: function clone(overwrites = {}, options = {}) {
7+
const { strict } = options;
8+
9+
const newAttributes = Object.assign({}, this.attributes, overwrites);
10+
11+
let cloneInstance;
12+
13+
if (strict) {
14+
cloneInstance = StructureClass.buildStrict(newAttributes);
15+
} else {
16+
cloneInstance = new StructureClass(newAttributes);
17+
}
18+
19+
return cloneInstance;
20+
},
21+
};
22+
};

0 commit comments

Comments
 (0)