Skip to content

Instantly share code, notes, and snippets.

@bauerca
Created June 19, 2015 23:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save bauerca/d5b976516f5189345cb2 to your computer and use it in GitHub Desktop.
Save bauerca/d5b976516f5189345cb2 to your computer and use it in GitHub Desktop.
class Type {
constructor() {
this.raw = null;
this.valid = undefined;
}
static clone(overrides) {
class cloned extends this {};
Object.assign(cloned.prototype, overrides);
return cloned;
}
static named(name) {
return this.clone({name});
}
static using(overrides) {
// maybe pre-process overrides?
return this.clone(overrides);
}
static fromDefaults() {
let defaulted = new this();
defaulted.set(defaulted.default);
return defaulted;
}
}
Type.prototype.default = null;
Type.prototype.optional = false;
Type.prototype.validators = [];
class AdaptationError extends Error {};
class Scalar extends Type {
constructor() {
super();
this.value = this.serialized = null;
}
set(raw) {
this.raw = raw;
try {
this.value = this.adapt(raw);
} catch (e) {
try {
this.serialized = this.serialize(raw);
} catch (e) {
this.serialized = '';
}
this.value = null;
return false;
}
this.serialized = this.serialize(this.value);
return true;
}
validate() {
if (this.value === null) {
this.valid = this.optional;
return this.valid;
}
this.valid = true;
this.validators.reduce((valid, v) => {
if (valid) {
valid = v.call(this);
}
return valid;
}, this.valid);
return this.valid;
}
}
class Str extends Scalar {
adapt(raw) {
return raw.toString();
}
serialize(value) {
return value;
}
}
class Int extends Scalar {
adapt(raw) {
let value = parseInt(raw, 10);
if (isNaN(value)) {
throw new AdaptationError(`${value} is not a number`);
}
return value;
}
serialize(value) {
return value.toString();
}
}
class List extends Type {
get value() {
return this.members.map(m => m.value);
}
set(raw) {
this.raw = raw;
if (!raw.forEach) {
return false;
}
let success = true;
this.members = [];
let items = [];
raw.forEach(mbr => {
let member = new this.memberType();
success = success & member.set(mbr);
items.push(member);
})
if (success) {
this.members = items;
}
}
static of(type) {
return this.clone({memberType: type});
}
}
List.prototype.members = [];
class Map extends Type {
static of(...members) {
this.members = members.reduce((ms, m) => {
ms[m.name] = m;
return ms;
}, {});
}
}
function expect(thing) {
return {
value: thing,
toBe(value) {
if (value !== this.value) {
console.log(`${this.value} was not ${value}`);
//throw new Error(`${this.value} was not ${value}`);
} else {
//console.log(`${this.value} is ${value}`);
}
},
toEqual(value) {
if (! this.value.every((x, i) => value[i] === x)) {
console.log(`${this.value} did not equal ${value}`);
}
}
}
}
var MyString = Str.named('string').using({default: 'default', optional: false});
var s = new MyString();
expect(s.name).toBe('string');
expect(s.default).toBe('default');
expect(s.optional).toBe(false);
// lifecycle
expect(s.value).toBe(null);
expect(s.valid).toBe(undefined);
expect(s.serialized).toBe(null);
expect(s.raw).toBe(null);
s.validate();
expect(s.valid).toBe(false);
s = MyString.fromDefaults();
expect(s.value).toBe(s.default);
s.validate();
expect(s.valid).toBe(true);
expect(s.set(123)).toBe(true);
expect(s.value).toBe('123');
expect(s.raw).toBe(123);
expect(s.serialized).toBe('123');
s.validate();
expect(s.valid).toBe(true);
let n = new Int();
n.set('yr mom');
expect(n.value).toBe(null)
n.set('123');
expect(n.value).toBe(123)
var Strings = List.of(Str);
let ss = new Strings();
ss.set(['yr', 'mom']);
expect(ss.value).toEqual(['yr', 'mom'])
var DefaultedStrings = Strings.using({default: ['foo', 'bar']});
let ds = new DefaultedStrings();
expect(ds.value).toEqual([]);
let dds = DefaultedStrings.fromDefaults();
expect(ds.value).toEqual(DefaultedStrings.defaults);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment