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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +15 -0
  5. data/.travis.yml +28 -0
  6. data/CHANGELOG.md +6 -0
  7. data/CODE_OF_CONDUCT.md +74 -0
  8. data/Gemfile +4 -0
  9. data/Gemfile.lock +83 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +39 -0
  12. data/Rakefile +21 -0
  13. data/bin/console +8 -0
  14. data/bin/setup +8 -0
  15. data/lib/smart_core/container.rb +135 -0
  16. data/lib/smart_core/container/arbitary_lock.rb +22 -0
  17. data/lib/smart_core/container/definition_dsl.rb +109 -0
  18. data/lib/smart_core/container/definition_dsl/command_set.rb +87 -0
  19. data/lib/smart_core/container/definition_dsl/commands.rb +9 -0
  20. data/lib/smart_core/container/definition_dsl/commands/base.rb +48 -0
  21. data/lib/smart_core/container/definition_dsl/commands/definition.rb +9 -0
  22. data/lib/smart_core/container/definition_dsl/commands/definition/compose.rb +49 -0
  23. data/lib/smart_core/container/definition_dsl/commands/definition/namespace.rb +54 -0
  24. data/lib/smart_core/container/definition_dsl/commands/definition/register.rb +54 -0
  25. data/lib/smart_core/container/definition_dsl/commands/instantiation.rb +8 -0
  26. data/lib/smart_core/container/definition_dsl/commands/instantiation/compose.rb +53 -0
  27. data/lib/smart_core/container/definition_dsl/commands/instantiation/freeze_state.rb +27 -0
  28. data/lib/smart_core/container/definition_dsl/inheritance.rb +23 -0
  29. data/lib/smart_core/container/dependency_compatability.rb +9 -0
  30. data/lib/smart_core/container/dependency_compatability/definition.rb +38 -0
  31. data/lib/smart_core/container/dependency_compatability/general.rb +61 -0
  32. data/lib/smart_core/container/dependency_compatability/registry.rb +36 -0
  33. data/lib/smart_core/container/dependency_resolver.rb +93 -0
  34. data/lib/smart_core/container/dependency_resolver/route.rb +83 -0
  35. data/lib/smart_core/container/dependency_resolver/route/cursor.rb +47 -0
  36. data/lib/smart_core/container/entities.rb +11 -0
  37. data/lib/smart_core/container/entities/base.rb +26 -0
  38. data/lib/smart_core/container/entities/dependency.rb +38 -0
  39. data/lib/smart_core/container/entities/dependency_builder.rb +50 -0
  40. data/lib/smart_core/container/entities/namespace.rb +73 -0
  41. data/lib/smart_core/container/entities/namespace_builder.rb +41 -0
  42. data/lib/smart_core/container/errors.rb +65 -0
  43. data/lib/smart_core/container/key_guard.rb +31 -0
  44. data/lib/smart_core/container/mixin.rb +83 -0
  45. data/lib/smart_core/container/registry.rb +250 -0
  46. data/lib/smart_core/container/registry_builder.rb +51 -0
  47. data/lib/smart_core/container/version.rb +9 -0
  48. data/smart_container.gemspec +38 -0
  49. metadata +191 -0
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.1.0
5
+ module SmartCore::Container::DefinitionDSL::Commands::Instantiation
6
+ require_relative 'instantiation/compose'
7
+ require_relative 'instantiation/freeze_state'
8
+ end
@@ -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