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,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
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ module SmartCore::Injection::Injector::Strategies
6
+ require_relative 'strategies/method_injection'
7
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ module SmartCore::Injection::Injector::Strategies::MethodInjection
6
+ class << self
7
+ # @param injection_settings [SmartCore::Injection::Injector::InjectionSettings]
8
+ # @return [void]
9
+ #
10
+ # @api private
11
+ # @since 0.1.0
12
+ def inject_instance_method(injection_settings)
13
+ inject_dependency(injection_settings, injection_settings.instance_level_injectable)
14
+ end
15
+
16
+ # @param injection_settings [SmartCore::Injection::Injector::InjectionSettings]
17
+ # @return [void]
18
+ #
19
+ # @api private
20
+ # @since 0.1.0
21
+ def inject_class_method(injection_settings)
22
+ inject_dependency(injection_settings, injection_settings.class_level_injectable)
23
+ end
24
+
25
+ private
26
+
27
+ # @param injection_settings [SmartCore::Injection::Injector::InjectionSettings]
28
+ # @param injectable [Class, Module]
29
+ # @return [void]
30
+ #
31
+ # @api private
32
+ # @since 0.1.0
33
+ def inject_dependency(injection_settings, injectable)
34
+ injection_settings.each_import do |import_key, import_path|
35
+ locator = build_locator(injection_settings, import_key, import_path)
36
+ process_injection_bindings(injection_settings, locator)
37
+ injection = build_injection(injection_settings, import_key, locator)
38
+ inject_injection(injection_settings, injection, injectable)
39
+ end
40
+ end
41
+
42
+ # @param injection_settings [SmartCore::Injection::Injector::InjectionSettings]
43
+ # @param import_key [String, Symbol]
44
+ # @param import_path [String]
45
+ # @return [SmartCore::Injection::Locator]
46
+ #
47
+ # @api private
48
+ # @since 0.1.0
49
+ def build_locator(injection_settings, import_key, import_path)
50
+ SmartCore::Injection::Locator::Factory.create(injection_settings, import_key, import_path)
51
+ end
52
+
53
+ # @param locator [SmartCore::Injection::Locator]
54
+ # @return [void]
55
+ #
56
+ # @api private
57
+ # @since 0.1.0
58
+ def process_injection_bindings(injection_settings, locator)
59
+ locator.bind! if injection_settings.bind_static?
60
+ end
61
+
62
+ # @param injection_settings [SmartCore::Injection::Injector::InjectionSettings]
63
+ # @param import_key [String, Symbol]
64
+ # @param locator [SmartCore::Injection::Locator]
65
+ # @return [Module]
66
+ #
67
+ # @api private
68
+ # @since 0.1.0
69
+ def build_injection(injection_settings, import_key, locator)
70
+ Module.new do
71
+ define_method(import_key) { locator.resolve_dependency }
72
+
73
+ case injection_settings.access
74
+ when :public then public(import_key)
75
+ when :private then private(import_key)
76
+ when :protected then protected(import_key)
77
+ end
78
+ end
79
+ end
80
+
81
+ # @param injection_settings [SmartCore::Injection::Injector::InjectionSettings]
82
+ # @param injection [Module]
83
+ # @param injectable [Class, Module]
84
+ # @return [void]
85
+ #
86
+ # @api private
87
+ # @since 0.1.0
88
+ def inject_injection(injection_settings, injection, injectable)
89
+ injectable.include(injection)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SmartCore::Injection::Locator
6
+ require_relative 'locator/container_proxy'
7
+ require_relative 'locator/dependency'
8
+ require_relative 'locator/factory'
9
+
10
+ # @return [String]
11
+ #
12
+ # @api private
13
+ # @since 0.1.0
14
+ attr_reader :import_path
15
+
16
+ # @param import_path [String]
17
+ # @param container_proxy [SmartCore::Injection::Locator::ContainerProxy]
18
+ # @return [void]
19
+ #
20
+ # @api private
21
+ # @since 0.1.0
22
+ def initialize(import_path, container_proxy)
23
+ @import_path = import_path
24
+ @container_proxy = container_proxy
25
+ @dependency = SmartCore::Injection::Locator::Dependency.new
26
+ end
27
+
28
+ # @return [Any]
29
+ #
30
+ # @api private
31
+ # @since 0.1.0
32
+ def resolve_dependency
33
+ dependency.bind { container_proxy.resolve_dependency(import_path) }
34
+ end
35
+ alias_method :bind!, :resolve_dependency
36
+
37
+ # @return [Any]
38
+ #
39
+ # @api private
40
+ # @since 0.1.0
41
+ def rebind_dependency
42
+ dependency.rebind { container_proxy.resolve(import_path) }
43
+ end
44
+ alias_method :rebind!, :rebind_dependency
45
+
46
+ private
47
+
48
+ # @return [SmartCore::Injection::Locator::Dependency]
49
+ #
50
+ # @api private
51
+ # @since 0.1.0
52
+ attr_reader :dependency
53
+
54
+ # @return [SmartCore::Injection::Locator::ContainerProxy]
55
+ #
56
+ # @api private
57
+ # @since 0.1.0
58
+ attr_reader :container_proxy
59
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SmartCore::Injection::Locator::ContainerProxy
6
+ # @param registered_containers [SmartCore::Injection::Injector::ContainerSet]
7
+ # @param explicitly_passed_container [NilClass, SmartCore::Container]
8
+ # @return [void]
9
+ #
10
+ # @api private
11
+ # @since 0.1.0
12
+ def initialize(registered_containers, explicitly_passed_container)
13
+ @registered_containers = registered_containers
14
+ @explicitly_passed_container = explicitly_passed_container
15
+ end
16
+
17
+ # @param dependency_path [String]
18
+ # @return [Any]
19
+ #
20
+ # @raise [SmartCore::Container::ResolvingError]
21
+ #
22
+ # @api private
23
+ # @since 0.1.0
24
+ def resolve_dependency(dependency_path)
25
+ resolving_error = nil
26
+
27
+ each_container do |container|
28
+ begin # rubocop:disable Style/RedundantBegin
29
+ return container.resolve(dependency_path)
30
+ rescue SmartCore::Container::ResolvingError => error
31
+ resolving_error = error
32
+ end
33
+ end
34
+
35
+ raise(resolving_error)
36
+ end
37
+
38
+ # @param import_path [String]
39
+ # @param observer [Block]
40
+ # @return [void]
41
+ #
42
+ # @api private
43
+ # @since 0.1.0
44
+ def observe(import_path, &observer)
45
+ # TODO: implement
46
+ end
47
+
48
+ private
49
+
50
+ # @return [SmartCore::Injection::Injector::ContainerSet]
51
+ #
52
+ # @api private
53
+ # @since 0.1.0
54
+ attr_reader :registered_containers
55
+
56
+ # @return [NilClass, SmartCore::Container]
57
+ #
58
+ # @api private
59
+ # @since 0.1.0
60
+ attr_reader :explicitly_passed_container
61
+
62
+ # @param block [Block]
63
+ # @yield [container]
64
+ # @yieldparam container [SmartCore::Container]
65
+ # @return [void]
66
+ #
67
+ # @api private
68
+ # @since 0.1.0
69
+ def each_container(&block)
70
+ yield(explicitly_passed_container) if explicitly_passed_container
71
+ registered_containers.reverse_each(&block)
72
+ end
73
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ class SmartCore::Injection::Locator::Dependency
6
+ # @return [void]
7
+ #
8
+ # @api private
9
+ # @since 0.1.0
10
+ def initialize
11
+ @binded = false
12
+ @value = nil
13
+ @barrier = SmartCore::Engine::Lock.new
14
+ end
15
+
16
+ # @param block [Block]
17
+ # @return [Any]
18
+ #
19
+ # @api private
20
+ # @since 0.1.0
21
+ def rebind(&block)
22
+ with_barrier do
23
+ @bind = false
24
+ bind(&block)
25
+ end
26
+ end
27
+
28
+ # @param block [Block]
29
+ # @return [Any]
30
+ #
31
+ # @api public
32
+ # @since 0.7.0
33
+ def bind(&block)
34
+ with_barrier do
35
+ if @binded
36
+ @value
37
+ else
38
+ @binded = true
39
+ @value = yield(@value)
40
+ end
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ # @param block [Block]
47
+ # @return [Any]
48
+ #
49
+ # @api private
50
+ # @since 0.1.0
51
+ def with_barrier(&block)
52
+ @barrier.synchronize(&block)
53
+ end
54
+ end