smart_injection 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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