proteus
=======

> A declarative way of creating objects, properties, and classes in ES5 JavaScript

### ˈprōtēəs; ˈprōˌt(y)oōs

>   In Greek Mythology a minor sea god (son of Oceanus and Tethys) who had the
>   power of prophecy but who would assume different shapes to avoid answering
>   questions.

>   From the Greek protos "first."

Overview
--------

**Proteus** is a little library of utility functions that I put together to help manage creating objects and implementing classical inheritance in JavaScript flavors 1.8+.

Object Creation and Modification
--------------------------------

### Proteus.create(proto, props)

Interface to Object.create, however, the props argument is a plain object of properties to copy over to the newly created object.  Getters and setters will be preserved.

### Proteus.defineProperty(obj, name, val, [spec])

Utility method for creating 'plain' properties on an object. Plain being {enumerable: true, writable: true, configurable: true}, the passed spec can override these defaults.

```js
Proteus.defineProperty(obj, "propName", 42);

Proteus.defineProperty(
    obj,
    "methodName",
    function () { /*...*/ },
    {enumerable: false}
);
```

### Proteus.defineProperties(obj, list)

Define multiple properties.

```js
Proteus.defineProperties(obj, [
    [obj, "propName", 42],
    [obj, "methodName", function () {
        /*...*/
    }, {
        enumerable: false
    }]
]);
```

### Proteus.defineGetter(obj, name, fn, [spec])

Utility method for creating a getter on an object. The property definition will default to {enumerable: true, configurable: true} unless overridden with the spec object.

### Proteus.defineSetter(obj, name, fn, [spec])

Utility method for creating a setter on an object. The property definition will default to {enumerable: true, configurable: true} unless overridden with the spec object.

### Proteus.defineGetSet(obj, name, getter, [setter], [spec])

Utility method for creating both a getter and a setter on an object. The property definition will default to {enumerable: true, configurable: true} unless overridden with the spec object.

if `setter` is not given, then the `getter` function will be used for both getting and setting

### Proteus.getPropertyNames(obj)

Get all property names for an object, all the way up the prototype chain.

### Proteus.getPropertyDescriptor(obj, name)

Get a property descriptor for an object where ever it may exist in the prototype chain.

### Proteus.copyOwnProperties(hidden = false, overwrite = true, supplier, receiver)

Copy properties from `supplier` to `receiver`. This method will preserve the property definitions from `supplier` to `receiver` (uses `Object.getOwnPropertyDescriptor` under the hood).

### Proteus.copyAllProperties(supplier, receiver)

Copy all properties from `supplier` to `receiver`.

### Proteus.applyProperties(supplier, receiver)

Copy all *enumerable* properties from `supplier` to `receiver` only if the property *does not* exist on the `receiver` object.

### Proteus.applyAllProperties(supplier, receiver)

Copy all properties from `supplier` to `receiver`, but do not overwrite existing properties on `receiver`.

### Proteus.merge(receiver, arg1, ..., argN)

Merge *enumerable* properties from all objects passed as arguments onto `receiver`.

### Proteus.mergeAll(receiver, arg1, ..., argN)

Merge all properties from all objects passed as arguments onto `reciever`.

### Proteus.apply(receiver, arg1, ..., argN)

Merge *enumerable* properties from all objects passed as arguments onto `receiver`, only if they do not exist on `receiver`.

### Proteus.applyAll(receiver, arg1, ..., argN)

Merge all properties from all objects passed as arguments onto `receiver`, only if they do not exist on `receiver`.

Proteus Utility Methods
-----------------------

<!--
### Proteus.extend(extendee, extender1, ..., extenderN)

Extend one object with the properties of another.

If the extending object (`extender1, ..., extenderN`) has a function named `extended`, it will be called with one argument, the object that was extended (`extendee`).

### Proteus.include(self, obj1, ..., objN)

Include properties onto the prototype of 'self' from a *Constructor* function's prototype, or a plain object. If `obj` has a property named `self`, the properties of `self` will be merged into the passed 'self'.

If the supplying object, `obj`, has a function named `included` it will be called with one argument, the 'self' object that was just modified.

### Proteus.derive(parent, props)

Derive a new *Constructor* function from another, optionally adding the supplied properties to its prototype.

If the parent *Constructor* has a function named `inherited` it will be called with one argument, the new *Constructor* function.
-->

### Proteus.slice(list, offset = 0, end = list.length)

Return a portion of the *array-like* object.

### Proteus.aliasMethod(name, [scope])

Return a function that is bound to call another function on the current object, or the supplied one.

### Proteus.delegateMethod(obj, name, [args, ...])

Delegate a function call to another object. Additional arguments will be *prepended* to the function call.

### Proteus.applyProto(self, [name], args = [])

Apply a method from the `self` object's prototype chain.

The `name` argument is optional if the function you are invoking from, and the one up the prototype chain you wish to invoke is named. e.g:

