Skip to content

Commit 48e66d9

Browse files
committed
Other: Moved custom wrappers to its own module instead, also makes the API easier to use manually, see #677
1 parent ed34b09 commit 48e66d9

File tree

7 files changed

+165
-19
lines changed

7 files changed

+165
-19
lines changed

src/common.js

+3-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"use strict";
22
module.exports = common;
33

4+
var Type = require("./type");
5+
46
/**
57
* Provides common type definitions.
68
* Can also be used to provide additional google types or your own custom types.
@@ -51,15 +53,7 @@ common("any", {
5153
type: "bytes",
5254
id: 2
5355
}
54-
}/*,
55-
options: Object.create({
56-
__fromObject: function(object) {
57-
return this.fromObject(object);
58-
},
59-
__toObject: function(options) {
60-
return this.toObject(options);
61-
}
62-
})*/
56+
}
6357
}
6458
});
6559

src/index-minimal.js

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ protobuf.BufferReader = require("./reader_buffer");
1919
protobuf.util = require("./util/minimal");
2020
protobuf.rpc = require("./rpc");
2121
protobuf.roots = require("./roots");
22+
protobuf.wrappers = require("./wrappers");
2223
protobuf.configure = configure;
2324

2425
/* istanbul ignore next */

src/type.js

+20-5
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ var Enum = require("./enum"),
1717
encoder = require("./encoder"),
1818
decoder = require("./decoder"),
1919
verifier = require("./verifier"),
20-
converter = require("./converter");
20+
converter = require("./converter"),
21+
wrappers = require("./wrappers");
2122

2223
/**
2324
* Constructs a new reflected message type instance.
@@ -428,10 +429,13 @@ Type.prototype.create = function create(properties) {
428429
Type.prototype.setup = function setup() {
429430
// Sets up everything at once so that the prototype chain does not have to be re-evaluated
430431
// multiple times (V8, soft-deopt prototype-check).
432+
431433
var fullName = this.fullName,
432434
types = [];
433435
for (var i = 0; i < /* initializes */ this.fieldsArray.length; ++i)
434436
types.push(this._fieldsArray[i].resolve().resolvedType);
437+
438+
// Replace setup methods with type-specific generated functions
435439
this.encode = encoder(this).eof(fullName + "$encode", {
436440
Writer : Writer,
437441
types : types,
@@ -450,14 +454,25 @@ Type.prototype.setup = function setup() {
450454
types : types,
451455
util : util
452456
});
453-
if (this.options && this.options.__formObject)
454-
this.fromObject = this.options.__formObject.bind({ fromObject: this.fromObject });
455457
this.toObject = converter.toObject(this).eof(fullName + "$toObject", {
456458
types : types,
457459
util : util
458460
});
459-
if (this.options && this.options.__toObject)
460-
this.toObject = this.options.__toObject.bind({ toObject: this.toObject });
461+
462+
// Inject custom wrappers for common types
463+
var wrapper = wrappers[fullName];
464+
if (wrapper) {
465+
var originalThis = Object.create(this);
466+
// if (wrapper.fromObject) {
467+
originalThis.fromObject = this.fromObject;
468+
this.fromObject = wrapper.fromObject.bind(originalThis);
469+
// }
470+
// if (wrapper.toObject) {
471+
originalThis.toObject = this.toObject;
472+
this.toObject = wrapper.toObject.bind(originalThis);
473+
// }
474+
}
475+
461476
return this;
462477
};
463478

src/util.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ util.decorateType = function decorateType(ctor, typeName) {
8585
return ctor.$type;
8686
}
8787

88-
/* istanbul ignore if */
88+
/* istanbul ignore next */
8989
if (!Type)
9090
Type = require("./type");
9191

