Skip to content

Commit

Permalink
Merge pull request #211 from iLib-js/cacheFix
Browse files Browse the repository at this point in the history
Cache fix
  • Loading branch information
ehoogerbeets authored Oct 10, 2019
2 parents da7739d + f98cdcf commit 581671a
Show file tree
Hide file tree
Showing 11 changed files with 199 additions and 30 deletions.
5 changes: 5 additions & 0 deletions docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ part of the locale specifier if it has a very common/default value

Bug Fixes:
* Fixed unit test failures which occur on QT 5.12
* Fixed problem where two resource bundle files with the same name and same locale but loaded from
different directories were cached in the same place.
* Introduced the new "basePath" property to ResBundle constructor to specify which directory
to load the resource bundle from. This property is used to differentiate files loaded from
different directories.

Build 006
-------
Expand Down
7 changes: 4 additions & 3 deletions js/lib/Loader.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* Loader.js - shared loader implementation
*
* Copyright © 2015, 2018, JEDLSoft
* Copyright © 2015, 2018-2019, JEDLSoft
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -86,8 +86,9 @@ Loader.prototype._loadFileAlongIncludePath = function(includePath, pathname) {
return undefined;
};

Loader.prototype.loadFiles = function(paths, sync, params, callback) {
var includePath = params && params.base ? [params.base].concat(this.includePath) : this.includePath;
Loader.prototype.loadFiles = function(paths, sync, params, callback, root) {
root = root || (params && params.base);
var includePath = root ? [root].concat(this.includePath) : this.includePath;

//console.log("Loader loadFiles called");
// make sure we know what we can load
Expand Down
4 changes: 2 additions & 2 deletions js/lib/NormString.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* NormString.js - ilib normalized string subclass definition
*
* Copyright © 2013-2015, 2018, JEDLSoft
* Copyright © 2013-2015, 2018-2019, JEDLSoft
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -141,7 +141,7 @@ NormString.init = function(options) {

if (files.length) {
//console.log("loading files " + JSON.stringify(files));
Utils._callLoadData(files, sync, loadParams, function(arr) {
Utils._callLoadData(files, sync, loadParams, undefined, function(arr) {
for (var i = 0; i < arr.length; i++) {
if (typeof(arr[i]) !== 'undefined') {
ilib.extend(ilib.data.norm[toLoad[i]], arr[i]);
Expand Down
14 changes: 11 additions & 3 deletions js/lib/ResBundle.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* ResBundle.js - Resource bundle definition
*
* Copyright © 2012-2016, 2018, JEDLSoft
* Copyright © 2012-2016, 2018-2019, JEDLSoft
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -71,6 +71,11 @@ var IString = require("./IString.js");
* The default behaviour is the same as before, which is to return the source string
* unchanged.
*
* <li><i>basePath</i> - look in the given path for the resource bundle files. This can be
* an absolute path or a relative path that is relative to the application's root.
* Default if this is not specified is to look in the standard path (ie. in the root
* of the app).
*
* <li><i>onLoad</i> - a callback function to call when the resources are fully
* loaded. When the onLoad option is given, this class will attempt to
* load any missing locale data using the ilib loader callback.
Expand Down Expand Up @@ -237,13 +242,17 @@ var ResBundle = function (options) {
this.type = options.type;
}
this.lengthen = options.lengthen || false;
this.path = options.basePath;

if (typeof(options.sync) !== 'undefined') {
this.sync = !!options.sync;
}

if (typeof(options.loadParams) !== 'undefined') {
this.loadParams = options.loadParams;
if (typeof (options.loadParams.root) !== 'undefined') {
this.path = options.loadParams.root;
}
}
if (typeof(options.missing) !== 'undefined') {
if (options.missing === "pseudo" || options.missing === "empty") {
Expand All @@ -257,14 +266,13 @@ var ResBundle = function (options) {
this.map = {};

lookupLocale = this.locale.isPseudo() ? new Locale("en-US") : this.locale;
var object = "ResBundle-" + this.baseName;

Utils.loadData({
object: object,
locale: lookupLocale,
name: this.baseName + ".json",
sync: this.sync,
loadParams: this.loadParams,
root: this.path,
callback: ilib.bind(this, function (map) {
if (!map) {
map = ilib.data[this.baseName] || {};
Expand Down
61 changes: 44 additions & 17 deletions js/lib/Utils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* Utils.js - Core utility routines
*
* Copyright © 2012-2015, 2018, JEDLSoft
* Copyright © 2012-2015, 2018-2019, JEDLSoft
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -25,6 +25,26 @@ var ISet = require("./ISet.js");

var Utils = {};

/**
* Return the property name inside of ilib.data that corresponds to the given locale data file.
*
* @private
* @param {String} basename the basename of the file
* @param {String} pathname the path from the root to the base file which usually encodes the
* locale of the file
* @param {String=} root the root directory of the file or undefined for the standard locale dir
*/
function getPropertyName(basename, pathname, root) {
var bits = [ basename ];
if (root) {
bits = bits.concat(root.split("\/"));
}
if (pathname) {
bits = bits.concat(pathname.split("\/"));
}
return bits.join('_');
}

/**
* Return an array of locales that represent the sublocales of
* the given locale. These sublocales are intended to be used
Expand Down Expand Up @@ -182,9 +202,10 @@ Utils.getSublocales = function(locale) {
* If false, concatenate array elements in object1 with items in object2.
* @param {boolean=} returnOne if true, only return the most locale-specific data. If false,
* merge all the relevant locale data together.
* @param {string=} root root path if there is one
* @return {Object?} the merged locale data
*/
Utils.mergeLocData = function (prefix, locale, replaceArrays, returnOne) {
Utils.mergeLocData = function (prefix, locale, replaceArrays, returnOne, root) {
var data = undefined;
var loc = locale || new Locale();
var mostSpecific;
Expand All @@ -194,7 +215,7 @@ Utils.mergeLocData = function (prefix, locale, replaceArrays, returnOne) {
mostSpecific = data;

Utils.getSublocales(loc).forEach(function(l) {
var property = (l === "root") ? prefix : prefix + '_' + l.replace(/-/g, "_");
var property = getPropertyName(prefix, (l === "root") ? undefined : l.replace(/-/g, "/"), root);

if (ilib.data[property]) {
if (returnOne) {
Expand Down Expand Up @@ -311,20 +332,25 @@ Utils.getLocFiles = function(locale, name) {
* @static
* @private
*/
Utils._callLoadData = function (files, sync, params, callback) {
Utils._callLoadData = function (files, sync, params, root, callback) {
// console.log("Utils._callLoadData called");
if (typeof(ilib._load) === 'function') {
// console.log("Utils._callLoadData: calling as a regular function");
return ilib._load(files, sync, params, callback);
} else if (typeof(ilib._load) === 'object' && typeof(ilib._load.loadFiles) === 'function') {
// console.log("Utils._callLoadData: calling as an object");
return ilib._load.loadFiles(files, sync, params, callback);
return ilib._load.loadFiles(files, sync, params, callback, root);
}

// console.log("Utils._callLoadData: not calling. Type is " + typeof(ilib._load) + " and instanceof says " + (ilib._load instanceof Loader));
return undefined;
};

function getPropertyNameFromFile(basename, filepath, root) {
var dir = Path.dirname(filepath);
return getPropertyName(basename, (dir === "." || dir === "/" || dir === "..") ? undefined : dir, root);
}

/**
* Return true if the locale data corresponding to the given pathname is not already loaded
* or assembled.
Expand All @@ -334,11 +360,8 @@ Utils._callLoadData = function (files, sync, params, callback) {
* @param locale
* @returns
*/
function dataNotExists(basename, pathname) {
var localeBits = pathname.split("\/").slice(0, -1).join('_');
var property = localeBits ? basename + '_' + localeBits : basename;

return !ilib.data[property];
function dataNotExists(basename, pathname, root) {
return !ilib.data[getPropertyNameFromFile(basename, pathname, root)];
}

/**
Expand All @@ -360,6 +383,9 @@ function dataNotExists(basename, pathname) {
* <li><i>replace</i> - boolean. When merging json objects, this parameter controls whether to merge arrays
* or have arrays replace each other. If true, arrays in child objects replace the arrays in parent
* objects. When false, the arrays in child objects are concatenated with the arrays in parent objects.
* <li><i>root</i> - String. If provided, look in this root directory first for files, and then fall back
* to the standard include paths if they are not found in this root. If not provided, just search the
* standard include paths.
* <li><i>loadParams</i> - Object. An object with parameters to pass to the loader function
* <li><i>sync</i> - boolean. Whether or not to load the data synchronously
* <li><i>callback</i> - function(?)=. callback Call back function to call when the data is available.
Expand All @@ -378,6 +404,7 @@ Utils.loadData = function(params) {
callback = undefined,
nonlocale = false,
replace = false,
root,
basename;

if (!params || typeof(params.callback) !== 'function') {
Expand Down Expand Up @@ -406,6 +433,7 @@ Utils.loadData = function(params) {
replace = params.replace;
}

root = params.root;
callback = params.callback;

if (!type) {
Expand Down Expand Up @@ -444,25 +472,24 @@ Utils.loadData = function(params) {

// find the ones we haven't loaded before
files = files.filter(ilib.bind(this, function(file) {
return !ilib.data.cache.fileSet.has(file) && dataNotExists(basename, file);
return !ilib.data.cache.fileSet.has(Path.join(root, file)) && dataNotExists(basename, file, root);
}));

if (files.length) {
Utils._callLoadData(files, sync, loadParams, ilib.bind(this, function(arr) {
Utils._callLoadData(files, sync, loadParams, root, ilib.bind(this, function(arr) {
for (var i = 0; i < files.length; i++) {
if (arr[i]) {
var localeBits = files[i].split("\/").slice(0, -1).join('_');
var property = !nonlocale && localeBits ? basename + '_' + localeBits : basename;
var property = nonlocale ? basename : getPropertyNameFromFile(basename, files[i], root);

if (!ilib.data[property]) {
ilib.data[property] = arr[i];
}
}
ilib.data.cache.fileSet.add(files[i]);
ilib.data.cache.fileSet.add(Path.join(root, files[i]));
}

if (!nonlocale) {
data = Utils.mergeLocData(basename, locale, replace, returnOne);
data = Utils.mergeLocData(basename, locale, replace, returnOne, root);
if (ilib._cacheMerged) ilib.data.cache.merged[spec] = data;
} else {
data = ilib.data[basename];
Expand All @@ -478,7 +505,7 @@ Utils.loadData = function(params) {

// No loader, or data already loaded? Then use whatever data we have already in ilib.data
if (!nonlocale) {
data = Utils.mergeLocData(basename, locale, replace, returnOne);
data = Utils.mergeLocData(basename, locale, replace, returnOne, root);
if (ilib._cacheMerged) ilib.data.cache.merged[spec] = data;
} else {
data = ilib.data[basename];
Expand Down
4 changes: 2 additions & 2 deletions js/lib/ilib.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* ilib.js - define the ilib name space
*
* Copyright © 2012-2018, JEDLSoft
* Copyright © 2012-2019, JEDLSoft
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -460,7 +460,7 @@ ilib.Loader = function() {};
* or resources, then that data can be lazy loaded dynamically when it is
* needed by calling this method. Each ilib class will first
* check for the existence of data under ilib.data, and if it is not there,
* it will attempt to load it by calling this method of the laoder, and then place
* it will attempt to load it by calling this method of the loader, and then place
* it there.<p>
*
* Suggested implementations of this method might load files
Expand Down
6 changes: 6 additions & 0 deletions js/test/root/resources/ja/basetest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"hello" : "こんにちは",
"Hello from {country}": "{country}からこんにちは",
"Hello from {city}": "{city}からこんにちは",
"Greetings from {city} in {country}": "{city}と{country}からこんにちは"
}
6 changes: 6 additions & 0 deletions js/test/root/resources2/ja/basetest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"hello" : "こんにちは2",
"Hello from {country}": "{country}からこんにちは2",
"Hello from {city}": "{city}からこんにちは2",
"Greetings from {city} in {country}": "{city}と{country}からこんにちは2"
}
57 changes: 56 additions & 1 deletion js/test/root/testresources.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* testresources.js - test the Resources object
*
* Copyright © 2012-2015, 2017-2018, JEDLSoft
* Copyright © 2012-2015, 2017-2019, JEDLSoft
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -26,6 +26,9 @@ if (typeof(ResBundle) === "undefined") {
if (typeof(Locale) === "undefined") {
var Locale = require("../../lib/Locale.js");
}
if (ilib._getPlatform() === "nodejs" && ilib._dyndata && ilib._dyncode) {
var path = require("path");
}

ilib.data.strings = {
"first string": "first",
Expand Down Expand Up @@ -2072,6 +2075,58 @@ module.exports.testresources = {
"dritte String 2"
]);

test.done();
},

testResBundleGetStringWithBasePath: function(test) {
if (ilib._getPlatform() !== "nodejs" || !ilib._dyndata || !ilib._dyncode) {
test.done();
return;
}

test.expect(4);

// clear this to be sure it is actually loading something
ilib.clearCache();

var base = path.relative(process.cwd(), path.resolve(__dirname, "./resources"));

var rb = new ResBundle({
locale: "ja-JP",
name: "basetest",
basePath: base
});

test.ok(rb !== null);

test.equal(rb.getString("Hello from {country}").toString(), "{country}からこんにちは");
test.equal(rb.getString("Hello from {city}").toString(), "{city}からこんにちは");
test.equal(rb.getString("Greetings from {city} in {country}").toString(), "{city}と{country}からこんにちは");
test.done();
},

testResBundleGetStringWithDifferentBasePath: function(test) {
if (ilib._getPlatform() !== "nodejs" || !ilib._dyndata || !ilib._dyncode) {
test.done();
return;
}

test.expect(4);

// don't clear the cache
var base = path.relative(process.cwd(), path.resolve(__dirname, "./resources2"));

var rb = new ResBundle({
locale: "ja-JP",
name: "basetest",
basePath: base
});

test.ok(rb !== null);

test.equal(rb.getString("Hello from {country}").toString(), "{country}からこんにちは2");
test.equal(rb.getString("Hello from {city}").toString(), "{city}からこんにちは2");
test.equal(rb.getString("Greetings from {city} in {country}").toString(), "{city}と{country}からこんにちは2");
test.done();
}
};
Loading

0 comments on commit 581671a

Please sign in to comment.