1 // ==========================================================================
  2 // Project:   SproutCore Costello - Property Observing Library
  3 // Copyright: ©2006-2011 Strobe Inc. and contributors.
  4 //            Portions ©2008-2011 Apple Inc. All rights reserved.
  5 // License:   Licensed under MIT license (see license.js)
  6 // ==========================================================================
  7 
  8 /** @namespace
  9   The `SC.ObjectMixinProtocol` protocol defines the properties and methods that you may implement
 10   in your mixin objects (i.e. JavaScript Objects passed to SC.Object's `extend` or `create`) in
 11   order to access additional functionality when used. They will be used if defined but are not
 12   required.
 13 
 14   # What is a Mixin?
 15 
 16   A mixin, in this context, is a simple JavaScript Object that can be used to provide extra
 17   functionality to SC.Object subclasses. While you can mix JavaScript Objects into "classes" (i.e.
 18   using `SC.mixin(SomeClass)`), this particular protocol only refers to mixins in the context of
 19   use with an SC.Object "instance" (i.e. SC.Object.create({ ... })).
 20 
 21   For example, in order to share a method between two different classes of object, we can use a
 22   mixin object that both will consume,
 23 
 24       // Common default properties and shared methods which our different classes may consume.
 25       MyApp.MyMixin = {
 26         a: true,
 27 
 28         aFunc: function () {
 29           this.set('a', false);
 30         },
 31 
 32         b: [], // SHARED OBJECT!
 33 
 34         c: {} // SHARED OBJECT!
 35       };
 36 
 37       // Two different object types, which both need the functionality provided by MyApp.MyMixin.
 38       MyApp.ObjectType1 = SC.Object.extend(MyApp.MyMixin);
 39       MyApp.ObjectType2 = SC.Object.extend();
 40 
 41       obj1 = MyApp.ObjectType1.create();
 42       obj2 = MyApp.ObjectType2.create(MyApp.MyMixin);
 43 
 44       // Some proofs.
 45       // 1. The default properties are copied over to the new objects.
 46       obj1.get('a'); // true <--
 47       obj2.get('a'); // true <--
 48 
 49       // 2. The primitive properties are unique to each object.
 50       obj1.set('a', false);
 51       obj1.get('a'); // false <--
 52       obj2.get('a'); // true <--
 53 
 54       // 3. The methods are copied over to the new objects.
 55       obj1.aFunc; // function () { ... } <--
 56       obj2.aFunc; // function () { ... } <--
 57 
 58       // 4. The functions/objects are shared between objects.
 59       obj1.aFunc === MyApp.MyMixin.aFunc; // true <--
 60       obj1.aFunc === obj2.aFunc; // true <--
 61       obj1.b === obj2.b; // true <-- !! Beware of modifying this object !!
 62       obj1.c === obj2.c; // true <-- !! Beware of modifying this object !!
 63 
 64   In this example, we used a mixin to share functionality between two classes, which is very easily
 65   achieved. There is one issue, that has been known to trip up developers, which should be
 66   highlighted. If you set default *Objects* (e.g. [] or {}) in a mixin, these same Objects will be
 67   shared between all of the mixin's consumers.
 68 
 69   If you want to set a default Object that is unique to each consumer of the mixin, a better
 70   practice is to set it in `initMixin()` or to check for its existence the first time it is used
 71   and only create it then.
 72 
 73   *Note: Do not mix `SC.ObjectMixinProtocol` into your classes. As a protocol, it exists only for
 74   reference sake. You only need define any of the properties or methods listed below in order to use
 75   this protocol.*
 76 */
 77 SC.ObjectMixinProtocol = {
 78 
 79   /**
 80     This *optional* method is called to further initialize the consumer of the mixin when it is
 81     created. When a mixin (i.e. JavaScript Object) is used to extend an `SC.Object` subclass, we may
 82     want to perform additional set up of the `SC.Object` instance when it is created according to
 83     the needs of the mixin. In order to support this, `SC.Object` will call this method,
 84     `initMixin`, *if implemented*, on each mixin in the order that they were added.
 85 
 86     For example, if we use two mixins that both initialize the same value, the last mixin added
 87     would win,
 88 
 89         myObject = SC.Object.create(
 90           // First mixin.
 91           {
 92             initMixin: function () {
 93               this.set('a', true);
 94             }
 95           },
 96 
 97           // Second mixin.
 98           {
 99             initMixin: function () {
100               this.set('a', false);
101             }
102           });
103 
104         myObject.get('a'); // false <--
105 
106     This was just an example to illustrate the order in which `initMixin` is called. It is rare
107     that mixins will collide with each other, but it is something to bear in mind when making heavy
108     use of mixins.
109 
110     Note, that unlike the similar `init()` method of `SC.Object`, you do *not* need to call
111     `sc_super` in `initMixin`.
112   */
113   initMixin: function () {},
114 
115   /**
116     This *optional* method is called to further de-initialize the consumer of the mixin when it is
117     destroyed. When a mixin (i.e. JavaScript Object) is used to extend an `SC.Object` subclass, we
118     may want to perform additional teardown of the `SC.Object` instance when it is destroyed
119     according to the needs of the mixin (e.g. to clean up objects that the mixin code initialized
120     and that may otherwise lead to memory leaks). In order to support this, `SC.Object` will call
121     this method, `destroyMixin`, *if implemented*, on each mixin in the order that they were
122     initially added.
123 
124     For example, if we use two mixins that both de-initialize the same value, the last mixin added
125     would win,
126 
127         myObject = SC.Object.create(
128           // Mixin.
129           {
130             initMixin: function () {
131               // Created extra object for some purpose.
132               this.set('anObject', SC.Object.create());
133             },
134 
135             destroyMixin: function () {
136               // Clean up extra object that the mixin is responsible for.
137               var anObject = this.get('anObject');
138               anObject.destroy();
139               this.set('anObject', null);
140             }
141           });
142 
143         myObject.get('a'); // false <--
144 
145     Note, that unlike the similar `destroy()` method of `SC.Object`, you do *not* need to call
146     `sc_super` in `destroyMixin`.
147   */
148   destroyMixin: function () {}
149 
150 };
151