Skip to content

Commit

Permalink
Other: Moved custom wrappers to its own module instead, also makes th…
Browse files Browse the repository at this point in the history
…e API easier to use manually, see #677
  • Loading branch information
dcodeIO committed Apr 11, 2017
1 parent ed34b09 commit 48e66d9
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 19 deletions.
12 changes: 3 additions & 9 deletions src/common.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"use strict";
module.exports = common;

var Type = require("./type");

/**
* Provides common type definitions.
* Can also be used to provide additional google types or your own custom types.
Expand Down Expand Up @@ -51,15 +53,7 @@ common("any", {
type: "bytes",
id: 2
}
}/*,
options: Object.create({
__fromObject: function(object) {
return this.fromObject(object);
},
__toObject: function(options) {
return this.toObject(options);
}
})*/
}
}
});

Expand Down
1 change: 1 addition & 0 deletions src/index-minimal.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ protobuf.BufferReader = require("./reader_buffer");
protobuf.util = require("./util/minimal");
protobuf.rpc = require("./rpc");
protobuf.roots = require("./roots");
protobuf.wrappers = require("./wrappers");
protobuf.configure = configure;

/* istanbul ignore next */
Expand Down
25 changes: 20 additions & 5 deletions src/type.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ var Enum = require("./enum"),
encoder = require("./encoder"),
decoder = require("./decoder"),
verifier = require("./verifier"),
converter = require("./converter");
converter = require("./converter"),
wrappers = require("./wrappers");

/**
* Constructs a new reflected message type instance.
Expand Down Expand Up @@ -428,10 +429,13 @@ Type.prototype.create = function create(properties) {
Type.prototype.setup = function setup() {
// Sets up everything at once so that the prototype chain does not have to be re-evaluated
// multiple times (V8, soft-deopt prototype-check).

var fullName = this.fullName,
types = [];
for (var i = 0; i < /* initializes */ this.fieldsArray.length; ++i)
types.push(this._fieldsArray[i].resolve().resolvedType);

// Replace setup methods with type-specific generated functions
this.encode = encoder(this).eof(fullName + "$encode", {
Writer : Writer,
types : types,
Expand All @@ -450,14 +454,25 @@ Type.prototype.setup = function setup() {
types : types,
util : util
});
if (this.options && this.options.__formObject)
this.fromObject = this.options.__formObject.bind({ fromObject: this.fromObject });
this.toObject = converter.toObject(this).eof(fullName + "$toObject", {
types : types,
util : util
});
if (this.options && this.options.__toObject)
this.toObject = this.options.__toObject.bind({ toObject: this.toObject });

// Inject custom wrappers for common types
var wrapper = wrappers[fullName];
if (wrapper) {
var originalThis = Object.create(this);
// if (wrapper.fromObject) {
originalThis.fromObject = this.fromObject;
this.fromObject = wrapper.fromObject.bind(originalThis);
// }
// if (wrapper.toObject) {
originalThis.toObject = this.toObject;
this.toObject = wrapper.toObject.bind(originalThis);
// }
}

return this;
};

Expand Down
4 changes: 2 additions & 2 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ util.decorateType = function decorateType(ctor, typeName) {
return ctor.$type;
}

/* istanbul ignore if */
/* istanbul ignore next */
if (!Type)
Type = require("./type");

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

/* istanbul ignore if */
/* istanbul ignore next */
if (!Enum)
Enum = require("./enum");

