Skip to content

Commit

Permalink
Addeed approve to the public API. Translations are not approved by …
Browse files Browse the repository at this point in the history
…default
  • Loading branch information
masylum committed Apr 3, 2011
1 parent 2a808b9 commit b2ae70a
Show file tree
Hide file tree
Showing 14 changed files with 245 additions and 37 deletions.
3 changes: 2 additions & 1 deletion Readme.md
Expand Up @@ -54,7 +54,8 @@ Dialect is a painless nodejs module to manage your translations.
* `config (key, value)`: Exposes configuration values. * `config (key, value)`: Exposes configuration values.
* `get (query)`: Gets a translation cached in memory. * `get (query)`: Gets a translation cached in memory.
* `set (query, translation, callback)`: Sets a translation on the store. * `set (query, translation, callback)`: Sets a translation on the store.
* `sync (locale, repeat, callback)`: Syncs the store with the memory cache. * `approve (approve?, query, callback)`: Approve or rejects a translation.
* `sync (locale, repeat, callback)`: Syncs all the approved translations of the store to the memory cache.
* `connect (callback)`: Connects to the database store. * `connect (callback)`: Connects to the database store.


### Plurals ### Plurals
Expand Down
3 changes: 3 additions & 0 deletions examples/contexts.js
Expand Up @@ -12,6 +12,9 @@ dialect.connect(function () {
dialect.set({original: 'Fight', locale: 'es', context: 'name'}, 'Lucha'); dialect.set({original: 'Fight', locale: 'es', context: 'name'}, 'Lucha');
dialect.set({original: 'Fight', locale: 'es', context: 'verb'}, 'Luchar'); dialect.set({original: 'Fight', locale: 'es', context: 'verb'}, 'Luchar');


dialect.approve({original: 'Fight', locale: 'es', context: 'name'}, true);
dialect.approve({original: 'Fight', locale: 'es', context: 'verb'}, true);

dialect.sync({interval: 3600}, function (err, foo) { dialect.sync({interval: 3600}, function (err, foo) {
console.log(_(['Fight', {context: 'name'}])); console.log(_(['Fight', {context: 'name'}]));
console.log(_(['Fight', {context: 'verb'}])); console.log(_(['Fight', {context: 'verb'}]));
Expand Down
3 changes: 2 additions & 1 deletion examples/hello_world.js
Expand Up @@ -9,11 +9,12 @@ var dialect = require('..').dialect({


dialect.connect(function () { dialect.connect(function () {
console.log(_(original)); console.log(_(original));

dialect.set({original: original, locale: 'es'}, translation); dialect.set({original: original, locale: 'es'}, translation);
dialect.approve({original: original, locale: 'es'}, true);


dialect.sync({interval: 3600}, function (err, foo) { dialect.sync({interval: 3600}, function (err, foo) {
console.log(_(original)); console.log(_(original));
console.log(_('Inexistant')); console.log(_('Inexistant'));
process.exit();
}); });
}); });
4 changes: 4 additions & 0 deletions examples/plurals.js
Expand Up @@ -14,6 +14,10 @@ dialect.connect(function () {
dialect.set({original: '{count} Beer', locale: 'es', plural: 2}, '{count} pivi'); dialect.set({original: '{count} Beer', locale: 'es', plural: 2}, '{count} pivi');
dialect.set({original: '{count} Beer', locale: 'es', plural: 3}, '{count} piva'); dialect.set({original: '{count} Beer', locale: 'es', plural: 3}, '{count} piva');


dialect.approve({original: '{count} Beer', locale: 'es', plural: 1}, true);
dialect.approve({original: '{count} Beer', locale: 'es', plural: 2}, true);
dialect.approve({original: '{count} Beer', locale: 'es', plural: 3}, true);

dialect.sync({interval: 3600}, function (err, foo) { dialect.sync({interval: 3600}, function (err, foo) {
[1, 2, 3].forEach(function (i) { [1, 2, 3].forEach(function (i) {
console.log(_(['{count} Beer', '{count} Beers', {count: i}])); console.log(_(['{count} Beer', '{count} Beers', {count: i}]));
Expand Down
24 changes: 24 additions & 0 deletions lib/dialect.js
Expand Up @@ -107,6 +107,30 @@ module.exports = function (options) {
} }
}; };


/**
* Approves or rejects a translation
*
* @param {Object} query {original, locale [, count] [, context]}
* @param {Boolean} aproved
* @param {Function} callback.
*
* @return dialect
*/
DIALECT.approve = function (query, approved, cb) {
if (!query || !query.original || !query.locale) {
throw Error("Original string and target locale are mandatory");
}

if (typeof approved === 'function' || approved === undefined) {
throw Error("Approved is mandatory");
}

// Database
DIALECT.store.approve(query, !!approved, cb);

return DIALECT;
};

/** /**
* Sets the translation to the store. * Sets the translation to the store.
* *
Expand Down
2 changes: 1 addition & 1 deletion lib/helpers/io.js
Expand Up @@ -56,7 +56,7 @@ module.exports.IO = function (DIALECT) {
throw Error("You must provide a locale"); throw Error("You must provide a locale");
} }


DIALECT.store.get({locale: locale}, function (err, data) { DIALECT.store.get({locale: locale, approved: true}, function (err, data) {
var dictionary = {}, var dictionary = {},
key = null, key = null,
i = null; i = null;
Expand Down
55 changes: 34 additions & 21 deletions lib/helpers/sqlizer.js
Expand Up @@ -5,21 +5,30 @@ module.exports = function (options) {
return o && Object.keys(o).length > 0; return o && Object.keys(o).length > 0;
}, },


_quotize = function (f) { _parse = function (f) {
var handleTypes = function (el) {
switch (typeof el) {
case 'string':
return "'" + el + "'";
case 'boolean':
return el ? 1 : 0;
default:
return el;
}
};

if (Array.isArray(f)) { if (Array.isArray(f)) {
return f.map(function (e) { return f.map(handleTypes);
return typeof e === 'string' ? "'" + e + "'": e;
});
} else { } else {
return typeof f === 'string' ? "'" + f + "'": f; return handleTypes(f);
} }
}, },


_parseOptions = function (query) { _parseOptions = function (query) {
var result = []; var result = [];


if (typeof query !== 'object' || query === null) { if (typeof query !== 'object' || query === null) {
return [(query === null ? ' IS ' : ' = ') + _quotize(query)]; return [(query === null ? ' IS ' : ' = ') + _parse(query)];
} else { } else {
Object.keys(query).forEach(function (key) { Object.keys(query).forEach(function (key) {
var value = query[key], ops; var value = query[key], ops;
Expand All @@ -31,7 +40,7 @@ module.exports = function (options) {
'$in': ' IN ', '$in': ' IN ',
'$nin': ' NOT IN ' '$nin': ' NOT IN '
}; };
result.push(ops[key] + '(' + _quotize(value).join(',') + ')'); result.push(ops[key] + '(' + _parse(value).join(',') + ')');
break; break;
case '$exists': case '$exists':
result.push(' IS ' + (value ? 'NOT ' : '') + 'null'); result.push(' IS ' + (value ? 'NOT ' : '') + 'null');
Expand All @@ -48,7 +57,7 @@ module.exports = function (options) {
'$lte': ' <= ', '$lte': ' <= ',
'$gte': ' >= ' '$gte': ' >= '
}; };
result.push(ops[key] + _quotize(value)); result.push(ops[key] + _parse(value));
break; break;
default: default:
throw Error('`' + key + '` not implemented yet'); throw Error('`' + key + '` not implemented yet');
Expand Down Expand Up @@ -171,7 +180,7 @@ module.exports = function (options) {
values.push(doc[i]); values.push(doc[i]);
} }


sql += ' VALUES (' + _quotize(values).join(', ') + ')'; sql += ' VALUES (' + _parse(values).join(', ') + ')';


return sql; return sql;
}; };
Expand All @@ -191,19 +200,23 @@ module.exports = function (options) {
updates = [], updates = [],


_parseUpdate = function (key, value) { _parseUpdate = function (key, value) {
var i = Object.keys(value)[0], var els = [];
v = value[i];

Object.keys(value).forEach(function (i) {
switch (key) { var v = value[i];
case '$set':
return i + ' = ' + _quotize(v); switch (key) {
case '$inc': case '$set':
if (v !== 0) { els.push(i + ' = ' + _parse(v));
return i + ' = ' + i + (v > 0 ? ' + ' : ' - ') + v; break;
} else { case '$inc':
return ''; if (v !== 0) {
els.push(i + ' = ' + i + (v > 0 ? ' + ' : ' - ') + v);
}
} }
} });

return els.join(', ');
}; };


SQLizer.sql = 'UPDATE ' + SQLizer.table + ' SET '; SQLizer.sql = 'UPDATE ' + SQLizer.table + ' SET ';
Expand Down
21 changes: 21 additions & 0 deletions lib/stores/mongodb.js
Expand Up @@ -110,6 +110,7 @@ module.exports = function (options) {


if (!data) { if (!data) {
doc.translation = translation; doc.translation = translation;
doc.approved = false;
STORE.collection.insert(doc, callback); STORE.collection.insert(doc, callback);
} else { } else {
callback(Error('This translation already exists'), null); callback(Error('This translation already exists'), null);
Expand All @@ -135,11 +136,31 @@ module.exports = function (options) {
callback = _default(callback); callback = _default(callback);
query = query || {}; query = query || {};


translation.approved = false;

STORE.collection.update(query, {'$set': translation}, {upsert: true}, callback); STORE.collection.update(query, {'$set': translation}, {upsert: true}, callback);


return STORE; return STORE;
}; };


/**
* Approve or rejects a translation
*
* @param {Object} query {original, locale}
* @param {String} translation
* @param {Function} callback
* @return store
*/
STORE.approve = function (query, approved, callback) {

callback = _default(callback);
query = query || {};

STORE.collection.update(query, {'$set': {approved: approved}}, {}, callback);

return STORE;
};

/** /**
* Destroy the translation * Destroy the translation
* *
Expand Down
24 changes: 23 additions & 1 deletion lib/stores/sqlite.js
Expand Up @@ -103,6 +103,7 @@ module.exports = function (options) {


if (!data || data.length === 0) { if (!data || data.length === 0) {
doc.translation = translation; doc.translation = translation;
doc.approved = false;
STORE.db.execute(sqlizer({table: _table}).insert(doc).sql, callback); STORE.db.execute(sqlizer({table: _table}).insert(doc).sql, callback);
} else { } else {
callback(Error('This translation already exists'), null); callback(Error('This translation already exists'), null);
Expand All @@ -127,7 +128,28 @@ module.exports = function (options) {


callback = _default(callback); callback = _default(callback);


STORE.db.execute(sqlizer({table: _table}).update(query, {'$set': {translation: translation}}).sql, callback); STORE.db.execute(sqlizer({table: _table}).update(
query,
{'$set': {translation: translation, approved: false}}).sql,
callback
);

return STORE;
};

/**
* Approve or rejects a translation
*
* @param {Object} query {original, locale}
* @param {Boolean} approved
* @param {Function} callback
* @return store
*/
STORE.approve = function (query, approved, callback) {

callback = _default(callback);

STORE.db.execute(sqlizer({table: _table}).update(query, {'$set': {approved: approved}}).sql, callback);


return STORE; return STORE;
}; };
Expand Down
64 changes: 63 additions & 1 deletion test/dialect.js
Expand Up @@ -47,7 +47,7 @@ testosterone
// config // config
//////////////////////////////////////////// ////////////////////////////////////////////


.add(' GIVEN a call to config \n' + .add('GIVEN a call to config \n' +
' WHEN just param `key` is given \n' + ' WHEN just param `key` is given \n' +
' THEN it should return `_option[key]`', function () { ' THEN it should return `_option[key]`', function () {
var options = {store: {mongodb: {}}, base_locale: 'en'}, var options = {store: {mongodb: {}}, base_locale: 'en'},
Expand Down Expand Up @@ -189,6 +189,68 @@ testosterone
d.set({original: 'hello', locale: 'foo'}, translation, callback); d.set({original: 'hello', locale: 'foo'}, translation, callback);
}) })


////////////////////////////////////////////
// approve
////////////////////////////////////////////

.add('GIVEN a call to `approve` \n' +
' WHEN `query.original` or `query.locale` are missing \n' +
' THEN an error should be thrown', function () {

var options = {locales: ['en', 'es'], store: {mongodb: {}}},
d = dialect(options);

assert.throws(function () {
d.approve(null, true);
});

assert.throws(function () {
d.approve({original: 'foo'}, true);
});

assert.throws(function () {
d.approve({locale: 'foo'}, true);
});

assert.throws(function () {
d.approve({original: 'foo', locale: 'foo'}, true);
});
})

.add(' WHEN `query.original` and `query.locale` are valid \n' +
' THEN should approve the translation to the `store`', function () {

var store = {mongodb: {}},
query = {original: 'hello', locale: 'foo'},
translation = 'foola',
options = {locales: ['en', 'es'], store: store},
callback = function (err, data) {
assert.equal(err, 'foo');
assert.equal(data, 'bar');
},
d = dialect(options);

gently.expect(d.store, 'approve', 2, function (q, u, cb) {
assert.deepEqual(q, query);
assert.deepEqual(u, true);
assert.deepEqual(cb, callback);
cb('foo', 'bar');
});

d.approve({original: 'hello', locale: 'foo'}, true, callback);
d.approve({original: 'hello', locale: 'foo'}, 'fleiba', callback);

gently.expect(d.store, 'approve', 2, function (q, u, cb) {
assert.deepEqual(q, query);
assert.deepEqual(u, false);
assert.deepEqual(cb, callback);
cb('foo', 'bar');
});

d.approve({original: 'hello', locale: 'foo'}, '', callback);
d.approve({original: 'hello', locale: 'foo'}, false, callback);
})

//////////////////////////////////////////// ////////////////////////////////////////////
// get // get
//////////////////////////////////////////// ////////////////////////////////////////////
Expand Down
4 changes: 2 additions & 2 deletions test/io.js
Expand Up @@ -51,7 +51,7 @@ testosterone
' THEN it should get the dictionary from the store \n' + ' THEN it should get the dictionary from the store \n' +
' AND cache it on memory', function () { ' AND cache it on memory', function () {
gently.expect(dialect.store, 'get', function (query, cb) { gently.expect(dialect.store, 'get', function (query, cb) {
assert.deepEqual(query, {locale: 'es'}); assert.deepEqual(query, {locale: 'es', approved: true});
assert.ok(query, cb); assert.ok(query, cb);
cb(null, [{original: 'hello', translation: 'hola'}]); cb(null, [{original: 'hello', translation: 'hola'}]);
}); });
Expand All @@ -65,7 +65,7 @@ testosterone


// with plural and contexts // with plural and contexts
gently.expect(dialect.store, 'get', function (query, cb) { gently.expect(dialect.store, 'get', function (query, cb) {
assert.deepEqual(query, {locale: 'es'}); assert.deepEqual(query, {locale: 'es', approved: true});
assert.ok(query, cb); assert.ok(query, cb);
cb(null, [{original: 'hello', translation: 'hola', context: 'salute', plural: 1}]); cb(null, [{original: 'hello', translation: 'hola', context: 'salute', plural: 1}]);
}); });
Expand Down
3 changes: 3 additions & 0 deletions test/sqlizer.js
Expand Up @@ -12,6 +12,8 @@ testosterone
.add('parseDoc', function () { .add('parseDoc', function () {
assert.equal(sqlizer.parseDoc({j: 4}), 'j = 4'); assert.equal(sqlizer.parseDoc({j: 4}), 'j = 4');
assert.equal(sqlizer.parseDoc({j: '4'}), "j = '4'"); assert.equal(sqlizer.parseDoc({j: '4'}), "j = '4'");
assert.equal(sqlizer.parseDoc({j: true}), "j = 1");
assert.equal(sqlizer.parseDoc({j: false}), "j = 0");
assert.equal(sqlizer.parseDoc({j: {'$exists': true}, f: null}), 'j IS NOT null AND f IS null'); assert.equal(sqlizer.parseDoc({j: {'$exists': true}, f: null}), 'j IS NOT null AND f IS null');
assert.equal(sqlizer.parseDoc({j: {'$exists': false}}), 'j IS null'); assert.equal(sqlizer.parseDoc({j: {'$exists': false}}), 'j IS null');
assert.equal(sqlizer.parseDoc({j: {'$ne': null}}), 'j IS NOT null'); assert.equal(sqlizer.parseDoc({j: {'$ne': null}}), 'j IS NOT null');
Expand Down Expand Up @@ -80,6 +82,7 @@ testosterone


.add('update', function () { .add('update', function () {
assert.equal(sqlizer.update({}, {'$set': {j: 4}}).sql, 'UPDATE test SET j = 4'); assert.equal(sqlizer.update({}, {'$set': {j: 4}}).sql, 'UPDATE test SET j = 4');
assert.equal(sqlizer.update({}, {'$set': {j: 4, i: false}}).sql, 'UPDATE test SET j = 4, i = 0');
assert.equal(sqlizer.update({}, {'$inc': {j: 2}}).sql, 'UPDATE test SET j = j + 2'); assert.equal(sqlizer.update({}, {'$inc': {j: 2}}).sql, 'UPDATE test SET j = j + 2');
assert.equal(sqlizer.update({d: 'foo'}, {'$set': {j: 'bar'}}).sql, "UPDATE test SET j = 'bar' WHERE d = 'foo'"); assert.equal(sqlizer.update({d: 'foo'}, {'$set': {j: 'bar'}}).sql, "UPDATE test SET j = 'bar' WHERE d = 'foo'");
assert.equal(sqlizer.update({d: 4, f: 3}, {'$inc': {j: 2}}).sql, 'UPDATE test SET j = j + 2 WHERE d = 4 AND f = 3'); assert.equal(sqlizer.update({d: 4, f: 3}, {'$inc': {j: 2}}).sql, 'UPDATE test SET j = j + 2 WHERE d = 4 AND f = 3');
Expand Down

0 comments on commit b2ae70a

Please sign in to comment.