1 | ModuleNotFound = require './ModuleNotFound'
|
2 | getArguments = require './get-arguments'
|
3 |
|
4 | construct = (c, args) ->
|
5 | class F
|
6 | constructor: -> c.apply @, args
|
7 | F.prototype = c.prototype
|
8 | return new F()
|
9 |
|
10 | runFactory = (factory, args) ->
|
11 | factory.apply null, args
|
12 |
|
13 | throwNotFound = (name, parent) ->
|
14 | throw new ModuleNotFound name, parent
|
15 |
|
16 | module.exports = class Container
|
17 | constructor: (conf = {}, @_parents = []) ->
|
18 | @_modules = conf.modules || conf
|
19 | @_registrations = {}
|
20 | @_instances = {}
|
21 |
|
22 | factory: (name, func) ->
|
23 | throw new Error 'A factory must be a function' unless func instanceof Function
|
24 | @_register 'factory', name, func
|
25 |
|
26 | value: (name, value) -> @_register 'value', name, value
|
27 |
|
28 | class: (name, constructor) ->
|
29 | throw new TypeError 'A constructor must be a function' unless constructor instanceof Function
|
30 | @_register 'class', name, constructor
|
31 |
|
32 | spread: (obj) -> @value(k, v) for k, v of obj
|
33 |
|
34 | get: (name) ->
|
35 | throwNotFound name unless @isRegistered name
|
36 | registeredAt = @_registeredAt(name)
|
37 | if registeredAt == 'local'
|
38 | @_instantiate name unless @_isInstantiated(name)
|
39 | return @_instances[name]
|
40 | else
|
41 | return @_parents[registeredAt].get(name)
|
42 |
|
43 | _isInstantiated: (name) -> Object.keys(@_instances).indexOf(name) != -1
|
44 |
|
45 | _registeredAt: (name) ->
|
46 | if @_registrations[name]?
|
47 | return 'local'
|
48 | else
|
49 | for p, i in @_parents
|
50 | if p._registeredAt(name)?
|
51 | parentIndex = i
|
52 | return parentIndex
|
53 |
|
54 | isRegistered: (name) -> @_registeredAt(name) != undefined
|
55 |
|
56 | getRegistrations: ->
|
57 | if @_parents.length == 0
|
58 | return @_registrations
|
59 |
|
60 | all = (p.getRegistrations() for p in @_parents)
|
61 | all.push @_registrations
|
62 | all.reduce (acc, item) ->
|
63 | Object.keys(item).forEach (key) -> acc[key] = item[key]
|
64 | return acc
|
65 | , {}
|
66 |
|
67 | getArguments: (name) ->
|
68 | if @_modules[name]?
|
69 | @_modules[name]
|
70 | else
|
71 | getArguments @_registrations[name].value
|
72 |
|
73 | loadAll: ->
|
74 | p.loadAll() for p in @_parents
|
75 | Object.keys(@_registrations).forEach (name) =>
|
76 | @_instantiate name unless @_isInstantiated(name)
|
77 |
|
78 | shutdown: ->
|
79 | Object.keys(@_instances).forEach (key) =>
|
80 | @_instances[key]?.__amendShutdown?()
|
81 | p.shutdown() for p in @_parents
|
82 |
|
83 | _register: (type, name, value) -> @_registrations[name] = value: value, type: type
|
84 |
|
85 | _instantiate: (name, parent) ->
|
86 | module = @_registrations[name]
|
87 | throwNotFound name, parent unless module?
|
88 | type = module.type
|
89 | value = module.value
|
90 | instance = if type == 'value' then value else @_instantiateWithDependencies name, value, type
|
91 | @_instances[name] = instance
|
92 | return instance
|
93 |
|
94 | _instantiateWithDependencies: (name, value, type) ->
|
95 | args = @getArguments name
|
96 |
|
97 | dependencies = args.map (depName) =>
|
98 | registeredAt = @_registeredAt(depName)
|
99 | if registeredAt == 'local'
|
100 | if @_isInstantiated(depName) then @_instances[depName] else @_instantiate depName, name
|
101 | else if registeredAt?
|
102 | @_parents[registeredAt].get(depName)
|
103 | else
|
104 | throwNotFound depName, name
|
105 |
|
106 | return runFactory value, dependencies if type == 'factory'
|
107 | return construct value, dependencies if type == 'class'
|