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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +29 -18
- data/README.md +85 -0
- data/lib/smart_core/injection.rb +33 -0
- data/lib/smart_core/injection/dsl.rb +107 -0
- data/lib/smart_core/injection/errors.rb +15 -0
- data/lib/smart_core/injection/injector.rb +165 -0
- data/lib/smart_core/injection/injector/container_set.rb +113 -0
- data/lib/smart_core/injection/injector/container_set/adding_listener.rb +30 -0
- data/lib/smart_core/injection/injector/injection_settings.rb +173 -0
- data/lib/smart_core/injection/injector/injection_settings/incompatability_control.rb +113 -0
- data/lib/smart_core/injection/injector/modulizer.rb +55 -0
- data/lib/smart_core/injection/injector/strategies.rb +7 -0
- data/lib/smart_core/injection/injector/strategies/method_injection.rb +92 -0
- data/lib/smart_core/injection/locator.rb +59 -0
- data/lib/smart_core/injection/locator/container_proxy.rb +142 -0
- data/lib/smart_core/injection/locator/dependency.rb +54 -0
- data/lib/smart_core/injection/locator/factory.rb +55 -0
- data/lib/smart_core/injection/version.rb +1 -1
- data/smart_injection.gemspec +7 -3
- metadata +64 -2
@@ -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
|