aboutsummaryrefslogtreecommitdiff
path: root/tools/addon-sdk-1.3/packages/api-utils/docs/traits.md
blob: 66e8f02e3619946df590dd882232473f0faf650f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
<!-- contributed by Irakli Gozalishvil [gozala@mozilla.com]  -->

The `traits` module provides base building blocks for secure object
composition. It exports base trait / constructor function that
constructs an instance of `Trait`.

[Traits](http://en.wikipedia.org/wiki/Trait_%28computer_science%29) are a
simple composition mechanism for structuring object-oriented programs. Traits
are similar to
[interfaces](http://en.wikipedia.org/wiki/Interface_%28object-oriented_programming%29),
except that they often define only a part of an object's data and behavior and
are intended to be used in conjunction with other traits to completely define
the object.

Traits are also considered to be a more robust alternative to
[mixins](http://en.wikipedia.org/wiki/Mixins) because, name conflicts have to
be resolved explicitly by composer & because trait composition is
order-independent (hence more declarative).


There are some other implementations of traits in JavaScript & some ideas /
APIs are borrowed from them:

- [traitsjs](http://www.traitsjs.org/)
- [joose](http://code.google.com/p/joose-js/)

Object-capability security model
--------------------------------

Implementation uses an
[object-capability security model](http://en.wikipedia.org/wiki/Object-capability_model)
to allow protection of private APIs. At the same private APIs can be shared
between among trait composition parties. To put it simply: All the properties
whose names start with `"_"` are considered to be **private**, and are
unaccessible from anywhere except other **public** methods / accessors of the
instance that had been defined during composition.

<api name="Trait">
@class
<api name="Trait">
@constructor
Creates an instance of Trait and returns it if it has no `constructor` method
defined. If instance has `constructor` method, then it is called with all the
arguments passed to this function and returned value is returned instead,
unless it's `undefined`. In that case instance is returned.

`Trait` function represents a base trait. As with any other trait it represents
a constructor function for creating instances of its own & a placeholder
for a trait compositions functions.
</api>

<api name="compose">
@method
Composes new trait out of itself and traits / property maps passed as an
arguments. If two or more traits / property maps have properties with the same
name, the new trait will contain a "conflict" property for that name (see
examples in Examples section to find out more about "conflict" properties).
This is a commutative and associative operation, and the order of its
arguments is not significant.

**Examples:**

Let's say we want to define a reusable piece of code for a lists of elements.

    var { Trait } = require('traits');
    var List = Trait.compose({
      // private API:
      _list: null,
      // public API
      constructor: function List() {
        this._list = [];
      },
      get length() this._list.length,
      add: function add(item) this._list.push(item),
      remove: function remove(item) {
        let list = this._list;
        let index = list.indexOf(item);
        if (0 <= index) list.splice(index, 1);
      }
    });

Instances of `List` can be created by calling `List` function with or without
`new` keyword.

    let l1 = List();
    l1 instanceof List;      // true
    let l2 = new List();
    l2 instanceof List;      // true

As you can see `add` and `remove` functions are capable of accessing private
`_list` property, but thats about it, there's nothing else that will be able
to access this property:

    '_list' in l1;              // false
    '_list' in l2;              // false
    '_list' in List.protoype;   // false
    l1.has = function(name) name in this
    l1.has('_list');            // false
    l1.length;                  // 0
    l1.add('test')
    l1.length                   // 1

@param trait1 {Object|Function}
    Trait or property map to compose new trait from.
@param trait2 {Object|Function}
    Trait or property map to compose new trait from.
@param ... {Object|Function}
    Traits or property maps to compose new trait from.

@returns {Function}
    New trait containing the combined properties of all the traits.
</api>

<api name="required">
@property {Object}
Singleton, used during trait composition to define "required" properties.

**Example:**

    var Enumerable = Trait.compose({
      list: Trait.required,
      forEach: function forEach(consumer) {
        return this.list.forEach(consumer);
      }
    });

    let c1 = Enumerable();      // Error: Missing required property: list

    var EnumerableList = List.compose({
      get list() this._list.slice(0)
    }, Enumerable);

    let c2 = EnumerableList();
    c2.add('test')
    c2.length                   // 1
    c2.list[0]                  // 'test'
    c2.forEach(console.log)     // > info: 'test 0 test'

</api>


<api name="resolve">
@method
Composes a new trait that has all the same properties
as the trait on which it is called, except that each property listed
in the `resolutions` argument will be renamed from the name
of the  property in the `resolutions` argument to its value.
And if its value is `null`, the property will become required.

**Example:**

    var Range = List.resolve({
      constructor: null,
      add: '_add',
    }).compose({
      min: null,
      max: null,
      get list() this._list.slice(0),
      constructor: function Range(min, max) {
        this.min = min;
        this.max = max;
        this._list = [];
      },
      add: function(item) {
        if (item <= this.max && item >= this.min)
          this._add(item)
      }
    });


    let r = Range(0, 10);
    r.min;                      // 0
    r.max;                      // 10
    r.length;                   // 0;
    r.add(5);
    r.length;                   // 1
    r.add(12);
    r.length;                   // 1 (12 was not in a range)

@param resolutions {Object}
@returns {Function}
    New resolved trait.
</api>

<api name="override">
@method
Composes a new trait with all of the combined properties of `this` and the
argument traits. In contrast to `compose`, `override` immediately resolves
all conflicts resulting from this composition by overriding the properties of
later traits. Trait priority is from left to right. I.e. the properties of
the leftmost trait are never overridden.

**Example:**

    // will compose trait with conflict property 'constructor'
    var ConstructableList = List.compose({
      constructor: function List() this._list = Array.slice(arguments)
    });
    // throws error with message 'Remaining conflicting property: constructor'
    ConstructableList(1, 2, 3);

    var ConstructableList = List.override({
      constructor: function List() this._list = Array.slice(arguments)
    });
    ConstructableList(1, 2, 3).length       // 3

@param trait1 {Object|Function}
    Trait or property map to compose new trait from.
@param trait2 {Object|Function}
    Trait or property map to compose new trait from.
@param ... {Object|Function}
    Traits or property maps to compose new trait from.

@returns {Function}
    New trait containing the combined properties of all the traits.
</api>

<api name="_public">
@property {Object}
Internal property of instance representing public API that is exposed to the
consumers of an instance.
</api>

<api name='toString'>
@method
Textual representation of an object. All the traits will return:
`'[object Trait]'` string, unless they have `constructor` property, in that
case string `'Trait'` is replaced with the name of `constructor` property.

**Example:**

    var MyTrait = Trait.compose({
      constructor: function MyTrait() {
        // do your initialization here
      }
    });
    MyTrait().toString();     // [object MyTrait]

</api>
</api>