@@ -109,7 +109,7 @@ util.decorateEnum = function decorateEnum(object) {
109109
if (object.$type)
110110
return object.$type;
111111

112-
/* istanbul ignore if */
112+
/* istanbul ignore next */
113113
if (!Enum)
114114
Enum = require("./enum");
115115

src/util/minimal.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -376,17 +376,17 @@ util.oneOfSetter = function setOneOf(fieldNames) {
376376

377377
/**
378378
* Default conversion options used for {@link Message#toJSON} implementations.
379-
*
379+
*
380380
* These options are close to proto3's JSON mapping with the exception that internal types like Any are handled just like messages. More precisely:
381-
*
381+
*
382382
* - Longs become strings
383383
* - Enums become string keys
384384
* - Bytes become base64 encoded strings
385385
* - (Sub-)Messages become plain objects
386386
* - Maps become plain objects with all string keys
387387
* - Repeated fields become arrays
388388
* - NaN and Infinity for float and double fields become strings
389-
*
389+
*
390390
* @type {ConversionOptions}
391391
* @see https://developers.google.com/protocol-buffers/docs/proto3?hl=en#json
392392
*/

src/wrappers.js

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"use strict";
2+
3+
/**
4+
* Wrappers for common types.
5+
* @namespace
6+
*/
7+
var wrappers = exports;
8+
9+
var util = require("./util/minimal");
10+
11+
/**
12+
* From object converter part of a {@link Wrapper}.
13+
* @typedef WrapperFromObjectConverter
14+
* @type {function}
15+
* @param {Object.<string,*>} object Plain object
16+
* @returns {Message<{}>}
17+
* @this Type
18+
*/
19+
20+
/**
21+
* To object converter part of a {@link Wrapper}.
22+
* @typedef WrapperToObjectConverter
23+
* @type {function}
24+
* @param {Message<{}>} message Message instance
25+
* @param {ConversionOptions=} options Conversion options
26+
* @returns {Object.<string,*>}
27+
* @this Type
28+
*/
29+
30+
/**
31+
* Common type wrapper part of {@link wrappers}.
32+
* @typedef Wrapper
33+
* @type {Object}
34+
* @property {WrapperFromObjectConverter} [fromObject] From object converter
35+
* @property {WrapperToObjectConverter} [toObject] To object converter
36+
*/
37+
38+
/**
39+
* Custom wrapper for Any.
40+
* @type {Wrapper}
41+
*/
42+
wrappers[".google.protobuf.Any"] = {
43+
44+
fromObject: function(object) {
45+
46+
// unwrap value type if mapped
47+
if (object && object["@type"]) {
48+
var type = this.lookup(object["@type"]);
49+
/* istanbul ignore else */
50+
if (type)
51+
return type.fromObject(object);
52+
}
53+
54+
return this.fromObject(object);
55+
},
56+
57+
toObject: function(message, options) {
58+
59+
// decode value if requested and unmapped
60+
if (options && options.json && message.type_url && message.value) {
61+
var type = this.lookup(message.type_url);
62+
/* istanbul ignore else */
63+
if (type)
64+
message = type.decode(message.value);
65+
}
66+
67+
// wrap value if unmapped
68+
if (!(message instanceof this.ctor)) {
69+
var object = message.toObject(options);
70+
object["@type"] = message.$type.fullName;
71+
return object;
72+
}
73+
74+
return this.toObject(message, options);
75+
}
76+
};

tests/comp_google_protobuf_any.js

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
var tape = require("tape");
2+
3+
var protobuf = require("..");
4+
5+
var root = protobuf.Root.fromJSON({
6+
nested: {
7+
Foo: {
8+
fields: {
9+
foo: {
10+
id: 1,
11+
type: "google.protobuf.Any"
12+
}
13+
}
14+
},
15+
Bar: {
16+
fields: {
17+
bar: {
18+
id: 1,
19+
type: "string"
20+
}
21+
}
22+
}
23+
}
24+
}).addJSON(protobuf.common["google/protobuf/any.proto"].nested).resolveAll();
25+
26+
var Any = root.lookupType(".google.protobuf.Any"),
27+
Foo = root.lookupType(".Foo"),
28+
Bar = root.lookupType(".Bar");
29+
30+
tape.test("google.protobuf.Any", function(test) {
31+
32+
var foo = Foo.fromObject({
33+
foo: {
34+
type_url: ".Bar",
35+
value: [1 << 3 | 2, 1, 97] // value = "a"
36+
}
37+
});
38+
test.ok(foo.foo instanceof Any.ctor, "should keep explicit Any in fromObject");
39+
test.same(foo.foo, { type_url: ".Bar", value: [10, 1, 97] }, "should keep explicit Any in fromObject properly");
40+
41+
var obj = Foo.toObject(foo);
42+
test.same(obj.foo, { type_url: ".Bar", value: [10, 1, 97] }, "should keep explicit Any in toObject properly");
43+
44+
obj = Foo.toObject(foo, { json: true });
45+
test.same(obj.foo, { "@type": ".Bar", bar: "a" }, "should decode explicitly Any in toObject if requested");
46+
47+
foo = Foo.fromObject({
48+
foo: {
49+
"@type": ".Bar",
50+
bar: "a"
51+
}
52+
});
53+
test.ok(foo.foo instanceof Bar.ctor, "should unwrap wrapped Bar in fromObject");
54+
test.same(foo.foo, { bar: "a" }, "should unwrap wrapper Bar in fromObject properly");
55+
56+
obj = Foo.toObject(foo);
57+
test.same(obj.foo, { "@type": ".Bar", bar: "a" }, "should wrap Bar in toObject properly");
58+
59+
test.end();
60+
});

0 commit comments

Comments
 (0)