[REF] web: data.js: refactoring of BufferedDataSet (less code and speed up)
This commit is contained in:
@@ -739,155 +739,93 @@ var BufferedDataSet = DataSetStatic.extend({
|
||||
self.last_default_get = res;
|
||||
});
|
||||
},
|
||||
get_cache: function (id) {
|
||||
if (!this.cache[id]) {
|
||||
this.cache[id] = {
|
||||
'id': id,
|
||||
'values': {},
|
||||
'from_read': {},
|
||||
'changes': {},
|
||||
'readonly_fields': {},
|
||||
'to_create': false,
|
||||
'to_delete': false};
|
||||
}
|
||||
return this.cache[id];
|
||||
},
|
||||
_update_cache: function (id, options) {
|
||||
// One should call this method after modifying this.from_read,
|
||||
// this.to_create or this.to_write. It updates this.cache and
|
||||
// this.from_readonly.
|
||||
var cached = _.detect(this.cache, function(x) {return x.id === id;});
|
||||
if (!cached) {
|
||||
this.cache.push(cached = {id: id, values: {}});
|
||||
}
|
||||
var record = _.detect(this.from_read, function(x) {return x.id === id;});
|
||||
if (record) {
|
||||
cached.values = _.clone(record.values);
|
||||
} else if (record = _.detect(this.to_create, function(x) {return x.id === id;})) {
|
||||
cached.values = _.clone(record.values);
|
||||
cached.values = _.extend(cached.values, record.defaults);
|
||||
} else {
|
||||
record = {};
|
||||
}
|
||||
|
||||
var record_to_write = _.detect(this.to_write, function(x) {return x.id === id;});
|
||||
if (record_to_write) {
|
||||
_.extend(cached.values, record_to_write.values);
|
||||
}
|
||||
|
||||
var record_from_readonly = _.detect(this.from_readonly, function(x) {return x.id === id;});
|
||||
if (options && options.readonly_fields) {
|
||||
if (!record_from_readonly) {
|
||||
this.from_readonly.push(record_from_readonly = {id: id, values: {}});
|
||||
}
|
||||
_.extend(record_from_readonly.values, options.readonly_fields);
|
||||
}
|
||||
if (record_from_readonly) {
|
||||
_.extend(cached.values, record_from_readonly.values);
|
||||
// this.to_create or this.change. It updates this.cache and
|
||||
// this.readonly_fields.
|
||||
var cached = this.get_cache(id);
|
||||
if (options) {
|
||||
_.extend(cached.from_read, options.from_read);
|
||||
_.extend(cached.changes, options.changes);
|
||||
_.extend(cached.readonly_fields, options.readonly_fields);
|
||||
if (options.to_create !== undefined) cached.to_create = options.to_create;
|
||||
if (options.to_delete !== undefined) cached.to_delete = options.to_delete;
|
||||
}
|
||||
cached.values = _.extend({'id': id}, cached.from_read, cached.changes, cached.readonly_fields);
|
||||
return cached;
|
||||
},
|
||||
create: function(data, options) {
|
||||
var cached = {
|
||||
id:_.uniqueId(this.virtual_id_prefix),
|
||||
values: _.extend({}, data, (options || {}).readonly_fields || {}),
|
||||
defaults: this.last_default_get
|
||||
};
|
||||
this.to_create.push(_.clone(cached));
|
||||
this._update_cache(cached.id, options);
|
||||
return $.Deferred().resolve(cached.id).promise();
|
||||
create: function(data, options) {
|
||||
var changes = _.extend({}, this.last_default_get, data);
|
||||
var cached = this._update_cache(_.uniqueId(this.virtual_id_prefix), _.extend({'changes': changes, 'to_create': true}, options));
|
||||
this.trigger("dataset_changed", data, options);
|
||||
return $.Deferred().resolve(cached.id);
|
||||
},
|
||||
write: function (id, data, options) {
|
||||
var self = this;
|
||||
var cached = this._update_cache(id, options);
|
||||
var cached = this.get_cache(id);
|
||||
|
||||
// read missing field to create the to_write diff
|
||||
var def_read = $.Deferred();
|
||||
this.mutex.def.then(function () {
|
||||
if (typeof id !== "number" && id.indexOf(self.virtual_id_prefix) === 0) {
|
||||
return def_read.resolve();
|
||||
}
|
||||
var data_fields = _.keys(data);
|
||||
var record_fields = _.keys((_.detect(self.from_read, function(x) {return x.id === id;}) || {}).values);
|
||||
var fields = _.union(data_fields, record_fields);
|
||||
if (fields.length !== data_fields.length || fields.length !== record_fields.length) {
|
||||
self.read_ids(self.ids, fields).always(function () {
|
||||
def_read.resolve();
|
||||
});
|
||||
} else {
|
||||
def_read.resolve();
|
||||
}
|
||||
});
|
||||
// if update after a remove, it's like an add before updating
|
||||
cached.to_delete = false;
|
||||
|
||||
// apply change
|
||||
var def = $.Deferred();
|
||||
def_read.then(function () {
|
||||
self.mutex.exec(function () {
|
||||
var dirty = false;
|
||||
|
||||
var record_to_create = _.detect(self.to_create, function(x) {return x.id === id;});
|
||||
if (record_to_create) {
|
||||
// update record in self.to_create
|
||||
_.each(data, function (v, k) {
|
||||
if (!_.isEqual(v, cached.values[k])) {
|
||||
record_to_create.values[k] = v;
|
||||
dirty = true;
|
||||
} else {
|
||||
delete data[k];
|
||||
}
|
||||
});
|
||||
this.mutex.exec(function () {
|
||||
var dirty = false;
|
||||
_.each(data, function (v, k) {
|
||||
if (!_.isEqual(v, cached.values[k])) {
|
||||
dirty = true;
|
||||
if (_.isEqual(v, cached.from_read[k])) { // clean changes
|
||||
delete cached.changes[k];
|
||||
} else {
|
||||
cached.changes[k] = v;
|
||||
}
|
||||
} else {
|
||||
// check if the record has changed, and update self.to_write accordingly
|
||||
var record_to_write = _.detect(self.to_write, function(x) {return x.id === id;});
|
||||
if (!record_to_write) {
|
||||
self.to_write.push(record_to_write = {id: id, values: {}});
|
||||
}
|
||||
|
||||
var record_from_read = _.detect(self.from_read, function(x) {return x.id === id;});
|
||||
if (!record_from_read) {
|
||||
record_from_read = {values: {}};
|
||||
}
|
||||
_.each(data, function (v, k) {
|
||||
if (!_.isEqual(v, cached.values[k])) {
|
||||
dirty = true;
|
||||
if (_.isEqual(v, record_from_read.values[k])) { // clean changes
|
||||
delete record_to_write.values[k];
|
||||
} else {
|
||||
record_to_write.values[k] = v;
|
||||
}
|
||||
} else {
|
||||
delete data[k];
|
||||
}
|
||||
});
|
||||
if (_.isEmpty(record_to_write.values)) {
|
||||
self.to_write.splice(self.to_write.indexOf(record_to_write), 1);
|
||||
}
|
||||
delete data[k];
|
||||
}
|
||||
|
||||
self._update_cache(id, options);
|
||||
|
||||
if (dirty) {
|
||||
self.trigger("dataset_changed", id, data, options);
|
||||
}
|
||||
|
||||
return def.resolve(data).promise();
|
||||
});
|
||||
self._update_cache(id, options);
|
||||
|
||||
if (dirty) {
|
||||
self.trigger("dataset_changed", id, data, options);
|
||||
}
|
||||
|
||||
return def.resolve(data).promise();
|
||||
});
|
||||
|
||||
return def;
|
||||
},
|
||||
unlink: function(ids, callback, error_callback) {
|
||||
var self = this;
|
||||
_.each(ids, function(id) {
|
||||
if (! _.detect(self.to_create, function(x) { return x.id === id; })) {
|
||||
self.to_delete.push({id: id});
|
||||
}
|
||||
_.each(ids, function (id) {
|
||||
self.get_cache(id).to_delete = true;
|
||||
});
|
||||
this.from_read = _.reject(this.from_read, function(x) { return _.include(ids, x.id);});
|
||||
this.to_create = _.reject(this.to_create, function(x) { return _.include(ids, x.id);});
|
||||
this.to_write = _.reject(this.to_write, function(x) { return _.include(ids, x.id);});
|
||||
this.cache = _.reject(this.cache, function(x) { return _.include(ids, x.id);});
|
||||
this.set_ids(_.without.apply(_, [this.ids].concat(ids)));
|
||||
this.set_ids(_.without(this.ids, _.pluck(_.filter(this.cache, function (c) {return c.to_delete;}), 'id')));
|
||||
this.trigger("dataset_changed", ids, callback, error_callback);
|
||||
return $.async_when({result: true}).done(callback);
|
||||
},
|
||||
reset_ids: function(ids, options) {
|
||||
this.set_ids(ids);
|
||||
this.to_delete = [];
|
||||
this.to_create = [];
|
||||
this.to_write = [];
|
||||
if (!options || !options.keep_read_data) {
|
||||
this.from_read = [];
|
||||
this.from_readonly = [];
|
||||
this.cache = {};
|
||||
} else {
|
||||
_.each(this.cache, function (cache) {
|
||||
cache.changes = {};
|
||||
cache.to_delete = false;
|
||||
});
|
||||
}
|
||||
this.cache = [];
|
||||
this.delete_all = false;
|
||||
_.each(_.clone(this.running_reads), function(el) {
|
||||
el.reject();
|
||||
@@ -897,21 +835,15 @@ var BufferedDataSet = DataSetStatic.extend({
|
||||
// read what is necessary from the server to have ids and the given
|
||||
// fields in this.from_read
|
||||
var self = this;
|
||||
var to_get = [];
|
||||
options = options || {};
|
||||
_.each(ids, function(id) {
|
||||
if (typeof id === 'number' || id.indexOf(self.virtual_id_prefix) === -1) {
|
||||
var record = _.detect(self.from_read, function(x) {return x.id === id;});
|
||||
if (!record || !_.all(fields, function(x) {return record.values[x] !== undefined;})) {
|
||||
to_get.push(id);
|
||||
}
|
||||
}
|
||||
var to_get = _.filter(ids, function(id) {
|
||||
var cache = self.get_cache(id);
|
||||
return !cache.to_create && _.any(fields, function(x) {return cache.from_read[x] === undefined;});
|
||||
});
|
||||
options = options || {};
|
||||
|
||||
var return_records = function() {
|
||||
var records = _.map(ids, function(id) {
|
||||
var cache = _.find(self.cache, function(cache) {return cache.id === id;});
|
||||
return _.extend({}, cache && cache.values, {"id": id}, options.readonly_fields || {});
|
||||
return self.get_cache(id).values;
|
||||
});
|
||||
if (self.debug_mode) {
|
||||
if (_.include(records, undefined)) {
|
||||
@@ -970,13 +902,7 @@ var BufferedDataSet = DataSetStatic.extend({
|
||||
_.each(records, function(record, index) {
|
||||
// add information into from_read
|
||||
var id = to_get[index];
|
||||
var record_from_read = _.detect(self.from_read, function(x) {return x.id === id;});
|
||||
if (record_from_read) {
|
||||
_.extend(record_from_read.values, record);
|
||||
} else {
|
||||
self.from_read.push({id: id, values: record});
|
||||
}
|
||||
self._update_cache(id, options);
|
||||
self._update_cache(id, _.extend(options, {'from_read': record}));
|
||||
});
|
||||
return return_records();
|
||||
});
|
||||
@@ -997,10 +923,9 @@ var BufferedDataSet = DataSetStatic.extend({
|
||||
// Don't evict records which haven't yet been saved: there is no more
|
||||
// recent data on the server (and there potentially isn't any data),
|
||||
// and this breaks the assumptions of other methods (that the data
|
||||
// for new and altered records is both in the cache and in the to_write
|
||||
// for new and altered records is both in the cache and in the change
|
||||
// or to_create collection)
|
||||
this.from_read = _.reject(this.from_read, function(x) { return x.id === id;});
|
||||
this._update_cache(id);
|
||||
this._update_cache(id, {'from_read': {}});
|
||||
},
|
||||
call_button: function (method, args) {
|
||||
this.evict_record(args[0][0]);
|
||||
@@ -1011,8 +936,11 @@ var BufferedDataSet = DataSetStatic.extend({
|
||||
return this._super(id, signal);
|
||||
},
|
||||
alter_ids: function(n_ids) {
|
||||
var dirty = !_.isEqual(this.ids, n_ids);
|
||||
this._super(n_ids);
|
||||
this.trigger("dataset_changed", n_ids);
|
||||
if (dirty) {
|
||||
this.trigger("dataset_changed", n_ids);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -442,19 +442,20 @@ var AbstractManyField = common.AbstractField.extend({
|
||||
this.set('value', []);
|
||||
this.starting_ids = [];
|
||||
this.mutex = new utils.Mutex();
|
||||
this.has_not_committed_changes = false;
|
||||
this.view.on("load_record", this, this._on_load_record);
|
||||
this.dataset.on('dataset_changed', this, function() {
|
||||
self.has_not_committed_changes = true;
|
||||
var options = _.clone(_.last(arguments));
|
||||
if (!_.isObject(options) || _.isArray(options)) {
|
||||
options = {};
|
||||
}
|
||||
// don't trigger changes if all commands are not resolved
|
||||
// the editable lists change the dataset without call AbstractManyField methods
|
||||
if (self.mutex.def.state() === "resolved" && !self.internal_dataset_changed) {
|
||||
self.trigger("change:commands");
|
||||
if (!self.internal_dataset_changed && !options.internal_dataset_changed) {
|
||||
self.trigger("change:commands", options);
|
||||
}
|
||||
});
|
||||
this.on("change:commands", this, function (options) {
|
||||
self.has_not_committed_changes = false;
|
||||
self._inhibit_on_change_flag = !!(options||{})._inhibit_on_change_flag;
|
||||
self._inhibit_on_change_flag = !!options._inhibit_on_change_flag;
|
||||
self.set({'value': self.dataset.ids.slice()});
|
||||
self._inhibit_on_change_flag = false;
|
||||
});
|
||||
@@ -463,9 +464,10 @@ var AbstractManyField = common.AbstractField.extend({
|
||||
_on_load_record: function (record) {
|
||||
this.starting_ids = [];
|
||||
// don't set starting_ids for the new record
|
||||
if (record.id && (!isNaN(record.id) || record.id.indexOf(this.dataset.virtual_id_prefix) === -1)) {
|
||||
if (record.id && record[this.name] && (!isNaN(record.id) || record.id.indexOf(this.dataset.virtual_id_prefix) === -1)) {
|
||||
this.starting_ids = record[this.name].slice();
|
||||
}
|
||||
this.trigger("load_record", record);
|
||||
},
|
||||
|
||||
set_value: function(ids) {
|
||||
@@ -574,6 +576,7 @@ var AbstractManyField = common.AbstractField.extend({
|
||||
var dataset = this.dataset;
|
||||
var res = true;
|
||||
options = options || {};
|
||||
var internal_options = _.extend({}, options, {'internal_dataset_changed': true});
|
||||
|
||||
_.each(command_list, function(command) {
|
||||
self.mutex.exec(function() {
|
||||
@@ -582,12 +585,12 @@ var AbstractManyField = common.AbstractField.extend({
|
||||
case COMMANDS.CREATE:
|
||||
var data = _.clone(command[2]);
|
||||
delete data.id;
|
||||
return dataset.create(data, options).then(function (id) {
|
||||
return dataset.create(data, internal_options).then(function (id) {
|
||||
dataset.ids.push(id);
|
||||
res = id;
|
||||
});
|
||||
case COMMANDS.UPDATE:
|
||||
return dataset.write(id, command[2], options).then(function () {
|
||||
return dataset.write(id, command[2], internal_options).then(function () {
|
||||
if (dataset.ids.indexOf(id) === -1) {
|
||||
dataset.ids.push(id);
|
||||
res = id;
|
||||
@@ -599,14 +602,14 @@ var AbstractManyField = common.AbstractField.extend({
|
||||
return dataset.unlink([id]);
|
||||
case COMMANDS.LINK_TO:
|
||||
if (dataset.ids.indexOf(id) === -1) {
|
||||
return dataset.add_ids([id], options);
|
||||
return dataset.add_ids([id], internal_options);
|
||||
}
|
||||
return;
|
||||
case COMMANDS.DELETE_ALL:
|
||||
return dataset.reset_ids([], {keep_read_data: true});
|
||||
case COMMANDS.REPLACE_WITH:
|
||||
dataset.ids = [];
|
||||
return dataset.alter_ids(command[2], options);
|
||||
return dataset.alter_ids(command[2], internal_options);
|
||||
default:
|
||||
throw new Error("send_commands to '"+self.name+"' receive a non command value." +
|
||||
"\n" + JSON.stringify(command_list));
|
||||
@@ -631,29 +634,27 @@ var AbstractManyField = common.AbstractField.extend({
|
||||
replace_with_ids = [],
|
||||
add_ids = [],
|
||||
command_list = [],
|
||||
id, index, alter_order;
|
||||
id, index, record;
|
||||
|
||||
function format_many2one (values) {
|
||||
values = _.clone(values);
|
||||
for (var k in values) {
|
||||
if ((values[k] instanceof Array) && values[k].length === 2 && typeof values[k][0] === "number" && typeof values[k][1] === "string") {
|
||||
values[k] = values[k][0];
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
_.each(this.get('value'), function (id) {
|
||||
index = starting_ids.indexOf(id);
|
||||
if (index !== -1) {
|
||||
starting_ids.splice(index, 1);
|
||||
}
|
||||
if (alter_order = _.detect(self.dataset.to_create, function(x) {return x.id === id;})) {
|
||||
command_list.push(COMMANDS.create(format_many2one(alter_order.values)));
|
||||
return;
|
||||
}
|
||||
if (alter_order = _.detect(self.dataset.to_write, function(x) {return x.id === id;})) {
|
||||
command_list.push(COMMANDS.update(alter_order.id, format_many2one(alter_order.values)));
|
||||
var record = self.dataset.get_cache(id);
|
||||
if (!_.isEmpty(record.changes)) {
|
||||
var values = _.clone(record.changes);
|
||||
// format many2one values
|
||||
for (var k in values) {
|
||||
if ((values[k] instanceof Array) && values[k].length === 2 && typeof values[k][0] === "number" && typeof values[k][1] === "string") {
|
||||
values[k] = values[k][0];
|
||||
}
|
||||
}
|
||||
if (record.to_create) {
|
||||
command_list.push(COMMANDS.create(values));
|
||||
} else {
|
||||
command_list.push(COMMANDS.update(record.id, values));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!is_one2many || self.dataset.delete_all) {
|
||||
@@ -683,7 +684,7 @@ var AbstractManyField = common.AbstractField.extend({
|
||||
},
|
||||
|
||||
is_valid: function () {
|
||||
return this.mutex.def.state() === "resolved" && !this.has_not_committed_changes && this._super();
|
||||
return this.mutex.def.state() === "resolved" && this._super();
|
||||
},
|
||||
|
||||
is_false: function() {
|
||||
@@ -853,9 +854,7 @@ var FieldX2Many = AbstractManyField.extend({
|
||||
if (view && view.type === "list" && view.controller.__focus) {
|
||||
var def = $.Deferred();
|
||||
view.controller._on_blur_one2many(true).always(function () {
|
||||
setTimeout(function () {def.resolve();},0);
|
||||
}, function () {
|
||||
setTimeout(function () {def.reject();},0);
|
||||
def.resolve();
|
||||
});
|
||||
return def;
|
||||
}
|
||||
@@ -887,17 +886,7 @@ var X2ManyDataSet = data.BufferedDataSet.extend({
|
||||
self.context.add(context);
|
||||
});
|
||||
return this.context;
|
||||
},
|
||||
create: function(data, options) {
|
||||
var self = this;
|
||||
var def = this._super(data, options);
|
||||
def.then(function (id) {
|
||||
setTimeout(function () {
|
||||
self.trigger("dataset_changed", id, data, options);
|
||||
},0);
|
||||
});
|
||||
return def;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
var X2ManyViewManager = ViewManager.extend({
|
||||
@@ -1046,6 +1035,7 @@ var X2ManyList = ListView.List.extend({
|
||||
var One2ManyListView = X2ManyListView.extend({
|
||||
_template: 'One2Many.listview',
|
||||
init: function (parent, dataset, view_id, options) {
|
||||
var self = this;
|
||||
this._super(parent, dataset, view_id, _.extend(options || {}, {
|
||||
GroupsType: One2ManyGroups,
|
||||
ListType: X2ManyList
|
||||
@@ -1065,6 +1055,13 @@ var One2ManyListView = X2ManyListView.extend({
|
||||
}
|
||||
};
|
||||
$(document).on('mousedown', this._mousedown_blur_line);
|
||||
|
||||
this.dataset.on('dataset_changed', this, function () {
|
||||
self._dataset_changed = true;
|
||||
});
|
||||
this.dataset.x2m.on('load_record', this, function () {
|
||||
self._dataset_changed = false;
|
||||
});
|
||||
},
|
||||
do_add_record: function () {
|
||||
if (this.editable()) {
|
||||
@@ -1130,10 +1127,10 @@ var One2ManyListView = X2ManyListView.extend({
|
||||
return $.when();
|
||||
}).done(function () {
|
||||
var ds = self.x2m.dataset;
|
||||
var cached_records = _.any([ds.to_create, ds.to_delete, ds.to_write], function(value) {
|
||||
return value.length;
|
||||
var changed_records = _.find(ds.cache, function(record) {
|
||||
return record.to_create || record.to_delete || !_.isEmpty(record.changes);
|
||||
});
|
||||
if (!self.x2m.options.reload_on_button && !cached_records) {
|
||||
if (!self.x2m.options.reload_on_button && !changed_records) {
|
||||
self.handle_button(name, id, callback);
|
||||
}else {
|
||||
self.handle_button(name, id, function(){
|
||||
@@ -1160,6 +1157,7 @@ var One2ManyListView = X2ManyListView.extend({
|
||||
},
|
||||
_on_focus_one2many: function () {
|
||||
this.dataset.x2m.internal_dataset_changed = true;
|
||||
this._dataset_changed = false;
|
||||
this.__focus = true;
|
||||
},
|
||||
_on_blur_one2many: function (force) {
|
||||
@@ -1180,11 +1178,12 @@ var One2ManyListView = X2ManyListView.extend({
|
||||
def = this.cancel_edition();
|
||||
}
|
||||
|
||||
def.then(function () {
|
||||
if (self.dataset.x2m.has_not_committed_changes) {
|
||||
def.then(function () {
|
||||
if (self._dataset_changed) {
|
||||
self.dataset.trigger('dataset_changed');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return def;
|
||||
},
|
||||
_after_edit: function () {
|
||||
|
||||
@@ -617,7 +617,6 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
||||
});
|
||||
});
|
||||
|
||||
var args = _.toArray(arguments);
|
||||
return mutex.def.then(function () { return self.onchanges_mutex.def; }).then(function() {
|
||||
var save_obj = self.save_list.pop();
|
||||
if (save_obj) {
|
||||
|
||||
Reference in New Issue
Block a user