UNPKG

7.04 kBJavaScriptView Raw
1/// boo.js --- Base primitives for prototypical OO
2//
3// Copyright (c) 2011 Quildreen "Sorella" Motta <quildreen@gmail.com>
4//
5// Permission is hereby granted, free of charge, to any person
6// obtaining a copy of this software and associated documentation files
7// (the "Software"), to deal in the Software without restriction,
8// including without limitation the rights to use, copy, modify, merge,
9// publish, distribute, sublicense, and/or sell copies of the Software,
10// and to permit persons to whom the Software is furnished to do so,
11// subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be
14// included in all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24/// Module boo
25void function(root, exports) {
26
27 //// -- Aliases -------------------------------------------------------------
28 var slice = [].slice
29 var keys = Object.keys
30 var inherit = Object.create
31 var define = Object.defineProperty
32 var descriptor = Object.getOwnPropertyDescriptor
33 var has_getter_p = function () {
34 try {
35 return true === Object.create( {}
36 , { x: { get:
37 function(){
38 return true }}}).x }
39 catch(e){ return false }}()
40
41
42
43 //// -- Interfaces ----------------------------------------------------------
44
45 ///// Interface DataObject
46 // DataObject :: { "to_data" -> () -> Object }
47
48 ///// Interface Mixin
49 // Mixin :: Object | DataObject
50
51
52 //// -- Helpers -------------------------------------------------------------
53
54 ///// Function copy_property
55 // :internal:
56 // Copies a property from ``source`' to ``target`'.
57 //
58 // copy_property! :: Object, target:Object*, String -> target
59 function copy_property(source, target, property) {
60 !has_getter_p? target[property] = source[property]
61 : /* otherwise */ define(target, property, descriptor(source, property))
62
63 return target
64 }
65
66 ///// Function data_obj_p
67 // :internal:
68 // Checks if the given subject matches the ``DataObject`` interface
69 //
70 // data_obj_p :: Any -> Bool
71 function data_obj_p(subject) {
72 return subject != null
73 && typeof subject.to_data == 'function' }
74
75
76 ///// Function resolve_mixins
77 // :internal:
78 // Returns the proper object for the given mixin.
79 //
80 // resolve_mixin :: Mixin -> Object
81 function resolve_mixin(subject) {
82 return data_obj_p(subject)? subject.to_data()
83 : /* otherwise */ subject }
84
85
86 ///// Function fast_extend
87 // :internal:
88 // Extends the target object with the provided mixins, using a
89 // right-most precedence rule — when a there's a property conflict, the
90 // property defined in the last object wins.
91 //
92 // ``DataObject``s are properly handled by the ``resolve_mixin``
93 // function.
94 //
95 // :warning: low-level
96 // This function is not meant to be called directly from end-user
97 // code, use the ``extend`` function instead.
98 //
99 // fast_extend! :: target:Object*, [Mixin] -> target
100 function fast_extend(object, mixins) {
101 var i, j, len, mixin, props, key
102 for (i = 0, len = mixins.length; i < len; ++i) {
103 mixin = resolve_mixin(mixins[i])
104 props = keys(mixin)
105 for (j = props.length; j--;) {
106 key = props[j]
107 copy_property(mixin, object, key) }}
108
109 return object }
110
111
112
113 //// -- Basic primitives ----------------------------------------------------
114
115 ///// Function extend
116 // Extends the target object with the provided mixins, using a
117 // right-most precedence rule.
118 //
119 // :see-also:
120 // - ``fast_extend`` — lower level function.
121 // - ``merge`` — pure version.
122 //
123 // extend! :: target:Object*, Mixin... -> target
124 function extend(target) {
125 return fast_extend(target, slice.call(arguments, 1)) }
126
127
128 ///// Function merge
129 // Creates a new object that merges the provided mixins, using a
130 // right-most precedence rule.
131 //
132 // :see-also:
133 // - ``extend`` — impure version.
134 //
135 // merge :: Mixin... -> Object
136 function merge() {
137 return fast_extend({}, arguments) }
138
139
140 ///// Function derive
141 // Creates a new object inheriting from the given prototype and extends
142 // the new instance with the provided mixins.
143 //
144 // derive :: proto:Object, Mixin... -> Object <| proto
145 function derive(proto) {
146 return fast_extend(inherit(proto), slice.call(arguments, 1)) }
147
148
149 ///// Function make
150 // Constructs a new instance of the given object.
151 //
152 // If the object provides an ``init`` function, that function is
153 // invoked to do initialisation on the new instance.
154 //
155 // make :: proto:Object, Any... -> Object <| proto
156 function make(base) {
157 return Base.make.apply(base, slice.call(arguments, 1)) }
158
159
160
161 //// -- Root object ---------------------------------------------------------
162
163 ///// Object Base
164 // The root object for basing all the OOP code. Provides the previous
165 // primitive combinators in an easy and OOP-way.
166 var Base = {
167
168 ////// Function make
169 // Constructs new instances of the object the function is being
170 // applied to.
171 //
172 // If the object provides an ``init`` function, that function is
173 // invoked to do initialisation on the new instance.
174 //
175 // make :: @this:Object, Any... -> Object <| this
176 make:
177 function _make() {
178 var result = inherit(this)
179 if (typeof result.init == 'function')
180 result.init.apply(result, arguments)
181
182 return result }
183
184 ////// Function derive
185 // Constructs a new object that inherits from the object this function
186 // is being applied to, and extends it with the provided mixins.
187 //
188 // derive :: @this:Object, Mixin... -> Object <| this
189 , derive:
190 function _derive() {
191 return fast_extend(inherit(this), arguments) }}
192
193
194
195 //// -- Exports -------------------------------------------------------------
196 exports.extend = extend
197 exports.merge = merge
198 exports.derive = derive
199 exports.make = make
200 exports.Base = Base
201 exports.internal = { data_obj_p : data_obj_p
202 , fast_extend : fast_extend
203 , resolve_mixin : resolve_mixin
204 , copy_property : copy_property
205 }
206
207}
208( this
209, typeof exports == 'undefined'? this.boo = this.boo || {}
210 /* otherwise, yay modules! */: exports
211)