spectacular
Version:
Advanced BDD framework for CoffeeScript and JavaScript
170 lines (130 loc) • 4.93 kB
text/coffeescript
spectacular.factories ||= new spectacular.GlobalizableObject 'build',
'create',
'factory',
'factoryMixin'
spectacular.factories.keepContext = false
spectacular.factories.buildMethodsCache = {}
spectacular.factories.build = (ctor, args=[]) ->
f = if spectacular.factories.buildMethodsCache[args.length]?
spectacular.factories.buildMethodsCache[args.length]
else
argumentsSignature = ''
comma = ''
if args.length > 0
argumentsSignature = ("arg#{n}" for n in [0..args.length-1]).join(',')
comma = ','
spectacular.factories.buildMethodsCache[args.length] = new Function(
"ctor#{comma}#{argumentsSignature}",
"return new ctor(#{argumentsSignature});"
)
f.apply null, [ctor].concat(args)
class spectacular.factories.Set
constructor: (, ) ->
apply: (instance) ->
if typeof is 'function'
instance[] =
else
instance[] =
class spectacular.factories.Trait
spectacular.Hookable
spectacular.Globalizable
spectacular.HasAncestors
spectacular.AncestorsProperties
'arguments'
'buildBlock'
'build'
globalizable: 'set createWith build after'.split(/\s+/g)
constructor: () ->
= {}
= []
set: (property, value) ->
.push new spectacular.factories.Set property, value
build: () ->
createWith: (...) ->
after: (hook, block) -> hook, block
applySet: (instance) ->
.forEach (setter) -> setter.apply instance
class spectacular.factories.Factory extends spectacular.factories.Trait
globalizable: 'set trait createWith build include after'.split(/\s+/g)
constructor: (name, ) ->
super name
= {}
trait: (name, block) ->
trait = [name] ||= new spectacular.factories.Trait name
trait.globalize()
block.call(trait)
trait.unglobalize()
buildInstance: (traits, options={}) ->
args = traits
instance = if ?
.instanciate args, traits
else
args, traits
instance
.applySet instance for trait in traits
instance[k] = v for k,v of options
hooks = 'buildHooks', traits
hook.call(instance, instance) for hook in hooks
instance
instanciate: (args, traits) ->
buildBlock = 'buildBlock', traits
if buildBlock?
buildBlock.apply this, [].concat(args)
else
build , args
fromTraitOrThis: (property, traits) ->
value = @[property]
for trait in traits
traitValue = [property]
value = traitValue if traitValue?
value
fromTraitAndThis: (property, traits) ->
value = @[property]
for trait in traits
traitValue = [property]
value = value.concat traitValue if traitValue?
value
include: (mixins...) ->
for mixinName in mixins
mixin = spectacular.factories.mixins[mixinName]
throw new Error "mixin '#{mixinName}' can't be found" unless mixin?
mixin.call this, this
getConstructorArguments: (traits) ->
args = 'arguments', traits
if args? and typeof args[0] is 'function'
args = args[0].call(spectacular.env.currentExample.context)
args or []
findTrait: (traitName) ->
trait = [traitName] or ?.findTrait traitName
throw new Error "unknown trait #{traitName}" unless trait?
trait
spectacular.factoriesCache = {}
spectacular.factories.factory = (name, options, block) ->
cache = spectacular.factoriesCache
[options, block] = [{}, options] if typeof options is 'function'
if options.extends?
parent = cache[options.extends]
unless parent?
throw new Error "parent factory '#{options.extends}' can't be found"
fct = cache[name] ||= new spectacular.factories.Factory name
fct.parent = parent
else if options.class?
fct = cache[name] ||= new spectacular.factories.Factory name, options.class
else
fct = cache[name]
unless fct?
throw new Error 'no class provided'
fct.globalize()
block.call(fct)
fct.unglobalize()
spectacular.factories.mixins = {}
spectacular.factories.factoryMixin = (name, block) ->
spectacular.factories.mixins[name] = block
spectacular.factories.create = (name, traits..., options) ->
throw new Error 'no factory name provided' unless name?
if typeof options is 'string'
traits.push options
options = null
fct = spectacular.factoriesCache[name]
throw new Error "missing factory #{name}" unless fct?
fct.buildInstance(traits, options)