oj
Version:
A unified templating language for the people. Thirsty people.
571 lines (462 loc) • 20 kB
text/coffeescript
# oj.createType.coffee
# ==============================================================================
path = require 'path'
fs = require 'fs'
oj = require '../oj.js'
describe 'oj.createType', ->
_parentConstructor = null
Parent = oj.createType 'Parent',
constructor: ->
expect(typeof @).to.equal 'object'
expect(typeof @set).to.equal 'function'
_parentConstructor = arguments[0]
methods:
method: -> 'Parent.method'
superMethod: -> 'Parent.superMethod'
parentMethod: -> 'Parent.parentMethod'
properties:
value: 'Parent.value'
readOnly: get: (-> 'Parent.readOnly')
readWrite:
get: -> if @_readWrite then ('Parent.readWrite' + @_readWrite) else 'Parent.readWriteDefault'
set: (v) -> @_readWrite = v
parentValue: 'Parent.parentValue'
parentReadOnly: get: (-> 'Parent.parentReadOnly')
parentReadWrite:
get: (-> if @_parentReadWrite then 'Parent.parentReadWrite' + @_parentReadWrite else 'Parent.parentReadWriteDefault')
set: ((v) -> @_parentReadWrite = v)
Parent.method = (-> 'Parent.method')
Parent.value = 'Parent.value'
Parent.parentMethod = (-> 'Parent.parentMethod')
Parent.parentValue = 'Parent.parentValue'
oj.addProperties Parent,
prop:
get: -> if @_prop then ('Parent.prop' + @_prop) else 'Parent.prop'
set: (v) -> @_prop = v
oj.addProperties Parent,
parentProp:
get: -> if @_parentProp then ('Parent.parentProp' + @_parentProp) else 'Parent.parentProp'
set: (v) -> @_parentProp = v
_childConstructor = null
Child = oj.createType 'Child',
base: Parent
constructor: ->
Child.base.constructor.apply @, arguments
expect(typeof @).to.equal 'object'
expect(typeof @set).to.equal 'function'
_childConstructor = arguments[0]
methods:
method: -> 'Child.method'
superMethod: -> Child.base.superMethod() + '.Child.superMethod'
childMethod: -> 'Child.childMethod'
properties:
value: 'Child.value'
readOnly: get: (-> 'Child.readOnly')
readWrite:
get: -> if @_readWrite then ('Child.readWrite' + @_readWrite) else 'Child.readWriteDefault'
set: (v) -> @_readWrite = v
childValue: 'Child.childValue'
childReadOnly: get: (-> 'Child.childReadOnly')
childReadWrite:
get: (-> if @_childReadWrite then 'Child.childReadWrite' + @_childReadWrite else 'Child.childReadWriteDefault')
set: ((v) -> @_childReadWrite = v)
Child.method = (-> 'Child.method')
Child.value = 'Child.value'
Child.childMethod = (-> 'Child.childMethod')
Child.childValue = 'Child.childValue'
oj.addProperties Child,
prop:
get: -> if @_prop then ('Child.prop' + @_prop) else 'Child.prop'
set: (v) -> @_prop = v
oj.addProperties Child,
childProp:
get: -> if @_childProp then ('Child.childProp' + @_childProp) else 'Child.childProp'
set: (v) -> @_childProp = v
_grandChildConstructor = null
GrandChild = oj.createType 'GrandChild',
base: Child
constructor: ->
GrandChild.base.constructor.apply @, arguments
_grandChildConstructor = arguments[0]
methods:
method: -> 'GrandChild.method'
superMethod: -> GrandChild.base.superMethod() + '.GrandChild.superMethod'
grandChildMethod: -> 'GrandChild.grandChildMethod'
properties:
value: 'GrandChild.value'
readOnly: get: (-> 'GrandChild.readOnly')
readWrite:
get: -> if @_readWrite then ('GrandChild.readWrite' + @_readWrite) else 'GrandChild.readWriteDefault'
set: (v) -> @_readWrite = v
grandChildValue: 'GrandChild.grandChildValue'
grandChildReadOnly: get: (-> 'GrandChild.grandChildReadOnly')
grandChildReadWrite:
get: (-> if @_grandChildReadWrite then 'GrandChild.grandChildReadWrite' + @_grandChildReadWrite else 'GrandChild.grandChildReadWriteDefault')
set: ((v) -> @_grandChildReadWrite = v)
GrandChild.method = (-> 'GrandChild.method')
GrandChild.value = 'GrandChild.value'
GrandChild.grandChildMethod = (-> 'GrandChild.grandChildMethod')
GrandChild.grandChildValue = 'GrandChild.grandChildValue'
oj.addProperties GrandChild,
prop:
get: -> if @_prop then ('GrandChild.prop' + @_prop) else 'GrandChild.prop'
set: (v) -> @_prop = v
oj.addProperties GrandChild,
grandChildProp:
get: -> if @_grandChildProp then ('GrandChild.grandChildProp' + @_grandChildProp) else 'GrandChild.grandChildProp'
set: (v) -> @_grandChildProp = v
it 'exists', ->
assert oj.createType != null, 'oj.createType is null'
oj.createType.should.be.a 'function'
it 'is detectable', ->
Empty = oj.createType 'Empty', {}
empty = new Empty()
expect(oj.isOJ(Empty)).to.equal true
expect(oj.isOJType(Empty)).to.equal true
expect(oj.isOJInstance(Empty)).to.equal false
expect(oj.isOJ(empty)).to.equal true
expect(oj.isOJType(empty)).to.equal false
expect(oj.isOJInstance(empty)).to.equal true
it 'empty createType', ->
Empty = oj.createType 'Empty', {}
empty = new Empty()
expect(empty.type).to.equal Empty
expect(empty.typeName).to.equal 'Empty'
expect(empty.properties).to.deep.equal []
expect(empty.methods).to.deep.equal []
it 'empty createType without new', ->
Empty = oj.createType 'Empty', {}
empty = Empty()
expect(empty.type).to.equal Empty
expect(empty.typeName).to.equal 'Empty'
expect(empty.properties).to.deep.equal []
expect(empty.methods).to.deep.equal []
it 'simple createType', ->
# Fixing current date for unit test consistency
currentDate = new Date(2013, 1,1)
User = oj.createType 'User',
constructor: (args) ->
@set args
# Types can define properties
properties:
name: 'default name'
birthDate: null
# Types can define methods
methods:
age: ->
# Calculate age from @birthDate
today = currentDate
age = today.getFullYear() - @birthDate.getFullYear()
m = today.getMonth() - @birthDate.getMonth()
if m < 0 or (m == 0 and today.getDate() < @birthDate.getDate())
age--
age
birthDate = new Date 1945, 6, 23
user = User name: 'Joseph', birthDate: birthDate
expect(user.name).to.equal 'Joseph'
expect(user.birthDate).to.equal birthDate
expect(user.age()).to.equal 67
it 'simple createType with default', ->
User = oj.createType 'User',
constructor: (args) ->
@set args
# Types can define properties
properties:
nameWithDefault: 'Default'
user = User nameWithDefault: 'Joseph'
expect(user.nameWithDefault).to.equal 'Joseph'
it 'simple types have get and set', ->
T = oj.createType 'T',
constructor: (args) ->
@set args
properties:
prop1: 42
prop2:
get: (v) -> @_prop2
set: (v) -> @_prop2 = v; return
t = new T(prop1: 1, prop2: 2)
# Get one at a time
expect(t.get('prop0')).to.not.exist
expect(t.get('prop1')).to.equal 1
expect(t.get('prop2')).to.equal 2
# Get all
getAll = t.get()
expect(getAll.prop1).to.equal 1
expect(getAll.prop2).to.equal 2
# Set one
t.set 'prop1', 11
expect(t.get 'prop1').to.equal 11
expect(t.get 'prop2').to.equal 2
expect(t.get().prop1).to.equal 11
# Set one that isn't a property
t.set 'prop0', 0
expect(t.get 'prop0').to.not.exist
expect(t.get().prop0).to.not.exist
# Set all
t.set prop0:'zero', prop1: 111, prop2: 222
expect(t.get('prop0')).to.not.exist
expect(t.get('prop1')).to.equal 111
expect(t.get('prop2')).to.equal 222
getAll = t.get()
expect(getAll.prop0).to.not.exist
expect(getAll.prop1).to.equal 111
expect(getAll.prop2).to.equal 222
it 'base createType', ->
parent = new Parent()
expect(parent.properties).to.deep.equal ["parentReadOnly", "parentReadWrite", "parentValue", "readOnly", "readWrite", "value"]
expect(parent.methods).to.deep.equal ['method', 'parentMethod', 'superMethod']
it 'base createType without new', ->
parent = Parent()
expect(parent.typeName).to.equal 'Parent'
expect(parent.value).to.equal 'Parent.value'
expect(parent.method()).to.equal 'Parent.method'
it 'base createType constructor', ->
parent = new Parent('Parent.constructor')
expect(_parentConstructor).to.equal 'Parent.constructor'
it 'base createType constructor without new', ->
parent = Parent('Parent.constructor')
expect(_parentConstructor).to.equal 'Parent.constructor'
it 'base createType value property', ->
parent = new Parent()
expect(parent.value).to.equal 'Parent.value'
it 'base createType readonly property', ->
parent = new Parent()
expect(parent.readOnly).to.equal 'Parent.readOnly'
parent.readOnly = 'DoesNotWork'
expect(parent.readOnly).to.equal 'Parent.readOnly'
it 'base createType readwrite property', ->
parent = new Parent()
expect(parent.readWrite).to.equal 'Parent.readWriteDefault'
parent.readWrite = 'Worked'
expect(parent.readWrite).to.equal 'Parent.readWriteWorked'
it 'base createType methods', ->
parent = new Parent()
expect(parent.method()).to.equal 'Parent.method'
expect(parent.parentMethod()).to.equal 'Parent.parentMethod'
it 'base createType class methods', ->
expect(Parent.method()).to.equal 'Parent.method'
expect(Parent.parentMethod()).to.equal 'Parent.parentMethod'
it 'base createType class value', ->
expect(Parent.value).to.equal 'Parent.value'
expect(Parent.parentValue).to.equal 'Parent.parentValue'
it 'base createType class property', ->
expect(Parent.prop).to.equal 'Parent.prop'
Parent.prop = 'Worked'
expect(Parent.prop).to.equal 'Parent.propWorked'
it 'base createType value', ->
parent = new Parent()
expect(parent.method()).to.equal 'Parent.method'
it 'inherited createType', ->
child = new Child()
expect(child.properties).to.deep.equal ["childReadOnly", "childReadWrite", "childValue", "parentReadOnly", "parentReadWrite", "parentValue", "readOnly", "readWrite", "value"]
expect(child.methods).to.deep.equal ['childMethod', 'method', 'parentMethod', 'superMethod']
it 'inherited createType with constructor', ->
child = new Child('Child.constructor', 'Child.constructor')
expect(_childConstructor).to.equal 'Child.constructor'
expect(_parentConstructor).to.equal 'Child.constructor'
it 'inherited createType without new', ->
child = Child()
expect(child.type).to.equal Child
expect(child.typeName).to.equal 'Child'
expect(child.childValue).to.equal 'Child.childValue'
expect(child.value).to.equal 'Child.value'
expect(child.method()).to.equal 'Child.method'
it 'inherited createType without new, with constructor', ->
child = Child('Child.constructor', 'Child.constructor')
expect(_childConstructor).to.equal 'Child.constructor'
expect(_parentConstructor).to.equal 'Child.constructor'
it 'inherited createType value property', ->
child = new Child()
expect(child.type).to.equal Child
expect(child.typeName).to.equal 'Child'
expect(child.value).to.equal 'Child.value'
expect(child.childValue).to.equal 'Child.childValue'
expect(child.parentValue).to.equal 'Parent.parentValue'
it 'inherited createType readonly property', ->
child = new Child()
expect(child.readOnly).to.equal 'Child.readOnly'
child.readOnly = 'DoesNotWork'
expect(child.readOnly).to.equal 'Child.readOnly'
expect(child.parentReadOnly).to.equal 'Parent.parentReadOnly'
child.parentReadOnly = 'DoesNotWork'
expect(child.parentReadOnly).to.equal 'Parent.parentReadOnly'
expect(child.childReadOnly).to.equal 'Child.childReadOnly'
child.childReadOnly = 'DoesNotWork'
expect(child.childReadOnly).to.equal 'Child.childReadOnly'
it 'inherited createType readwrite property', ->
child = new Child()
expect(child.readWrite).to.equal 'Child.readWriteDefault'
child.readWrite = 'Worked'
expect(child.readWrite).to.equal 'Child.readWriteWorked'
it 'inherited createType class methods inherit', ->
expect(Child.method()).to.equal 'Child.method'
expect(Child.parentMethod()).to.equal 'Parent.parentMethod'
expect(Child.childMethod()).to.equal 'Child.childMethod'
it 'inherited createType methods inherit', ->
child = new Child()
expect(child.method()).to.equal 'Child.method'
expect(child.childMethod()).to.equal 'Child.childMethod'
expect(child.parentMethod()).to.equal 'Parent.parentMethod'
it 'inherited createType methods call super', ->
child = new Child()
expect(child.superMethod()).to.equal 'Parent.superMethod.Child.superMethod'
it 'inherited createType class value', ->
expect(Child.value).to.equal 'Child.value'
expect(Child.parentValue).to.equal 'Parent.parentValue'
expect(Child.childValue).to.equal 'Child.childValue'
it 'inherited createType class property', ->
expect(Child.prop).to.equal 'Child.prop'
Child.prop = 'Worked'
expect(Child.prop).to.equal 'Child.propWorked'
expect(Child.parentProp).to.equal 'Parent.parentProp'
Child.parentProp = 'Worked'
expect(Child.parentProp).to.equal 'Parent.parentPropWorked'
expect(Child.childProp).to.equal 'Child.childProp'
Child.childProp = 'Worked'
expect(Child.childProp).to.equal 'Child.childPropWorked'
it 'deeply inherited createType', ->
grandChild = new GrandChild()
expect(grandChild.properties).to.deep.equal ["childReadOnly", "childReadWrite", "childValue", "grandChildReadOnly", "grandChildReadWrite", "grandChildValue", "parentReadOnly", "parentReadWrite", "parentValue", "readOnly", "readWrite", "value"]
expect(grandChild.methods).to.deep.equal ['childMethod', 'grandChildMethod', 'method', 'parentMethod', 'superMethod']
it 'deeply inherited createType with constructor', ->
grandChild = new GrandChild('GrandChild.constructor', 'GrandChild.constructor')
expect(_grandChildConstructor).to.equal 'GrandChild.constructor'
expect(_parentConstructor).to.equal 'GrandChild.constructor'
expect(_childConstructor).to.equal 'GrandChild.constructor'
it 'deeply inherited createType without new', ->
grandChild = GrandChild()
expect(grandChild.type).to.equal GrandChild
expect(grandChild.typeName).to.equal 'GrandChild'
expect(grandChild.value).to.equal 'GrandChild.value'
expect(grandChild.grandChildValue).to.equal 'GrandChild.grandChildValue'
expect(grandChild.childValue).to.equal 'Child.childValue'
expect(grandChild.parentValue).to.equal 'Parent.parentValue'
it 'deeply inherited createType without new, with constructor', ->
grandChild = GrandChild('GrandChild.constructor', 'GrandChild.constructor')
expect(_grandChildConstructor).to.equal 'GrandChild.constructor'
expect(_childConstructor).to.equal 'GrandChild.constructor'
expect(_parentConstructor).to.equal 'GrandChild.constructor'
it 'deeply inherited createType value property', ->
grandChild = new GrandChild()
expect(grandChild.type).to.equal GrandChild
expect(grandChild.typeName).to.equal 'GrandChild'
expect(grandChild.value).to.equal 'GrandChild.value'
expect(grandChild.grandChildValue).to.equal 'GrandChild.grandChildValue'
expect(grandChild.parentValue).to.equal 'Parent.parentValue'
it 'deeply inherited createType readonly property', ->
grandChild = new GrandChild()
expect(grandChild.readOnly).to.equal 'GrandChild.readOnly'
grandChild.readOnly = 'DoesNotWork'
expect(grandChild.readOnly).to.equal 'GrandChild.readOnly'
expect(grandChild.grandChildReadOnly).to.equal 'GrandChild.grandChildReadOnly'
grandChild.grandChildReadOnly = 'DoesNotWork'
expect(grandChild.grandChildReadOnly).to.equal 'GrandChild.grandChildReadOnly'
it 'deeply inherited createType readwrite property', ->
grandChild = new GrandChild()
expect(grandChild.readWrite).to.equal 'GrandChild.readWriteDefault'
grandChild.readWrite = 'Worked'
expect(grandChild.readWrite).to.equal 'GrandChild.readWriteWorked'
it 'deeply inherited createType methods correctly inherit', ->
grandChild = new GrandChild()
expect(grandChild.method()).to.equal 'GrandChild.method'
expect(grandChild.childMethod()).to.equal 'Child.childMethod'
expect(grandChild.parentMethod()).to.equal 'Parent.parentMethod'
it 'deeply inherited createType methods call super', ->
grandChild = new GrandChild()
expect(grandChild.superMethod()).to.equal 'Parent.superMethod.Child.superMethod.GrandChild.superMethod'
it 'deeply inherited createType class method', ->
expect(GrandChild.method()).to.equal 'GrandChild.method'
expect(GrandChild.parentMethod()).to.equal 'Parent.parentMethod'
expect(GrandChild.grandChildMethod()).to.equal 'GrandChild.grandChildMethod'
it 'deeply inherited createType class value', ->
expect(GrandChild.value).to.equal 'GrandChild.value'
expect(GrandChild.parentValue).to.equal 'Parent.parentValue'
expect(GrandChild.grandChildValue).to.equal 'GrandChild.grandChildValue'
it 'deeply inherited createType class property', ->
expect(GrandChild.prop).to.equal 'GrandChild.prop'
GrandChild.prop = 'Worked'
expect(GrandChild.prop).to.equal 'GrandChild.propWorked'
expect(GrandChild.parentProp).to.equal 'Parent.parentProp'
GrandChild.parentProp = 'Worked'
expect(GrandChild.parentProp).to.equal 'Parent.parentPropWorked'
expect(GrandChild.grandChildProp).to.equal 'GrandChild.grandChildProp'
GrandChild.grandChildProp = 'Worked'
expect(GrandChild.grandChildProp).to.equal 'GrandChild.grandChildPropWorked'
it 'deeply inherited createType calls constructor minimally', ->
_increment = 0
P = oj.createType 'P',
constructor: ->
_increment++
C = oj.createType 'C',
base: P
constructor: ->
C.base.constructor.apply @, arguments
_increment++
GC = oj.createType 'GC',
base: C
constructor: ->
GC.base.constructor.apply @, arguments
_increment++
p = new P()
expect(_increment).to.equal 1
_increment = 0
c = new C()
expect(_increment).to.equal 2
_increment = 0
gc = new GC()
expect(_increment).to.equal 3
it 'deeply inherited createType calls constructor minimally without new', ->
_increment = 0
P = oj.createType 'P',
constructor: ->
_increment++
C = oj.createType 'C',
base: P
constructor: ->
C.base.constructor.apply @, arguments
_increment++
GC = oj.createType 'GC',
base: C
constructor: ->
GC.base.constructor.apply @, arguments
_increment++
p = P()
expect(_increment).to.equal 1
_increment = 0
c = C()
expect(_increment).to.equal 2
_increment = 0
gc = GC()
expect(_increment).to.equal 3
it 'inherited createType automatically creates constructor calling base', ->
# P constructor
_increment = 0
P = oj.createType 'P',
constructor: (v) ->
if v?
@prop = v
_increment += @prop
properties:
prop: 24
C = oj.createType 'C',
base: P
# Purposely don't create a constructor because the default constructor
# should be automatically implemented to call base.constructor
properties:
prop: 42
_increment = 0
p = new P()
expect(p.prop).to.equal 24
expect(_increment).to.equal 24
_increment = 0
c = new C()
expect(c.prop).to.equal 42
expect(_increment).to.equal 42
_increment = 0
p = P(84)
expect(p.prop).to.equal 84
expect(_increment).to.equal 84
_increment = 0
c = C(84)
expect(c.prop).to.equal 84
expect(_increment).to.equal 84