##// END OF EJS Templates
Change custom serialization to use custom models, rather than transmitting the serializer name across the wire...
Jason Grout -
Show More
@@ -14,18 +14,33 b' define(['
14 "widgets/js/widget_selection",
14 "widgets/js/widget_selection",
15 "widgets/js/widget_selectioncontainer",
15 "widgets/js/widget_selectioncontainer",
16 "widgets/js/widget_string",
16 "widgets/js/widget_string",
17 ], function(widgetmanager, linkModels) {
17 ], function(widgetmanager) {
18 for (var target_name in linkModels) {
18
19 if (linkModels.hasOwnProperty(target_name)) {
19
20 widgetmanager.WidgetManager.register_widget_model(target_name, linkModels[target_name]);
20 /**
21 * From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith
22 * Can be removed with the string endsWith function is implemented in major browsers
23 */
24 var endsWith = function(target, searchString, position) {
25 var subjectString = target.toString();
26 if (position === undefined || position > subjectString.length) {
27 position = subjectString.length;
21 }
28 }
22 }
29 position -= searchString.length;
30 var lastIndex = subjectString.indexOf(searchString, position);
31 return lastIndex !== -1 && lastIndex === position;
32 };
23
33
24 // Register all of the loaded views with the widget manager.
34 // Register all of the loaded models and views with the widget manager.
25 for (var i = 2; i < arguments.length; i++) {
35 for (var i = 1; i < arguments.length; i++) {
26 for (var target_name in arguments[i]) {
36 var module = arguments[i];
27 if (arguments[i].hasOwnProperty(target_name)) {
37 for (var target_name in module) {
28 widgetmanager.WidgetManager.register_widget_view(target_name, arguments[i][target_name]);
38 if (module.hasOwnProperty(target_name)) {
39 if (endsWith(target_name, "View")) {
40 widgetmanager.WidgetManager.register_widget_view(target_name, module[target_name]);
41 } else if (endsWith(target_name, "Model")) {
42 widgetmanager.WidgetManager.register_widget_model(target_name, module[target_name]);
43 }
29 }
44 }
30 }
45 }
31 }
46 }
@@ -32,7 +32,6 b' define(["widgets/js/manager",'
32 this.state_lock = null;
32 this.state_lock = null;
33 this.id = model_id;
33 this.id = model_id;
34 this.views = {};
34 this.views = {};
35 this.serializers = {};
36 this._resolve_received_state = {};
35 this._resolve_received_state = {};
37
36
38 if (comm !== undefined) {
37 if (comm !== undefined) {
@@ -146,23 +145,17 b' define(["widgets/js/manager",'
146 var state = msg.content.data.state || {};
145 var state = msg.content.data.state || {};
147 var buffer_keys = msg.content.data.buffers || [];
146 var buffer_keys = msg.content.data.buffers || [];
148 var buffers = msg.buffers || [];
147 var buffers = msg.buffers || [];
149 var metadata = msg.content.data.metadata || {};
150 var i,k;
151 for (var i=0; i<buffer_keys.length; i++) {
148 for (var i=0; i<buffer_keys.length; i++) {
152 k = buffer_keys[i];
149 state[buffer_keys[i]] = buffers[i];
153 state[k] = buffers[i];
154 }
150 }
155
151
156 // for any metadata specifying a deserializer, set the
152 // deserialize fields that have custom deserializers
157 // state to a promise that resolves to the deserialized version
153 var serializers = that.constructor.serializers;
158 // also, store the serialization function for the attribute
154 if (serializers) {
159 var keys = Object.keys(metadata);
155 for (var k in state) {
160 for (var i=0; i<keys.length; i++) {
156 if (serializers[k] && serializers[k].deserialize) {
161 k = keys[i];
157 state[k] = (serializers[k].deserialize)(state[k], that);
162 if (metadata[k] && metadata[k].serialization) {
158 }
163 that.serializers[k] = utils.load_class.apply(that,
164 metadata[k].serialization);
165 state[k] = that.deserialize(that.serializers[k], state[k]);
166 }
159 }
167 }
160 }
168 return utils.resolve_promises_dict(state);
161 return utils.resolve_promises_dict(state);
@@ -188,19 +181,6 b' define(["widgets/js/manager",'
188 }
181 }
189 },
182 },
190
183
191 deserialize: function(serializer, value) {
192 // given a serializer dict and a value,
193 // return a promise for the deserialized value
194 var that = this;
195 return serializer.then(function(s) {
196 if (s.deserialize) {
197 return s.deserialize(value, that);
198 } else {
199 return value;
200 }
201 });
202 },
203
204 set_state: function (state) {
184 set_state: function (state) {
205 var that = this;
185 var that = this;
206 // Handle when a widget is updated via the python side.
186 // Handle when a widget is updated via the python side.
@@ -362,25 +342,15 b' define(["widgets/js/manager",'
362 var that = this;
342 var that = this;
363 // first, build a state dictionary with key=the attribute and the value
343 // first, build a state dictionary with key=the attribute and the value
364 // being the value or the promise of the serialized value
344 // being the value or the promise of the serialized value
365 var state_promise_dict = {};
345 var serializers = this.constructor.serializers;
366 var keys = Object.keys(attrs);
346 if (serializers) {
367 for (var i=0; i<keys.length; i++) {
347 for (k in attrs) {
368 // bind k and v locally; needed since we have an inner async function using v
348 if (serializers[k] && serializers[k].serialize) {
369 (function(k,v) {
349 attrs[k] = (serializers[k].serialize)(attrs[k], this);
370 if (that.serializers[k]) {
371 state_promise_dict[k] = that.serializers[k].then(function(f) {
372 if (f.serialize) {
373 return f.serialize(v, that);
374 } else {
375 return v;
376 }
377 })
378 } else {
379 state_promise_dict[k] = v;
380 }
350 }
381 })(keys[i], attrs[keys[i]])
351 }
382 }
352 }
383 utils.resolve_promises_dict(state_promise_dict).then(function(state) {
353 utils.resolve_promises_dict(attrs).then(function(state) {
384 // get binary values, then send
354 // get binary values, then send
385 var keys = Object.keys(state);
355 var keys = Object.keys(state);
386 var buffers = [];
356 var buffers = [];
@@ -402,20 +372,6 b' define(["widgets/js/manager",'
402 });
372 });
403 },
373 },
404
374
405 serialize: function(model, attrs) {
406 // Serialize the attributes into a sync message
407 var keys = Object.keys(attrs);
408 var key, value;
409 var buffers, metadata, buffer_keys, serialize;
410 for (var i=0; i<keys.length; i++) {
411 key = keys[i];
412 serialize = model.serializers[key];
413 if (serialize && serialize.serialize) {
414 attrs[key] = serialize.serialize(attrs[key]);
415 }
416 }
417 },
418
419 save_changes: function(callbacks) {
375 save_changes: function(callbacks) {
420 /**
376 /**
421 * Push this model's state to the back-end
377 * Push this model's state to the back-end
@@ -4,10 +4,41 b''
4 define([
4 define([
5 "widgets/js/widget",
5 "widgets/js/widget",
6 "jqueryui",
6 "jqueryui",
7 "underscore",
7 "base/js/utils",
8 "base/js/utils",
8 "bootstrap",
9 "bootstrap",
9 ], function(widget, $, utils){
10 ], function(widget, $, _, utils){
10 "use strict";
11 "use strict";
12 var unpack_models = function unpack_models(value, model) {
13 /**
14 * Replace model ids with models recursively.
15 */
16 var unpacked;
17 if ($.isArray(value)) {
18 unpacked = [];
19 _.each(value, function(sub_value, key) {
20 unpacked.push(unpack_models(sub_value, model));
21 });
22 return Promise.all(unpacked);
23 } else if (value instanceof Object) {
24 unpacked = {};
25 _.each(value, function(sub_value, key) {
26 unpacked[key] = unpack_models(sub_value, model);
27 });
28 return utils.resolve_promises_dict(unpacked);
29 } else if (typeof value === 'string' && value.slice(0,10) === "IPY_MODEL_") {
30 // get_model returns a promise already
31 return model.widget_manager.get_model(value.slice(10, value.length));
32 } else {
33 return Promise.resolve(value);
34 }
35 };
36
37 var BoxModel = widget.WidgetModel.extend({}, {
38 serializers: _.extend({
39 children: {deserialize: unpack_models}
40 }, widget.WidgetModel.serializers)
41 });
11
42
12 var BoxView = widget.DOMWidgetView.extend({
43 var BoxView = widget.DOMWidgetView.extend({
13 initialize: function(){
44 initialize: function(){
@@ -148,6 +179,7 b' define(['
148 });
179 });
149
180
150 return {
181 return {
182 'BoxModel': BoxModel,
151 'BoxView': BoxView,
183 'BoxView': BoxView,
152 'FlexBoxView': FlexBoxView,
184 'FlexBoxView': FlexBoxView,
153 };
185 };
@@ -89,31 +89,6 b' def register(key=None):'
89 return wrap
89 return wrap
90
90
91
91
92 def _widget_to_json(x):
93 if isinstance(x, dict):
94 return {k: _widget_to_json(v) for k, v in x.items()}
95 elif isinstance(x, (list, tuple)):
96 return [_widget_to_json(v) for v in x]
97 elif isinstance(x, Widget):
98 return "IPY_MODEL_" + x.model_id
99 else:
100 return x
101
102 def _json_to_widget(x):
103 if isinstance(x, dict):
104 return {k: _json_to_widget(v) for k, v in x.items()}
105 elif isinstance(x, (list, tuple)):
106 return [_json_to_widget(v) for v in x]
107 elif isinstance(x, string_types) and x.startswith('IPY_MODEL_') and x[10:] in Widget.widgets:
108 return Widget.widgets[x[10:]]
109 else:
110 return x
111
112 widget_serialization = {
113 'from_json': _json_to_widget,
114 'to_json': lambda x: (_widget_to_json(x), {'serialization': ('models', 'widgets/js/types')})
115 }
116
117 class Widget(LoggingConfigurable):
92 class Widget(LoggingConfigurable):
118 #-------------------------------------------------------------------------
93 #-------------------------------------------------------------------------
119 # Class attributes
94 # Class attributes
@@ -241,12 +216,10 b' class Widget(LoggingConfigurable):'
241 key : unicode, or iterable (optional)
216 key : unicode, or iterable (optional)
242 A single property's name or iterable of property names to sync with the front-end.
217 A single property's name or iterable of property names to sync with the front-end.
243 """
218 """
244 state, buffer_keys, buffers, metadata = self.get_state(key=key)
219 state, buffer_keys, buffers = self.get_state(key=key)
245 msg = {"method": "update", "state": state}
220 msg = {"method": "update", "state": state}
246 if buffer_keys:
221 if buffer_keys:
247 msg['buffers'] = buffer_keys
222 msg['buffers'] = buffer_keys
248 if metadata:
249 msg['metadata'] = metadata
250 self._send(msg, buffers=buffers)
223 self._send(msg, buffers=buffers)
251
224
252 def get_state(self, key=None):
225 def get_state(self, key=None):
@@ -278,19 +251,16 b' class Widget(LoggingConfigurable):'
278 state = {}
251 state = {}
279 buffers = []
252 buffers = []
280 buffer_keys = []
253 buffer_keys = []
281 metadata = {}
282 for k in keys:
254 for k in keys:
283 f = self.trait_metadata(k, 'to_json', self._trait_to_json)
255 f = self.trait_metadata(k, 'to_json', self._trait_to_json)
284 value = getattr(self, k)
256 value = getattr(self, k)
285 serialized, md = f(value)
257 serialized = f(value)
286 if isinstance(serialized, memoryview):
258 if isinstance(serialized, memoryview):
287 buffers.append(serialized)
259 buffers.append(serialized)
288 buffer_keys.append(k)
260 buffer_keys.append(k)
289 else:
261 else:
290 state[k] = serialized
262 state[k] = serialized
291 if md is not None:
263 return state, buffer_keys, buffers
292 metadata[k] = md
293 return state, buffer_keys, buffers, metadata
294
264
295 def set_state(self, sync_data):
265 def set_state(self, sync_data):
296 """Called when a state is received from the front-end."""
266 """Called when a state is received from the front-end."""
@@ -439,11 +409,8 b' class Widget(LoggingConfigurable):'
439 self._display_callbacks(self, **kwargs)
409 self._display_callbacks(self, **kwargs)
440
410
441 def _trait_to_json(self, x):
411 def _trait_to_json(self, x):
442 """Convert a trait value to json.
412 """Convert a trait value to json."""
443
413 return x
444 Metadata (the second return value) is not sent
445 """
446 return x, None
447
414
448 def _trait_from_json(self, x):
415 def _trait_from_json(self, x):
449 """Convert json values to objects."""
416 """Convert json values to objects."""
@@ -6,13 +6,40 b' Represents a container that can be used to group other widgets.'
6 # Copyright (c) IPython Development Team.
6 # Copyright (c) IPython Development Team.
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8
8
9 from .widget import DOMWidget, register, widget_serialization
9 from .widget import DOMWidget, Widget, register
10 from IPython.utils.traitlets import Unicode, Tuple, TraitError, Int, CaselessStrEnum
10 from IPython.utils.traitlets import Unicode, Tuple, TraitError, Int, CaselessStrEnum
11 from IPython.utils.warn import DeprecatedClass
11 from IPython.utils.warn import DeprecatedClass
12
12
13 def _widget_to_json(x):
14 if isinstance(x, dict):
15 return {k: _widget_to_json(v) for k, v in x.items()}
16 elif isinstance(x, (list, tuple)):
17 return [_widget_to_json(v) for v in x]
18 elif isinstance(x, Widget):
19 return "IPY_MODEL_" + x.model_id
20 else:
21 return x
22
23 def _json_to_widget(x):
24 if isinstance(x, dict):
25 return {k: _json_to_widget(v) for k, v in x.items()}
26 elif isinstance(x, (list, tuple)):
27 return [_json_to_widget(v) for v in x]
28 elif isinstance(x, string_types) and x.startswith('IPY_MODEL_') and x[10:] in Widget.widgets:
29 return Widget.widgets[x[10:]]
30 else:
31 return x
32
33 widget_serialization = {
34 'from_json': _json_to_widget,
35 'to_json': _widget_to_json
36 }
37
38
13 @register('IPython.Box')
39 @register('IPython.Box')
14 class Box(DOMWidget):
40 class Box(DOMWidget):
15 """Displays multiple widgets in a group."""
41 """Displays multiple widgets in a group."""
42 _model_name = Unicode('BoxModel', sync=True)
16 _view_name = Unicode('BoxView', sync=True)
43 _view_name = Unicode('BoxView', sync=True)
17
44
18 # Child widgets in the container.
45 # Child widgets in the container.
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now