```js
var Proteus = require("proteus"),
    objA = {
        someMethod: function someMethod () {
            console.log("I'm object A");
        }
    },
    objB = Proteus.create(objA, {
        someMethod: function someMethod () {
            Proteus.applyProto(this, arguments);
            console.log("I'm object B");
        }
    })
;

objB.someMethod();

// => I'm object A
// => I'm object B
```

### Proteus.callProto(self, name, [arg1], [...], [argN])

Call a method `name` from the `self` object's prototype chain passing the remaining arguments as arguments.

*Note:* the `name` parameter is **not** optional.


Proteus.Class
-------------

`Proteus.Class` starts off the Proteus inheritance chain (itself is a descendent of `Object`). From there you can derive new classes from `Proteus.Class` (or any classes that are derived from `Proteus.Class`) to develop your class hierarchy.

```js
var MyClass = Proteus.Class.derive({
        // props for MyClass
    }),
    MySubClass = MyClass.derive({
        // props for MySubClass
    })
;
```

### Deriving Inheritance

**_ProteusClass_.derive(props)**

As shown above, use the static methods on the core `Proteus.Class`, or the *Constructor* functions returned by `derive`, to derive new subclasses.

In addition, `Proteus` allows you to define static properties on your subclass' *Constructor* function. Simply provide a property `self` in the passed properties for your class, and those will be copied to the *Constructor* function instead of the *Constructor's prototype*.

```js
var MyClass = Proteus.Class.derive({
        self: {
            // Static properties for 'MyClass'
            myStaticMethod: function () {
                return true;
            }
        },
        // All other properties are put on the prototype
        instancePropA: "somevalue"
    });
    
MyClass.myStaticMethod(); // => true
(new MyClass()).instancePropA === "somevalue"; // => true
```

#### The Inherited Event

When one class is derived from another, and the superclass possesses a function property named `inherited`, the function will be called and passed the newly created *Constructor* function (i.e: the new subclass).

```js
var MyBaseClass = Proteus.Class.derive({
        self: {
            inherited: function (subclass) {
                subclass.superParent = "MyBaseClass";
            }
        }
    }),
    MySubClass = MyBaseClass.derive({/* ... */})
;

MySubClass.superParent === "MyBaseClass"; // => true
```

### *ProteusClass*.\_\_super\_\_ Property

Every class derived from Proteus.Class has a `__super__` property that points to its super class' prototype.

```js
var MyBaseClass = Proteus.Class.derive({
        someMethod: function () {
            // ...
        }
    }),
    MySubClass = MyBaseClass.derive({
        someMethod: function () {
            MySubClass.__super__.someMethod.call(this);
        }
    })
;
```

### Including Instance Functionality

**_ProteusClass_.include(obj1, ..., objN)**

You can mix-in functionality from other *Constructor* function prototype's, or plain objects, into Proteus derived classes with the `include` method.

If passed a *Constructor* function, Proteus will copy the function's prototype properties to your Class' prototype. If passed a plain object, it will copy over those properties to the prototype of the class.

```js
// Copy 'OtherClass' prototype properties to 'MyClass' prototype.
MyClass.include(OtherClass);

// Merge the passed object into 'MyClass'
MyClass.include({
    // Additional properties for MyClass.prototype
});
```

#### The Included Event

When an object, or *Constructor* function, is included into a Proteus Class its `included` function will be called and given the object that it was included into (the Proteus Class *Constructor*).

```js
var MyModule = Proteus.Class.derive({
        self: {
            decorators: [],
            included: function (includee) {
                includee.decorators = this.decorators.slice();
            }
        }
    }),
    MyClass = Proteus.Class.derive({/* ... */})
;

MyClass.include(MyModule);
MyClass.decorators; // => []
```

#### Extending Your Class

**_ProteusClass_.extend(obj)**

You can use a *ProteusClass's* `extend` method to extend another object with its functionality.

```js
var MyClass = Proteus.class.derive({
        self: {
            someMethod: function () {
                // ...
            }
        }
    });
    
// elsewhere
MyClass.extend(obj);
typeof MyClass.someMethod; // => "function"
```

#### The Extended Event

### Instantiation

Report an Issue
---------------

* [Bugs](http://github.com/jhamlet/proteus/issues)
* Contact the author: <jhamlet@hamletink.com>


License
-------

> Copyright (c) 2012 Jerry Hamlet <jerry@hamletink.com>
> 
> Permission is hereby granted, free of charge, to any person
> obtaining a copy of this software and associated documentation
> files (the "Software"), to deal in the Software without
> restriction, including without limitation the rights to use,
> copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the
> Software is furnished to do so, subject to the following
> conditions:
> 
> The above copyright notice and this permission notice shall be
> included in all copies or substantial portions of the Software.
> 
> The Software shall be used for Good, not Evil.
> 
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
> OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
> HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
> WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> OTHER DEALINGS IN THE SOFTWARE.

