smart_container 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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +15 -0
- data/.travis.yml +28 -0
- data/CHANGELOG.md +6 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +83 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/Rakefile +21 -0
- data/bin/console +8 -0
- data/bin/setup +8 -0
- data/lib/smart_core/container.rb +135 -0
- data/lib/smart_core/container/arbitary_lock.rb +22 -0
- data/lib/smart_core/container/definition_dsl.rb +109 -0
- data/lib/smart_core/container/definition_dsl/command_set.rb +87 -0
- data/lib/smart_core/container/definition_dsl/commands.rb +9 -0
- data/lib/smart_core/container/definition_dsl/commands/base.rb +48 -0
- data/lib/smart_core/container/definition_dsl/commands/definition.rb +9 -0
- data/lib/smart_core/container/definition_dsl/commands/definition/compose.rb +49 -0
- data/lib/smart_core/container/definition_dsl/commands/definition/namespace.rb +54 -0
- data/lib/smart_core/container/definition_dsl/commands/definition/register.rb +54 -0
- data/lib/smart_core/container/definition_dsl/commands/instantiation.rb +8 -0
- data/lib/smart_core/container/definition_dsl/commands/instantiation/compose.rb +53 -0
- data/lib/smart_core/container/definition_dsl/commands/instantiation/freeze_state.rb +27 -0
- data/lib/smart_core/container/definition_dsl/inheritance.rb +23 -0
- data/lib/smart_core/container/dependency_compatability.rb +9 -0
- data/lib/smart_core/container/dependency_compatability/definition.rb +38 -0
- data/lib/smart_core/container/dependency_compatability/general.rb +61 -0
- data/lib/smart_core/container/dependency_compatability/registry.rb +36 -0
- data/lib/smart_core/container/dependency_resolver.rb +93 -0
- data/lib/smart_core/container/dependency_resolver/route.rb +83 -0
- data/lib/smart_core/container/dependency_resolver/route/cursor.rb +47 -0
- data/lib/smart_core/container/entities.rb +11 -0
- data/lib/smart_core/container/entities/base.rb +26 -0
- data/lib/smart_core/container/entities/dependency.rb +38 -0
- data/lib/smart_core/container/entities/dependency_builder.rb +50 -0
- data/lib/smart_core/container/entities/namespace.rb +73 -0
- data/lib/smart_core/container/entities/namespace_builder.rb +41 -0
- data/lib/smart_core/container/errors.rb +65 -0
- data/lib/smart_core/container/key_guard.rb +31 -0
- data/lib/smart_core/container/mixin.rb +83 -0
- data/lib/smart_core/container/registry.rb +250 -0
- data/lib/smart_core/container/registry_builder.rb +51 -0
- data/lib/smart_core/container/version.rb +9 -0
- data/smart_container.gemspec +38 -0
- metadata +191 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SmartCore::Container::DefinitionDSL::Commands::Instantiation
|
4
|
+
# @api private
|
5
|
+
# @since 0.1.0
|
6
|
+
class Compose < SmartCore::Container::DefinitionDSL::Commands::Base
|
7
|
+
# @since 0.1.0
|
8
|
+
self.inheritable = true
|
9
|
+
|
10
|
+
# @param container_klass [Class<SmartCore::Container>]
|
11
|
+
# @return [void]
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
# @since 0.1.0
|
15
|
+
def initialize(container_klass)
|
16
|
+
raise(
|
17
|
+
SmartCore::ArgumentError,
|
18
|
+
'Container class should be a subtype of Quantum::Container'
|
19
|
+
) unless container_klass < SmartCore::Container
|
20
|
+
|
21
|
+
@container_klass = container_klass
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param registry [SmartCore::Container::Registry]
|
25
|
+
# @return [void]
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
# @since 0.1.0
|
29
|
+
def call(registry)
|
30
|
+
SmartCore::Container::RegistryBuilder.build_state(
|
31
|
+
container_klass, registry, ignored_commands: [
|
32
|
+
SmartCore::Container::DefinitionDSL::Commands::Instantiation::FreezeState
|
33
|
+
]
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [SmartCore::Container::DefinitionDSL::Commands::Instantiation::Compose]
|
38
|
+
#
|
39
|
+
# @api private
|
40
|
+
# @since 0.1.0
|
41
|
+
def dup
|
42
|
+
self.class.new(container_klass)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# @return [Class<SmartCore::Container>]
|
48
|
+
#
|
49
|
+
# @api private
|
50
|
+
# @since 0.1.0
|
51
|
+
attr_reader :container_klass
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SmartCore::Container::DefinitionDSL::Commands::Instantiation
|
4
|
+
# @api private
|
5
|
+
# @since 0.1.0
|
6
|
+
class FreezeState < SmartCore::Container::DefinitionDSL::Commands::Base
|
7
|
+
# @since 0.1.0
|
8
|
+
self.inheritable = false
|
9
|
+
|
10
|
+
# @param registry [SmartCore::Container::Registry]
|
11
|
+
# @return [void]
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
# @since 0.1.0
|
15
|
+
def call(registry)
|
16
|
+
registry.freeze!
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [SmartCore::Container::DefinitionDSL::Commands::Instantiation::FreezeState]
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
# @since 0.1.0
|
23
|
+
def dup
|
24
|
+
self.class.new
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.1.0
|
5
|
+
module SmartCore::Container::DefinitionDSL::Inheritance
|
6
|
+
class << self
|
7
|
+
# @option base [Class<SmartCore::Container>]
|
8
|
+
# @option child [Class<SmartCore::Container>]
|
9
|
+
# @return [void]
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
# @since 0.1.0
|
13
|
+
def inherit(base:, child:)
|
14
|
+
child.__container_definition_commands__.concat(
|
15
|
+
base.__container_definition_commands__, &:inheritable?
|
16
|
+
)
|
17
|
+
|
18
|
+
child.__container_instantiation_commands__.concat(
|
19
|
+
base.__container_instantiation_commands__, &:inheritable?
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.1.0
|
5
|
+
module SmartCore::Container::DependencyCompatability
|
6
|
+
require_relative 'dependency_compatability/general'
|
7
|
+
require_relative 'dependency_compatability/definition'
|
8
|
+
require_relative 'dependency_compatability/registry'
|
9
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.1.0
|
5
|
+
module SmartCore::Container::DependencyCompatability::Definition
|
6
|
+
class << self
|
7
|
+
# @since 0.1.0
|
8
|
+
include SmartCore::Container::DependencyCompatability::General
|
9
|
+
|
10
|
+
# @param container_klass [Class<SmartCore::Container>]
|
11
|
+
# @param dependency_name [String, Symbol]
|
12
|
+
# @return [Boolean]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.1.0
|
16
|
+
def potential_namespace_overlap?(container_klass, dependency_name)
|
17
|
+
anonymous_container = Class.new(container_klass).new
|
18
|
+
anonymous_container.register(dependency_name, &(proc {}))
|
19
|
+
false
|
20
|
+
rescue SmartCore::Container::DependencyOverNamespaceOverlapError
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param container_klass [Class<SmartCore::Container>]
|
25
|
+
# @param namespace_name [String, Symbol]
|
26
|
+
# @return [Boolean]
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
# @since 0.1.0
|
30
|
+
def potential_dependency_overlap?(container_klass, namespace_name)
|
31
|
+
anonymous_container = Class.new(container_klass).new
|
32
|
+
anonymous_container.namespace(namespace_name, &(proc {}))
|
33
|
+
false
|
34
|
+
rescue SmartCore::Container::NamespaceOverDependencyOverlapError
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.1.0
|
5
|
+
module SmartCore::Container::DependencyCompatability::General
|
6
|
+
# @param context [Class<SmartCore::Container>, SmartCore::Container::Registry]
|
7
|
+
# @param dependency_name [String, Symbol]
|
8
|
+
# @return [void]
|
9
|
+
#
|
10
|
+
# @raise [SmartCore::Container::DependencyOverNamespaceOverlapError]
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
# @since 0.1.0
|
14
|
+
def prevent_namespace_overlap!(context, dependency_name)
|
15
|
+
raise(
|
16
|
+
SmartCore::Container::DependencyOverNamespaceOverlapError,
|
17
|
+
"Trying to overlap already registered '#{dependency_name}' namespace " \
|
18
|
+
"with '#{dependency_name}' dependency!"
|
19
|
+
) if potential_namespace_overlap?(context, dependency_name)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param context [Class<SmartCore::Container>, SmartCore::Container::Registry]
|
23
|
+
# @param namespace_name [String, Symbol]
|
24
|
+
# @return [void]
|
25
|
+
#
|
26
|
+
# @raise [SmartCore::Container::NamespaceOverDependencyOverlapError]
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
# @since 0.1.0
|
30
|
+
def prevent_dependency_overlap!(context, namespace_name)
|
31
|
+
raise(
|
32
|
+
SmartCore::Container::NamespaceOverDependencyOverlapError,
|
33
|
+
"Trying to overlap already registered '#{namespace_name}' dependency " \
|
34
|
+
"with '#{namespace_name}' namespace!"
|
35
|
+
) if potential_dependency_overlap?(context, namespace_name)
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param context [Class<SmartCore::Container>, SmartCore::Container::Registry]
|
39
|
+
# @param dependency_name [String, Symbol]
|
40
|
+
# @return [Boolean]
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
# @since 0.1.0
|
44
|
+
def potential_namespace_overlap?(context, dependency_name)
|
45
|
+
# :nocov:
|
46
|
+
raise NoMethodError
|
47
|
+
# :nocov:
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param context [Class<SmartCore::Container>, SmartCore::Container::Registry]
|
51
|
+
# @param namespace_name [String, Symbol]
|
52
|
+
# @return [Boolean]
|
53
|
+
#
|
54
|
+
# @api private
|
55
|
+
# @since 0.1.0
|
56
|
+
def potential_dependency_overlap?(context, namespace_name)
|
57
|
+
# :nocov:
|
58
|
+
raise NoMethodError
|
59
|
+
# :nocov:
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.1.0
|
5
|
+
module SmartCore::Container::DependencyCompatability::Registry
|
6
|
+
class << self
|
7
|
+
# @since 0.1.0
|
8
|
+
include SmartCore::Container::DependencyCompatability::General
|
9
|
+
|
10
|
+
# @param registry [SmartCore::Container::Registry]
|
11
|
+
# @param dependency_name [String, Symbol]
|
12
|
+
# @return [Boolean]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.1.0
|
16
|
+
def potential_namespace_overlap?(registry, dependency_name)
|
17
|
+
registry.any? do |(entity_name, entity)|
|
18
|
+
next unless entity.is_a?(SmartCore::Container::Entities::Namespace)
|
19
|
+
entity.namespace_name == dependency_name
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param registry [SmartCore::Container::Registry]
|
24
|
+
# @param namespace_name [String, Symbol]
|
25
|
+
# @return [Boolean]
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
# @since 0.1.0
|
29
|
+
def potential_dependency_overlap?(registry, namespace_name)
|
30
|
+
registry.any? do |(entity_name, entity)|
|
31
|
+
next unless entity.is_a?(SmartCore::Container::Entities::Dependency)
|
32
|
+
entity.dependency_name == namespace_name
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.1.0
|
5
|
+
module SmartCore::Container::DependencyResolver
|
6
|
+
require_relative 'dependency_resolver/route'
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# @param container [SmartCore::Container]
|
10
|
+
# @param dependency_path [String, Symbol]
|
11
|
+
# @return [SmartCore::Container, Any]
|
12
|
+
#
|
13
|
+
# @see SmartCore::Container::Registry#resolve
|
14
|
+
# @see SmartCore::Container::Entities::Namespace#reveal
|
15
|
+
# @see SmartCore::Container::Entities::Dependency#reveal
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
# @since 0.1.0
|
19
|
+
def fetch(container, dependency_path)
|
20
|
+
container.registry.resolve(dependency_path).reveal
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param container [SmartCore::Container]
|
24
|
+
# @param dependency_path [String, Symbol]
|
25
|
+
# @return [SmartCore::Container, Any]
|
26
|
+
#
|
27
|
+
# @see SmartCore::Container::Registry#resolve
|
28
|
+
# @see SmartCore::Container::Entities::Namespace#reveal
|
29
|
+
# @see SmartCore::Container::Entities::Dependency#reveal
|
30
|
+
#
|
31
|
+
# @raise [SmartCore::Container::ResolvingError]
|
32
|
+
#
|
33
|
+
# @api private
|
34
|
+
# @since 0.1.0
|
35
|
+
# @version 0.1.0
|
36
|
+
def resolve(container, dependency_path)
|
37
|
+
entity = container
|
38
|
+
Route.build(dependency_path).each do |cursor|
|
39
|
+
entity = entity.registry.resolve(cursor.current_path)
|
40
|
+
prevent_cursor_overflow!(cursor, entity)
|
41
|
+
entity = entity.reveal
|
42
|
+
end
|
43
|
+
entity
|
44
|
+
rescue SmartCore::Container::ResolvingError => error
|
45
|
+
process_resolving_error(dependency_path, error)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# @param cursor [SmartCore::Container::DependencyResolver::Route::Cursor]
|
51
|
+
# @param entity [SmartCore::Container::Entities::Base]
|
52
|
+
# @return [void]
|
53
|
+
#
|
54
|
+
# @raise [SmartCore::Container::ResolvingError]
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
# @since 0.1.0
|
58
|
+
def prevent_cursor_overflow!(cursor, entity)
|
59
|
+
if cursor.last? && entity.is_a?(SmartCore::Container::Entities::Namespace)
|
60
|
+
raise(
|
61
|
+
SmartCore::Container::ResolvingError.new(
|
62
|
+
'Trying to resolve a namespace as a dependency',
|
63
|
+
path_part: cursor.current_path
|
64
|
+
)
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
if !cursor.last? && entity.is_a?(SmartCore::Container::Entities::Dependency)
|
69
|
+
raise(
|
70
|
+
SmartCore::Container::ResolvingError.new(
|
71
|
+
'Trying to resolve nonexistent dependency',
|
72
|
+
path_part: cursor.current_path
|
73
|
+
)
|
74
|
+
)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# @param dependency_path [String, Symbol]
|
79
|
+
# @param error [SmartCore::Container::ResolvingError]
|
80
|
+
# @return [void]
|
81
|
+
#
|
82
|
+
# @raise [SmartCore::Container::ResolvingError]
|
83
|
+
#
|
84
|
+
# @api private
|
85
|
+
# @since 0.1.0
|
86
|
+
def process_resolving_error(dependency_path, error)
|
87
|
+
full_dependency_path = Route.build_path(dependency_path, error.path_part)
|
88
|
+
raise(SmartCore::Container::ResolvingError.new(<<~MESSAGE, path_part: full_dependency_path))
|
89
|
+
#{error.message} (incorrect path: "#{full_dependency_path}")
|
90
|
+
MESSAGE
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.1.0
|
5
|
+
class SmartCore::Container::DependencyResolver::Route
|
6
|
+
require_relative 'route/cursor'
|
7
|
+
|
8
|
+
# @since 0.1.0
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
# @return [String]
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
# @since 0.1.0
|
15
|
+
PATH_PART_SEPARATOR = '.'
|
16
|
+
|
17
|
+
class << self
|
18
|
+
# @param path [String, Symbol]
|
19
|
+
# @return [SmartCore::Container::DependencyResolver::Route]
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
# @since 0.1.0
|
23
|
+
def build(path)
|
24
|
+
new(SmartCore::Container::KeyGuard.indifferently_accessable_key(path))
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [Array<String>]
|
28
|
+
#
|
29
|
+
# @api private
|
30
|
+
# @since 0.1.0
|
31
|
+
def build_path(*path_parts)
|
32
|
+
path_parts.join(PATH_PART_SEPARATOR)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [Integer]
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
# @since 0.1.0
|
40
|
+
attr_reader :size
|
41
|
+
|
42
|
+
# @return [String]
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
# @since 0.1.0
|
46
|
+
attr_reader :path
|
47
|
+
|
48
|
+
# @param path [String]
|
49
|
+
# @return [void]
|
50
|
+
#
|
51
|
+
# @api private
|
52
|
+
# @since 0.1.0
|
53
|
+
def initialize(path)
|
54
|
+
@path = path
|
55
|
+
@path_parts = path.split(PATH_PART_SEPARATOR).freeze
|
56
|
+
@size = @path_parts.size
|
57
|
+
end
|
58
|
+
|
59
|
+
# @param block [Block]
|
60
|
+
# @yield cursor [SmartCore::Container::DependencyResolver::Route::Cursor]
|
61
|
+
# @return [Enumerable]
|
62
|
+
#
|
63
|
+
# @api private
|
64
|
+
# @since 0.1.0
|
65
|
+
def each(&block)
|
66
|
+
enumerator = Enumerator.new do |yielder|
|
67
|
+
path_parts.each_with_index do |path_part, path_part_index|
|
68
|
+
cursor = Cursor.new(path_part, path_part_index, self)
|
69
|
+
yielder.yield(cursor)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
block_given? ? enumerator.each(&block) : enumerator
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# @return [Array<String>]
|
79
|
+
#
|
80
|
+
# @api private
|
81
|
+
# @since 0.1.0
|
82
|
+
attr_reader :path_parts
|
83
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.1.0
|
5
|
+
class SmartCore::Container::DependencyResolver::Route::Cursor
|
6
|
+
# @return [String]
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
# @since 0.1.0
|
10
|
+
attr_reader :path_part
|
11
|
+
alias_method :current_path, :path_part
|
12
|
+
|
13
|
+
# @param path_part [String]
|
14
|
+
# @param path_part_index [Integer]
|
15
|
+
# @param route [SmartCore::Container::DependencyResolver::Route]
|
16
|
+
# @return [void]
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
# @since 0.1.0
|
20
|
+
def initialize(path_part, path_part_index, route)
|
21
|
+
@path_part = path_part
|
22
|
+
@path_part_index = path_part_index
|
23
|
+
@route = route
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [Boolean]
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
# @since 0.1.0
|
30
|
+
def last?
|
31
|
+
route.size <= (path_part_index + 1)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# @return [Integer]
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
# @since 0.1.0
|
40
|
+
attr_reader :path_part_index
|
41
|
+
|
42
|
+
# @return [SmartCore::Container::DependencyResolver::Route]
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
# @since 0.1.0
|
46
|
+
attr_reader :route
|
47
|
+
end
|