smart_container 0.3.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -2
- data/.travis.yml +7 -13
- data/CHANGELOG.md +31 -0
- data/Gemfile.lock +46 -31
- data/README.md +280 -1
- data/Rakefile +2 -1
- data/lib/smart_core/container.rb +137 -6
- data/lib/smart_core/container/{arbitary_lock.rb → arbitrary_lock.rb} +1 -1
- data/lib/smart_core/container/definition_dsl.rb +2 -2
- data/lib/smart_core/container/definition_dsl/command_set.rb +1 -1
- data/lib/smart_core/container/definition_dsl/commands/definition/register.rb +1 -1
- data/lib/smart_core/container/dependency_resolver.rb +79 -8
- data/lib/smart_core/container/dependency_resolver/route.rb +5 -8
- data/lib/smart_core/container/dependency_watcher.rb +149 -0
- data/lib/smart_core/container/dependency_watcher/observer.rb +46 -0
- data/lib/smart_core/container/entities/memoized_dependency.rb +1 -1
- data/lib/smart_core/container/entities/namespace.rb +1 -1
- data/lib/smart_core/container/errors.rb +2 -1
- data/lib/smart_core/container/mixin.rb +2 -2
- data/lib/smart_core/container/registry.rb +92 -5
- data/lib/smart_core/container/version.rb +2 -2
- data/smart_container.gemspec +5 -5
- metadata +14 -12
data/Rakefile
CHANGED
@@ -4,6 +4,7 @@ require 'bundler/gem_tasks'
|
|
4
4
|
require 'rspec/core/rake_task'
|
5
5
|
require 'rubocop'
|
6
6
|
require 'rubocop/rake_task'
|
7
|
+
require 'rubocop-rails'
|
7
8
|
require 'rubocop-performance'
|
8
9
|
require 'rubocop-rspec'
|
9
10
|
require 'rubocop-rake'
|
@@ -11,8 +12,8 @@ require 'rubocop-rake'
|
|
11
12
|
RuboCop::RakeTask.new(:rubocop) do |t|
|
12
13
|
config_path = File.expand_path(File.join('.rubocop.yml'), __dir__)
|
13
14
|
t.options = ['--config', config_path]
|
14
|
-
t.requires << 'rubocop-performance'
|
15
15
|
t.requires << 'rubocop-rspec'
|
16
|
+
t.requires << 'rubocop-performance'
|
16
17
|
t.requires << 'rubocop-rake'
|
17
18
|
end
|
18
19
|
|
data/lib/smart_core/container.rb
CHANGED
@@ -7,10 +7,10 @@ require 'smart_core'
|
|
7
7
|
module SmartCore
|
8
8
|
# @api public
|
9
9
|
# @since 0.1.0
|
10
|
-
class Container
|
10
|
+
class Container # rubocop:disable Metrics/ClassLength
|
11
11
|
require_relative 'container/version'
|
12
12
|
require_relative 'container/errors'
|
13
|
-
require_relative 'container/
|
13
|
+
require_relative 'container/arbitrary_lock'
|
14
14
|
require_relative 'container/key_guard'
|
15
15
|
require_relative 'container/entities'
|
16
16
|
require_relative 'container/definition_dsl'
|
@@ -18,8 +18,30 @@ module SmartCore
|
|
18
18
|
require_relative 'container/registry'
|
19
19
|
require_relative 'container/registry_builder'
|
20
20
|
require_relative 'container/dependency_resolver'
|
21
|
+
require_relative 'container/dependency_watcher'
|
21
22
|
require_relative 'container/mixin'
|
22
23
|
|
24
|
+
class << self
|
25
|
+
# @param initial_container_klass [Class<SmartCore::Container>]
|
26
|
+
# @param container_definitions [Block]
|
27
|
+
# @return [SmartCore::Container]
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
# @since 0.7.0
|
31
|
+
def define(initial_container_klass = self, &container_definitions)
|
32
|
+
unless initial_container_klass <= SmartCore::Container
|
33
|
+
raise(SmartCore::Container::ArgumentError, <<~ERROR_MESSAGE)
|
34
|
+
Base class should be a type of SmartCore::Container
|
35
|
+
ERROR_MESSAGE
|
36
|
+
end
|
37
|
+
|
38
|
+
Class.new(initial_container_klass, &container_definitions).new
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# @since 0.4.0
|
43
|
+
include ::Enumerable
|
44
|
+
|
23
45
|
# @since 0.1.0
|
24
46
|
include DefinitionDSL
|
25
47
|
|
@@ -35,7 +57,7 @@ module SmartCore
|
|
35
57
|
# @since 0.1.0
|
36
58
|
def initialize
|
37
59
|
build_registry!
|
38
|
-
@access_lock =
|
60
|
+
@access_lock = ArbitraryLock.new
|
39
61
|
end
|
40
62
|
|
41
63
|
# @param dependency_name [String, Symbol]
|
@@ -44,8 +66,16 @@ module SmartCore
|
|
44
66
|
#
|
45
67
|
# @api public
|
46
68
|
# @sicne 0.1.0
|
47
|
-
|
48
|
-
|
69
|
+
# @version 0.8.0
|
70
|
+
def register(
|
71
|
+
dependency_name,
|
72
|
+
memoize: SmartCore::Container::Registry::DEFAULT_MEMOIZATION_BEHAVIOR,
|
73
|
+
&dependency_definition
|
74
|
+
)
|
75
|
+
thread_safe do
|
76
|
+
registry.register_dependency(dependency_name, memoize: memoize, &dependency_definition)
|
77
|
+
watcher.notify(dependency_name)
|
78
|
+
end
|
49
79
|
end
|
50
80
|
|
51
81
|
# @param namespace_name [String, Symbol]
|
@@ -54,8 +84,12 @@ module SmartCore
|
|
54
84
|
#
|
55
85
|
# @api public
|
56
86
|
# @since 0.1.0
|
87
|
+
# @version 0.8.0
|
57
88
|
def namespace(namespace_name, &dependencies_definition)
|
58
|
-
thread_safe
|
89
|
+
thread_safe do
|
90
|
+
registry.register_namespace(namespace_name, &dependencies_definition)
|
91
|
+
watcher.notify(namespace_name)
|
92
|
+
end
|
59
93
|
end
|
60
94
|
|
61
95
|
# @param dependency_path [String, Symbol]
|
@@ -102,6 +136,61 @@ module SmartCore
|
|
102
136
|
thread_safe { build_registry! }
|
103
137
|
end
|
104
138
|
|
139
|
+
# @option all_variants [Boolean]
|
140
|
+
# @return [Array<String>]
|
141
|
+
#
|
142
|
+
# @api public
|
143
|
+
# @since 0.4.0
|
144
|
+
def keys(all_variants: SmartCore::Container::Registry::DEFAULT_KEY_EXTRACTION_BEHAVIOUR)
|
145
|
+
thread_safe { registry.keys(all_variants: all_variants) }
|
146
|
+
end
|
147
|
+
|
148
|
+
# @param key [String, Symbol]
|
149
|
+
# @return [Boolean]
|
150
|
+
#
|
151
|
+
# @api public
|
152
|
+
# @since 0.5.0
|
153
|
+
def key?(key)
|
154
|
+
thread_safe { DependencyResolver.key?(self, key) }
|
155
|
+
end
|
156
|
+
|
157
|
+
# @param namespace_path [String, Symbol]
|
158
|
+
# @return [Boolean]
|
159
|
+
#
|
160
|
+
# @api public
|
161
|
+
# @since 0.5.0
|
162
|
+
def namespace?(namespace_path)
|
163
|
+
thread_safe { DependencyResolver.namespace?(self, namespace_path) }
|
164
|
+
end
|
165
|
+
|
166
|
+
# @param dependency_path [String, Symbol]
|
167
|
+
# @option memoized [NilClass, Boolean]
|
168
|
+
# @return [Boolean]
|
169
|
+
#
|
170
|
+
# @api public
|
171
|
+
# @since 0.5.0
|
172
|
+
def dependency?(dependency_path, memoized: nil)
|
173
|
+
thread_safe { DependencyResolver.dependency?(self, dependency_path, memoized: memoized) }
|
174
|
+
end
|
175
|
+
|
176
|
+
# @option yield_all [Boolean]
|
177
|
+
# @param block [Block]
|
178
|
+
# @yield [dependency_name, dependency_value]
|
179
|
+
# @yield_param dependency_name [String]
|
180
|
+
# @yield_param dependency_value [Any, SmartCore::Container]
|
181
|
+
# @return [Enumerable]
|
182
|
+
#
|
183
|
+
# @api public
|
184
|
+
# @since 0.4.0
|
185
|
+
def each_dependency(
|
186
|
+
yield_all: SmartCore::Container::Registry::DEFAULT_ITERATION_YIELD_BEHAVIOUR,
|
187
|
+
&block
|
188
|
+
)
|
189
|
+
thread_safe { registry.each_dependency(yield_all: yield_all, &block) }
|
190
|
+
end
|
191
|
+
alias_method :each, :each_dependency
|
192
|
+
alias_method :each_pair, :each_dependency
|
193
|
+
|
105
194
|
# @option resolve_dependencies [Boolean]
|
106
195
|
# @return [Hash<String|Symbol,SmartCore::Container::Entities::Base|Any>]
|
107
196
|
#
|
@@ -113,14 +202,56 @@ module SmartCore
|
|
113
202
|
alias_method :to_h, :hash_tree
|
114
203
|
alias_method :to_hash, :hash_tree
|
115
204
|
|
205
|
+
# @param entity_path [String]
|
206
|
+
# @param observer [Block]
|
207
|
+
# @yield [entity_path, container]
|
208
|
+
# @yieldparam entity_path [String]
|
209
|
+
# @yieldparam container [SmartCore::Container]
|
210
|
+
# @return [SmartCore::Container::DependencyWatcher::Observer]
|
211
|
+
#
|
212
|
+
# @api public
|
213
|
+
# @since 0.8.0
|
214
|
+
def observe(entity_path, &observer) # TODO: support for pattern-based pathes
|
215
|
+
thread_safe { watcher.watch(entity_path, &observer) }
|
216
|
+
end
|
217
|
+
alias_method :subscribe, :observe
|
218
|
+
|
219
|
+
# @param observer [SmartCore::Container::DependencyWatcher::Observer]
|
220
|
+
# @return [Boolean]
|
221
|
+
#
|
222
|
+
# @api public
|
223
|
+
# @since 0.8.0
|
224
|
+
def unobserve(observer)
|
225
|
+
thread_safe { watcher.unwatch(observer) }
|
226
|
+
end
|
227
|
+
alias_method :unsubscribe, :unobserve
|
228
|
+
|
229
|
+
# @param entity_path [String, Symbol, NilClass]
|
230
|
+
# @return [void]
|
231
|
+
#
|
232
|
+
# @api public
|
233
|
+
# @since 0.8.0
|
234
|
+
def clear_observers(entity_path = nil) # TODO: support for pattern-based pathes
|
235
|
+
thread_safe { watcher.clear_listeners(entity_path) }
|
236
|
+
end
|
237
|
+
alias_method :clear_listeners, :clear_observers
|
238
|
+
|
116
239
|
private
|
117
240
|
|
241
|
+
# @return [SmartCore::Container::DependencyWatcher]
|
242
|
+
#
|
243
|
+
# @api private
|
244
|
+
# @since 0.8.0
|
245
|
+
attr_reader :watcher
|
246
|
+
|
118
247
|
# @return [void]
|
119
248
|
#
|
120
249
|
# @api private
|
121
250
|
# @since 0.1.0
|
251
|
+
# @version 0.8.0
|
122
252
|
def build_registry!
|
123
253
|
@registry = RegistryBuilder.build(self)
|
254
|
+
@watcher = SmartCore::Container::DependencyWatcher.new(self)
|
124
255
|
end
|
125
256
|
|
126
257
|
# @param block [Block]
|
@@ -17,7 +17,7 @@ class SmartCore::Container
|
|
17
17
|
def included(base_klass)
|
18
18
|
base_klass.instance_variable_set(:@__container_definition_commands__, CommandSet.new)
|
19
19
|
base_klass.instance_variable_set(:@__container_instantiation_commands__, CommandSet.new)
|
20
|
-
base_klass.instance_variable_set(:@__container_definition_lock__,
|
20
|
+
base_klass.instance_variable_set(:@__container_definition_lock__, ArbitraryLock.new)
|
21
21
|
base_klass.singleton_class.send(:attr_reader, :__container_definition_commands__)
|
22
22
|
base_klass.singleton_class.send(:attr_reader, :__container_instantiation_commands__)
|
23
23
|
base_klass.extend(ClassMethods)
|
@@ -36,7 +36,7 @@ class SmartCore::Container
|
|
36
36
|
def inherited(child_klass)
|
37
37
|
child_klass.instance_variable_set(:@__container_definition_commands__, CommandSet.new)
|
38
38
|
child_klass.instance_variable_set(:@__container_instantiation_commands__, CommandSet.new)
|
39
|
-
child_klass.instance_variable_set(:@__container_definition_lock__,
|
39
|
+
child_klass.instance_variable_set(:@__container_definition_lock__, ArbitraryLock.new)
|
40
40
|
SmartCore::Container::DefinitionDSL::Inheritance.inherit(base: self, child: child_klass)
|
41
41
|
child_klass.singleton_class.prepend(ClassInheritance)
|
42
42
|
super
|
@@ -18,7 +18,7 @@ class SmartCore::Container::DefinitionDSL::CommandSet
|
|
18
18
|
# @since 0.1.0
|
19
19
|
def initialize
|
20
20
|
@commands = []
|
21
|
-
@access_lock = SmartCore::Container::
|
21
|
+
@access_lock = SmartCore::Container::ArbitraryLock.new
|
22
22
|
end
|
23
23
|
|
24
24
|
# @param [SmartCore::Container::DefinitionDSL::Commands::Base]
|
@@ -36,7 +36,7 @@ module SmartCore::Container::DefinitionDSL::Commands::Definition
|
|
36
36
|
# @since 0.1.0
|
37
37
|
# @version 0.2.0
|
38
38
|
def call(registry)
|
39
|
-
registry.register_dependency(dependency_name, memoize, &dependency_definition)
|
39
|
+
registry.register_dependency(dependency_name, memoize: memoize, &dependency_definition)
|
40
40
|
end
|
41
41
|
|
42
42
|
# @return [SmartCore::Container::DefinitionDSL::Commands::Definition::Register]
|
@@ -5,6 +5,12 @@
|
|
5
5
|
module SmartCore::Container::DependencyResolver
|
6
6
|
require_relative 'dependency_resolver/route'
|
7
7
|
|
8
|
+
# @return [String]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
# @since 0.4.0
|
12
|
+
PATH_PART_SEPARATOR = '.'
|
13
|
+
|
8
14
|
class << self
|
9
15
|
# @param container [SmartCore::Container]
|
10
16
|
# @param dependency_path [String, Symbol]
|
@@ -20,6 +26,54 @@ module SmartCore::Container::DependencyResolver
|
|
20
26
|
container.registry.resolve(dependency_path).reveal
|
21
27
|
end
|
22
28
|
|
29
|
+
# @param container [SmartCore::Container]
|
30
|
+
# @param key [String, Symbol]
|
31
|
+
# @return [Boolean]
|
32
|
+
#
|
33
|
+
# @api private
|
34
|
+
# @since 0.5.0
|
35
|
+
def key?(container, key)
|
36
|
+
extract(container, key)
|
37
|
+
true
|
38
|
+
rescue SmartCore::Container::ResolvingError
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param container [SmartCore::Container]
|
43
|
+
# @param namespace_path [String, Symbol]
|
44
|
+
# @return [Boolean]
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
# @since 0.5.0
|
48
|
+
def namespace?(container, namespace_path)
|
49
|
+
extract(container, namespace_path).is_a?(SmartCore::Container::Entities::Namespace)
|
50
|
+
rescue SmartCore::Container::ResolvingError
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
# @param container [SmartCore::Container]
|
55
|
+
# @param dependency_path [String, Symbol]
|
56
|
+
# @option memoized [NilClass, Boolean]
|
57
|
+
# @return [Boolean]
|
58
|
+
#
|
59
|
+
# @api private
|
60
|
+
# @since 0.5.0
|
61
|
+
def dependency?(container, dependency_path, memoized: nil)
|
62
|
+
entity = extract(container, dependency_path)
|
63
|
+
|
64
|
+
case
|
65
|
+
when memoized.nil?
|
66
|
+
entity.is_a?(SmartCore::Container::Entities::Dependency)
|
67
|
+
when !!memoized == true
|
68
|
+
entity.is_a?(SmartCore::Container::Entities::MemoizedDependency)
|
69
|
+
when !!memoized == false
|
70
|
+
entity.is_a?(SmartCore::Container::Entities::Dependency) &&
|
71
|
+
!entity.is_a?(SmartCore::Container::Entities::MemoizedDependency)
|
72
|
+
end
|
73
|
+
rescue SmartCore::Container::ResolvingError
|
74
|
+
false
|
75
|
+
end
|
76
|
+
|
23
77
|
# @param container [SmartCore::Container]
|
24
78
|
# @param dependency_path [String, Symbol]
|
25
79
|
# @return [SmartCore::Container, Any]
|
@@ -32,12 +86,11 @@ module SmartCore::Container::DependencyResolver
|
|
32
86
|
#
|
33
87
|
# @api private
|
34
88
|
# @since 0.1.0
|
35
|
-
# @version 0.1.0
|
36
89
|
def resolve(container, dependency_path)
|
37
90
|
entity = container
|
38
91
|
Route.build(dependency_path).each do |cursor|
|
39
92
|
entity = entity.registry.resolve(cursor.current_path)
|
40
|
-
|
93
|
+
prevent_ambiguous_resolving!(cursor, entity)
|
41
94
|
entity = entity.reveal
|
42
95
|
end
|
43
96
|
entity
|
@@ -47,6 +100,25 @@ module SmartCore::Container::DependencyResolver
|
|
47
100
|
|
48
101
|
private
|
49
102
|
|
103
|
+
# @param container [SmartCore::Container]
|
104
|
+
# @param entity_path [String, Symbol]
|
105
|
+
# @return [SmartCore::Container::Entities::Base]
|
106
|
+
#
|
107
|
+
# @api private
|
108
|
+
# @since 0.5.0
|
109
|
+
def extract(container, entity_path)
|
110
|
+
resolved_entity = container
|
111
|
+
extracted_entity = container
|
112
|
+
|
113
|
+
Route.build(entity_path).each do |cursor|
|
114
|
+
resolved_entity = resolved_entity.registry.resolve(cursor.current_path)
|
115
|
+
extracted_entity = resolved_entity
|
116
|
+
resolved_entity = resolved_entity.reveal
|
117
|
+
end
|
118
|
+
|
119
|
+
extracted_entity
|
120
|
+
end
|
121
|
+
|
50
122
|
# @param cursor [SmartCore::Container::DependencyResolver::Route::Cursor]
|
51
123
|
# @param entity [SmartCore::Container::Entities::Base]
|
52
124
|
# @return [void]
|
@@ -54,8 +126,8 @@ module SmartCore::Container::DependencyResolver
|
|
54
126
|
# @raise [SmartCore::Container::ResolvingError]
|
55
127
|
#
|
56
128
|
# @api private
|
57
|
-
# @since 0.
|
58
|
-
def
|
129
|
+
# @since 0.5.0
|
130
|
+
def prevent_ambiguous_resolving!(cursor, entity)
|
59
131
|
if cursor.last? && entity.is_a?(SmartCore::Container::Entities::Namespace)
|
60
132
|
raise(
|
61
133
|
SmartCore::Container::ResolvingError.new(
|
@@ -84,10 +156,9 @@ module SmartCore::Container::DependencyResolver
|
|
84
156
|
# @api private
|
85
157
|
# @since 0.1.0
|
86
158
|
def process_resolving_error(dependency_path, error)
|
87
|
-
full_dependency_path = Route.build_path(
|
88
|
-
|
89
|
-
|
90
|
-
MESSAGE
|
159
|
+
full_dependency_path = Route.build_path(error.path_part)
|
160
|
+
message = "#{error.message} (incorrect path: \"#{full_dependency_path}\")"
|
161
|
+
raise(SmartCore::Container::ResolvingError.new(message, path_part: full_dependency_path))
|
91
162
|
end
|
92
163
|
end
|
93
164
|
end
|
@@ -2,18 +2,13 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
# @since 0.1.0
|
5
|
+
# @version 0.4.0
|
5
6
|
class SmartCore::Container::DependencyResolver::Route
|
6
7
|
require_relative 'route/cursor'
|
7
8
|
|
8
9
|
# @since 0.1.0
|
9
10
|
include Enumerable
|
10
11
|
|
11
|
-
# @return [String]
|
12
|
-
#
|
13
|
-
# @api private
|
14
|
-
# @since 0.1.0
|
15
|
-
PATH_PART_SEPARATOR = '.'
|
16
|
-
|
17
12
|
class << self
|
18
13
|
# @param path [String, Symbol]
|
19
14
|
# @return [SmartCore::Container::DependencyResolver::Route]
|
@@ -28,8 +23,9 @@ class SmartCore::Container::DependencyResolver::Route
|
|
28
23
|
#
|
29
24
|
# @api private
|
30
25
|
# @since 0.1.0
|
26
|
+
# @version 0.4.0
|
31
27
|
def build_path(*path_parts)
|
32
|
-
path_parts.join(PATH_PART_SEPARATOR)
|
28
|
+
path_parts.join(SmartCore::Container::DependencyResolver::PATH_PART_SEPARATOR)
|
33
29
|
end
|
34
30
|
end
|
35
31
|
|
@@ -50,9 +46,10 @@ class SmartCore::Container::DependencyResolver::Route
|
|
50
46
|
#
|
51
47
|
# @api private
|
52
48
|
# @since 0.1.0
|
49
|
+
# @version 0.4.0
|
53
50
|
def initialize(path)
|
54
51
|
@path = path
|
55
|
-
@path_parts = path.split(PATH_PART_SEPARATOR).freeze
|
52
|
+
@path_parts = path.split(SmartCore::Container::DependencyResolver::PATH_PART_SEPARATOR).freeze
|
56
53
|
@size = @path_parts.size
|
57
54
|
end
|
58
55
|
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.8.0
|
5
|
+
class SmartCore::Container::DependencyWatcher
|
6
|
+
require_relative 'dependency_watcher/observer'
|
7
|
+
|
8
|
+
# @param container [SmartCore::Container]
|
9
|
+
# @return [void]
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
# @since 0.8.0
|
13
|
+
def initialize(container)
|
14
|
+
@container = container
|
15
|
+
@observers = Hash.new { |k, v| k[v] = [] }
|
16
|
+
@access_lock = SmartCore::Container::ArbitraryLock.new
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param entity_path [String, Symbol]
|
20
|
+
# @return [void]
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
# @since 0.8.0
|
24
|
+
def notify(entity_path)
|
25
|
+
thread_safe { notify_listeners(entity_path) }
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param entity_path [String, Symbol]
|
29
|
+
# @param observer [Block]
|
30
|
+
# @return [SmartCore::Container::DependencyWatcher::Observer]
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
# @since 0.8.0
|
34
|
+
def watch(entity_path, &observer) # TODO: support for pattern-based pathes
|
35
|
+
thread_safe { listen(entity_path, observer) }
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param observer [SmartCore::Container::DependencyWatcher::Observer]
|
39
|
+
# @return [Boolean]
|
40
|
+
#
|
41
|
+
# @api private
|
42
|
+
# @since 0.8.0
|
43
|
+
def unwatch(observer)
|
44
|
+
thread_safe { remove_listener(observer) }
|
45
|
+
end
|
46
|
+
|
47
|
+
# @param entity_path [String, Symbol, NilClass]
|
48
|
+
# @return [void]
|
49
|
+
#
|
50
|
+
# @api private
|
51
|
+
# @since 0.8.0
|
52
|
+
def clear_listeners(entity_path = nil) # TODO: support for pattern-based pathes
|
53
|
+
thread_safe { remove_listeners(entity_path) }
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# @return [SmartCore::Container]
|
59
|
+
#
|
60
|
+
# @api private
|
61
|
+
# @since 0.8.0
|
62
|
+
attr_reader :container
|
63
|
+
|
64
|
+
# @return [Hash<String,SmartCore::Container::DependencyWatcher::Observer>]
|
65
|
+
#
|
66
|
+
# @api private
|
67
|
+
# @since 0.8.0
|
68
|
+
attr_reader :observers
|
69
|
+
|
70
|
+
# @param entity_path [String, Symbol]
|
71
|
+
# @return [void]
|
72
|
+
#
|
73
|
+
# @api private
|
74
|
+
# @since 0.8.0
|
75
|
+
def notify_listeners(entity_path)
|
76
|
+
entity_path = indifferently_accessable_path(entity_path)
|
77
|
+
observers.fetch(entity_path).each(&:notify!) if observers.key?(entity_path)
|
78
|
+
end
|
79
|
+
|
80
|
+
# @param entity_path [String, Symbol]
|
81
|
+
# @param observer [Proc]
|
82
|
+
# @return [SmartCore::Container::DependencyWatcher::Observer]
|
83
|
+
#
|
84
|
+
# @api private
|
85
|
+
# @since 0.8.0
|
86
|
+
def listen(entity_path, observer) # TODO: support for pattern-based pathes
|
87
|
+
raise(SmartCore::Container::ArgumentError, <<~ERROR_MESSAGE) unless observer.is_a?(Proc)
|
88
|
+
Observer is missing: you should provide an observer proc object (block).
|
89
|
+
ERROR_MESSAGE
|
90
|
+
|
91
|
+
entity_path = indifferently_accessable_path(entity_path)
|
92
|
+
Observer.new(container, entity_path, observer).tap { |obs| observers[entity_path] << obs }
|
93
|
+
end
|
94
|
+
|
95
|
+
# @param observer [SmartCore::Container::DependencyWatcher::Observer]
|
96
|
+
# @return [Boolean]
|
97
|
+
#
|
98
|
+
# @api private
|
99
|
+
# @since 0.8.0
|
100
|
+
def remove_listener(observer)
|
101
|
+
unless observer.is_a?(SmartCore::Container::DependencyWatcher::Observer)
|
102
|
+
raise(SmartCore::Container::ArgumentError, <<~ERROR_MESSAGE)
|
103
|
+
You should provide an observer object for unsubscribion
|
104
|
+
(an instance of SmartCore::Container::DependencyWatcher::Observer).
|
105
|
+
ERROR_MESSAGE
|
106
|
+
end
|
107
|
+
|
108
|
+
unsubscribed = false
|
109
|
+
observers.each_value do |observer_list|
|
110
|
+
if observer_list.delete(observer)
|
111
|
+
unsubscribed = true
|
112
|
+
break
|
113
|
+
end
|
114
|
+
end
|
115
|
+
unsubscribed
|
116
|
+
end
|
117
|
+
|
118
|
+
# @param entity_path [String, Symbol]
|
119
|
+
# @return [void]
|
120
|
+
#
|
121
|
+
# @api private
|
122
|
+
# @since 0.8.0
|
123
|
+
def remove_listeners(entity_path) # TODO: support for pattern-based pathes
|
124
|
+
if entity_path.nil?
|
125
|
+
observers.each_value(&:clear)
|
126
|
+
else
|
127
|
+
entity_path = indifferently_accessable_path(entity_path)
|
128
|
+
observers[entity_path].clear if observers.key?(entity_path)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# @param entity_path [String, Symbol]
|
133
|
+
# @return [String]
|
134
|
+
#
|
135
|
+
# @api private
|
136
|
+
# @since 0.8.0
|
137
|
+
def indifferently_accessable_path(entity_path)
|
138
|
+
SmartCore::Container::KeyGuard.indifferently_accessable_key(entity_path)
|
139
|
+
end
|
140
|
+
|
141
|
+
# @param block [Block]
|
142
|
+
# @return [Any]
|
143
|
+
#
|
144
|
+
# @api private
|
145
|
+
# @since 0.8.0
|
146
|
+
def thread_safe(&block)
|
147
|
+
@access_lock.thread_safe(&block)
|
148
|
+
end
|
149
|
+
end
|