Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Finished first release.

Completely documented and tested.
  • Loading branch information...
commit c3b62a9116be88d14789bcb291b05863337cace7 1 parent 68b2ae8
@daankets authored
View
99 README.md
@@ -1,8 +1,95 @@
-Blackbit Consulting - Common node classes & utilities
-=====================================================
+Private.JS
+==========
-This library contains many common classes and utilties
-for use with node.js.
+Introduction
+------------
+
+**What is private.js?**
+
+Private.js is a [JavaScript](http://www.ecma-international.org/publications/standards/Ecma-262.htm) library that allows you to work with 'private' variables and methods withing JavaScript
+classes. The library is compatible with browsers and server-side javascript (for example [node.js](http://nodejs.org)).
+
+The library respects the following best-practices/conventions:
+
+- Adheres to **'use strict'**.
+- Uses **prototypes** - does NOT re-create methods per instance.
+- Is fully **documented** with **JSDoc** and a **MarkDown** reference.
+- Follows the **_underscore** notation convention for private variables in JavaScript
+- Exposes a **DSL** for convenience.
+- Respects '**this**' contract even within private scope.
+- Fully **unit-tested** (using nodeunit).
+
+Documentation
+-------------
+
+You can find the documentation [here](./doc/index.md).
+
+Example
+-------
+As nothing can tell you more than an example, here is one:
+
+ var Private = require('private').Private;
+
+ // If using Node.JS only:
+ // var util = require('util'); // Node.JS only
+
+ var MyClass = function(){
+
+ Private.call(this); // Extend the Private class (and call the super constructor).
+
+ // Initialize a private variable. This corresponds to this._privates
+ Private(this).setValue('Some private value');
+
+ };
+ MyClass.prototype = Object.create(Private.prototype); // Extend the prototype
+
+ // Or using Node.JS style:
+ // util.inherits(MyClass, Private);
+
+ // Public getValue method with access to private variables (style 1):
+
+ MyClass.prototype.getValue = Private.hasAccess(function(privates){
+ return privates.myValue;
+ });
+
+ // Invocation of the getValue method is transparent (as the privates are injected):
+ MyClass.prototype.toString = function(){
+
+ // Invoke the getValue method - without parameters!
+ return this.getValue();
+ };
+
+ // Or, the alternative - without parameter injection (style 2):
+ MyClass.prototype.getValue2 = function(){
+
+ // Or access the private value directly, through Private(...).
+ return Private(this).myValue;
+ };
+
+ // Declare the Private method setValue:
+ MyClass.prototype.privateMethod('setValue',function(privates, value){
+ privates.myValue = value;
+ });
+
+ // Private method toJSON:
+ MyClass.prototype.privateMethod('toJSON', function(privates){
+
+ // Note that 'this' within this context is the instance of MyClass, NOT the privates.
+ return JSON.stringify(this); // JSON stringification will NOT include the privates.
+ });
+
+
+
+Copyright
+---------
+
+Copyright (C) 2012 by [Daan Kets](mailto:daankets@blackbit.be), [Blackbit Consulting](http://www.blackbit.be)
+
+License
+-------
+![Creative Commons Attribution-ShareAlike 3.0 Unported License](http://i.creativecommons.org/l/by-sa/3.0/88x31.png)
+
+This library by [Daan Kets - Blackbit Consulting](http://www.blackbit.be) is licensed under a [Creative Commons Attribution-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-sa/3.0/).
+You may use this library even within a commercial product as long as you attribute to the original source.
+You may created derived work as long as you give back your modifications to the orignal source.
-This library is NOT open source, but corporate
-property of Blackbit Consulting, Belgium.
View
135 doc/index.md
@@ -0,0 +1,135 @@
+Private.JS documentation
+------------------------
+
+The Private function/class
+--------------------------
+
+The '**Private**' function exposed by the library acts both as a constructor and a classic function.
+
+The **constructor** style is used for creating a new object **instance** with private members, or as a **base class** for
+defining new classes with private member support.
+
+You do NOT necessarily need to extend though, you may just 'enable' a prototype or object with private support as well.
+
+- Instance:
+`var objectWithPrivates = new Private();`
+- Extension:
+`var MyClass = function(){
+ Private.call(this);
+};
+MyClass.prototype = Object.create(Private.prototype);
+var myObject = new MyClass();`
+- Enable:
+`var MyClass = function(){
+ Private.enable(this);
+};
+Private.enable(MyClass.prototype);`
+
+The **function** style is used for retrieving the private members object for an instance (or prototype). This function is
+used internally for injection as well:
+
+Note that if you use **Private.enable**, you _SHOULD_ apply this method to the prototype and you **MUST** apply this
+method to the object (preferably within or the object constructor).
+
+- Function:
+`var privates = Private(this); // Store a reference to the privates into 'privates'`
+
+How does it work?
+-----------------
+
+The **Private(**object**)** function actually retrieves a hidden member of the object or prototype, that was defined by
+the prototype of the Private class. This object is not visible in most debuggers and IDE's due to the use of an
+_(underscore) in the name, and due to the fact that is declared using the **\['squareBracket']** notation.
+
+The variable used is called **_privates**. So, in fact, all prototypes and instances using _Private_ have a hidden
+member _privates.
+
+In order to be able to use this in a clean way, the API hides the actual implementation from the developer, and uses the
+Private(...) function to access the _privates object.
+
+Extending
+---------
+
+The **preferred** way of using this library is by extending a class:
+
+ var MyClass = function(){
+ Private.call(this); // Call the super constructor.
+ };
+ MyClass.prototype = Object.create(Private.prototype); // Extend the prototype of Private
+
+The advantage is that upon construction, the _privates object of your instance is automatically provisioned with a
+property `_self': this._privates._self = this;`
+
+This property is used by the injection mechanism for private methods in order to be able to swap the 'this' argument
+from the reference to _privates to the reference of the owning instance.
+
+Enabling
+--------
+
+Sometimes, it may be more convenient if you just 'enable' a class:
+
+ var MyClass = function(){
+ Private.enable(this);
+ };
+ Private.enable(MyClass.prototype);
+
+Or just a single instance:
+
+ var myObject = {};
+ Private.enable(myObject);
+ // --> (Private(myObject) === myObject._privates) && (Private(myObject)._self === myObject)
+
+This may prove to be convenient for enabling singletons that are otherwise defined.
+
+Public methods
+--------------
+
+In order to make it easy to declare public methods, that do not have to use '**Private(this)**' all over, the Private class
+exposes a static function '**hasAccess(_function_)**' that will wrap a function with the necessary code for injecting the
+_privates member upon invocation as the first parameter:
+
+ // Declare MyClass, which extends Private.
+ var MyClass = function(somePrivateValue){
+ Private.call(this);
+ Private(this).somePrivateProperty = somePrivateValue;
+ };
+ MyClass.prototype = Object.create(Private.prototype);
+
+ MyClass.prototype.getSomeProperty = Private.hasAccess(function(privates){
+ return privates.someProperty;
+ };
+
+ var myInstance = new MyClass(2);
+ console.log(myInstance.getSomeProperty());
+
+As you can see in the example above, there is no need to pass on the _privates when invoking the public function.
+Neither does the function have to use **Private(this).someProperty**. This is because the 'privates' parameter is injected
+upon invocation by the function returned by **Private.hasAccess(...)**.
+
+Private methods
+---------------
+
+In order to make it possible to create private methods, the Private class has a '**privateMethod(name, function)**'
+function on its prototype. This method is inherited by the prototype of each sub class (and by each instance).
+You can use this function in order to create private methods.
+
+If we extend the example from above:
+
+ // Declare MyClass, which extends Private.
+ var MyClass = function(somePrivateValue){
+ Private.call(this);
+ Private(this).setSomeProperty(somePrivateValue);
+ };
+ MyClass.prototype = Object.create(Private.prototype);
+
+ MyClass.prototype.getSomeProperty = Private.hasAccess(function(privates){
+ return privates.someProperty;
+ };
+
+ MyClass.prototype.privateMethod('setSomeProperty',function(privates, value){
+ privates.someProperty = value;
+ });
+
+ var myInstance = new MyClass(2);
+ console.log(myInstance.getSomeProperty());
+
View
106 lib/index.js
@@ -1,4 +1,106 @@
+/**
+ * Created with JetBrains WebStorm.
+ * User: daankets
+ * Date: 22/10/12
+ * Time: 10:15
+ * To change this template use File | Settings | File Templates.
+ */
-var inheritance = require('./inheritance');
+var PRIVATES_VAR_NAME = '_privates';
+var THIS_VAR_NAME = '_this';
-module.exports.Private = inheritance.Private;
+/**
+ * This class constructs an instance of the Private class. The Private class provides a simple, clear-specs
+ * implementation of 'privates' for JavaScript. The prototype of the class will be extended with a '_privates' member,
+ * that will contain the private variables. The name of this member is customizable using a constant, but it's not
+ * advised to change it.
+ *
+ * The Private class exposes a static method 'access' that will wrap a function with the necessary code to inject the
+ * first parameter 'privates' upon each invocation.
+ *
+ * @param {Object} object Optional object parameter for non-constructor invocation. Not to be used with constructor
+ * style.
+ *
+ */
+var Private = function (object) {
+ "use strict";
+
+ // If this is a classic function invocation, and NOT a constructor call.
+ if (object) {
+ return object[PRIVATES_VAR_NAME];
+ }
+
+ // In other cases, this is an extension - initialize (update) the self reference.
+ Private.enable(this); // Update the self reference to 'this'.
+};
+
+/**
+ * Checks if this prototype or object has privates support.
+ * @param object The object to test.
+ * @return {Boolean} true if supported, false if not.
+ */
+Private.hasPrivates = function (object) {
+ "use strict";
+
+ return (object && object[PRIVATES_VAR_NAME]);
+};
+
+/**
+ * Method used for wrapping a function with the necessary code for injecting the privates upon invocation. The function
+ * will return the original function wrapped with invocation code. This code will make sure the privates are injected
+ * as the first parameter, and that the 'this' reference is correctly passed.
+ *
+ * @param method The actual method implementation, accepting the first parameter 'privates'.
+ * @return {Function}
+ */
+Private.hasAccess = function (method) {
+ "use strict";
+
+ return function () {
+ return method.apply(this, [Private(this)].concat(Array.prototype.slice.call(arguments)));
+ };
+};
+
+/**
+ * Method used for adding a new PRIVATE method to the prototype of the privates, that will inject the 'privates' as the
+ * first argument, and will make sure the method is called with respect to 'this' of the current class.
+ * In fact, the created method becomes a memeber of the 'privates' of the prototype (or instance).
+ *
+ * The function can later be invoked by a function with access to the privates.
+ *
+ * @param name The name of the method to add.
+ * @param method The actual method implementation, accepting the first parameter 'privates'.
+ */
+var privateMethod = function (name, method) {
+ "use strict";
+
+ // Swap the this and privates arguments!
+ Private(this)[name] = function () {
+ return method.apply(this[THIS_VAR_NAME], [this].concat(Array.prototype.slice.call(arguments)));
+ };
+};
+
+/**
+ * Enable a prototype or instance with privates support. This can be used on BOTH a prototype or single instance.
+ * You may also later enable an object with privates support without extending from Private.
+ * @param object The object or prototype to enable.
+ */
+Private.enable = function (object) {
+ "use strict";
+
+ if (object) {
+ if (!object[PRIVATES_VAR_NAME]) {
+ object[PRIVATES_VAR_NAME] = {};
+ }
+ if (!object.privateMethod) {
+ object.privateMethod = privateMethod;
+ }
+ }
+ Private(object)[THIS_VAR_NAME] = object;
+};
+
+// Enable the Private prototype for Private support.
+Private.enable(Private.prototype);
+
+// Export the Private class.
+module.exports.Private = Private;
View
59 lib/inheritance.js
@@ -1,59 +0,0 @@
-/**
- * Created with JetBrains WebStorm.
- * User: daankets
- * Date: 22/10/12
- * Time: 10:15
- * To change this template use File | Settings | File Templates.
- */
-
-var PRIVATES_VAR_NAME = '_privates';
-
-/**
- * This class constructs an instance of the Private class. The Private class provides a simple, clear-specs
- * implementation of 'privates' for JavaScript. The prototype of the class will be extended with a '_privates' member,
- * that will contain the private variables. The name of this member is customizable using a constant, but it's not
- * advised to change it.
- *
- * The Private class exposes a static method 'access' that will wrap a function with the necessary code to inject the
- * first parameter 'privates' upon each invocation.
- *
- *
- *
- * @constructor
- */
-var Private = function () {
- "use strict";
-
- this[PRIVATES_VAR_NAME].self = this; // Expose to methods defined via prototype.
-};
-Private.prototype[PRIVATES_VAR_NAME] = {};
-
-/**
- * Method used for wrapping a function with the necessary code for injecting the privates upon invocation.
- * @param method The actual method implementation, accepting the first parameter 'privates'.
- */
-Private.access = function (method) {
- "use strict";
-
- return function () {
- return method.apply(this, [this[PRIVATES_VAR_NAME]].concat(Array.prototype.slice.call(arguments)));
- };
-};
-
-/**
- * Method used for adding a new PRIVATE method to the prototype of the privates, that will inject the 'privates' as the
- * first argument, and will make sure the method is called with respect to 'this' of the current class.
- *
- * @param name The name of the method to add.
- * @param method The actual method implementation, accepting the first parameter 'privates'.
- */
-Private.prototype.addPrivateMethod = function (name, method) {
- "use strict";
-
- // Swap the this and privates arguments!
- this[PRIVATES_VAR_NAME][name] = function () {
- return method.apply(this.self, [this].concat(Array.prototype.slice.call(arguments)));
- };
-};
-
-module.exports.Private = Private;
View
15 package.json
@@ -1,26 +1,29 @@
{
- "name": "blackbit-node-common",
+ "name": "private-js",
"version": "0.1.0",
- "description": "Blackbit Consulting - Common node classes and utilities",
+ "description": "Clean 'private' support for JavaScript.\n\
+ This library contains classes and utilities that will allow you to add 'privates' to your JavaScript classes in a clean, \
+ spec-compliant way. This library will work both within a browser environment and node.js. It was created in order to \
+ make it easier for a distributed team of developers to handle privates in a consistent way. The library is based on \
+ JavaScript prototypes in order to avoid repetitive creation of functions.",
"main": "lib/index.js",
"scripts": {
"test": "test/tests.js"
},
"repository": {
"type": "git",
- "url": "git@git.assembla.com:blackbit-node-common.git"
+ "url": "git@git.assembla.com:private-js.git"
},
"keywords": [
"common",
"node",
"classes",
"utilities",
- "blackbit",
- "consulting"
+ "private"
],
"devDependencies" : {
"nodeunit" : "0.7.x"
},
"author": "Daan Kets",
- "license": "private"
+ "license": "Creative Commons Attribution-ShareAlike 3.0 Unported License"
}
View
131 test/index.js
@@ -0,0 +1,131 @@
+/**
+ * Created with JetBrains WebStorm.
+ * User: daankets
+ * Date: 22/10/12
+ * Time: 11:18
+ * To change this template use File | Settings | File Templates.
+ */
+
+// Import the Private class/function
+var Private = require('../lib/index').Private;
+var PRIVATES_VAR_NAME = '_privates';
+var THIS_VAR_NAME = '_this';
+
+// Define a TestClass that inherits from Private.
+var TestClass = function (value) {
+ "use strict";
+
+ Private.call(this);
+ this.setValue(value);
+};
+TestClass.prototype = Object.create(Private.prototype);
+
+TestClass.prototype.privateMethod('capitalize', function (privates, someString) {
+ "use strict";
+
+ return someString.toUpperCase();
+});
+
+TestClass.prototype.setValue = Private.hasAccess(function (privates, value) {
+ "use strict";
+
+ return (privates.value = value);
+});
+
+TestClass.prototype.toString = Private.hasAccess(function (privates) {
+ "use strict";
+
+ return privates.capitalize(privates.value);
+});
+
+var testPrivateFunction = function (test) {
+ "use strict";
+
+ var object = {};
+ object[PRIVATES_VAR_NAME] = "test";
+
+ test.equals(Private(object), "test");
+ test.done();
+};
+
+var testPrivateConstructor = function (test) {
+ "use strict";
+
+ var testObject = new Private();
+ test.ok(Private(testObject)[THIS_VAR_NAME] === testObject);
+ test.done();
+};
+
+var testPrivateConstructorExtension = function (test) {
+ "use strict";
+
+ var testObject = new TestClass('test');
+ test.ok(Private(testObject)[THIS_VAR_NAME] === testObject);
+ //test.ok(Private(testObject)['value'] === 'test');
+ test.done();
+};
+
+var testPrivateVariable = function (test) {
+ "use strict";
+
+ var testObject = new Private();
+ Private(testObject).value = 'test';
+ test.ok(Private(testObject)['value'] === 'test');
+ test.done();
+};
+
+var testPublicMethod = function (test) {
+ "use strict";
+
+ var MyClass = function () {
+ Private.call(this);
+ Private(this).value = 'test';
+ };
+ MyClass.prototype = Object.create(Private.prototype);
+ MyClass.prototype.getValue = Private.hasAccess(function (privates) {
+ return privates.value;
+ });
+ test.equal(new MyClass().getValue(), 'test');
+ test.done();
+
+};
+
+var testPrivateMethod = function (test) {
+ "use strict";
+
+ var MyClass = function () {
+ Private.call(this);
+ Private(this).setValue('test');
+ };
+ MyClass.prototype = Object.create(Private.prototype);
+ MyClass.prototype.privateMethod('setValue', function (privates, value) {
+ privates.value = value;
+ });
+ test.equal(Private(new MyClass()).value, 'test');
+ test.done();
+};
+
+var testEnable = function (test) {
+ "use strict";
+
+ var myObject, MyClass = function () {
+ Private.enable(this);
+ };
+ Private.enable(MyClass.prototype);
+
+ test.ok(Private(MyClass.prototype)[THIS_VAR_NAME] === MyClass.prototype);
+
+ myObject = new MyClass();
+ test.equal(Private(myObject)[THIS_VAR_NAME], myObject);
+ test.done();
+};
+
+module.exports = {
+ testPrivateFunction : testPrivateFunction,
+ testPrivateConstructor : testPrivateConstructor,
+ testPrivateConstructorExtension : testPrivateConstructorExtension,
+ testPrivateVariable : testPrivateVariable,
+ testPublicMethod : testPublicMethod,
+ testPrivateMethod : testPrivateMethod,
+ testEnable : testEnable
+};
View
50 test/inheritanceTests.js
@@ -1,50 +0,0 @@
-/**
- * Created with JetBrains WebStorm.
- * User: daankets
- * Date: 22/10/12
- * Time: 11:18
- * To change this template use File | Settings | File Templates.
- */
-
-var Private = require('../lib/inheritance').Private;
-
-var TestClass = function (value) {
- "use strict";
-
- Private.call(this);
- this.setValue(value);
-};
-TestClass.prototype = Object.create(Private.prototype);
-
-TestClass.prototype.addPrivateMethod('capitalize', function (privates, someString) {
- "use strict";
-
- return someString.toUpperCase();
-});
-
-TestClass.prototype.setValue = Private.access(function (privates, value) {
- "use strict";
-
- return (privates.value = value);
-});
-
-TestClass.prototype.toString = Private.access(function (privates) {
- "use strict";
-
- return privates.capitalize(privates.value);
-});
-
-var testWithPrivates = function (test) {
- "use strict";
-
- var testObject = new TestClass('hello');
- test.equal(testObject.toString(), 'HELLO');
- test.equal(JSON.stringify(testObject), '{}');
- test.done();
-
-
-};
-
-module.exports = {
- testWithPrivates : testWithPrivates
-};
View
9 test/tests.js
@@ -1,9 +0,0 @@
-/**
- * Created with JetBrains WebStorm.
- * User: daankets
- * Date: 22/10/12
- * Time: 11:17
- * To change this template use File | Settings | File Templates.
- */
-
-module.exports.inheritanceTests = require('./inheritanceTests');
Please sign in to comment.
Something went wrong with that request. Please try again.