<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.2.1/jasmine.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.2.1/jasmine.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.2.1/jasmine-html.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.2.1/boot.js"></script>
<script src="Accessor.js"></script>
<!-- <script>window.accessor = new Accessor(); console.log(accessor);</script> -->
<script src="spec.js"></script>
</head>
<body></body>
</html>
/* jshint jasmine: true */
'use strict';
describe("Accessor", function() {
var a;
beforeEach(function() {
a = new Accessor();
});
describe('constructor', function() {
it('should return a function', function() {
expect(typeof a).toBe('function');
});
it('should set the initial value', function() {
a = new Accessor('val');
expect(a()).toBe('val');
});
});
describe('fn', function() {
it('should call set if an argument is passed', function() {
spyOn(a, 'set');
a(10);
expect(a.set).toHaveBeenCalledWith(10);
});
it('should call get if no argument is passed', function() {
spyOn(a, 'get');
a();
expect(a.get).toHaveBeenCalledWith();
});
});
describe('get', function() {
it('should return the stored value', function() {
a._current = 'asdf';
expect(a.get()).toBe('asdf');
});
it('should be overridable', function() {
a._current = 1000;
a.get = function() {};
expect(a.get()).not.toBeDefined();
expect(a()).not.toBeDefined();
});
});
describe('set', function() {
it('should set the stored value', function() {
a.set(10);
expect(a._current).toBe(10);
});
it('should be overridable', function() {
a(1000);
a.set = function () {};
a(0);
a.set(500);
expect(a()).toBe(1000);
});
});
describe('read-only', function() {
beforeEach(function() {
a = new Accessor("read-only", {write: false});
});
it('should be able to disable writing', function() {
a('write');
expect(a()).toBe('read-only');
});
it('should be able to force writing by passing force=true to set', function() {
a.set('written', true);
expect(a()).toBe('written');
});
it('should not be able to force writing by passing force=true to fn', function() {
a('written', true);
expect(a()).toBe('read-only');
});
});
describe('write-only', function() {
beforeEach(function() {
a = new Accessor('write-only', {read: false});
});
it('should be able to disable reading', function() {
expect(a._current).toBe('write-only');
expect(a()).not.toBeDefined();
expect(a.get()).not.toBeDefined();
});
it('should be able to force reading by passing force=true to get', function() {
expect(a.get(true)).toBe('write-only');
});
});
describe('changed', function() {
it('should return true when a new value is set', function() {
expect(a.changed()).toBe(false);
a(1);
expect(a.changed()).toBe(true);
})
});
describe('update', function() {
it('should set changed to false', function() {
a(1);
expect(a.changed()).toBe(true);
a.update();
expect(a.changed()).toBe(false);
});
it('should not affect the stored value', function() {
a(1).update();
expect(a()).toBe(1);
});
});
describe('reset', function () {
it('should set changed to false', function() {
a(1);
expect(a.changed()).toBe(true);
a.update();
expect(a.changed()).toBe(false);
});
it('should revert to the previous (or initial) stored value', function() {
a(1).reset();
expect(a()).not.toBeDefined();
});
});
describe('toJSON', function() {
it('should return the stored value', function() {
expect(a(10).toJSON()).toBe(10);
});
it('should stringify to value', function() {
a(true);
expect(JSON.stringify({a: a})).toBe('{"a":true}')
})
});
describe('inheritance', function() {
it('should be subclassable', function() {
function Integer(initial) {
return Accessor.call(this, initial);
}
Integer.prototype = Object.create(Accessor.prototype);
Integer.prototype.constructor = Integer;
Integer.prototype.set = function (newVal) {
newVal = parseInt(newVal, 10) || 0;
return Accessor.prototype.set.call(this, newVal);
};
var i = new Integer('123abc');
expect(i()).toBe(123);
});
});
});
'use strict';
function Accessor(initial, options) {
options = options || {};
var fn = function(newVal) {
return arguments.length ? fn.set(newVal) : fn.get();
};
fn._config = {
read: !options.hasOwnProperty('read') || options.read,
write: !options.hasOwnProperty('write') || options.read
};
if (options.safe || Object.prototype.__proto__ === undefined) {
for (var method in Accessor.prototype) {
if (Accessor.prototype.hasOwnProperty(method)) {
fn[method] = this[method].bind(fn);
}
}
} else {
fn.__proto__ = this.__proto__;
}
return fn.set(initial, true).update();
}
Accessor.prototype.set = function(newVal, force) {
if (this._config.write || force) {
this._current = newVal;
}
return this;
};
Accessor.prototype.get = function(force) {
if (this._config.read || force) {
return this._current;
}
};
Accessor.prototype.update = function() {
this._previous = this._current;
return this;
};
Accessor.prototype.reset = function() {
this._current = this._previous;
return this;
};
Accessor.prototype.changed = function() {
return this._current !== this._previous;
};
Accessor.prototype.toJSON = function() {
return this.get(false);
};
// polyfill
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof fNOP ? this : oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}