@@ -10,6 +10,7 @@ var Instance = require('./Instance');
1010var  validate  =  require ( './utils/validation' ) ; 
1111var  Concoction  =  require ( 'concoction' ) ; 
1212var  ObjectID  =  require ( 'mongodb' ) . ObjectID ; 
13+ var  NoOpCache  =  require ( './caches/NoOpCache' ) ; 
1314
1415
1516( require . modules  ||  { } ) . Model  =  module . exports  =  Model ; 
@@ -76,6 +77,11 @@ function Model(database, collection, schema, options) {
7677		enumerable : false 
7778	} ) ; 
7879
80+ 	Object . defineProperty ( this ,  'cache' ,  { 
81+ 		value : options . cache  ||  new  NoOpCache ( ) , 
82+ 		enumerable : false 
83+ 	} ) 
84+ 
7985
8086	var  extraValidators  =  [ ] ; 
8187	for ( var  i  =  0 ;  i  <  database . plugins . length ;  i ++ )  { 
@@ -167,7 +173,7 @@ Model.prototype.wrap = function (document, isNew) {
167173	return  new  this . Instance ( document ,  isNew ) ; 
168174} ; 
169175
170- Model . prototype . onRetrieved  =  function ( results ,  callback ,  wrapper )  { 
176+ Model . prototype . onRetrieved  =  function ( results ,  callback ,  wrapper ,   options )  { 
171177	///<signature> 
172178	///<summary>Handles any post-receive hooks and the wrapping of objects from the database</summary> 
173179	///<param name="results" type="Object">The object retrieved from the database</param> 
@@ -190,9 +196,23 @@ Model.prototype.onRetrieved = function(results, callback, wrapper) {
190196	///<param name="callback" type="Function">The function to be called once the objects have been wrapped</param> 
191197	///<param name="wrapper" type="Function">A function which converts the retrieved objects prior to submission</param> 
192198	///</signature> 
199+ 	///<signature> 
200+ 	///<summary>Handles any post-receive hooks and the wrapping of objects from the database</summary> 
201+ 	///<param name="results" type="Array" elementType="Object">The objects retrieved from the database</param> 
202+ 	///<param name="callback" type="Function">The function to be called once the objects have been wrapped</param> 
203+ 	///<param name="wrapper" type="Function">A function which converts the retrieved objects prior to submission</param> 
204+ 	///<param name="options" type="Object">A set of options determining how to handle the retrieved object</param> 
205+ 	///</signature> 
193206
194207	var  $  =  this ; 
195- 	wrapper  =  ( wrapper  !==  true  &&  wrapper )  ||  this . wrap . bind ( this ) ; 
208+ 
209+ 	wrapper  =  wrapper  ||  this . wrap . bind ( this ) ; 
210+ 	options  =  options  ||  { } ; 
211+ 
212+ 	_ . defaults ( options ,  { 
213+ 		wrap : true , 
214+ 		cache : true 
215+ 	} ) ; 
196216
197217	var  returnArray  =  Array . isArray ( results ) ; 
198218	if ( ! returnArray )  results  =  [ results ] ; 
@@ -216,12 +236,19 @@ Model.prototype.onRetrieved = function(results, callback, wrapper) {
216236			doHook ( this . options . hooks . retrieved ,  target ,  ( function ( err )  { 
217237				if ( err )  return  done ( err ) ; 
218238
219- 				var  wrapped  =  wrapper ( target ) ; 
239+ 				var  cacheDoc  =  _ . cloneDeep ( target ) ; 
240+ 
241+ 				var  wrapped  =  options . wrap  ? wrapper ( target )  : target ; 
220242
221- 				doHook ( this . options . hooks . ready ,  wrapped ,  function ( err )  { 
243+ 				doHook ( this . options . hooks . ready ,  wrapped ,  ( function ( err )  { 
222244					if ( err )  return  done ( err ) ; 
223- 					return  done ( null ,  wrapped ) ; 
224- 				} ) ; 
245+ 					if ( options . cache ) 
246+ 						return  this . cache . store ( cacheDoc ,  function ( )  { 
247+ 							return  done ( null ,  wrapped ) ; 
248+ 						} ) ; 
249+ 					else 
250+ 						return  done ( null ,  wrapped ) ; 
251+ 				} ) . bind ( this ) ) ; 
225252			} ) . bind ( this ) ) ; 
226253		} ) . bind ( this ) ; 
227254	} ,  this ) ,  function ( err ,  output )  { 
@@ -269,12 +296,10 @@ Model.prototype.onSaving = function(instance, changes, callback) {
269296		} 
270297	} 
271298
272- 	var  $  =  this ; 
273- 
274- 	doHook ( $ . options . hooks . saving ,  instance ,  [ changes ] ,  callback ) ; 
299+ 	doHook ( this . options . hooks . saving ,  instance ,  [ changes ] ,  callback ) ; 
275300} ; 
276301
277- Model . prototype . find  =  function  ( conditions ,  wrap ,  callback )  { 
302+ Model . prototype . find  =  function  ( conditions ,  options ,  callback )  { 
278303	/// <signature> 
279304	/// <summary>Gets all objects in the collection.</summary> 
280305	/// <param name="callback" type="Function">A function to be called with the results once they have been retrieved.</param> 
@@ -291,48 +316,54 @@ Model.prototype.find = function (conditions, wrap, callback) {
291316	/// </signature> 
292317	/// <signature> 
293318	/// <summary>Gets all objects in the collection.</summary> 
294- 	/// <param name="wrap " type="Boolean">Whether or not to wrap results in an Instance object </param> 
319+ 	/// <param name="options " type="Object">Options dictating how Iridium handles this request </param> 
295320	/// <param name="callback" type="Function">A function to be called with the results once they have been retrieved.</param> 
296321	/// </signature> 
297322	/// <signature> 
298323	/// <summary>Finds all occurences in the collection with an _id field matching the given conditions.</summary> 
299324	/// <param name="conditions" type="Mixed">The _id field of the object to locate</param> 
300- 	/// <param name="wrap " type="Boolean">Whether or not to wrap results in an Instance object </param> 
325+ 	/// <param name="options " type="Object">Options dictating how Iridium handles this request </param> 
301326	/// <param name="callback" type="Function">A function to be called with the results once they have been retrieved.</param> 
302327	/// </signature> 
303328	/// <signature> 
304329	/// <summary>Finds all occurences in the collection which match the given conditions.</summary> 
305330	/// <param name="conditions" type="Object">The conditions which will be used to select matches</param> 
306- 	/// <param name="wrap " type="Boolean">Whether or not to wrap results in an Instance object </param> 
331+ 	/// <param name="options " type="Object">Options dictating how Iridium handles this request </param> 
307332	/// <param name="callback" type="Function">A function to be called with the results once they have been retrieved.</param> 
308333	/// </signature> 
309334
310335	var  args  =  Array . prototype . splice . call ( arguments ,  0 ) ; 
311336
312- 	conditions  =  { } ; 
313- 	wrap  =  true ; 
337+ 	conditions  =  null ; 
338+ 	options  =  null ; 
314339
315340	for ( var  i  =  0 ;  i  <  args . length ;  i ++ )  { 
316341		if ( 'function'  ==  typeof  args [ i ] ) 
317342			callback  =  args [ i ] ; 
318- 		else  if ( 'boolean'  ==  typeof  args [ i ] ) 
319- 			wrap  =  args [ i ] ; 
320- 		else 
343+ 		else  if ( ! conditions ) 
321344			conditions  =  args [ i ] ; 
345+ 		else  options  =  args [ i ] ; 
322346	} 
323347
348+ 	conditions  =  conditions  ||  { } ; 
349+ 	options  =  options  ||  { } ; 
350+ 	_ . defaults ( options ,  { 
351+ 		wrap : true , 
352+ 		cache : true 
353+ 	} ) ; 
354+ 
324355	var  $  =  this ; 
325356	if  ( ! _ . isPlainObject ( conditions ) )  conditions  =  this . downstreamID ( conditions ) ; 
326357	this . toSource ( conditions ) ; 
327358
328359	this . collection . find ( conditions ) . toArray ( function  ( err ,  results )  { 
329360		if  ( err )  return  callback ( err ) ; 
330361		if  ( ! results )  return  callback ( null ,  null ) ; 
331- 		return  $ . onRetrieved ( results ,  callback ,  wrap  ||  function ( value )  {  return  value ;  } ) ; 
362+ 		return  $ . onRetrieved ( results ,  callback ,  options . wrap  ||  function ( value )  {  return  value ;  } ) ; 
332363	} ) ; 
333364} ; 
334365
335- Model . prototype . findOne  =  Model . prototype . get  =  function  ( conditions ,  wrap ,  callback )  { 
366+ Model . prototype . findOne  =  Model . prototype . get  =  function  ( conditions ,  options ,  callback )  { 
336367	/// <signature> 
337368	/// <summary>Gets a single object from the collection.</summary> 
338369	/// <param name="callback" type="Function">A function to be called with the results once they have been retrieved.</param> 
@@ -349,51 +380,69 @@ Model.prototype.findOne = Model.prototype.get = function (conditions, wrap, call
349380	/// </signature> 
350381	/// <signature> 
351382	/// <summary>Gets a single object from the collection.</summary> 
352- 	/// <param name="wrap " type="Boolean">Whether or not to wrap results in an Instance object </param> 
383+ 	/// <param name="options " type="Object">Options dictating how Iridium handles this request </param> 
353384	/// <param name="callback" type="Function">A function to be called with the results once they have been retrieved.</param> 
354385	/// </signature> 
355386	/// <signature> 
356387	/// <summary>Finds the first occurence in the collection with an _id field matching the given conditions.</summary> 
357388	/// <param name="conditions" type="Mixed">The _id field of the object to locate</param> 
358- 	/// <param name="wrap " type="Boolean">Whether or not to wrap results in an Instance object </param> 
389+ 	/// <param name="options " type="Object">Options dictating how Iridium handles this request </param> 
359390	/// <param name="callback" type="Function">A function to be called with the results once they have been retrieved.</param> 
360391	/// </signature> 
361392	/// <signature> 
362393	/// <summary>Finds the first occurence in the collection which matches the given conditions.</summary> 
363394	/// <param name="conditions" type="Object">The conditions which will be used to select matches</param> 
364- 	/// <param name="wrap " type="Boolean">Whether or not to wrap results in an Instance object </param> 
395+ 	/// <param name="options " type="Object">Options dictating how Iridium handles this request </param> 
365396	/// <param name="callback" type="Function">A function to be called with the results once they have been retrieved.</param> 
366397	/// </signature> 
367398
368399	var  args  =  Array . prototype . splice . call ( arguments ,  0 ) ; 
369400
370- 	conditions  =  { } ; 
371- 	wrap  =  true ; 
401+ 	conditions  =  null ; 
402+ 	options  =  null ; 
372403
373404	for ( var  i  =  0 ;  i  <  args . length ;  i ++ )  { 
374405		if ( 'function'  ==  typeof  args [ i ] ) 
375406			callback  =  args [ i ] ; 
376- 		else  if ( 'boolean'  ==  typeof  args [ i ] ) 
377- 			wrap  =  args [ i ] ; 
378- 		else 
407+ 		else  if ( ! conditions ) 
379408			conditions  =  args [ i ] ; 
409+ 		else  options  =  args [ i ] ; 
380410	} 
381411
412+ 	conditions  =  conditions  ||  { } ; 
413+ 	options  =  options  ||  { } ; 
414+ 	_ . defaults ( options ,  { 
415+ 		wrap : true , 
416+ 		cache : true 
417+ 	} ) ; 
382418
383- 	var  $  =  this ; 
384- 	if  ( ! _ . isPlainObject ( conditions ) )  conditions  =  this . downstreamID ( conditions ) ; 
385419
420+ 	var  isID  =  ! _ . isPlainObject ( conditions ) ; 
421+ 
422+ 	if  ( isID )  conditions  =  this . downstreamID ( conditions ) ; 
386423	this . toSource ( conditions ) ; 
387- 	
388- 	this . collection . findOne ( conditions ,  function  ( err ,  results )  { 
389- 		if  ( err )  return  callback ( err ) ; 
390- 		if  ( ! results )  return  callback ( null ,  null ) ; 
391- 		
392- 		return  $ . onRetrieved ( results ,  callback ,  wrap  ||  function ( value )  {  return  value ;  } ) ; 
393- 	} ) ; 
424+ 
425+ 	var  fromDB  =  ( function ( )  { 	
426+ 		this . collection . findOne ( conditions ,  ( function  ( err ,  results )  { 
427+ 			if  ( err )  return  callback ( err ) ; 
428+ 			if  ( ! results )  return  callback ( null ,  null ) ; 
429+ 			
430+ 			return  this . onRetrieved ( results ,  callback ,  null ,  {  wrap : options . wrap ,  cache : options . cache  } ) ; 
431+ 		} ) . bind ( this ) ) ; 
432+ 	} ) . bind ( this ) ; 
433+ 
434+ 	if ( isID  &&  this . cache  &&  options . cache ) 
435+ 		this . cache . fetch ( conditions . _id ,  ( function ( err ,  doc )  { 
436+ 			if ( ! err  &&  doc ) 
437+ 				return  this . onRetrieved ( doc ,  callback ,  null ,  {  wrap : options . wrap ,  cache : false  } ) ; 
438+ 			else 
439+ 				return  fromDB ( ) ; 
440+ 		} ) . bind ( this ) ) ; 
441+ 	else 
442+ 		return  fromDB ( ) ; 
394443} ; 
395444
396- Model . prototype . insert  =  Model . prototype . create  =  function  ( object ,  wrap ,  callback )  { 
445+ Model . prototype . insert  =  Model . prototype . create  =  function  ( object ,  options ,  callback )  { 
397446	/// <signature> 
398447	/// <summary>Inserts the given object into the database</summary> 
399448	/// <param name="object" type="Object">The properties to set on the newly created object</param> 
@@ -414,13 +463,13 @@ Model.prototype.insert = Model.prototype.create = function (object, wrap, callba
414463	/// </signature> 
415464	/// <summary>Inserts the given object into the database</summary> 
416465	/// <param name="object" type="Object">The properties to set on the newly created object</param> 
417- 	/// <param name="wrap " type="Boolean">Whether or not to wrap results in an Instance object </param> 
466+ 	/// <param name="options " type="Object">Options dictating how Iridium handles this request </param> 
418467	/// <param name="callback" type="Function">A function to be called once the object has been created</param> 
419468	/// </signature> 
420469	/// <signature> 
421470	/// <summary>Inserts the given object into the database</summary> 
422471	/// <param name="object" type="Array" elementType="Object">An array of objects representing the properties to set on the newly created objects</param> 
423- 	/// <param name="wrap " type="Boolean">Whether or not to wrap results in an Instance object </param> 
472+ 	/// <param name="options " type="Object">Options dictating how Iridium handles this request </param> 
424473	/// <param name="callback" type="Function">A function to be called once the objects have been created</param> 
425474	/// </signature> 
426475
@@ -429,10 +478,14 @@ Model.prototype.insert = Model.prototype.create = function (object, wrap, callba
429478	var  returnArray  =  true ; 
430479
431480	if ( ! callback )  { 
432- 		callback  =  wrap ; 
433- 		wrap  =  true ; 
481+ 		callback  =  options ; 
482+ 		options  =  options   ||   { } ; 
434483	} 
435484
485+ 	_ . defaults ( options ,  { 
486+ 		wrap : true 
487+ 	} ) ; 
488+ 
436489	if ( ! Array . isArray ( object ) )  { 
437490		object  =  [ object ] ; 
438491		returnArray  =  false ; 
@@ -454,7 +507,7 @@ Model.prototype.insert = Model.prototype.create = function (object, wrap, callba
454507		$ . collection . insert ( prepped ,  {  w : callback  ? 1  : 0  } ,  function ( err ,  inserted )  { 
455508			if ( err )  return  end ( err ) ; 
456509			if ( callback ) 
457- 				return  $ . onRetrieved ( inserted ,  end ,  wrap   ||   function ( value )   {   return   value ;   } ) ; 
510+ 				return  $ . onRetrieved ( inserted ,  end ,  null ,   options ) ; 
458511			return  end ( ) ; 
459512		} ) ; 
460513	} ; 
0 commit comments