Expand Down
6 changes: 3 additions & 3 deletions src/util/minimal.js
Original file line number Diff line number Diff line change
Expand Up @@ -376,17 +376,17 @@ util.oneOfSetter = function setOneOf(fieldNames) {

/**
* Default conversion options used for {@link Message#toJSON} implementations.
*
*
* These options are close to proto3's JSON mapping with the exception that internal types like Any are handled just like messages. More precisely:
*
*
* - Longs become strings
* - Enums become string keys
* - Bytes become base64 encoded strings
* - (Sub-)Messages become plain objects
* - Maps become plain objects with all string keys
* - Repeated fields become arrays
* - NaN and Infinity for float and double fields become strings
*
*
* @type {ConversionOptions}
* @see https://developers.google.com/protocol-buffers/docs/proto3?hl=en#json
*/
Expand Down
76 changes: 76 additions & 0 deletions src/wrappers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"use strict";

/**
* Wrappers for common types.
* @namespace
*/
var wrappers = exports;

var util = require("./util/minimal");

/**
* From object converter part of a {@link Wrapper}.
* @typedef WrapperFromObjectConverter
* @type {function}
* @param {Object.<string,*>} object Plain object
* @returns {Message<{}>}
* @this Type
*/

/**
* To object converter part of a {@link Wrapper}.
* @typedef WrapperToObjectConverter
* @type {function}
* @param {Message<{}>} message Message instance
* @param {ConversionOptions=} options Conversion options
* @returns {Object.<string,*>}
* @this Type
*/

/**
* Common type wrapper part of {@link wrappers}.
* @typedef Wrapper
* @type {Object}
* @property {WrapperFromObjectConverter} [fromObject] From object converter
* @property {WrapperToObjectConverter} [toObject] To object converter
*/

/**
* Custom wrapper for Any.
* @type {Wrapper}
*/
wrappers[".google.protobuf.Any"] = {

fromObject: function(object) {

// unwrap value type if mapped
if (object && object["@type"]) {
var type = this.lookup(object["@type"]);
/* istanbul ignore else */
if (type)
return type.fromObject(object);
}

return this.fromObject(object);
},

toObject: function(message, options) {

// decode value if requested and unmapped
if (options && options.json && message.type_url && message.value) {
var type = this.lookup(message.type_url);
/* istanbul ignore else */
if (type)
message = type.decode(message.value);
}

// wrap value if unmapped
if (!(message instanceof this.ctor)) {
var object = message.toObject(options);
object["@type"] = message.$type.fullName;
return object;
}

return this.toObject(message, options);
}
};
60 changes: 60 additions & 0 deletions tests/comp_google_protobuf_any.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
var tape = require("tape");

var protobuf = require("..");

var root = protobuf.Root.fromJSON({
nested: {
Foo: {
fields: {
foo: {
id: 1,
type: "google.protobuf.Any"
}
}
},
Bar: {
fields: {
bar: {
id: 1,
type: "string"
}
}
}
}
}).addJSON(protobuf.common["google/protobuf/any.proto"].nested).resolveAll();

var Any = root.lookupType(".google.protobuf.Any"),
Foo = root.lookupType(".Foo"),
Bar = root.lookupType(".Bar");

tape.test("google.protobuf.Any", function(test) {

var foo = Foo.fromObject({
foo: {
type_url: ".Bar",
value: [1 << 3 | 2, 1, 97] // value = "a"
}
});
test.ok(foo.foo instanceof Any.ctor, "should keep explicit Any in fromObject");
test.same(foo.foo, { type_url: ".Bar", value: [10, 1, 97] }, "should keep explicit Any in fromObject properly");

var obj = Foo.toObject(foo);
test.same(obj.foo, { type_url: ".Bar", value: [10, 1, 97] }, "should keep explicit Any in toObject properly");

obj = Foo.toObject(foo, { json: true });
test.same(obj.foo, { "@type": ".Bar", bar: "a" }, "should decode explicitly Any in toObject if requested");

foo = Foo.fromObject({
foo: {
"@type": ".Bar",
bar: "a"
}
});
test.ok(foo.foo instanceof Bar.ctor, "should unwrap wrapped Bar in fromObject");
test.same(foo.foo, { bar: "a" }, "should unwrap wrapper Bar in fromObject properly");

obj = Foo.toObject(foo);
test.same(obj.foo, { "@type": ".Bar", bar: "a" }, "should wrap Bar in toObject properly");

test.end();
});

0 comments on commit 48e66d9

Please sign in to comment.