smart_container 0.3.0 → 0.8.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/.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
|