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,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_dependency(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,142 @@
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
+ @observers = Hash.new { |h, k| h[k] = [] }
16
+ registered_containers.listen_addings { |container| listen_changes(container) }
17
+ end
18
+
19
+ # @param dependency_path [String]
20
+ # @return [Any]
21
+ #
22
+ # @raise [SmartCore::Injection::NoRegisteredContainersError]
23
+ # @raise [SmartCore::Container::ResolvingError]
24
+ #
25
+ # @api private
26
+ # @since 0.1.0
27
+ def resolve_dependency(dependency_path)
28
+ resolving_error = nil
29
+
30
+ each_container do |container|
31
+ begin # rubocop:disable Style/RedundantBegin
32
+ return container.resolve(dependency_path)
33
+ rescue SmartCore::Container::ResolvingError => error
34
+ resolving_error = error
35
+ end
36
+ end
37
+
38
+ unless resolving_error
39
+ raise(SmartCore::Injection::NoRegisteredContainersError, <<~ERROR_MESSAGE)
40
+ You haven't registered any containers for import.
41
+ ERROR_MESSAGE
42
+ else
43
+ raise(resolving_error)
44
+ end
45
+ end
46
+
47
+ # @param import_path [String]
48
+ # @param observer [Block]
49
+ # @return [void]
50
+ #
51
+ # @api private
52
+ # @since 0.1.0
53
+ def observe(import_path, &observer)
54
+ register_observer(import_path, observer)
55
+
56
+ each_container do |container|
57
+ container.observe(import_path) { |path, cntr| process_changement(cntr, path) }
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ # @return [Hash<String,Proc>]
64
+ #
65
+ # @api private
66
+ # @since 0.1.0
67
+ attr_reader :observers
68
+
69
+ # @return [SmartCore::Injection::Injector::ContainerSet]
70
+ #
71
+ # @api private
72
+ # @since 0.1.0
73
+ attr_reader :registered_containers
74
+
75
+ # @return [NilClass, SmartCore::Container]
76
+ #
77
+ # @api private
78
+ # @since 0.1.0
79
+ attr_reader :explicitly_passed_container
80
+
81
+ # @param import_path [NilClass, String]
82
+ # @param container [SmartCore::Container]
83
+ # @return [void]
84
+ #
85
+ # @api private
86
+ # @since 0.1.0
87
+ def process_changement(container, import_path = nil)
88
+ observed_pathes = import_path ? [import_path] : observed_import_pathes
89
+
90
+ observed_pathes.each do |observed_path|
91
+ suitable_container = each_container.find { |cntr| cntr.key?(observed_path) }
92
+ break unless suitable_container
93
+ notify_observers(observed_path) if suitable_container == container
94
+ end
95
+ end
96
+
97
+ # @param import_path [String]
98
+ # @param observer [SmartCore::Container]
99
+ # @return [void]
100
+ #
101
+ # @api private
102
+ # @since 0.1.0
103
+ def register_observer(import_path, observer)
104
+ observers[import_path] << observer
105
+ end
106
+
107
+ # @return [Array<String>]
108
+ #
109
+ # @api private
110
+ # @since 0.1.0
111
+ def observed_import_pathes
112
+ observers.keys
113
+ end
114
+
115
+ # @param import_pathes [String]
116
+ # @return [void]
117
+ #
118
+ # @api private
119
+ # @since 0.1.0
120
+ def notify_observers(*import_pathes)
121
+ import_pathes.each { |import_path| observers.fetch(import_path).each(&:call) }
122
+ end
123
+
124
+ # @param block [Block]
125
+ # @yield [container]
126
+ # @yieldparam container [SmartCore::Container]
127
+ # @return [Enumerable]
128
+ #
129
+ # @api private
130
+ # @since 0.1.0
131
+ def each_container(&block)
132
+ enumerator = Enumerator.new do |yielder|
133
+ if explicitly_passed_container
134
+ yielder.yield(explicitly_passed_container)
135
+ else
136
+ registered_containers.reverse_each(&yielder)
137
+ end
138
+ end
139
+
140
+ block_given? ? enumerator.each(&block) : enumerator.each
141
+ end
142
+ 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
+ @binded = 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
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
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ module SmartCore::Injection::Locator::Factory
6
+ class << self
7
+ # @param injection_settings [SmartCore::Injection::Injector::InjectionSettings]
8
+ # @return [SmartCore::Injection::Locator]
9
+ #
10
+ # @api private
11
+ # @since 0.1.0
12
+ def create(injection_settings, import_key, import_path)
13
+ container_proxy = create_container_proxy(injection_settings)
14
+ create_locator(import_path, container_proxy).tap do |locator|
15
+ control_injection_memoization(injection_settings, container_proxy, locator, import_path)
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ # @return [SmartCore::Injection::Locator::ContainerProxy]
22
+ #
23
+ # @api private
24
+ # @since 0.1.0
25
+ def create_container_proxy(injection_settings)
26
+ SmartCore::Injection::Locator::ContainerProxy.new(
27
+ injection_settings.container_set,
28
+ injection_settings.from
29
+ )
30
+ end
31
+
32
+ # @param import_path [String]
33
+ # @param container_proxy [SmartCore::Injection::Locator::ContainerProxy]
34
+ # @return [SmartCore::Injection::Locator]
35
+ #
36
+ # @api private
37
+ # @since 0.1.0
38
+ def create_locator(import_path, container_proxy)
39
+ SmartCore::Injection::Locator.new(import_path, container_proxy)
40
+ end
41
+
42
+ # @param injection_settings [SmartCore::Injection::Injector::InjectionSettings]
43
+ # @param locator [SmartCore::Injection::Locator]
44
+ # @param import_path [String]
45
+ # @return [void]
46
+ #
47
+ # @api private
48
+ # @since 0.1.0
49
+ def control_injection_memoization(injection_settings, container_proxy, locator, import_path)
50
+ container_proxy.observe(import_path) do
51
+ locator.rebind!
52
+ end unless injection_settings.memoize
53
+ end
54
+ end
55
+ end
@@ -6,6 +6,6 @@ module SmartCore
6
6
  #
7
7
  # @api public
8
8
  # @since 0.1.0
9
- VERSION = '0.0.0'
9
+ VERSION = '0.1.0'
10
10
  end
11
11
  end
@@ -5,10 +5,10 @@ require_relative 'lib/smart_core/injection/version'
5
5
  Gem::Specification.new do |spec|
6
6
  spec.required_ruby_version = Gem::Requirement.new('>= 2.4.10')
7
7
 
8
- spec.name = "smart_injection"
8
+ spec.name = 'smart_injection'
9
9
  spec.version = SmartCore::Injection::VERSION
10
- spec.authors = ["Rustam Ibragimov"]
11
- spec.email = ["iamdaiver@gmail.com"]
10
+ spec.authors = ['Rustam Ibragimov']
11
+ spec.email = ['iamdaiver@gmail.com']
12
12
 
13
13
  spec.summary = 'DI principles and idioms realized in scope of Ruby'
14
14
  spec.description = 'DI principles and idioms realized in scope of Ruby'
@@ -30,9 +30,13 @@ Gem::Specification.new do |spec|
30
30
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
31
  spec.require_paths = ['lib']
32
32
 
33
+ spec.add_runtime_dependency 'smart_engine', '~> 0.7'
34
+ spec.add_runtime_dependency 'smart_container', '~> 0.8', '>= 0.8.1'
35
+
33
36
  spec.add_development_dependency 'bundler', '~> 2.1'
34
37
  spec.add_development_dependency 'rake', '~> 13.0'
35
38
  spec.add_development_dependency 'rspec', '~> 3.9'
36
39
  spec.add_development_dependency 'armitage-rubocop', '~> 0.85'
37
40
  spec.add_development_dependency 'simplecov', '~> 0.18'
41
+ spec.add_development_dependency 'pry', '~> 0.13'
38
42
  end