smart_container 0.1.0

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