Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

fixed bug with empty predicate not leaving off where clause

  • Loading branch information...
commit 7d7c3fdee6d5ba4e78968c5566287c0d83db0ae0 1 parent d3f2adb
John Wright authored
5 Cakefile
View
@@ -1,6 +1,4 @@
-require.paths.unshift "/Users/johnw/js/mumblr_machine"
-require.paths.unshift "/Users/johnw/js/mumblr_machine/external"
-require.paths.unshift "/Users/johnw/js/mumblr_machine/db"
+require.paths.unshift "."
fs: require 'fs'
coffee: require 'coffee-script'
@@ -9,7 +7,6 @@ require "underscore"
task 'test', 'run the Mumblr Machine test suite', ->
process.mixin require 'assert'
process.mixin require 'sys'
- TestSuite: require('async_testing').TestSuite
passed_count: 0
failed_count: 0
6 README.md
View
@@ -7,7 +7,7 @@ The goal of NoSQLite is to exploit that even more than other SQLite libraries.
* zero-config - No "create table", "alter table".
* zero-schema - Just store my JS object in an intelligent way.
-* simple DSL - just find stuff, save stuff, or find_or_save stuff (ok remove stuff too).
+* zero-SQL - just a simple DSL to find stuff, save stuff, or find_or_save stuff (ok remove stuff too).
Other goals
---------------
@@ -26,6 +26,4 @@ Currently Requires
* [node](http://nodejs.org)
* [CoffeeScript](http://jashkenas.github.com/coffee-script/) - fun, clean way to write JavaScript. Includes Cake to run the Cakefile and tests.
-* [node-sqlite](http://github.com/grumdrig/node-sqlite) - I am working on rewriting this to be async and be more HTML 5 compatible.
-* [underscore.js](http://github.com/documentcloud/underscore) - this needs to be in your node require path as well
-
+* [node-sqlite](http://github.com/grumdrig/node-sqlite) - I am working on rewriting this to be async and be more HTML 5 compatible. You will have to get it and compile the node bindings and put it in your node require path
2  nosqlite.coffee
View
@@ -3,7 +3,7 @@ fs: require "fs"
require "underscore"
assert: require "assert"
sql: require "./sql"
-sqlite: require("sqlite")
+sqlite: require "sqlite"
assert: require "assert"
# A library to make it as easy as possible to store and retrieve JS objects
8 sql.coffee
View
@@ -11,7 +11,13 @@ class SQL
select: (table, predicate) ->
- sql: "select rowid, * from " + table + " where"
+ sql: "select rowid, * from " + table
+ if (not predicate?) or _.isEmpty(predicate)
+ @escaped: sql
+ @placeholder: sql
+ return this
+
+ sql += " where"
predicates: []
#allow the user to pass in a single object or multiple objects
8 sql.js
View
@@ -13,7 +13,13 @@
};
SQL.prototype.select = function select(table, predicate) {
var _a, _b, _c, _d, ands_escaped, ands_placeholder, key, key_sql, predicates, sql;
- sql = "select rowid, * from " + table + " where";
+ sql = "select rowid, * from " + table;
+ if ((!(typeof predicate !== "undefined" && predicate !== null)) || _.isEmpty(predicate)) {
+ this.escaped = sql;
+ this.placeholder = sql;
+ return this;
+ }
+ sql += " where";
predicates = [];
//allow the user to pass in a single object or multiple objects
!_.isArray(predicate) ? predicates.push(predicate) : (predicates = predicate);
2  test/test_sql.coffee
View
@@ -21,6 +21,8 @@ ok sql.select("log", {"external_id": true}).escaped, "select rowid, * from log w
ok sql.select("log", {"external_id": false}).escaped, "select rowid, * from log where(external_id = 'false')", "should handle predicate value = false"
ok sql.select("log", {"external_id": false}).placeholder, "select rowid, * from log where(external_id = ?)", "placeholder property should be set"
ok sql.select("log", {"external_id": false}).values.length, 1, "values property should be array of right size"
+ok sql.select("log", {}).escaped, "select rowid, * from log", "empty object predicate should leave off where clause"
+ok sql.select("log", []).escaped, "select rowid, * from log", "empty array predicate should leave off where clause"
puts "testing insert"
ok sql.insert("log", {text: "hello", log_type: "mumble"}).placeholder, "insert or replace into log(text,log_type) values (?,?)", "insert placeholder should be correct"
2  test/test_sql.js
View
@@ -33,6 +33,8 @@
ok(sql.select("log", {
"external_id": false
}).values.length, 1, "values property should be array of right size");
+ ok(sql.select("log", {}).escaped, "select rowid, * from log", "empty object predicate should leave off where clause");
+ ok(sql.select("log", []).escaped, "select rowid, * from log", "empty array predicate should leave off where clause");
puts("testing insert");
ok(sql.insert("log", {
text: "hello",
692 underscore.js
View
@@ -0,0 +1,692 @@
+// Underscore.js
+// (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
+// Underscore is freely distributable under the terms of the MIT license.
+// Portions of Underscore are inspired by or borrowed from Prototype.js,
+// Oliver Steele's Functional, and John Resig's Micro-Templating.
+// For all details and documentation:
+// http://documentcloud.github.com/underscore
+
+(function() {
+ // ------------------------- Baseline setup ---------------------------------
+
+ // Establish the root object, "window" in the browser, or "global" on the server.
+ var root = this;
+
+ // Save the previous value of the "_" variable.
+ var previousUnderscore = root._;
+
+ // Establish the object that gets thrown to break out of a loop iteration.
+ var breaker = typeof StopIteration !== 'undefined' ? StopIteration : '__break__';
+
+ // Quick regexp-escaping function, because JS doesn't have RegExp.escape().
+ var escapeRegExp = function(s) { return s.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1'); };
+
+ // Save bytes in the minified (but not gzipped) version:
+ var ArrayProto = Array.prototype, ObjProto = Object.prototype;
+
+ // Create quick reference variables for speed access to core prototypes.
+ var slice = ArrayProto.slice,
+ unshift = ArrayProto.unshift,
+ toString = ObjProto.toString,
+ hasOwnProperty = ObjProto.hasOwnProperty,
+ propertyIsEnumerable = ObjProto.propertyIsEnumerable;
+
+ // All ECMA5 native implementations we hope to use are declared here.
+ var
+ nativeForEach = ArrayProto.forEach,
+ nativeMap = ArrayProto.map,
+ nativeReduce = ArrayProto.reduce,
+ nativeReduceRight = ArrayProto.reduceRight,
+ nativeFilter = ArrayProto.filter,
+ nativeEvery = ArrayProto.every,
+ nativeSome = ArrayProto.some,
+ nativeIndexOf = ArrayProto.indexOf,
+ nativeLastIndexOf = ArrayProto.lastIndexOf,
+ nativeIsArray = Array.isArray,
+ nativeKeys = Object.keys;
+
+ // Create a safe reference to the Underscore object for reference below.
+ var _ = function(obj) { return new wrapper(obj); };
+
+ // Export the Underscore object for CommonJS.
+ if (typeof exports !== 'undefined') exports._ = _;
+
+ // Export underscore to global scope.
+ root._ = _;
+
+ // Current version.
+ _.VERSION = '0.6.0';
+
+ // ------------------------ Collection Functions: ---------------------------
+
+ // The cornerstone, an each implementation.
+ // Handles objects implementing forEach, arrays, and raw objects.
+ // Delegates to JavaScript 1.6's native forEach if available.
+ var each = _.forEach = function(obj, iterator, context) {
+ try {
+ if (nativeForEach && obj.forEach === nativeForEach) {
+ obj.forEach(iterator, context);
+ } else if (_.isNumber(obj.length)) {
+ for (var i = 0, l = obj.length; i < l; i++) iterator.call(context, obj[i], i, obj);
+ } else {
+ for (var key in obj) {
+ if (hasOwnProperty.call(obj, key)) iterator.call(context, obj[key], key, obj);
+ }
+ }
+ } catch(e) {
+ if (e != breaker) throw e;
+ }
+ return obj;
+ };
+
+ // Return the results of applying the iterator to each element.
+ // Delegates to JavaScript 1.6's native map if available.
+ _.map = function(obj, iterator, context) {
+ if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
+ var results = [];
+ each(obj, function(value, index, list) {
+ results.push(iterator.call(context, value, index, list));
+ });
+ return results;
+ };
+
+ // Reduce builds up a single result from a list of values, aka inject, or foldl.
+ // Delegates to JavaScript 1.8's native reduce if available.
+ _.reduce = function(obj, memo, iterator, context) {
+ if (nativeReduce && obj.reduce === nativeReduce) return obj.reduce(_.bind(iterator, context), memo);
+ each(obj, function(value, index, list) {
+ memo = iterator.call(context, memo, value, index, list);
+ });
+ return memo;
+ };
+
+ // The right-associative version of reduce, also known as foldr. Uses
+ // Delegates to JavaScript 1.8's native reduceRight if available.
+ _.reduceRight = function(obj, memo, iterator, context) {
+ if (nativeReduceRight && obj.reduceRight === nativeReduceRight) return obj.reduceRight(_.bind(iterator, context), memo);
+ var reversed = _.clone(_.toArray(obj)).reverse();
+ return _.reduce(reversed, memo, iterator, context);
+ };
+
+ // Return the first value which passes a truth test.
+ _.detect = function(obj, iterator, context) {
+ var result;
+ each(obj, function(value, index, list) {
+ if (iterator.call(context, value, index, list)) {
+ result = value;
+ _.breakLoop();
+ }
+ });
+ return result;
+ };
+
+ // Return all the elements that pass a truth test.
+ // Delegates to JavaScript 1.6's native filter if available.
+ _.filter = function(obj, iterator, context) {
+ if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
+ var results = [];
+ each(obj, function(value, index, list) {
+ iterator.call(context, value, index, list) && results.push(value);
+ });
+ return results;
+ };
+
+ // Return all the elements for which a truth test fails.
+ _.reject = function(obj, iterator, context) {
+ var results = [];
+ each(obj, function(value, index, list) {
+ !iterator.call(context, value, index, list) && results.push(value);
+ });
+ return results;
+ };
+
+ // Determine whether all of the elements match a truth test.
+ // Delegates to JavaScript 1.6's native every if available.
+ _.every = function(obj, iterator, context) {
+ iterator = iterator || _.identity;
+ if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
+ var result = true;
+ each(obj, function(value, index, list) {
+ if (!(result = result && iterator.call(context, value, index, list))) _.breakLoop();
+ });
+ return result;
+ };
+
+ // Determine if at least one element in the object matches a truth test.
+ // Delegates to JavaScript 1.6's native some if available.
+ _.some = function(obj, iterator, context) {
+ iterator = iterator || _.identity;
+ if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
+ var result = false;
+ each(obj, function(value, index, list) {
+ if (result = iterator.call(context, value, index, list)) _.breakLoop();
+ });
+ return result;
+ };
+
+ // Determine if a given value is included in the array or object using '==='.
+ _.include = function(obj, target) {
+ if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
+ var found = false;
+ each(obj, function(value) {
+ if (found = value === target) _.breakLoop();
+ });
+ return found;
+ };
+
+ // Invoke a method with arguments on every item in a collection.
+ _.invoke = function(obj, method) {
+ var args = _.rest(arguments, 2);
+ return _.map(obj, function(value) {
+ return (method ? value[method] : value).apply(value, args);
+ });
+ };
+
+ // Convenience version of a common use case of map: fetching a property.
+ _.pluck = function(obj, key) {
+ return _.map(obj, function(value){ return value[key]; });
+ };
+
+ // Return the maximum item or (item-based computation).
+ _.max = function(obj, iterator, context) {
+ if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
+ var result = {computed : -Infinity};
+ each(obj, function(value, index, list) {
+ var computed = iterator ? iterator.call(context, value, index, list) : value;
+ computed >= result.computed && (result = {value : value, computed : computed});
+ });
+ return result.value;
+ };
+
+ // Return the minimum element (or element-based computation).
+ _.min = function(obj, iterator, context) {
+ if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
+ var result = {computed : Infinity};
+ each(obj, function(value, index, list) {
+ var computed = iterator ? iterator.call(context, value, index, list) : value;
+ computed < result.computed && (result = {value : value, computed : computed});
+ });
+ return result.value;
+ };
+
+ // Sort the object's values by a criteria produced by an iterator.
+ _.sortBy = function(obj, iterator, context) {
+ return _.pluck(_.map(obj, function(value, index, list) {
+ return {
+ value : value,
+ criteria : iterator.call(context, value, index, list)
+ };
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }), 'value');
+ };
+
+ // Use a comparator function to figure out at what index an object should
+ // be inserted so as to maintain order. Uses binary search.
+ _.sortedIndex = function(array, obj, iterator) {
+ iterator = iterator || _.identity;
+ var low = 0, high = array.length;
+ while (low < high) {
+ var mid = (low + high) >> 1;
+ iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
+ }
+ return low;
+ };
+
+ // Convert anything iterable into a real, live array.
+ _.toArray = function(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) return iterable.toArray();
+ if (_.isArray(iterable)) return iterable;
+ if (_.isArguments(iterable)) return slice.call(iterable);
+ return _.values(iterable);
+ };
+
+ // Return the number of elements in an object.
+ _.size = function(obj) {
+ return _.toArray(obj).length;
+ };
+
+ // -------------------------- Array Functions: ------------------------------
+
+ // Get the first element of an array. Passing "n" will return the first N
+ // values in the array. Aliased as "head". The "guard" check allows it to work
+ // with _.map.
+ _.first = function(array, n, guard) {
+ return n && !guard ? slice.call(array, 0, n) : array[0];
+ };
+
+ // Returns everything but the first entry of the array. Aliased as "tail".
+ // Especially useful on the arguments object. Passing an "index" will return
+ // the rest of the values in the array from that index onward. The "guard"
+ //check allows it to work with _.map.
+ _.rest = function(array, index, guard) {
+ return slice.call(array, _.isUndefined(index) || guard ? 1 : index);
+ };
+
+ // Get the last element of an array.
+ _.last = function(array) {
+ return array[array.length - 1];
+ };
+
+ // Trim out all falsy values from an array.
+ _.compact = function(array) {
+ return _.filter(array, function(value){ return !!value; });
+ };
+
+ // Return a completely flattened version of an array.
+ _.flatten = function(array) {
+ return _.reduce(array, [], function(memo, value) {
+ if (_.isArray(value)) return memo.concat(_.flatten(value));
+ memo.push(value);
+ return memo;
+ });
+ };
+
+ // Return a version of the array that does not contain the specified value(s).
+ _.without = function(array) {
+ var values = _.rest(arguments);
+ return _.filter(array, function(value){ return !_.include(values, value); });
+ };
+
+ // Produce a duplicate-free version of the array. If the array has already
+ // been sorted, you have the option of using a faster algorithm.
+ _.uniq = function(array, isSorted) {
+ return _.reduce(array, [], function(memo, el, i) {
+ if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo.push(el);
+ return memo;
+ });
+ };
+
+ // Produce an array that contains every item shared between all the
+ // passed-in arrays.
+ _.intersect = function(array) {
+ var rest = _.rest(arguments);
+ return _.filter(_.uniq(array), function(item) {
+ return _.every(rest, function(other) {
+ return _.indexOf(other, item) >= 0;
+ });
+ });
+ };
+
+ // Zip together multiple lists into a single array -- elements that share
+ // an index go together.
+ _.zip = function() {
+ var args = _.toArray(arguments);
+ var length = _.max(_.pluck(args, 'length'));
+ var results = new Array(length);
+ for (var i = 0; i < length; i++) results[i] = _.pluck(args, String(i));
+ return results;
+ };
+
+ // If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
+ // we need this function. Return the position of the first occurence of an
+ // item in an array, or -1 if the item is not included in the array.
+ // Delegates to JavaScript 1.8's native indexOf if available.
+ _.indexOf = function(array, item) {
+ if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
+ for (var i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
+ return -1;
+ };
+
+
+ // Delegates to JavaScript 1.6's native lastIndexOf if available.
+ _.lastIndexOf = function(array, item) {
+ if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
+ var i = array.length;
+ while (i--) if (array[i] === item) return i;
+ return -1;
+ };
+
+ // Generate an integer Array containing an arithmetic progression. A port of
+ // the native Python range() function. See:
+ // http://docs.python.org/library/functions.html#range
+ _.range = function(start, stop, step) {
+ var a = _.toArray(arguments);
+ var solo = a.length <= 1;
+ var start = solo ? 0 : a[0], stop = solo ? a[0] : a[1], step = a[2] || 1;
+ var len = Math.ceil((stop - start) / step);
+ if (len <= 0) return [];
+ var range = new Array(len);
+ for (var i = start, idx = 0; true; i += step) {
+ if ((step > 0 ? i - stop : stop - i) >= 0) return range;
+ range[idx++] = i;
+ }
+ };
+
+ // ----------------------- Function Functions: ------------------------------
+
+ // Create a function bound to a given object (assigning 'this', and arguments,
+ // optionally). Binding with arguments is also known as 'curry'.
+ _.bind = function(func, obj) {
+ var args = _.rest(arguments, 2);
+ return function() {
+ return func.apply(obj || {}, args.concat(_.toArray(arguments)));
+ };
+ };
+
+ // Bind all of an object's methods to that object. Useful for ensuring that
+ // all callbacks defined on an object belong to it.
+ _.bindAll = function(obj) {
+ var funcs = _.rest(arguments);
+ if (funcs.length == 0) funcs = _.functions(obj);
+ each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
+ return obj;
+ };
+
+ // Delays a function for the given number of milliseconds, and then calls
+ // it with the arguments supplied.
+ _.delay = function(func, wait) {
+ var args = _.rest(arguments, 2);
+ return setTimeout(function(){ return func.apply(func, args); }, wait);
+ };
+
+ // Defers a function, scheduling it to run after the current call stack has
+ // cleared.
+ _.defer = function(func) {
+ return _.delay.apply(_, [func, 1].concat(_.rest(arguments)));
+ };
+
+ // Returns the first function passed as an argument to the second,
+ // allowing you to adjust arguments, run code before and after, and
+ // conditionally execute the original function.
+ _.wrap = function(func, wrapper) {
+ return function() {
+ var args = [func].concat(_.toArray(arguments));
+ return wrapper.apply(wrapper, args);
+ };
+ };
+
+ // Returns a function that is the composition of a list of functions, each
+ // consuming the return value of the function that follows.
+ _.compose = function() {
+ var funcs = _.toArray(arguments);
+ return function() {
+ var args = _.toArray(arguments);
+ for (var i=funcs.length-1; i >= 0; i--) {
+ args = [funcs[i].apply(this, args)];
+ }
+ return args[0];
+ };
+ };
+
+ // ------------------------- Object Functions: ------------------------------
+
+ // Retrieve the names of an object's properties.
+ // Delegates to ECMA5's native Object.keys
+ _.keys = nativeKeys || function(obj) {
+ if (_.isArray(obj)) return _.range(0, obj.length);
+ var keys = [];
+ for (var key in obj) if (hasOwnProperty.call(obj, key)) keys.push(key);
+ return keys;
+ };
+
+ // Retrieve the values of an object's properties.
+ _.values = function(obj) {
+ return _.map(obj, _.identity);
+ };
+
+ // Return a sorted list of the function names available on the object.
+ _.functions = function(obj) {
+ return _.filter(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort();
+ };
+
+ // Extend a given object with all of the properties in a source object.
+ _.extend = function(destination, source) {
+ for (var property in source) destination[property] = source[property];
+ return destination;
+ };
+
+ // Create a (shallow-cloned) duplicate of an object.
+ _.clone = function(obj) {
+ if (_.isArray(obj)) return obj.slice(0);
+ return _.extend({}, obj);
+ };
+
+ // Invokes interceptor with the obj, and then returns obj.
+ // The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
+ _.tap = function(obj, interceptor) {
+ interceptor(obj);
+ return obj;
+ };
+
+ // Perform a deep comparison to check if two objects are equal.
+ _.isEqual = function(a, b) {
+ // Check object identity.
+ if (a === b) return true;
+ // Different types?
+ var atype = typeof(a), btype = typeof(b);
+ if (atype != btype) return false;
+ // Basic equality test (watch out for coercions).
+ if (a == b) return true;
+ // One is falsy and the other truthy.
+ if ((!a && b) || (a && !b)) return false;
+ // One of them implements an isEqual()?
+ if (a.isEqual) return a.isEqual(b);
+ // Check dates' integer values.
+ if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();
+ // Both are NaN?
+ if (_.isNaN(a) && _.isNaN(b)) return true;
+ // Compare regular expressions.
+ if (_.isRegExp(a) && _.isRegExp(b))
+ return a.source === b.source &&
+ a.global === b.global &&
+ a.ignoreCase === b.ignoreCase &&
+ a.multiline === b.multiline;
+ // If a is not an object by this point, we can't handle it.
+ if (atype !== 'object') return false;
+ // Check for different array lengths before comparing contents.
+ if (a.length && (a.length !== b.length)) return false;
+ // Nothing else worked, deep compare the contents.
+ var aKeys = _.keys(a), bKeys = _.keys(b);
+ // Different object sizes?
+ if (aKeys.length != bKeys.length) return false;
+ // Recursive comparison of contents.
+ for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
+ return true;
+ };
+
+ // Is a given array or object empty?
+ _.isEmpty = function(obj) {
+ if (_.isArray(obj)) return obj.length === 0;
+ for (var key in obj) if (hasOwnProperty.call(obj, key)) return false;
+ return true;
+ };
+
+ // Is a given value a DOM element?
+ _.isElement = function(obj) {
+ return !!(obj && obj.nodeType == 1);
+ };
+
+ // Is a given value an array?
+ // Delegates to ECMA5's native Array.isArray
+ _.isArray = nativeIsArray || function(obj) {
+ return !!(obj && obj.concat && obj.unshift);
+ };
+
+ // Is a given variable an arguments object?
+ _.isArguments = function(obj) {
+ return obj && _.isNumber(obj.length) && !obj.concat && !obj.substr && !obj.apply && !propertyIsEnumerable.call(obj, 'length');
+ };
+
+ // Is a given value a function?
+ _.isFunction = function(obj) {
+ return !!(obj && obj.constructor && obj.call && obj.apply);
+ };
+
+ // Is a given value a string?
+ _.isString = function(obj) {
+ return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
+ };
+
+ // Is a given value a number?
+ _.isNumber = function(obj) {
+ return (obj === +obj) || (toString.call(obj) === '[object Number]');
+ };
+
+ // Is a given value a boolean?
+ _.isBoolean = function(obj) {
+ return obj === true || obj === false;
+ };
+
+ // Is a given value a date?
+ _.isDate = function(obj) {
+ return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
+ };
+
+ // Is the given value a regular expression?
+ _.isRegExp = function(obj) {
+ return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
+ };
+
+ // Is the given value NaN -- this one is interesting. NaN != NaN, and
+ // isNaN(undefined) == true, so we make sure it's a number first.
+ _.isNaN = function(obj) {
+ return _.isNumber(obj) && isNaN(obj);
+ };
+
+ // Is a given value equal to null?
+ _.isNull = function(obj) {
+ return obj === null;
+ };
+
+ // Is a given variable undefined?
+ _.isUndefined = function(obj) {
+ return typeof obj == 'undefined';
+ };
+
+ // -------------------------- Utility Functions: ----------------------------
+
+ // Run Underscore.js in noConflict mode, returning the '_' variable to its
+ // previous owner. Returns a reference to the Underscore object.
+ _.noConflict = function() {
+ root._ = previousUnderscore;
+ return this;
+ };
+
+ // Keep the identity function around for default iterators.
+ _.identity = function(value) {
+ return value;
+ };
+
+ // Run a function n times.
+ _.times = function (n, iterator, context) {
+ for (var i = 0; i < n; i++) iterator.call(context, i);
+ };
+
+ // Break out of the middle of an iteration.
+ _.breakLoop = function() {
+ throw breaker;
+ };
+
+ // Add your own custom functions to the Underscore object, ensuring that
+ // they're correctly added to the OOP wrapper as well.
+ _.mixin = function(obj) {
+ each(_.functions(obj), function(name){
+ addToWrapper(name, _[name] = obj[name]);
+ });
+ };
+
+ // Generate a unique integer id (unique within the entire client session).
+ // Useful for temporary DOM ids.
+ var idCounter = 0;
+ _.uniqueId = function(prefix) {
+ var id = idCounter++;
+ return prefix ? prefix + id : id;
+ };
+
+ // By default, Underscore uses ERB-style template delimiters, change the
+ // following template settings to use alternative delimiters.
+ _.templateSettings = {
+ start : '<%',
+ end : '%>',
+ interpolate : /<%=(.+?)%>/g
+ };
+
+ // JavaScript templating a-la ERB, pilfered from John Resig's
+ // "Secrets of the JavaScript Ninja", page 83.
+ // Single-quote fix from Rick Strahl's version.
+ // With alterations for arbitrary delimiters.
+ _.template = function(str, data) {
+ var c = _.templateSettings;
+ var endMatch = new RegExp("'(?=[^"+c.end.substr(0, 1)+"]*"+escapeRegExp(c.end)+")","g");
+ var fn = new Function('obj',
+ 'var p=[],print=function(){p.push.apply(p,arguments);};' +
+ 'with(obj){p.push(\'' +
+ str.replace(/[\r\t\n]/g, " ")
+ .replace(endMatch,"\t")
+ .split("'").join("\\'")
+ .split("\t").join("'")
+ .replace(c.interpolate, "',$1,'")
+ .split(c.start).join("');")
+ .split(c.end).join("p.push('")
+ + "');}return p.join('');");
+ return data ? fn(data) : fn;
+ };
+
+ // ------------------------------- Aliases ----------------------------------
+
+ _.each = _.forEach;
+ _.foldl = _.inject = _.reduce;
+ _.foldr = _.reduceRight;
+ _.select = _.filter;
+ _.all = _.every;
+ _.any = _.some;
+ _.head = _.first;
+ _.tail = _.rest;
+ _.methods = _.functions;
+
+ // ------------------------ Setup the OOP Wrapper: --------------------------
+
+ // If Underscore is called as a function, it returns a wrapped object that
+ // can be used OO-style. This wrapper holds altered versions of all the
+ // underscore functions. Wrapped objects may be chained.
+ var wrapper = function(obj) { this._wrapped = obj; };
+
+ // Helper function to continue chaining intermediate results.
+ var result = function(obj, chain) {
+ return chain ? _(obj).chain() : obj;
+ };
+
+ // A method to easily add functions to the OOP wrapper.
+ var addToWrapper = function(name, func) {
+ wrapper.prototype[name] = function() {
+ var args = _.toArray(arguments);
+ unshift.call(args, this._wrapped);
+ return result(func.apply(_, args), this._chain);
+ };
+ };
+
+ // Add all of the Underscore functions to the wrapper object.
+ _.mixin(_);
+
+ // Add all mutator Array functions to the wrapper.
+ each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
+ var method = ArrayProto[name];
+ wrapper.prototype[name] = function() {
+ method.apply(this._wrapped, arguments);
+ return result(this._wrapped, this._chain);
+ };
+ });
+
+ // Add all accessor Array functions to the wrapper.
+ each(['concat', 'join', 'slice'], function(name) {
+ var method = ArrayProto[name];
+ wrapper.prototype[name] = function() {
+ return result(method.apply(this._wrapped, arguments), this._chain);
+ };
+ });
+
+ // Start chaining a wrapped Underscore object.
+ wrapper.prototype.chain = function() {
+ this._chain = true;
+ return this;
+ };
+
+ // Extracts the result from a wrapped and chained object.
+ wrapper.prototype.value = function() {
+ return this._wrapped;
+ };
+
+})();
Please sign in to comment.
Something went wrong with that request. Please try again.