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 || {}
|
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 | get: (name) ->
|
33 | throwNotFound name unless @isRegistered name
|
34 | registeredAt = @_registeredAt(name)
|
35 | if registeredAt == 'local'
|
36 | @_instantiate name unless @_isInstantiated(name)
|
37 | return @_instances[name]
|
38 | else
|
39 | return @_parents[registeredAt].get(name)
|
40 |
|
41 | _isInstantiated: (name) -> Object.keys(@_instances).indexOf(name) != -1
|
42 |
|
43 | _registeredAt: (name) ->
|
44 | if @_registrations[name]?
|
45 | return 'local'
|
46 | else
|
47 | for p, i in @_parents
|
48 | if p._registeredAt(name)?
|
49 | parentIndex = i
|
50 | return parentIndex
|
51 |
|
52 | isRegistered: (name) -> @_registeredAt(name) != undefined
|
53 |
|
54 | getRegistrations: ->
|
55 | if @_parents.length == 0
|
56 | return @_registrations
|
57 |
|
58 | all = (p.getRegistrations() for p in @_parents)
|
59 | all.push @_registrations
|
60 | all.reduce (acc, item) ->
|
61 | Object.keys(item).forEach (key) -> acc[key] = item[key]
|
62 | return acc
|
63 | , {}
|
64 |
|
65 | getArguments: (name) ->
|
66 | if @_modules[name]?
|
67 | @_modules[name]
|
68 | else
|
69 | getArguments @_registrations[name].value
|
70 |
|
71 | loadAll: ->
|
72 | p.loadAll() for p in @_parents
|
73 | Object.keys(@_registrations).forEach (name) =>
|
74 | @_instantiate name unless @_isInstantiated(name)
|
75 |
|
76 | shutdown: ->
|
77 | Object.keys(@_instances).forEach (key) =>
|
78 | @_instances[key]?.__amendShutdown?()
|
79 | p.shutdown() for p in @_parents
|
80 |
|
81 | _register: (type, name, value) -> @_registrations[name] = value: value, type: type
|
82 |
|
83 | _instantiate: (name, parent) ->
|
84 | module = @_registrations[name]
|
85 | throwNotFound name, parent unless module?
|
86 | type = module.type
|
87 | value = module.value
|
88 | instance = if type == 'value' then value else @_instantiateWithDependencies name, value, type
|
89 | @_instances[name] = instance
|
90 | return instance
|
91 |
|
92 | _instantiateWithDependencies: (name, value, type) ->
|
93 | args = @getArguments name
|
94 |
|
95 | dependencies = args.map (depName) =>
|
96 | registeredAt = @_registeredAt(depName)
|
97 | if registeredAt == 'local'
|
98 | if @_isInstantiated(depName) then @_instances[depName] else @_instantiate depName, name
|
99 | else if registeredAt?
|
100 | @_parents[registeredAt].get(depName)
|
101 | else
|
102 | throwNotFound depName, name
|
103 |
|
104 | return runFactory value, dependencies if type == 'factory'
|
105 | return construct value, dependencies if type == 'class'
|