smart_injection 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SmartCore::Injection::Injector::ContainerSet
6
+ require_relative 'container_set/adding_listener'
7
+
8
+ # @since 0.1.0
9
+ include Enumerable
10
+
11
+ # @return [void]
12
+ #
13
+ # @api private
14
+ # @since 0.1.0
15
+ def initialize
16
+ @containers = [] # NOTE: we use Array cuz we need an ordered set
17
+ @adding_listeners = []
18
+ @access_lock = SmartCore::Engine::Lock.new
19
+ end
20
+
21
+ # @param container [SmartCore::Container]
22
+ # @return [void]
23
+ #
24
+ # @api private
25
+ # @since 0.1.0
26
+ def add(container)
27
+ thread_safe { append_container(container) }
28
+ end
29
+ alias_method :<<, :add
30
+
31
+ # @param listener [Block]
32
+ # @yield [container]
33
+ # @yieldparam container [SmartCore::Container]
34
+ # @return [void]
35
+ #
36
+ # @api private
37
+ # @since 0.1.0
38
+ def listen_addings(&listener)
39
+ thread_safe { add_adding_listener(listener) }
40
+ end
41
+
42
+ # @param block [Block]
43
+ # @yield [container]
44
+ # @yieldparam container [SmartCore::Container]
45
+ # @return [Enumerator]
46
+ #
47
+ # @api private
48
+ # @since 0.1.0
49
+ def each(&block)
50
+ thread_safe { block_given? ? containers.each(&block) : containers.each }
51
+ end
52
+
53
+ # @param block [Block]
54
+ # @yield [container]
55
+ # @yieldparam container [SmartCore::Container]
56
+ # @return [Enumerator]
57
+ #
58
+ # @api private
59
+ # @since 0.1.0
60
+ def reverse_each(&block)
61
+ thread_safe { block_given? ? containers.reverse_each(&block) : containers.reverse_each }
62
+ end
63
+
64
+ # @return [Array<SmartCore::Container>]
65
+ #
66
+ # @api private
67
+ # @since 0.1.0
68
+ def list
69
+ thread_safe { containers.dup }
70
+ end
71
+
72
+ private
73
+
74
+ # @return [Array<SmartCore::Container>]
75
+ #
76
+ # @api private
77
+ # @since 0.1.0
78
+ attr_reader :containers
79
+
80
+ # @return [Array<SmartCore::Injection::Injector::ContainerSet::AddingListener>]
81
+ attr_reader :adding_listeners
82
+
83
+ # @param listener [Proc]
84
+ # @return [void]
85
+ #
86
+ # @api private
87
+ # @since 0.1.0
88
+ def add_adding_listener(listener)
89
+ adding_listeners << SmartCore::Injection::Injector::ContainerSet::AddingListener.new(listener)
90
+ end
91
+
92
+ # @param container [SmartCore::Container]
93
+ # @return [void]
94
+ #
95
+ # @api private
96
+ # @since 0.1.0
97
+ def append_container(container)
98
+ # NOTE:
99
+ # - #concant is used to prevent container duplications in ordered set;
100
+ # - @containers should have an ordered unified container list;
101
+ containers.concat([container])
102
+ adding_listeners.each { |listener| listener.notify(container) }
103
+ end
104
+
105
+ # @param block [Block]
106
+ # @return [Any]
107
+ #
108
+ # @api private
109
+ # @since 0.1.0
110
+ def thread_safe(&block)
111
+ @access_lock.synchronize(&block)
112
+ end
113
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SmartCore::Injection::Injector::ContainerSet::AddingListener
6
+ # @param listener [Proc]
7
+ # @return [void]
8
+ #
9
+ # @api private
10
+ # @since 0.1.0
11
+ def initialize(listener)
12
+ @listener = listener
13
+ end
14
+
15
+ # @param cotnainer [SmartCore::Container]
16
+ #
17
+ # @api private
18
+ # @since 0.1.0
19
+ def notify(container)
20
+ listener.call(container)
21
+ end
22
+
23
+ private
24
+
25
+ # @return [Proc]
26
+ #
27
+ # @api private
28
+ # @since 0.1.0
29
+ attr_reader :listener
30
+ 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
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ module SmartCore::Injection::Injector::InjectionSettings::IncompatabilityControl
6
+ class << self
7
+ # @param injectable [Class, Module]
8
+ # @param imports [Hash<String|Symbol,String>]
9
+ # @param memoize [Boolean]
10
+ # @param access [Symbol]
11
+ # @param bind [Symbol]
12
+ # @param from [NilClass, SmartCore::Container]
13
+ # @return [void]
14
+ #
15
+ # @api private
16
+ # @since 0.1.0
17
+ def prevent_incompatabilities!(injectable, imports, memoize, access, bind, from)
18
+ prevent_injectable_incompatabilities!(injectable)
19
+ prevent_imports_incompatabilites!(imports)
20
+ prevent_memoize_incompatabilites(memoize)
21
+ prevent_access_incompatabilites(access)
22
+ prevent_bind_incompatabilites(bind)
23
+ prevent_from_incompatabilites(from)
24
+ end
25
+
26
+ private
27
+
28
+ # @param injectable [Class, Module]
29
+ # @return [void]
30
+ #
31
+ # @api private
32
+ # @since 0.1.0
33
+ def prevent_injectable_incompatabilities!(injectable)
34
+ unless injectable.is_a?(Class) || injectable.is_a?(Module)
35
+ raise(SmartCore::Injection::ArgumentError, <<~ERROR_MESSAGE)
36
+ ERROR_MESSAGE
37
+ end
38
+ end
39
+
40
+ # @param imports [Hash<String|Symbol,String>]
41
+ # @return [void]
42
+ #
43
+ # @api private
44
+ # @since 0.1.0
45
+ def prevent_imports_incompatabilites!(imports)
46
+ unless imports.is_a?(::Hash)
47
+ raise(SmartCore::Injection::ArgumentError, <<~ERROR_MESSAGE)
48
+ Incorrect import list (should be a type of Hash)
49
+ ERROR_MESSAGE
50
+ end
51
+
52
+ unless imports.keys.all? { |key| key.is_a?(String) || key.is_a?(Symbol) }
53
+ raise(SmartCore::Injection::ArgumentError, <<~ERROR_MESSAGE)
54
+ Some method aliases are incorret (they should be a type of String or Symbol)
55
+ ERROR_MESSAGE
56
+ end
57
+
58
+ unless imports.values.all? { |value| value.is_a?(String) }
59
+ raise(SmartCore::Injection::ArgumentError, <<~ERROR_MESSAGE)
60
+ Some injection pathes are incorrect (they should be a type of String)
61
+ ERROR_MESSAGE
62
+ end
63
+ end
64
+
65
+ # @param memoize [Boolean]
66
+ # @return [void]
67
+ #
68
+ # @api private
69
+ # @since 0.1.0
70
+ def prevent_memoize_incompatabilites(memoize)
71
+ unless memoize.is_a?(::TrueClass) || memoize.is_a?(::FalseClass)
72
+ raise(SmartCore::Injection::ArgumentError, <<~ERROR_MESSAGE)
73
+ ERROR_MESSAGE
74
+ end
75
+ end
76
+
77
+ # @param access [Symbol]
78
+ # @return [void]
79
+ #
80
+ # @api private
81
+ # @since 0.1.0
82
+ def prevent_access_incompatabilites(access)
83
+ unless SmartCore::Injection::Injector::InjectionSettings::ACCESS_MARKS.include?(access)
84
+ raise(SmartCore::Injection::ArgumentError, <<~ERROR_MESSAGE)
85
+ ERROR_MESSAGE
86
+ end
87
+ end
88
+
89
+ # @param bind [Symbol]
90
+ # @return [void]
91
+ #
92
+ # @api private
93
+ # @since 0.1.0
94
+ def prevent_bind_incompatabilites(bind)
95
+ unless SmartCore::Injection::Injector::InjectionSettings::BINDING_STRATEGIES.include?(bind)
96
+ raise(SmartCore::Injection::ArgumentError, <<~ERROR_MESSAGE)
97
+ ERROR_MESSAGE
98
+ end
99
+ end
100
+
101
+ # @param from [NilClass, SmartCore::Container]
102
+ # @return [void]
103
+ #
104
+ # @api private
105
+ # @since 0.1.0
106
+ def prevent_from_incompatabilites(from)
107
+ unless from.is_a?(NilClass) || from.is_a?(SmartCore::Container)
108
+ raise(SmartCore::Injection::ArgumentError, <<~ERROR_MESSAGE)
109
+ ERROR_MESSAGE
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ module SmartCore::Injection::Injector::Modulizer
6
+ class << self
7
+ # @param containers [Array<SmartCore::Container>]
8
+ # @return [Module]
9
+ #
10
+ # @api private
11
+ # @since 0.1.0
12
+ def with_containers(containers)
13
+ prevent_inconsistency!(containers)
14
+ build_container_injectable_module(containers)
15
+ end
16
+
17
+ # @param base_klass [Class, Module]
18
+ # @return [void]
19
+ #
20
+ # @api private
21
+ # @since 0.1.0
22
+ def inject_to(base_klass)
23
+ base_klass.include(::SmartCore::Injection::DSL)
24
+ end
25
+
26
+ private
27
+
28
+ # @param containers [Array<SmartCore::Container>]
29
+ # @return [void]
30
+ #
31
+ # @api private
32
+ # @since 0.1.0
33
+ def prevent_inconsistency!(containers)
34
+ unless containers.is_a?(Array) && containers.all? { |cont| cont.is_a?(SmartCore::Container) }
35
+ raise(SmartCore::Injection::ArgumentError, <<~ERROR_MESSAGE)
36
+ Injectable containers should be a type of SmartCore::Container
37
+ ERROR_MESSAGE
38
+ end
39
+ end
40
+
41
+ # @param containers [Array<SmartCore::Container>]
42
+ # @return [Module]
43
+ #
44
+ # @api private
45
+ # @since 0.1.0
46
+ def build_container_injectable_module(containers)
47
+ Module.new do
48
+ define_singleton_method :included do |base_klass|
49
+ base_klass.include(::SmartCore::Injection::DSL)
50
+ base_klass.register_container(*containers)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end