smart_injection 0.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'smart_core/container'
4
+ require 'set'
5
+
6
+ # @api public
7
+ # @since 0.1.0
8
+ module SmartCore
9
+ # TODO: обязательно учесть наследование
10
+
11
+ class << self
12
+ # @param containers [Array<SmartCore::Container>]
13
+ # @return [Module]
14
+ #
15
+ # @api public
16
+ # @since 0.1.0
17
+ # rubocop:disable Naming/MethodName
18
+ def Injection(*containers)
19
+ ::SmartCore::Injection::Injector::Modulizer.with_containers(containers)
20
+ end
21
+ # rubocop:enable Naming/MethodName
22
+ end
23
+
24
+ # @api public
25
+ # @since 0.1.0
26
+ module Injection
27
+ require_relative 'injection/version'
28
+ require_relative 'injection/injector'
29
+ require_relative 'injection/locator'
30
+ require_relative 'injection/dsl'
31
+
32
+ class << self
33
+ # @param base_klass [Class, Module]
34
+ # @return [void]
35
+ #
36
+ # @api private
37
+ # @since 0.1.0
38
+ def included(base_klass)
39
+ ::SmartCore::Injection::Injector::Modulizer.inject_to(base_klass)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api public
4
+ # @since 0.1.0
5
+ module SmartCore::Injection::DSL
6
+ class << self
7
+ # @param base_klass [Class, Module]
8
+ # @return [void]
9
+ #
10
+ # @api private
11
+ # @since 0.1.0
12
+ def included(base_klass)
13
+ base_klass.instance_variable_set(
14
+ :@__smart_injection_injector__,
15
+ SmartCore::Injection::Injector.new(base_klass)
16
+ )
17
+ base_klass.extend(ClassMethods)
18
+ end
19
+ end
20
+
21
+ # @api private
22
+ # @since 0.1.0
23
+ module ClassMethods
24
+ # @param imports [Hash<String|Symbol,String>]
25
+ # @option memoize [Boolean]
26
+ # @option access [Symbol]
27
+ # @option bind [Symbol]
28
+ # @option from [NilClass, SmartCore::Container]
29
+ # @return [void]
30
+ #
31
+ # @api public
32
+ # @sincd 0.1.0
33
+ def import(
34
+ imports,
35
+ memoize: SmartCore::Injection::Injector::InjectionSettings::DEFAULT_MEMOIZE,
36
+ access: SmartCore::Injection::Injector::InjectionSettings::DEFAULT_ACCESS,
37
+ bind: SmartCore::Injection::Injector::InjectionSettings::DEFAULT_BINDING_STRATEGY,
38
+ from: SmartCore::Injection::Injector::InjectionSettings::EMPTY_CONTAINER_DESTINATION
39
+ )
40
+ __smart_injection_injector__.inject(imports, memoize, access, bind, from)
41
+ end
42
+
43
+ # @param imports [Hash<String|Symbol,String>]
44
+ # @option memoize [Boolean]
45
+ # @option access [Symbol]
46
+ # @option bind [Symbol]
47
+ # @option from [NilClass, SmartCore::Container]
48
+ # @return [void]
49
+ #
50
+ # @api public
51
+ # @sincd 0.1.0
52
+ def import_static(
53
+ imports,
54
+ memoize: SmartCore::Injection::Injector::InjectionSettings::DEFAULT_MEMOIZE,
55
+ access: SmartCore::Injection::Injector::InjectionSettings::DEFAULT_ACCESS,
56
+ bind: SmartCore::Injection::Injector::InjectionSettings::DEFAULT_BINDING_STRATEGY,
57
+ from: SmartCore::Injection::Injector::InjectionSettings::EMPTY_CONTAINER_DESTINATION
58
+ )
59
+ __smart_injection_injector__.inject_static(imports, memoize, access, bind, from)
60
+ end
61
+
62
+ # @param containers [Array<SmartCore::Container>]
63
+ # @return [void]
64
+ #
65
+ # @api public
66
+ # @since 0.1.0
67
+ def register_container(*containers)
68
+ __smart_injection_injector__.register_container(containers)
69
+ end
70
+
71
+ # @return [Array<SmartCore::Container>]
72
+ #
73
+ # @api public
74
+ # @since 0.1.0
75
+ def linked_containers
76
+ __smart_injection_injector__.associated_containers
77
+ end
78
+
79
+ private
80
+
81
+ # @return [SmartCore::Injection::Injector]
82
+ #
83
+ # @api private
84
+ # @since 0.1.0
85
+ attr_reader :__smart_injection_injector__
86
+ end
87
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmartCore::Injection
4
+ # @api public
5
+ # @since 0.1.0
6
+ Error = Class.new(SmartCore::Error)
7
+
8
+ # @api public
9
+ # @since 0.1.0
10
+ ArgumentError = Class.new(SmartCore::ArgumentError)
11
+ end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SmartCore::Injection::Injector
6
+ require_relative 'injector/container_set'
7
+ require_relative 'injector/injection_settings'
8
+ require_relative 'injector/modulizer'
9
+ require_relative 'injector/strategies'
10
+
11
+ # @param injectable [Class, Module]
12
+ # @return [void]
13
+ #
14
+ # @api private
15
+ # @since 0.1.0
16
+ def initialize(injectable)
17
+ @injectable = injectable
18
+ @linked_containers = SmartCore::Injection::Injector::ContainerSet.new
19
+ @access_lock = SmartCore::Engine::Lock.new
20
+ end
21
+
22
+ # @param imports [Hash<String|Symbol,String>]
23
+ # @param memoize [Boolean]
24
+ # @param access [Symbol]
25
+ # @param bind [Symbol]
26
+ # @param from [NilClass, SmartCore::Container]
27
+ # @return [void]
28
+ #
29
+ # @api private
30
+ # @since 0.1.0
31
+ def inject(imports, memoize, access, bind, from)
32
+ thread_safe { inject_instance_method(imports, memoize, access, bind, from) }
33
+ end
34
+
35
+ # @param imports [Hash<String|Symbol,String>]
36
+ # @param memoize [Boolean]
37
+ # @param access [Symbol]
38
+ # @param bind [Symbol]
39
+ # @param from [NilClass, SmartCore::Container]
40
+ # @return [void]
41
+ #
42
+ # @api private
43
+ # @since 0.1.0
44
+ def inject_static(imports, memoize, access, bind, from)
45
+ thread_safe { inject_class_method(imports, memoize, access, bind, from) }
46
+ end
47
+
48
+ # @param containers [Array<SmartCore::Container>]
49
+ # @return [void]
50
+ #
51
+ # @api private
52
+ # @since 0.1.0
53
+ def register_container(containers)
54
+ thread_safe { link_container(containers) }
55
+ end
56
+
57
+ # @return [Array<SmartCore::Container>]
58
+ #
59
+ # @api private
60
+ # @since 0.1.0
61
+ def associated_containers
62
+ thread_safe { linked_containers.list }
63
+ end
64
+
65
+ private
66
+
67
+ # @return [Class, Module]
68
+ #
69
+ # @api private
70
+ # @since 0.1.0
71
+ attr_reader :injectable
72
+
73
+ # @return [Array<SmartCore::Container>]
74
+ #
75
+ # @api private
76
+ # @since 0.1.0
77
+ attr_reader :linked_containers
78
+
79
+ # @param imports [Hash<String|Symbol,String>]
80
+ # @param memoize [Boolean]
81
+ # @param access [Symbol]
82
+ # @param bind [Symbol]
83
+ # @param from [NilClass, SmartCore::Container]
84
+ # @return [void]
85
+ #
86
+ # @api private
87
+ # @since 0.1.0
88
+ def inject_instance_method(imports, memoize, access, bind, from)
89
+ SmartCore::Injection::Injector::Strategies::MethodInjection.inject_instance_method(
90
+ SmartCore::Injection::Injector::InjectionSettings.new(
91
+ injectable,
92
+ linked_containers,
93
+ imports,
94
+ memoize: memoize,
95
+ access: access,
96
+ bind: bind,
97
+ from: from
98
+ )
99
+ )
100
+ end
101
+
102
+ # @param imports [Hash<String|Symbol,String>]
103
+ # @param memoize [Boolean]
104
+ # @param access [Symbol]
105
+ # @param bind [Symbol]
106
+ # @param from [NilClass, SmartCore::Container]
107
+ # @return [void]
108
+ #
109
+ # @api private
110
+ # @since 0.1.0
111
+ def inject_class_method(imports, memoize, access, bind, from)
112
+ SmartCore::Injection::Injector::Strategies::MethodInjection.inject_class_method(
113
+ SmartCore::Injection::Injector::InjectionSettings.new(
114
+ injectable,
115
+ linked_containers,
116
+ imports,
117
+ memoize: memoize,
118
+ access: access,
119
+ bind: bind,
120
+ from: from
121
+ )
122
+ )
123
+ end
124
+
125
+ # @param containers [Array<SmartCore::Container>]
126
+ # @return [void]
127
+ #
128
+ # @api private
129
+ # @since 0.1.0
130
+ def link_container(containers)
131
+ containers.each do |container|
132
+ linked_containers.add(container)
133
+ end
134
+ end
135
+
136
+ # @return [Any]
137
+ #
138
+ # @api private
139
+ # @since 0.1.0
140
+ def thread_safe(&block)
141
+ @access_lock.synchronize(&block)
142
+ end
143
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SmartCore::Injection::Injector::ContainerSet
6
+ # @since 0.1.0
7
+ include Enumerable
8
+
9
+ # @return [void]
10
+ #
11
+ # @api private
12
+ # @since 0.1.0
13
+ def initialize
14
+ @containers = [] # NOTE: we use Array cuz we need an ordered set
15
+ @access_lock = SmartCore::Engine::Lock.new
16
+ end
17
+
18
+ # @param container [SmartCore::Container]
19
+ # @return [void]
20
+ #
21
+ # @api private
22
+ # @since 0.1.0
23
+ def add(container)
24
+ thread_safe { append_container(container) }
25
+ end
26
+ alias_method :<<, :add
27
+
28
+ # @param block [Block]
29
+ # @yield [container]
30
+ # @yieldparam container [SmartCore::Container]
31
+ # @return [Enumerator]
32
+ #
33
+ # @api private
34
+ # @since 0.1.0
35
+ def each(&block)
36
+ thread_safe { block_given? ? containers.each(&block) : containers.each }
37
+ end
38
+
39
+ # @param block [Block]
40
+ # @yield [container]
41
+ # @yieldparam container [SmartCore::Container]
42
+ # @return [Enumerator]
43
+ #
44
+ # @api private
45
+ # @since 0.1.0
46
+ def reverse_each(&block)
47
+ thread_safe { block_given? ? containers.reverse_each(&block) : containers.reverse_each }
48
+ end
49
+
50
+ # @return [Array<SmartCore::Container>]
51
+ #
52
+ # @api private
53
+ # @since 0.1.0
54
+ def list
55
+ thread_safe { containers.dup }
56
+ end
57
+
58
+ private
59
+
60
+ # @return [Array<SmartCore::Container>]
61
+ #
62
+ # @api private
63
+ # @since 0.1.0
64
+ attr_reader :containers
65
+
66
+ # @param container [SmartCore::Container]
67
+ # @return [void]
68
+ #
69
+ # @api private
70
+ # @since 0.1.0
71
+ def append_container(container)
72
+ # NOTE:
73
+ # - #concant is used to prevent container duplications in ordered set;
74
+ # - @containers should have an ordered unified container list;
75
+ containers.concat([container])
76
+ end
77
+
78
+ # @param block [Block]
79
+ # @return [Any]
80
+ #
81
+ # @api private
82
+ # @since 0.1.0
83
+ def thread_safe(&block)
84
+ @access_lock.synchronize(&block)
85
+ end
86
+ end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SmartCore::Injection::Injector::InjectionSettings
6
+ require_relative 'injection_settings/incompatability_control'
7
+
8
+ # @return [Hash<String|Symbol,String>]
9
+ #
10
+ # @api private
11
+ # @since 0.1.0
12
+ DEFAULT_IMPORTS = {}.freeze
13
+
14
+ # @return [Symbol]
15
+ #
16
+ # @api private
17
+ # @since 0.1.0
18
+ DEFAULT_ACCESS = :public
19
+
20
+ # @return [Array<Symbol>]
21
+ #
22
+ # @api private
23
+ # @since 0.1.0
24
+ ACCESS_MARKS = %i[public protected private].freeze
25
+
26
+ # @return [Symbol]
27
+ #
28
+ # @api private
29
+ # @since 0.1.0
30
+ DEFAULT_BINDING_STRATEGY = :dynamic
31
+
32
+ # @return [Array<Symbol>]
33
+ #
34
+ # @api private
35
+ # @since 0.1.0
36
+ BINDING_STRATEGIES = %i[static dynamic].freeze
37
+
38
+ # @return [Boolean]
39
+ #
40
+ # @api private
41
+ # @since 0.1.0
42
+ DEFAULT_MEMOIZE = false
43
+
44
+ # @return [NilClass]
45
+ #
46
+ # @api private
47
+ # @since 0.1.0
48
+ EMPTY_CONTAINER_DESTINATION = nil
49
+
50
+ # @return [Hash<String|Symbol,String>]
51
+ #
52
+ # @api private
53
+ # @since 0.1.0
54
+ attr_reader :imports
55
+
56
+ # @return [Symbol]
57
+ #
58
+ # @api private
59
+ # @since 0.1.0
60
+ attr_reader :access
61
+
62
+ # @return [Symbol]
63
+ #
64
+ # @api private
65
+ # @since 0.1.0
66
+ attr_reader :bind
67
+
68
+ # @return [NilClass, <SmartCore::Container>]
69
+ #
70
+ # @api private
71
+ # @since 0.1.0
72
+ attr_reader :from
73
+
74
+ # @return [Boolean]
75
+ #
76
+ # @api private
77
+ # @since 0.1.0
78
+ attr_reader :memoize
79
+
80
+ # @return [SmartCore::Injection::Injector::ContainerSet]
81
+ #
82
+ # @api private
83
+ # @since 0.1.0
84
+ attr_reader :container_set
85
+
86
+ # @return [Class, Module]
87
+ #
88
+ # @api private
89
+ # @since 0.1.0
90
+ attr_reader :injectable
91
+
92
+ # @param injectable [Class, Module]
93
+ # @param container_set [SmartCore::Injection::Injector::ContainerSet]
94
+ # @param import [Hash<String|Symbol,String>]
95
+ # @option memoize [Boolean]
96
+ # @option access [Symbol]
97
+ # @option bind [Symbol]
98
+ # @option from [NilClass, SmartCore::Container]
99
+ # @return [void]
100
+ #
101
+ # @api private
102
+ # @since 0.1.0
103
+ def initialize(
104
+ injectable,
105
+ container_set,
106
+ imports,
107
+ memoize: DEFAULT_MEMOIZE,
108
+ access: DEFAULT_ACCESS,
109
+ bind: DEFAULT_BINDING_STRATEGY,
110
+ from: EMPTY_CONTAINER_DESTINATION
111
+ )
112
+ IncompatabilityControl.prevent_incompatabilities!(
113
+ injectable,
114
+ imports,
115
+ memoize,
116
+ access,
117
+ bind,
118
+ from
119
+ )
120
+
121
+ @injectable = injectable
122
+ @container_set = container_set
123
+ @imports = imports
124
+ @memoize = memoize
125
+ @access = access
126
+ @bind = bind
127
+ @from = from
128
+ end
129
+
130
+ # @return [Class, Module]
131
+ #
132
+ # @api private
133
+ # @since 0.1.0
134
+ def instance_level_injectable
135
+ injectable
136
+ end
137
+
138
+ # @return [Class]
139
+ #
140
+ # @api private
141
+ # @since 0.1.0
142
+ def class_level_injectable
143
+ class << injectable; self; end
144
+ end
145
+
146
+ # @return [Boolean]
147
+ #
148
+ # @api private
149
+ # @since 0.1.0
150
+ def bind_dynamic?
151
+ bind == :dynamic
152
+ end
153
+
154
+ # @return [Boolean]
155
+ #
156
+ # @api private
157
+ # @since 0.1.0
158
+ def bind_static?
159
+ bind == :static
160
+ end
161
+
162
+ # @param block [Block]
163
+ # @yield [import_key, import_path]
164
+ # @yieldparam import_key [String, Symbol]
165
+ # @yieldparam import_path [String]
166
+ # @return [Enumerable]
167
+ #
168
+ # @api private
169
+ # @since 0.1.0
170
+ def each_import(&block)
171
+ block_given? ? imports.each_pair(&block) : imports.each_pair
172
+ end
173
+ end