Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

refactor done

  • Loading branch information...
commit 9f60ec8359bd42073f8bbc925eb7d2423323e2ac 1 parent b7765d4
@masylum authored
View
10 Makefile
@@ -1,6 +1,14 @@
NODE = node
-test: test_dialect
+test: test_dialect test_helpers test_stores
test_dialect:
@$(NODE) test/dialect.js
+
+test_helpers:
+ @$(NODE) test/io.js
+ @$(NODE) test/sqlizer.js
+
+test_stores:
+ @$(NODE) test/stores/mongodb.js
+ @$(NODE) test/stores/sqlite.js
View
53 Readme.md
@@ -8,15 +8,10 @@
`Wbmd"MML..JMML.`Moo9^Yo..JMML.`Mbmmd' YMbmd' `Mbmo
-Dialect is a painless nodejs module that deals with i18n, and L10n.
-
-This module may contain traces of bugs.
+Dialect is the painless nodejs module that deals with i18n.
## Install
-Currently dialect just supports MongoDB, so, you need to install it or fork
-your own storage solution .
-
npm install dialect
## Philosphy
@@ -28,31 +23,17 @@ your own storage solution .
## Example
- var dialect = require('dialect').dialect({
- path:'./dictionaries',
- base_locale: 'en',
- });
-
- // change our locale to Spanish
- dialect.config('locale', 'es');
-
- // Configs and open the storage connection
- require('dialect').store(
- {store: 'mongodb', database: 'translations'},
- function (error, store) {
- dialect.config('store', store);
- dialect.getTranslation('Hello World!');
- }
- );
-
-## How does it work?
+ var dialect = require('dialect'),
+ d = dialect.dialect({current_locale: 'es', store: 'mongodb'}, function (err, store) {
+ d.get('Hello World!'); // => Hola mundo
+ }).reCache();
-Dialect stores automatically all the strings pending to be translated.
-Once your translate the strings, they will be cached on a JSON file
-for every machine that needs the translations. The JSON will be loaded
-to memory, so you will acces the translations from memory.
+## API
-Dialect supports counts and contexts.
+* `config`:
+* `get`:
+* `set`:
+* `reCache`:
### Usings counts
@@ -62,7 +43,7 @@ You need to provide an array with the singular, plural and
the number.
[1, 2, 3].forEach(function (i) {
- dialect.getTranslation([
+ dialect.get([
'Hello World',
'Hello Worlds',
{count: i}
@@ -81,7 +62,7 @@ about a sentence. It helps the translator and it may generate
diferent translations depending on the context.
['female', 'male'].forEach(function (gender) {
- dialect.getTranslation([
+ dialect.get([
'My friends',
gender
]);
@@ -98,7 +79,7 @@ meanings although they can be used with interpolations.
[1, 2].forEach(function (count) {
['female', 'male'].forEach(function (gender) {
- dialect.getTranslation([
+ dialect.get([
'You have {count} friend called {name}',
'You have {count} friends called {name}',
{count: count, context: context, name: 'Anna'}
@@ -112,9 +93,9 @@ meanings although they can be used with interpolations.
### Store translations
-To store a new translation, use the method setTranslation.
+To store a new translation, use the method `set`.
- dialect.setTranslation(
+ dialect.set(
{original: 'I love gazpacho', locale: 'es'},
'Me encanta el gazpacho',
function () {
@@ -131,9 +112,9 @@ Try [express-dialect](http://www.github.com/masylum/express-dialect)
## Test
-Dialect is heavily tested using a mix of Vows and node asserts module.
+Dialect is heavily tested using [testosterone](http://www.github.com/masylum/testosterone)
- make test
+ make
## Benchmarks
View
34 benchmarks/app.js
@@ -1,17 +1,11 @@
-var fs = require('fs'),
- http = require('http'),
- dialect = require('./..').dialect({
- path: __dirname + '/data',
- base_locale: 'en_us',
- current_locale: 'es_es',
- locales: ['en_en', 'es_es']
- });
-
-dialect.config('locales').forEach(function (locale) {
- fs.writeFileSync(dialect.config('path') + locale + '.js', '{}', 'utf8');
-});
-
+// benchmark
require('./..').store({store: 'mongodb', database: 'bench'}, function (error, store) {
+ var dialect = require('./..').dialect({
+ base_locale: 'en_us',
+ current_locale: 'es_es',
+ locales: ['en_en', 'es_es'],
+ store: store
+ });
store.collection.remove({}, function (err, data) {
@@ -27,22 +21,22 @@ require('./..').store({store: 'mongodb', database: 'bench'}, function (error, st
output = original[0],
now = Date.now();
- dialect.config('store', store);
-
- dialect.getTranslation(original, function () {
- dialect.setTranslation({
+ dialect.get(original, function () {
+ dialect.set({
original: original[0],
locale: dialect.config('current_locale'),
context: 'female',
count: 'singular'
}, translation, function () {
console.log('running...');
- for(;i < tests; i += 1) {
- dialect.getTranslation(original);
+
+ for (;i < tests; i += 1) {
+ dialect.get(original);
}
+
var time = Date.now() - now;
console.log(time + 'ms');
- console.log(time/tests + 'ms per request');
+ console.log(time / tests + 'ms per request');
});
});
});
View
1  benchmarks/data/en_en.js
@@ -1 +0,0 @@
-{"You have {count} friend called {name}":{"translation":null,"count":"singular","context":null},"You have {count} friends called {name}":{"translation":null,"count":"plural","context":null}}
View
1  benchmarks/data/es_es.js
@@ -1 +0,0 @@
-{"You have {count} friend called {name}":{"translation":"Tienes {count} amiga llamada {name}"},"You have {count} friends called {name}":{"translation":null,"count":"plural","context":null}}
View
1  index.js
@@ -1,2 +1 @@
module.exports.dialect = require('./lib/dialect');
-module.exports.store = require('./lib/store');
View
12 lib/dialect.js
@@ -2,15 +2,11 @@ if (global.GENTLY) {
require = global.GENTLY.hijack(require);
}
-var fs = require('fs'),
- funk = require('funk');
-
module.exports = function (options) {
var DIALECT = {},
_io = require('./helpers/io').IO(DIALECT),
_options = options || {},
- _current_language = null,
_parse = function (str, params) {
var matches = /(\{(.*?)\})+/g.exec(str);
@@ -63,8 +59,7 @@ module.exports = function (options) {
current_dictionary = DIALECT.dictionaries[_options.current_locale],
params = Array.isArray(original) ? original.pop() : {},
index = Array.isArray(original) ? original[0] : original,
- pluralize = require('./helpers/plurals')(_options.base_locale),
- i = 0;
+ pluralize = require('./helpers/plurals')(_options.base_locale);
if ((typeof original !== 'string' && !Array.isArray(original)) || original.length === 0) {
throw Error("Original is not valid");
@@ -87,8 +82,7 @@ module.exports = function (options) {
if (typeof translation !== 'string') {
_options.store.add(
{original: index, locale: _options.current_locale},
- translation,
- function () { }
+ translation
);
return _parse(Array.isArray(original) ? original[pluralize(params.count)] : original, params);
} else {
@@ -117,7 +111,7 @@ module.exports = function (options) {
}
// Database
- _options.store.set(query, translation, cb);
+ _options.store.set(query, {translation: translation}, cb);
return DIALECT;
};
View
22 lib/helpers/io.js
@@ -2,9 +2,7 @@ if (global.GENTLY) {
require = global.GENTLY.hijack(require);
}
-var fs = require('fs'),
- plurals = require('./plurals'),
- funk = require('funk');
+var plurals = require('./plurals');
module.exports.IO = function (DIALECT) {
var IO = {},
@@ -28,7 +26,11 @@ module.exports.IO = function (DIALECT) {
IO.getKeyFromQuery = function (query) {
var key = query.original;
- if (query.count) {
+ if (!key) {
+ throw Error("Original must be provided");
+ }
+
+ if (query.count !== undefined) {
key += '|p:' + _getPluralForm(query.count);
}
@@ -43,13 +45,17 @@ module.exports.IO = function (DIALECT) {
* Caches the dictionaries from the Store to the JSON files
*
* @param {String} locale
- * Target dictionary we want to cache, if not set use config('locales')
+ * Target dictionary we want to cache
* @param {Function} cb
* Callback when its done with async
* @returns IO
*/
IO.cacheDictionary = function (locale, cb) {
+ if (!locale) {
+ throw Error("You must provide a locale");
+ }
+
DIALECT.config('store').get({locale: locale}, function (err, data) {
var dictionary = {},
key = null,
@@ -61,16 +67,16 @@ module.exports.IO = function (DIALECT) {
for (i in data) {
key = IO.getKeyFromQuery({
original: data[i].original,
- count: data[i].count,
+ count: data[i].plural,
context: data[i].context
});
- dictionary[data[key].original] = data[i].translation;
+ dictionary[key] = data[i].translation;
}
// loads the dictionary to memory
DIALECT.dictionaries[locale] = dictionary;
- cb(err, data);
+ cb(err, dictionary);
}
});
View
241 lib/helpers/sqlizer.js
@@ -0,0 +1,241 @@
+module.exports = function (options) {
+ var SQLizer = {},
+
+ _objectNotEmpty = function (o) {
+ return o && Object.keys(o).length > 0;
+ },
+
+ _quotize = function (f) {
+ if (Array.isArray(f)) {
+ return f.map(function (e) {
+ return typeof e === 'string' ? "'" + e + "'": e;
+ });
+ } else {
+ return typeof f === 'string' ? "'" + f + "'": f;
+ }
+ },
+
+ _parseOptions = function (query) {
+ var result = [];
+
+ if (typeof query !== 'object' || query === null) {
+ return [(query === null ? ' IS ' : ' = ') + _quotize(query)];
+ } else {
+ Object.keys(query).forEach(function (key) {
+ var value = query[key], ops;
+
+ switch (key) {
+ case '$in':
+ case '$nin':
+ ops = {
+ '$in': ' IN ',
+ '$nin': ' NOT IN '
+ };
+ result.push(ops[key] + '(' + _quotize(value).join(',') + ')');
+ break;
+ case '$exists':
+ result.push(' IS ' + (value ? 'NOT ' : '') + 'null');
+ break;
+ case '$ne':
+ case '$lt':
+ case '$gt':
+ case '$lte':
+ case '$gte':
+ ops = {
+ '$ne': value === null ? ' IS NOT ' : ' <> ',
+ '$lt': ' < ',
+ '$gt': ' > ',
+ '$lte': ' <= ',
+ '$gte': ' >= '
+ };
+ result.push(ops[key] + _quotize(value));
+ break;
+ default:
+ throw Error('`' + key + '` not implemented yet');
+ }
+ });
+
+ return result;
+ }
+ };
+
+ if (!options.table) {
+ throw Error('You must specify a table');
+ }
+
+ SQLizer.table = options.table;
+
+ SQLizer.parseDoc = function (query) {
+ var result = [];
+
+ Object.keys(query).forEach(function (key) {
+ var value = query[key];
+
+ // ['> 4', '< 8', '= 4']
+ if (key === '$or') {
+ (function () {
+ var or = [];
+ value.forEach(function (val) {
+ or.push(SQLizer.parseDoc(val));
+ });
+ result.push('(' + or.join(' OR ') + ')');
+ }());
+ } else {
+ _parseOptions(value).forEach(function (val) {
+ result.push(key + val);
+ });
+ }
+ });
+
+ return result.join(' AND ');
+ };
+
+ SQLizer.find = function (query, fields) {
+ var i,
+ selects = [];
+
+ SQLizer.sql = 'SELECT ';
+
+ if (!_objectNotEmpty(fields)) {
+ SQLizer.sql += '*';
+ } else {
+ for (i in fields) {
+ if (fields[i] === 1) {
+ selects.push(i);
+ }
+ }
+ SQLizer.sql += selects.length ? (selects.join(', ')) : '*';
+ }
+
+ SQLizer.sql += ' FROM ' + SQLizer.table;
+
+ if (_objectNotEmpty(query)) {
+ SQLizer.sql += ' WHERE ' + SQLizer.parseDoc(query);
+ }
+
+ return SQLizer;
+ };
+
+ SQLizer.sort = function (doc) {
+ var i,
+ sorts = [];
+
+ if (_objectNotEmpty(doc)) {
+ SQLizer.sql += ' ORDER BY ';
+
+ for (i in doc) {
+ sorts.push(i + ' ' + (doc[i] > 0 ? 'ASC' : 'DESC'));
+ }
+
+ SQLizer.sql += sorts.join(', ');
+ }
+
+ return SQLizer;
+ };
+
+ SQLizer.limit = function (num) {
+ if (/(LIMIT) \d( OFFSET \d)/.test(SQLizer.sql)) {
+ SQLizer.sql = SQLizer.sql.replace(/(LIMIT) \d( OFFSET \d)/, '$1 ' + num + '$2');
+ } else {
+ SQLizer.sql += ' LIMIT ' + num + ' OFFSET 0';
+ }
+
+ return SQLizer;
+ };
+
+ SQLizer.skip = function (num) {
+ if (/(LIMIT \d) (OFFSET )\d/.test(SQLizer.sql)) {
+ SQLizer.sql = SQLizer.sql.replace(/(LIMIT \d) (OFFSET )\d/, '$1 $2' + num);
+ } else {
+ SQLizer.sql += ' LIMIT 0 OFFSET ' + num;
+ }
+
+ return SQLizer;
+ };
+
+ SQLizer.count = function (num) {
+ if (/SELECT (.*) FROM/.test(SQLizer.sql)) {
+ SQLizer.sql = SQLizer.sql.replace(/(SELECT) .* (FROM)/, '$1 COUNT(*) $2');
+ }
+
+ return SQLizer;
+ };
+
+ SQLizer.insert = function (docs) {
+ var parse = function (doc) {
+ var i,
+ values = [],
+ sql = 'INSERT INTO ' + SQLizer.table + ' (' + Object.keys(doc).join(', ') + ')';
+
+ for (i in doc) {
+ values.push(doc[i]);
+ }
+
+ sql += ' VALUES (' + _quotize(values).join(', ') + ')';
+
+ return sql;
+ };
+
+ if (Array.isArray(docs)) {
+ SQLizer.sql = docs.map(parse).join('; ');
+ } else {
+ SQLizer.sql = parse(docs);
+ }
+
+ return SQLizer;
+ };
+
+ SQLizer.update = function (where, update) {
+ var i,
+ value = null,
+ updates = [],
+
+ _parseUpdate = function (key, value) {
+ var i = Object.keys(value)[0],
+ v = value[i];
+
+ switch (key) {
+ case '$set':
+ return i + ' = ' + _quotize(v);
+ case '$inc':
+ if (v !== 0) {
+ return i + ' = ' + i + (v > 0 ? ' + ' : ' - ') + v;
+ } else {
+ return '';
+ }
+ }
+ };
+
+ SQLizer.sql = 'UPDATE ' + SQLizer.table + ' SET ';
+
+ if (_objectNotEmpty(update)) {
+ for (i in update) {
+ value = update[i];
+ if (typeof value === 'object') {
+ updates.push(_parseUpdate(i, value));
+ }
+ }
+ }
+
+ SQLizer.sql += updates.join(', ');
+
+ if (_objectNotEmpty(where)) {
+ SQLizer.sql += ' WHERE ' + SQLizer.parseDoc(where);
+ }
+
+ return SQLizer;
+ };
+
+ SQLizer.remove = function (where) {
+
+ SQLizer.sql = 'DELETE FROM ' + SQLizer.table;
+
+ if (_objectNotEmpty(where)) {
+ SQLizer.sql += ' WHERE ' + SQLizer.parseDoc(where);
+ }
+
+ return SQLizer;
+ };
+
+ return SQLizer;
+};
View
29 lib/store.js
@@ -1,26 +1,5 @@
-/**
- * Initialize Store with the given `options`.
- *
- * @param {Object} options
- * @api public
- */
-
-module.exports = function (options, callback) {
-
- if (!options || !options.store) {
- throw new Error("Please select which store you want to use with dialect");
- }
-
- options = options || {};
-
- try {
- require('./stores/' + options.store)(options, callback);
- } catch (exc) {
- if (exc.message === "Cannot find module './stores/" + options.store + "'") {
- throw new Error("This store is not available");
- } else {
- throw exc;
- }
- }
-};
+var path = './stores/';
+['mongodb', 'sqlite'/*, 'json', 'redis'*/].forEach(function (store) {
+ module.exports[store] = require(path + store);
+});
View
162 lib/stores/json.js
@@ -0,0 +1,162 @@
+var fs = require('fs');
+
+/**
+ * Initialize Store with the given `options`.
+ *
+ * @param {Object} options [path]
+ * @return store
+ */
+
+module.exports = function (options) {
+
+ var STORE = {},
+
+ _default = function (callback) {
+ callback = callback || function () { };
+ };
+
+ options = options || {};
+
+ /**
+ * Connects to the Store
+ *
+ * @param {Function} callback
+ * @return store
+ */
+ STORE.connect = function (callback) {
+
+ _default(callback);
+
+ fs.readdir(options.path, function (err, files) {
+ files.forEach(function (file) {
+ fs.readFile(options.path + '/' + file, function (err, data) {
+ console.log(data);
+ });
+ });
+ });
+
+ STORE.db.open(function (err, db) {
+ if (err) {
+ callback(err, null);
+ } else {
+ db.collection(options.collection || 'translations', function (err, collection) {
+ STORE.collection = collection;
+ callback(err, collection);
+ });
+ }
+ });
+
+ return STORE;
+ };
+
+ /**
+ * Attempt to fetch a translation
+ *
+ * @param {Object} query
+ * @param {Function} callback
+ * @return store
+ */
+ STORE.get = function (query, callback) {
+
+ _default(callback);
+ query = query || {};
+
+ STORE.collection.find(query, function (err, cursor) {
+ if (err) {
+ callback(err, null);
+ } else {
+ cursor.toArray(callback);
+ }
+ });
+
+ return STORE;
+ };
+
+ /**
+ * Add a translation
+ *
+ * @param {Object} doc {original, locale, [, plural] [, context]}
+ * @param {String} translation
+ * @param {Function} callback
+ * @return store
+ */
+ STORE.add = function (doc, translation, callback) {
+
+ _default(callback);
+
+ STORE.collection.findOne(doc, function (err, data) {
+ if (err) {
+ callback(err);
+ } else {
+
+ if (!data) {
+ doc.translation = translation;
+ STORE.collection.insert(doc, callback);
+ } else {
+ callback(Error('This translation already exists'), null);
+ }
+
+ }
+ });
+
+ return STORE;
+ };
+
+ /**
+ * Set a translation
+ * If the translation is new, set it to null
+ *
+ * @param {Object} query {original, locale}
+ * @param {String} translation
+ * @param {Function} callback
+ * @return store
+ */
+ STORE.set = function (query, translation, callback) {
+
+ _default(callback);
+ query = query || {};
+
+ STORE.collection.update(query, {'$set': translation}, callback);
+
+ return STORE;
+ };
+
+ /**
+ * Destroy the translation
+ *
+ * @param {Object} query {original, locale}
+ * @param {Function} callback
+ * @return store
+ */
+
+ STORE.destroy = function (query, callback) {
+
+ _default(callback);
+ query = query || {};
+
+ STORE.collection.remove(query, callback);
+
+ return STORE;
+ };
+
+ /**
+ * Fetch number of translations.
+ *
+ * @param {Object} query {locale, translation...} [optional]
+ * @param {Function} callback
+ * @return store
+ */
+
+ STORE.count = function (query, callback) {
+
+ _default(callback);
+ query = query || {};
+
+ STORE.collection.count(query, callback);
+
+ return STORE;
+ };
+
+ return STORE;
+};
+
View
207 lib/stores/mongodb.js
@@ -1,102 +1,108 @@
-/**
- * Module dependencies.
- */
-
-var DB = require('mongodb/lib/mongodb/db').Db,
- Server = require('mongodb/lib/mongodb/connection').Server;
+var DB = require('mongodb/db').Db,
+ Server = require('mongodb/connection').Server;
/**
- * Initialize MongoStore with the given `options`.
+ * Initialize Store with the given `options`.
*
- * @param {Object} options
- * @api public
+ * @param {Object} options [database, host, port, collection]
+ * @return store
*/
-module.exports = function (options, callback) {
+module.exports = function (options) {
+
+ var STORE = {},
+
+ _default = function (callback) {
+ callback = callback || function () { };
+ };
+
+ options = options || {};
+
+ Object.defineProperty(STORE, 'db', {value : new DB(
+ options.database || 'dialect',
+ new Server(
+ options.host || '127.0.0.1',
+ options.port || 27017,
+ {}
+ ),
+ {}
+ )});
+
+ /**
+ * Connects to the Store
+ *
+ * @param {Function} callback
+ * @return store
+ */
+ STORE.connect = function (callback) {
+
+ _default(callback);
- if (!options.database) {
- throw new Error("Please select your database");
- }
+ STORE.db.open(function (err, db) {
+ if (err) {
+ callback(err, null);
+ } else {
+ db.collection(options.collection || 'translations', function (err, collection) {
+ STORE.collection = collection;
+ callback(err, collection);
+ });
+ }
+ });
- var mongoStore = options,
- connection = new DB(options.database, new Server(options.host || '127.0.0.1', options.port || 27017, {}), {});
+ return STORE;
+ };
/**
* Attempt to fetch a translation
*
- * @param {Object} query {original, locale}
+ * @param {Object} query
* @param {Function} callback
- * @api public
+ * @return store
*/
- mongoStore.get = function (query, callback) {
- this.collection.find(query, function (err, cursor) {
- cursor.toArray(callback);
+ STORE.get = function (query, callback) {
+
+ _default(callback);
+ query = query || {};
+
+ STORE.collection.find(query, function (err, cursor) {
+ if (err) {
+ callback(err, null);
+ } else {
+ cursor.toArray(callback);
+ }
});
+
+ return STORE;
};
/**
* Add a translation
*
- * @param {Object} query {original, locale}
+ * @param {Object} doc {original, locale, [, plural] [, context]}
* @param {String} translation
* @param {Function} callback
- * @api public
+ * @return store
*/
- mongoStore.add = function (query, translation, callback) {
- var self = this, doc, params;
- try {
-
- if (Array.isArray(query.original)) {
-
- // COUNT
- if (query.original.length === 3) {
- doc = [];
- doc[0] = {original: query.original[0], locale: query.locale, translation: translation, count: 'singular'};
- doc[1] = {original: query.original[1], locale: query.locale, translation: translation, count: 'plural'};
- params = query.original[2];
- } else if (query.original.length === 2) {
- doc = {original: query.original, locale: query.locale, translation: translation};
- params = query.original[1];
- }
+ STORE.add = function (doc, translation, callback) {
- // CONTEXT
- if (params.context) {
- (function () {
- var setContext = function (d) {
- d.context = null;
- };
- if (Array.isArray(doc)) {
- doc.forEach(setContext);
- } else {
- setContext(doc);
- }
- }());
- }
+ _default(callback);
+
+ STORE.collection.findOne(doc, function (err, data) {
+ if (err) {
+ callback(err);
} else {
- doc = {original: query.original, locale: query.locale, translation: translation};
- }
- this.collection.findOne({original: query.original, locale: query.locale}, function (err, data) {
if (!data) {
- self.collection.insert(
- doc,
- function (err, data) {
- if (callback) {
- callback(err, mongoStore);
- }
- }
- );
+ doc.translation = translation;
+ STORE.collection.insert(doc, callback);
} else {
- if (callback) {
- callback(null, mongoStore);
- }
+ callback(Error('This translation already exists'), null);
}
- });
- } catch (exc) {
- if (exc) {
- callback(exc, null);
+
}
- }
+ });
+
+ return STORE;
};
/**
@@ -106,58 +112,53 @@ module.exports = function (options, callback) {
* @param {Object} query {original, locale}
* @param {String} translation
* @param {Function} callback
- * @api public
+ * @return store
*/
- mongoStore.set = function (query, update, callback) {
- try {
- this.collection.update(
- query,
- {'$set': update},
- function (err, data) {
- if (callback) {
- callback(err, mongoStore);
- }
- }
- );
- } catch (exc) {
- if (exc) {
- callback(exc, null);
- }
- }
+ STORE.set = function (query, translation, callback) {
+
+ _default(callback);
+ query = query || {};
+
+ STORE.collection.update(query, {'$set': translation}, callback);
+
+ return STORE;
};
/**
* Destroy the translation
*
* @param {Object} query {original, locale}
- * @param {String} locale
* @param {Function} callback
- * @api public
+ * @return store
*/
- mongoStore.destroy = function (query, callback) {
- this.collection.remove(query, callback);
+ STORE.destroy = function (query, callback) {
+
+ _default(callback);
+ query = query || {};
+
+ STORE.collection.remove(query, callback);
+
+ return STORE;
};
/**
* Fetch number of translations.
*
* @param {Object} query {locale, translation...} [optional]
- * @param {Function} fn
- * @api public
+ * @param {Function} callback
+ * @return store
*/
- mongoStore.count = function (query, callback) {
+ STORE.count = function (query, callback) {
+
+ _default(callback);
query = query || {};
- this.collection.count(query, callback);
+
+ STORE.collection.count(query, callback);
+
+ return STORE;
};
- connection.open(function (err, db) {
- db.collection(options.collection || 'translations', function (err, collection) {
- mongoStore.collection = collection;
- if (callback) {
- callback(null, mongoStore);
- }
- });
- });
+ return STORE;
};
View
307 lib/stores/sqlite.js
@@ -1,153 +1,103 @@
-/**
- * Module dependencies.
- */
-
-var sqlite = require('sqlite');
+var sqlite = require('sqlite'),
+ sqlizer = require('../helpers/sqlizer');
/**
- * Initialize sqliteStore with the given `options`.
+ * Initialize Store with the given `options`.
*
- * @param {Object} options
- * @api public
+ * @param {Object} options [database, table]
+ * @return store
*/
-module.exports = function (options, callback) {
-
- var client = new sqlite.Database(),
- sqliteStore = {},
- table = options.table || 'dialect';
-
- function buildQuery(query, prefix) {
- var values = {},
- keys = Object.keys(query),
- placeholders = keys.map(function (name) {
- var key = (prefix || '$') + name,
- op = query[name] === null ? ' is ' : ' = ',
- k,
- value = query[name];
-
- if (value !== null && typeof value === 'object') {
- Object.keys(value).forEach(function (name) {
- var ops = {
- '$ne': value[name] === null ? ' is not ' : ' <> ',
- '$lt': ' < ',
- '$gt': ' > ',
- '$lte': ' <= ',
- '$gte': ' >= '
- };
- value = value[name];
- op = ops[name] || ' = ';
- });
- values[key] = query[name];
+module.exports = function (options) {
+
+ options = options || {};
+
+ var STORE = {},
+
+ _table = options.table || 'dialect',
+
+ _default = function (callback) {
+ callback = callback || function () { };
+ };
+
+ Object.defineProperty(STORE, 'db', {value : new sqlite.Database()});
+
+ /**
+ * Connects to the Store
+ *
+ * @param {Function} callback
+ * @return store
+ */
+ STORE.connect = function (callback) {
+
+ _default(callback);
+
+ STORE.db.open(options.database || 'dialect.db', function (err) {
+ if (err) {
+ callback(err, null);
+ } else {
+ STORE.db.execute(
+ 'CREATE TABLE IF NOT EXISTS ' + _table +
+ ' (original TEXT, locale TEXT, translation TEXT,' +
+ ' plural NUMBER, context TEXT, PRIMARY KEY(original, locale, plural, context))',
+ function (err, data) {
+ callback(err, data);
}
+ );
+ }
+ });
- values[key] = value;
- return name + op + key;
- });
- return {values: values, keys: keys, placeholders: placeholders};
- }
+ return STORE;
+ };
/**
* Attempt to fetch a translation
*
- * @param {Object} query {original, locale}
+ * @param {Object} query
* @param {Function} callback
- * @api public
+ * @return store
*/
- sqliteStore.get = function (query, callback) {
- var where = buildQuery(query);
+ STORE.get = function (query, callback) {
+
+ _default(callback);
- client.execute(
- 'SELECT * FROM ' + table + ' WHERE ' + where.placeholders.join(' AND '),
- where.values,
+ STORE.db.execute(
+ sqlizer({table: _table}).find(query).sql,
function (error, rows) {
- if (callback) {
- callback(error, error ? [] : rows);
- }
+ callback(error, error ? [] : rows);
}
);
+
+ return STORE;
};
/**
* Add a translation
*
- * @param {Object} query {original, locale}
+ * @param {Object} doc {original, locale, [, plural] [, context]}
* @param {String} translation
* @param {Function} callback
- * @api public
+ * @return store
*/
- sqliteStore.add = function (query, translation, callback) {
- var self = this, doc, params, where;
-
- if (Array.isArray(query.original)) {
- // COUNT
- if (query.original.length === 3) {
- doc = [];
- doc[0] = {original: query.original[0], locale: query.locale, translation: translation, count: 'singular', context: query.context || null};
- doc[1] = {original: query.original[1], locale: query.locale, translation: translation, count: 'plural', context: query.context || null};
- } else if (query.original.length === 2) {
- doc = {original: query.original, locale: query.locale, translation: translation, count: '', context: query.context || null};
- }
- else {
- doc = {original: query.original, locale: query.locale, translation: translation, count: '', context: query.context || null};
- }
- } else {
- doc = {original: query.original, locale: query.locale, translation: translation};
- }
-
- function insertValues(doc, callback) {
- var build = buildQuery(doc);
-
- client.execute(
- 'INSERT INTO ' + table + ' (' + build.keys.join(', ') + ') ' +
- ' VALUES (' + Object.keys(build.values).join(', ') + ')',
- build.values,
- function (err, data) {
- if (callback) {
- callback(err);
- }
- }
- );
- }
-
- where = buildQuery({original: query.original, locale: query.locale});
-
- client.execute(
- 'SELECT ' + where.keys.join(', ') + ' FROM ' + table +
- ' WHERE ' + where.placeholders.join(' AND ') + ' LIMIT 1',
- where.values,
- function (error, data) {
- if (!error && (!data || data.length === 0)) {
- var insert, count = 1, err,
- fireCallback = function (error) {
- count -= 1;
- if (error) {
- err = error;
- }
- if (count <= 0) {
- if (callback) {
- callback(err, sqliteStore);
- }
- }
- };
-
- if (Array.isArray(doc)) {
- count = doc.length;
- doc.forEach(function (doc) {
- insertValues(doc, fireCallback);
- }, doc);
- }
- else {
- insertValues(doc, fireCallback);
- }
+ STORE.add = function (doc, translation, callback) {
+ _default(callback);
+
+ STORE.get(doc, function (err, data) {
+ if (err) {
+ callback(err, null);
+ } else {
+
+ if (!data || data.length === 0) {
+ doc.translation = translation;
+ STORE.db.execute(sqlizer({table: _table}).insert(doc).sql, callback);
+ } else {
+ callback(Error('This translation already exists'), null);
}
- else {
- if (callback) {
- callback(error, sqliteStore);
- }
- }
+
}
- );
+ });
+
+ return STORE;
};
/**
@@ -157,104 +107,51 @@ module.exports = function (options, callback) {
* @param {Object} query {original, locale}
* @param {String} translation
* @param {Function} callback
- * @api public
+ * @return store
*/
- sqliteStore.set = function (query, translation, callback) {
- var where = buildQuery(query, '$w_'),
- update = buildQuery(
- typeof translation === 'string' ? {translation : translation} : translation,
- '$u_'
- ),
- values = update.values;
-
- Object.keys(where.values).forEach(function (name) {
- values[name] = where.values[name];
- });
- client.execute(
- 'UPDATE ' + table + ' SET ' + update.placeholders.join(', ') +
- ' WHERE ' + where.placeholders.join(' AND '),
- values,
- function (error, data) {
- if (callback) {
- callback(error, sqliteStore);
- }
- }
- );
- };
+ STORE.set = function (query, translation, callback) {
- /**
- * Destroy the session associated with the given `hash`.
- *
- * @param {String} query
- * @param {String} callback
- * @return {Object} client
- * @api public
- */
- sqliteStore.destroy = function (query, callback) {
- var where = buildQuery(query);
+ _default(callback);
- client.execute(
- 'DELETE FROM ' + table + ' WHERE ' + where.placeholders.join(' AND '),
- where.values,
- callback
- );
+ STORE.db.execute(sqlizer({table: _table}).update(query, {'$set': {translation: translation}}).sql, callback);
+
+ return STORE;
};
/**
- * Fetch number of translations.
+ * Destroy the translation
*
+ * @param {Object} query {original, locale}
* @param {Function} callback
- * @api public
+ * @return store
*/
- sqliteStore.length = function (callback) {
- client.execute(
- 'SELECT COUNT(*) AS count FROM ' + table,
- function (err, data) {
- callback(err, !err && data && data.length ? data[0].count : null);
- }
- );
+
+ STORE.destroy = function (query, callback) {
+
+ _default(callback);
+
+ STORE.db.execute(sqlizer({table: _table}).remove(query).sql, callback);
+
+ return STORE;
};
/**
- * Fetch number of translations that match the query.
+ * Fetch number of translations.
*
- * @param {Object} query
+ * @param {Object} query {locale, translation...} [optional]
* @param {Function} callback
- * @api public
+ * @return store
*/
- sqliteStore.count = function (query, callback) {
- var where = buildQuery(query);
-
- client.execute(
- 'SELECT COUNT(*) AS count FROM ' + table + ' WHERE ' + where.placeholders.join(' AND '),
- where.values,
- function (err, data) {
- callback(err, !err && data && data.length ? data[0].count : null);
- }
- );
- };
+ STORE.count = function (query, callback) {
- /**
- * Get the client
- *
- * @param
- * @api public
- */
- sqliteStore.getClient = function (callback) {
- return client;
+ _default(callback);
+
+ STORE.db.execute(sqlizer({table: _table}).find(query).count().sql, function (err, data) {
+ callback(err, !err && data && data.length ? data[0].count : 0);
+ });
+
+ return STORE;
};
- client.open(options.database, function (error) {
- if (error) {
- throw error;
- }
- client.execute(
- 'CREATE TABLE IF NOT EXISTS ' + table + ' (original TEXT, locale TEXT, translation TEXT, count TEXT, context TEXT, PRIMARY KEY(original, locale, count))',
- function (err, data) {
- if (callback) {
- callback(null, sqliteStore);
- }
- }
- );
- });
+ return STORE;
};
View
5 package.json
@@ -1,10 +1,9 @@
{
"name": "dialect",
"description": "I18n and L10n for nodejs",
- "version": "0.0.5",
+ "version": "0.9.0",
"author": "Pau Ramon <masylum@gmail.com>",
- "keywords": ["i18n", "l10n"],
- "dependencies": { "mongodb": ">=0.7.9", "funk": ">=0.0.2", "vows": ">=0.5.2", "eyes": ">=0.1.6", "lingo": ">=0.0.3"},
+ "dependencies": {"mongodb": ">=0.7.9", "funk": ">=0.0.2"},
"repository" : {"type": "git" , "url": "http://github.com/masylum/dialect.git" },
"main": "./",
"engines": { "node": ">= 0.2.0" }
View
4 test/data/default/en.js
@@ -1,4 +0,0 @@
-{
- "Chega": "Enough",
- "de": "of"
-}
View
4 test/data/default/es.js
@@ -1,4 +0,0 @@
-{
- "Chega": "Basta",
- "de": "de"
-}
View
4 test/data/default/pt.js
@@ -1,4 +0,0 @@
-{
- "Chega": "Chega",
- "de": "de"
-}
View
1  test/data/en.js
@@ -1 +0,0 @@
-{"Chega":"Enough","de":"of","Os desafinados tamb\u00e9m t\u00eam um cora\u00e7\u00e3o":null}
View
1  test/data/es.js
@@ -1 +0,0 @@
-{"Chega":"Basta","de":"de","Os desafinados tamb\u00e9m t\u00eam um cora\u00e7\u00e3o":null}
View
1  test/data/pt.js
@@ -1 +0,0 @@
-{"Chega":"Chega","de":"de","Os desafinados tamb\u00e9m t\u00eam um cora\u00e7\u00e3o":"Os desafinados tamb\u00e9m t\u00eam um cora\u00e7\u00e3o"}
View
5 test/dialect.js
@@ -1,7 +1,6 @@
var testosterone = require('testosterone')({title: 'Dialect core'}),
assert = testosterone.assert,
gently = global.GENTLY = new (require('gently')),
- fs = require('fs'),
dialect = require('./..').dialect,
_stubIO = function () {
@@ -156,7 +155,7 @@ testosterone
gently.expect(store, 'set', function (q, u, cb) {
assert.deepEqual(q, query);
- assert.deepEqual(u, translation);
+ assert.deepEqual(u, {translation: translation});
assert.deepEqual(cb, callback);
cb('foo', 'bar');
});
@@ -253,8 +252,6 @@ testosterone
gently.expect(store, 'add', function (q, u, cb) {
assert.deepEqual(q, {original: original, locale: 'es'});
assert.deepEqual(u, undefined);
- assert.ok(cb);
- cb();
});
};
View
88 test/io.js
@@ -0,0 +1,88 @@
+var testosterone = require('testosterone')({title: 'IO helper lib'}),
+ assert = testosterone.assert,
+ gently = global.GENTLY = new (require('gently')),
+ store = {},
+ dialect = require('./..').dialect({locales: ['en', 'es'], current_locale: 'es', store: store}),
+ io = require('./../lib/helpers/io').IO(dialect);
+
+testosterone
+
+ ////////////////////////////////////////////
+ // getKeyFromQuery
+ ////////////////////////////////////////////
+
+ .add(' GIVEN a call to `getKeyFromQuery` \n' +
+ ' WHEN no `query.original` is provided \n' +
+ ' THEN it should throw an error', function (spec) {
+ spec(function () {
+ assert.throws(function () {
+ io.getKeyFromQuery();
+ });
+ assert.throws(function () {
+ io.getKeyFromQuery({foo: 'bar'});
+ });
+ })();
+ })
+
+ .add(' WHEN a query containing `original` and optional `count` and `context` \n' +
+ ' THEN it should return a valid key', function (spec) {
+ spec(function () {
+ assert.equal(io.getKeyFromQuery({original: 'foo'}), 'foo');
+ assert.equal(io.getKeyFromQuery({original: 'foo', count: 0}), 'foo|p:2');
+ assert.equal(io.getKeyFromQuery({original: 'foo', count: 1}), 'foo|p:1');
+ assert.equal(io.getKeyFromQuery({original: 'foo', context: 'bar'}), 'foo|c:bar');
+ assert.equal(io.getKeyFromQuery({original: 'foo', count: 0, context: 'bar'}), 'foo|p:2|c:bar');
+
+ dialect.config('current_locale', 'fr');
+ assert.equal(io.getKeyFromQuery({original: 'foo', count: 0}), 'foo|p:1');
+ })();
+ })
+
+ ////////////////////////////////////////////
+ // cacheDictionary
+ ////////////////////////////////////////////
+
+ .add(' GIVEN a call to `cacheDictionary` \n' +
+ ' WHEN no `locale` is provided \n' +
+ ' THEN it should throw an error', function (spec) {
+ spec(function () {
+ assert.throws(function () {
+ io.cacheDictionary();
+ });
+ })();
+ })
+
+ .add(' WHEN `locale` is provided \n' +
+ ' THEN it should get the dictionary from the store \n' +
+ ' AND cache it on memory', function (spec) {
+ spec(function () {
+ gently.expect(store, 'get', function (query, cb) {
+ assert.deepEqual(query, {locale: 'es'});
+ assert.ok(query, cb);
+ cb(null, [{original: 'hello', translation: 'hola'}]);
+ });
+
+ io.cacheDictionary('es', function (err, data) {
+ assert.equal(err, null);
+ assert.deepEqual(data, {hello: 'hola'});
+ });
+
+ assert.deepEqual(dialect.dictionaries.es, {hello: 'hola'});
+
+ // with plural and contexts
+ gently.expect(store, 'get', function (query, cb) {
+ assert.deepEqual(query, {locale: 'es'});
+ assert.ok(query, cb);
+ cb(null, [{original: 'hello', translation: 'hola', context: 'salute', plural: 1}]);
+ });
+
+ io.cacheDictionary('es', function (err, data) {
+ assert.equal(err, null);
+ assert.deepEqual(data, {'hello|p:1|c:salute': 'hola'});
+ });
+
+ assert.deepEqual(dialect.dictionaries.es, {'hello|p:1|c:salute': 'hola'});
+ })();
+ })
+
+ .serial(function () { });
View
113 test/sqlizer.js
@@ -0,0 +1,113 @@
+var testosterone = require('testosterone')({title: 'SQLizer helper lib'}),
+ assert = testosterone.assert,
+ gently = global.GENTLY = new (require('gently')),
+ sqlizer = require('./../lib/helpers/sqlizer')({table: 'test'});
+
+testosterone
+
+ ////////////////////////////////////////////
+ // parseDoc
+ ////////////////////////////////////////////
+
+ .add('parseDoc', function (spec) {
+ spec(function () {
+ assert.equal(sqlizer.parseDoc({j: 4}), 'j = 4');
+ assert.equal(sqlizer.parseDoc({j: '4'}), "j = '4'");
+ 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: {'$ne': null}}), 'j IS NOT null');
+ assert.equal(sqlizer.parseDoc({j: {'$ne': 4, '$gt': 8}}), 'j <> 4 AND j > 8');
+ assert.equal(sqlizer.parseDoc({j: {'$lte': 4}, f: 4}), 'j <= 4 AND f = 4');
+ assert.equal(sqlizer.parseDoc({j: {'$lt': 4}, '$or': [{f: 4}, {g: {'$gte': 10}}]}), 'j < 4 AND (f = 4 OR g >= 10)');
+ assert.equal(sqlizer.parseDoc({j: {'$in': [2, 3, 4]}}), 'j IN (2,3,4)');
+ assert.equal(sqlizer.parseDoc({j: {'$nin': [2, 3, 4]}}), 'j NOT IN (2,3,4)');
+ })();
+ })
+
+ ////////////////////////////////////////////
+ // find
+ ////////////////////////////////////////////
+
+ .add('find', function (spec) {
+ spec(function () {
+ assert.equal(sqlizer.find().sql, 'SELECT * FROM test');
+ assert.equal(sqlizer.find({}).sql, 'SELECT * FROM test');
+ assert.equal(sqlizer.find({j: 4}).sql, 'SELECT * FROM test WHERE j = 4');
+ assert.equal(sqlizer.find({j: 4, f: 5}, {a: 1}).sql, 'SELECT a FROM test WHERE j = 4 AND f = 5');
+ assert.equal(sqlizer.find({j: 4, f: 5}, {a: 1, b: 1}).sql, 'SELECT a, b FROM test WHERE j = 4 AND f = 5');
+ assert.equal(sqlizer.find({j: 4, f: 5}, {a: 2, b: 2}).sql, 'SELECT * FROM test WHERE j = 4 AND f = 5');
+ })();
+ })
+
+ ////////////////////////////////////////////
+ // sort
+ ////////////////////////////////////////////
+
+ .add('sort', function (spec) {
+ spec(function () {
+ assert.equal(sqlizer.find({j: 4}).sort({b: 1}).sql, 'SELECT * FROM test WHERE j = 4 ORDER BY b ASC');
+ assert.equal(sqlizer.find({j: 4}).sort({b: -1}).sql, 'SELECT * FROM test WHERE j = 4 ORDER BY b DESC');
+ assert.equal(sqlizer.find({}).sort({a: -1, b: 1}).sql, 'SELECT * FROM test ORDER BY a DESC, b ASC');
+ })();
+ })
+
+ ////////////////////////////////////////////
+ // skip and limit
+ ////////////////////////////////////////////
+
+ .add('skip and limit', function (spec) {
+ spec(function () {
+ assert.equal(sqlizer.find({j: 4}).skip(5).sql, 'SELECT * FROM test WHERE j = 4 LIMIT 0 OFFSET 5');
+ assert.equal(sqlizer.find({j: 4}).limit(5).sql, 'SELECT * FROM test WHERE j = 4 LIMIT 5 OFFSET 0');
+ assert.equal(sqlizer.find({j: 4}).skip(2).limit(5).sql, 'SELECT * FROM test WHERE j = 4 LIMIT 5 OFFSET 2');
+ assert.equal(sqlizer.find({j: 4}).limit(5).skip(2).sql, 'SELECT * FROM test WHERE j = 4 LIMIT 5 OFFSET 2');
+ })();
+ })
+
+ ////////////////////////////////////////////
+ // count
+ ////////////////////////////////////////////
+
+ .add('count', function (spec) {
+ spec(function () {
+ assert.equal(sqlizer.find({j: 4}).count().sql, 'SELECT COUNT(*) FROM test WHERE j = 4');
+ })();
+ })
+
+ ////////////////////////////////////////////
+ // insert
+ ////////////////////////////////////////////
+
+ .add('insert', function (spec) {
+ spec(function () {
+ assert.equal(sqlizer.insert({j: 4, f: 3}).sql, 'INSERT INTO test (j, f) VALUES (4, 3)');
+ assert.equal(sqlizer.insert({j: 'foo', f: 3}).sql, "INSERT INTO test (j, f) VALUES ('foo', 3)");
+ assert.equal(sqlizer.insert([{j: 4, f: 3}, {d: 2}]).sql, 'INSERT INTO test (j, f) VALUES (4, 3); INSERT INTO test (d) VALUES (2)');
+ })();
+ })
+
+ ////////////////////////////////////////////
+ // update
+ ////////////////////////////////////////////
+
+ .add('update', function (spec) {
+ spec(function () {
+ assert.equal(sqlizer.update({}, {'$set': {j: 4}}).sql, 'UPDATE test SET j = 4');
+ 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: 4, f: 3}, {'$inc': {j: 2}}).sql, 'UPDATE test SET j = j + 2 WHERE d = 4 AND f = 3');
+ })();
+ })
+
+ ////////////////////////////////////////////
+ // remove
+ ////////////////////////////////////////////
+
+ .add('remove', function (spec) {
+ spec(function () {
+ assert.equal(sqlizer.remove({}).sql, 'DELETE FROM test');
+ assert.equal(sqlizer.remove({j: 2}).sql, 'DELETE FROM test WHERE j = 2');
+ })();
+ })
+
+ .serial(function () { });
View
245 test/stores/mongodb.js
@@ -0,0 +1,245 @@
+var testosterone = require('testosterone')({title: 'Mongodb store'}),
+ assert = testosterone.assert,
+ gently = global.GENTLY = new (require('gently'));
+
+testosterone
+
+ ////////////////////////////////////////////
+ // connect
+ ////////////////////////////////////////////
+
+ .add('GIVEN a call to `connect` \n' +
+ ' WHEN no `callback` is given \n' +
+ ' THEN it should provide with a default one \n' +
+ ' AND return the `collection` or `error`', function (spec) {
+
+ spec(function () {
+ var db = {},
+ store = require('../../lib/store').mongodb();
+
+ // error
+ gently.expect(store.db, 'open', function (cb) {
+ cb('foo', null);
+ });
+
+ store.connect(function (err, coll) {
+ assert.equal(err, 'foo');
+ assert.equal(coll, null);
+ assert.equal(store.collection, null);
+ });
+
+ // data
+ gently.expect(store.db, 'open', function (cb) {
+ cb(null, db);
+ });
+
+ gently.expect(db, 'collection', function (coll, cb) {
+ cb('blah', 'bar');
+ });
+
+ store.connect(function (err, coll) {
+ assert.equal(err, 'blah');
+ assert.equal(coll, 'bar');
+ assert.equal(store.collection, 'bar');
+ });
+
+ })();
+ })
+
+ ////////////////////////////////////////////
+ // get
+ ////////////////////////////////////////////
+
+ .add('GIVEN a call to `get` \n' +
+ ' WHEN no `callback` is given \n' +
+ ' THEN it should provide with a default one \n' +
+ ' AND return the translation according to `query`', function (spec) {
+
+ spec(function () {
+ var store = require('../../lib/store').mongodb();
+
+ store.collection = {};
+
+ // error
+ gently.expect(store.collection, 'find', function (query, cb) {
+ assert.deepEqual(query, {});
+ assert.ok(cb);
+ cb('foo', null);
+ });
+
+ store.get(null, function (err, doc) {
+ assert.equal(err, 'foo');
+ assert.equal(doc, null);
+ });
+
+ // data
+ gently.expect(store.collection, 'find', function (query, cb) {
+ var cursor = {};
+ assert.deepEqual(query, {foo: 'bar'});
+ assert.ok(cb);
+
+ gently.expect(cursor, 'toArray', function (cb) {
+ cb('foo', [{hey: 'ya'}]);
+ });
+ cb(null, cursor);
+ });
+
+ store.get({foo: 'bar'}, function (err, doc) {
+ assert.equal(err, 'foo');
+ assert.deepEqual(doc, [{hey: 'ya'}]);
+ });
+
+ })();
+ })
+
+ ////////////////////////////////////////////
+ // add
+ ////////////////////////////////////////////
+
+ .add('GIVEN a call to `add` \n' +
+ ' WHEN no `callback` is given \n' +
+ ' THEN it should provide with a default one \n' +
+ ' AND add the `translation` if is not on the store', function (spec) {
+
+ spec(function () {
+ var store = require('../../lib/store').mongodb(),
+ original = {original: 'hello'},
+ new_doc = {original: 'hello', translation: 'hola'};
+
+ store.collection = {};
+
+ // error
+ gently.expect(store.collection, 'findOne', function (query, cb) {
+ assert.deepEqual(query, original);
+ assert.ok(cb);
+ cb('foo', null);
+ });
+
+ store.add(original, 'hola', function (err, doc) {
+ assert.equal(err, 'foo');
+ assert.equal(doc, null);
+ });
+
+ // no error, no data
+ gently.expect(store.collection, 'findOne', function (query, cb) {
+ assert.deepEqual(query, original);
+ assert.ok(cb);
+ cb(null, {original: 'hello', translation: 'hola'});
+ });
+
+ store.add(original, 'hola', function (err, doc) {
+ assert.ok(err);
+ assert.equal(doc, null);
+ });
+
+ // data
+ gently.expect(store.collection, 'findOne', function (query, cb) {
+ assert.deepEqual(query, original);
+ assert.ok(cb);
+ gently.expect(store.collection, 'insert', function (doc, cb) {
+ assert.deepEqual(doc, new_doc);
+ assert.ok(cb);
+ cb(null, new_doc);
+ });
+ cb(null, null);
+ });
+
+ store.add(original, 'hola', function (err, doc) {
+ assert.equal(err, null);
+ assert.equal(doc, new_doc);
+ });
+
+ })();
+ })
+
+ ////////////////////////////////////////////
+ // set
+ ////////////////////////////////////////////
+
+ .add('GIVEN a call to `set` \n' +
+ ' WHEN no `callback` is given \n' +
+ ' THEN it should provide with a default one \n' +
+ ' AND update the translations according to `query` and `update`', function (spec) {
+
+ spec(function () {
+ var store = require('../../lib/store').mongodb(),
+ original = {original: 'hello'},
+ translation = {translation: 'hola'},
+ new_doc = {original: 'hello', translation: 'hola'};
+
+ store.collection = {};
+
+ gently.expect(store.collection, 'update', function (query, update, cb) {
+ assert.deepEqual(query, original);
+ assert.deepEqual(update, {'$set': translation});
+ assert.ok(cb);
+ cb('foo', new_doc);
+ });
+
+ store.set(original, translation, function (err, doc) {
+ assert.equal(err, 'foo');
+ assert.equal(doc, new_doc);
+ });
+
+ })();
+ })
+
+ ////////////////////////////////////////////
+ // destroy
+ ////////////////////////////////////////////
+
+ .add('GIVEN a call to `destroy` \n' +
+ ' WHEN no `callback` is given \n' +
+ ' THEN it should provide with a default one \n' +
+ ' AND remove the translation according to `query`', function (spec) {
+
+ spec(function () {
+ var store = require('../../lib/store').mongodb(),
+ original = {original: 'hello'};
+
+ store.collection = {};
+
+ gently.expect(store.collection, 'remove', function (query, cb) {
+ assert.deepEqual(query, original);
+ assert.ok(cb);
+ cb('foo', original);
+ });
+
+ store.destroy(original, function (err, doc) {
+ assert.equal(err, 'foo');
+ assert.equal(doc, original);
+ });
+
+ })();
+ })
+
+ ////////////////////////////////////////////
+ // count
+ ////////////////////////////////////////////
+
+ .add('GIVEN a call to `count` \n' +
+ ' WHEN no `callback` is given \n' +
+ ' THEN it should provide with a default one \n' +
+ ' AND count the translations according to `query`', function (spec) {
+
+ spec(function () {
+ var store = require('../../lib/store').mongodb(),
+ original = {original: 'hello'};
+
+ store.collection = {};
+
+ gently.expect(store.collection, 'count', function (query, cb) {
+ assert.deepEqual(query, original);
+ assert.ok(cb);
+ cb('foo', 3);
+ });
+
+ store.count(original, function (err, doc) {
+ assert.equal(err, 'foo');
+ assert.equal(doc, 3);
+ });
+
+ })();
+ })
+
+ .serial(function () { });
View
256 test/stores/sqlite.js
@@ -0,0 +1,256 @@
+var testosterone = require('testosterone')({title: 'SQLite store'}),
+ assert = testosterone.assert,
+ gently = global.GENTLY = new (require('gently'));
+
+testosterone
+
+ ////////////////////////////////////////////
+ // connect
+ ////////////////////////////////////////////
+
+ .add('GIVEN a call to `connect` \n' +
+ ' WHEN no `callback` is given \n' +
+ ' THEN it should provide with a default one \n' +
+ ' AND return the `collection` or `error`', function (spec) {
+
+ spec(function () {
+ var db = {},
+ store = require('../../lib/store').sqlite({table: 'test'});
+
+ // error
+ gently.expect(store.db, 'open', function (database, cb) {
+ assert.equal(database, 'dialect.db');
+ cb('foo', null);
+ });
+
+ store.connect(function (err, coll) {
+ assert.equal(err, 'foo');
+ assert.equal(coll, null);
+ assert.equal(store.collection, null);
+ });
+
+ // data
+ gently.expect(store.db, 'open', function (database, cb) {
+ assert.equal(database, 'dialect.db');
+ cb(null, db);
+ });
+
+ gently.expect(store.db, 'execute', function (sql, cb) {
+ assert.equal(sql,
+ 'CREATE TABLE IF NOT EXISTS test' +
+ ' (original TEXT, locale TEXT, translation TEXT,' +
+ ' plural NUMBER, context TEXT, PRIMARY KEY(original, locale, plural, context))'
+ );
+
+ cb('foo', 'bar');
+ });
+
+ store.connect(function (err, data) {
+ assert.equal(err, 'foo');
+ assert.equal(data, 'bar');
+ });
+
+ })();
+ })
+
+ ////////////////////////////////////////////
+ // get
+ ////////////////////////////////////////////
+
+ .add('GIVEN a call to `get` \n' +
+ ' WHEN no `callback` is given \n' +
+ ' THEN it should provide with a default one \n' +
+ ' AND return the translation according to `query`', function (spec) {
+
+ spec(function () {
+ var store = require('../../lib/store').sqlite();
+
+ store.collection = {};
+
+ // error
+ gently.expect(store.db, 'execute', function (sql, cb) {
+ assert.equal(sql, "SELECT * FROM dialect WHERE original = 'foo'");
+ assert.ok(cb);
+ cb('foo', null);
+ });
+
+ store.get({original: 'foo'}, function (err, doc) {
+ assert.equal(err, 'foo');
+ assert.deepEqual(doc, []);
+ });
+
+ // data
+ gently.expect(store.db, 'execute', function (sql, cb) {
+ assert.equal(sql, "SELECT * FROM dialect WHERE original = 'foo'");
+ assert.ok(cb);
+ cb(null, 'foo');
+ });
+
+ store.get({original: 'foo'}, function (err, doc) {
+ assert.equal(err, null);
+ assert.equal(doc, 'foo');
+ });
+
+ })();
+ })
+
+ ////////////////////////////////////////////
+ // add
+ ////////////////////////////////////////////
+
+ .add('GIVEN a call to `add` \n' +
+ ' WHEN no `callback` is given \n' +
+ ' THEN it should provide with a default one \n' +
+ ' AND add the `translation` if is not on the store', function (spec) {
+
+ spec(function () {
+ var store = require('../../lib/store').sqlite(),
+ original = {original: 'hello'},
+ new_doc = {original: 'hello', translation: 'hola'};
+
+ store.collection = {};
+
+ // error on get
+ gently.expect(store, 'get', function (doc, cb) {
+ assert.deepEqual(doc, original);
+ assert.ok(cb);
+ cb('foo', []);
+ });
+
+ store.add(original, 'hola', function (err, doc) {
+ assert.equal(err, 'foo');
+ assert.equal(doc, null);
+ });
+
+ // already exists
+ gently.expect(store, 'get', function (doc, cb) {
+ assert.deepEqual(doc, original);
+ assert.ok(cb);
+ cb(null, [new_doc]);
+ });
+
+ store.add(original, 'hola', function (err, doc) {
+ assert.ok(err);
+ });
+
+ // success
+ gently.expect(store, 'get', function (doc, cb) {
+ assert.deepEqual(doc, original);
+ assert.ok(cb);
+ cb(null, []);
+ });
+
+ gently.expect(store.db, 'execute', function (sql, cb) {
+ assert.equal(sql, "INSERT INTO dialect (original, translation) VALUES ('hello', 'hola')");
+ assert.ok(cb);
+ cb(null, []);
+ });
+
+ store.add(original, 'hola', function (err, doc) {
+ assert.equal(err, null);
+ assert.deepEqual(doc, []);
+ });
+
+ })();
+ })
+
+ ////////////////////////////////////////////
+ // set
+ ////////////////////////////////////////////
+
+ .add('GIVEN a call to `set` \n' +
+ ' WHEN no `callback` is given \n' +
+ ' THEN it should provide with a default one \n' +
+ ' AND update the translations according to `query` and `update`', function (spec) {
+
+ spec(function () {
+ var store = require('../../lib/store').sqlite();
+
+ store.collection = {};
+
+ gently.expect(store.db, 'execute', function (sql, cb) {
+ assert.equal(sql, "UPDATE dialect SET translation = 'bar' WHERE original = 'foo'");
+ assert.ok(cb);
+ cb('foo', 'bar');
+ });
+
+ store.set({original: 'foo'}, 'bar', function (err, doc) {
+ assert.equal(err, 'foo');
+ assert.deepEqual(doc, 'bar');
+ });
+
+ })();
+ })
+
+ ////////////////////////////////////////////
+ // destroy
+ ////////////////////////////////////////////
+
+ .add('GIVEN a call to `destroy` \n' +
+ ' WHEN no `callback` is given \n' +
+ ' THEN it should provide with a default one \n' +
+ ' AND remove the translation according to `query`', function (spec) {
+
+ spec(function () {
+ var store = require('../../lib/store').sqlite(),
+ original = {original: 'hello'};
+
+ store.collection = {};
+
+ gently.expect(store.db, 'execute', function (sql, cb) {
+ assert.equal(sql, "DELETE FROM dialect WHERE original = 'foo'");
+ assert.ok(cb);
+ cb('foo', 'bar');
+ });
+
+ store.destroy({original: 'foo'}, function (err, doc) {
+ assert.equal(err, 'foo');
+ assert.deepEqual(doc, 'bar');
+ });
+
+ })();
+ })
+
+ ////////////////////////////////////////////
+ // count
+ ////////////////////////////////////////////
+
+ .add('GIVEN a call to `count` \n' +
+ ' WHEN no `callback` is given \n' +
+ ' THEN it should provide with a default one \n' +
+ ' AND count the translations according to `query`', function (spec) {
+
+ spec(function () {
+ var store = require('../../lib/store').sqlite(),
+ original = {original: 'hello'};
+
+ store.collection = {};
+
+ // error
+ gently.expect(store.db, 'execute', function (sql, cb) {
+ assert.equal(sql, "SELECT COUNT(*) FROM dialect WHERE original = 'foo'");
+ assert.ok(cb);
+ cb('foo', null);
+ });
+
+ store.count({original: 'foo'}, function (err, doc) {
+ assert.equal(err, 'foo');
+ assert.deepEqual(doc, 0);
+ });
+
+ // succes
+ gently.expect(store.db, 'execute', function (sql, cb) {
+ assert.equal(sql, "SELECT COUNT(*) FROM dialect WHERE original = 'foo'");
+ assert.ok(cb);
+ cb(null, [{count: 3}]);
+ });
+
+ store.count({original: 'foo'}, function (err, doc) {
+ assert.equal(err, null);
+ assert.deepEqual(doc, 3);
+ });
+
+ })();
+ })
+
+ .serial(function () { });
Please sign in to comment.
Something went wrong with that request. Please try again.