smart_core 0.4.0 → 0.5.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -2
  3. data/.travis.yml +3 -3
  4. data/CHANGELOG.md +3 -0
  5. data/Gemfile +0 -3
  6. data/README.md +5 -33
  7. data/Rakefile +2 -0
  8. data/lib/smart_core/container/command_definer.rb +117 -0
  9. data/lib/smart_core/container/command_set.rb +82 -0
  10. data/lib/smart_core/container/commands/base.rb +12 -0
  11. data/lib/smart_core/container/commands/namespace.rb +53 -0
  12. data/lib/smart_core/container/commands/register.rb +63 -0
  13. data/lib/smart_core/container/commands.rb +9 -0
  14. data/lib/smart_core/container/definition_dsl.rb +66 -0
  15. data/lib/smart_core/container/dependency.rb +44 -0
  16. data/lib/smart_core/container/dependency_builder.rb +48 -0
  17. data/lib/smart_core/container/dependency_compatability/abstract.rb +59 -0
  18. data/lib/smart_core/container/dependency_compatability/command_set.rb +35 -0
  19. data/lib/smart_core/container/dependency_compatability/registry.rb +37 -0
  20. data/lib/smart_core/container/dependency_compatability.rb +9 -0
  21. data/lib/smart_core/container/dependency_resolver.rb +16 -0
  22. data/lib/smart_core/container/entity.rb +26 -0
  23. data/lib/smart_core/container/exceptions.rb +31 -0
  24. data/lib/smart_core/container/key_guard.rb +31 -0
  25. data/lib/smart_core/container/memoized_dependency.rb +28 -0
  26. data/lib/smart_core/container/mixin.rb +82 -0
  27. data/lib/smart_core/container/namespace.rb +51 -0
  28. data/lib/smart_core/container/registry.rb +227 -0
  29. data/lib/smart_core/container/registry_builder.rb +18 -0
  30. data/lib/smart_core/container.rb +123 -0
  31. data/lib/smart_core/exceptions.rb +23 -0
  32. data/lib/smart_core/initializer/attribute/builder.rb +99 -0
  33. data/lib/smart_core/initializer/attribute/value_finalizer/lambda.rb +32 -0
  34. data/lib/smart_core/initializer/attribute/value_finalizer/method.rb +32 -0
  35. data/lib/smart_core/initializer/attribute/value_finalizer.rb +24 -0
  36. data/lib/smart_core/initializer/attribute.rb +123 -0
  37. data/lib/smart_core/initializer/attribute_definer.rb +162 -0
  38. data/lib/smart_core/{operation → initializer}/attribute_set.rb +17 -17
  39. data/lib/smart_core/initializer/exceptions.rb +47 -0
  40. data/lib/smart_core/initializer/extension.rb +39 -0
  41. data/lib/smart_core/initializer/extension_definer.rb +62 -0
  42. data/lib/smart_core/initializer/extension_set.rb +75 -0
  43. data/lib/smart_core/{operation → initializer}/initialization_dsl.rb +82 -42
  44. data/lib/smart_core/initializer/instance_attribute_accessing.rb +49 -0
  45. data/lib/smart_core/initializer/instance_builder.rb +164 -0
  46. data/lib/smart_core/initializer/type.rb +49 -0
  47. data/lib/smart_core/initializer/type_set.rb +52 -0
  48. data/lib/smart_core/initializer.rb +89 -0
  49. data/lib/smart_core/injector.rb +6 -0
  50. data/lib/smart_core/operation/exceptions.rb +4 -20
  51. data/lib/smart_core/operation/instance_builder.rb +16 -124
  52. data/lib/smart_core/operation/state.rb +7 -0
  53. data/lib/smart_core/operation/step.rb +43 -0
  54. data/lib/smart_core/operation/step_set.rb +73 -0
  55. data/lib/smart_core/operation/success.rb +11 -0
  56. data/lib/smart_core/operation.rb +10 -12
  57. data/lib/smart_core/schema.rb +6 -0
  58. data/lib/smart_core/validator/commands.rb +2 -0
  59. data/lib/smart_core/validator/exceptions.rb +1 -1
  60. data/lib/smart_core/version.rb +1 -1
  61. data/lib/smart_core.rb +5 -0
  62. data/smart_core.gemspec +14 -14
  63. metadata +57 -15
  64. data/lib/smart_core/operation/attribute.rb +0 -66
  65. data/lib/smart_core/operation/attribute_definer.rb +0 -160
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmartCore::Container::DependencyCompatability
4
+ # @api private
5
+ # @since 0.5.0
6
+ class CommandSet < Abstract
7
+ class << self
8
+ # @param command_set [SmartCore::Container::CommandSet]
9
+ # @param dependency_name [String]
10
+ # @return [Boolean]
11
+ #
12
+ # @api private
13
+ # @since 0.5.0
14
+ def potential_namespace_overlap?(command_set, dependency_name)
15
+ command_set.any? do |command|
16
+ next unless command.is_a?(SmartCore::Container::Commands::Namespace)
17
+ command.namespace_name == dependency_name
18
+ end
19
+ end
20
+
21
+ # @param command_set [SmartCore::Container::CommandSet]
22
+ # @param namespace_name [String]
23
+ # @return [Boolean]
24
+ #
25
+ # @api private
26
+ # @since 0.5.0
27
+ def potential_dependency_overlap?(command_set, namespace_name)
28
+ command_set.any? do |command|
29
+ next unless command.is_a?(SmartCore::Container::Commands::Register)
30
+ command.dependency_name == namespace_name
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmartCore::Container::DependencyCompatability
4
+ # @api private
5
+ # @since 0.5.0
6
+ class Registry < Abstract
7
+ class << self
8
+ # @param registry [SmartCore::Container::Regsitry]
9
+ # @param dependency_name [String]
10
+ # @return [Boolean]
11
+ #
12
+ # @api private
13
+ # @since 0.5.0
14
+ def potential_namespace_overlap?(registry, dependency_name)
15
+ registry.any? do |registered_dependency|
16
+ next unless registered_dependency.is_a?(SmartCore::Container::Namespace)
17
+ # NOTE: registered_dependency is a namespace
18
+ registered_dependency.external_name == dependency_name
19
+ end
20
+ end
21
+
22
+ # @param registry [SmartCore::Container::Regsitry]
23
+ # @param namespace_name [String]
24
+ # @return [Boolean]
25
+ #
26
+ # @api private
27
+ # @since 0.5.0
28
+ def potential_dependency_overlap?(registry, namespace_name)
29
+ registry.any? do |registered_dependency|
30
+ next unless registered_dependency.is_a?(SmartCore::Container::Dependency)
31
+ # NOTE: registered_dependency is a dependency/memoized dependency
32
+ registered_dependency.external_name == namespace_name
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.5.0
5
+ module SmartCore::Container::DependencyCompatability
6
+ require_relative 'dependency_compatability/abstract'
7
+ require_relative 'dependency_compatability/command_set'
8
+ require_relative 'dependency_compatability/registry'
9
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.5.0
5
+ module SmartCore::Container::DependencyResolver
6
+ class << self
7
+ # @param registry [SmartCore::Container::Registry]
8
+ # @return [Any]
9
+ #
10
+ # @api private
11
+ # @since 0.5.0
12
+ def resolve(registry, dependency_path)
13
+ registry.resolve(dependency_path).call
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.5.0
5
+ class SmartCore::Container::Entity
6
+ # @return [String]
7
+ #
8
+ # @api private
9
+ # @since 0.5.0
10
+ attr_reader :external_name
11
+
12
+ # @param external_name [String]
13
+ # @return [void]
14
+ #
15
+ # @api private
16
+ # @since 0.5.0
17
+ def initialize(external_name)
18
+ @external_name = external_name
19
+ end
20
+
21
+ # @return [void]
22
+ #
23
+ # @api private
24
+ # @since 0.5.0
25
+ def call; end
26
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SmartCore::Container
4
+ # @api public
5
+ # @since 0.5.0
6
+ Error = Class.new(SmartCore::Error)
7
+
8
+ # @api public
9
+ # @since 0.5.0
10
+ ArgumentError = Class.new(SmartCore::ArgumentError)
11
+
12
+ # @api public
13
+ # @since 0.5.0
14
+ NamespaceOverlapError = Class.new(Error)
15
+
16
+ # @api public
17
+ # @since 0.5.0
18
+ DependencyOverlapError = Class.new(Error)
19
+
20
+ # @api public
21
+ # @since 0.5.0
22
+ UnexistentDependencyError = Class.new(Error)
23
+
24
+ # @api public
25
+ # @since 0.5.0
26
+ FrozenRegistryError = Class.new(SmartCore::FrozenError)
27
+
28
+ # @api private
29
+ # @since 0.5.0
30
+ IncompatibleDependencyTree = Class.new(Error)
31
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api priavate
4
+ # @since 0.5.0
5
+ module SmartCore::Container::KeyGuard
6
+ class << self
7
+ # @param key [Symbol, String]
8
+ # @return [void]
9
+ #
10
+ # @raise [SmartCore::Container::ArgumentError]
11
+ #
12
+ # @api private
13
+ # @since 0.5.0
14
+ def prevent_incomparabilities!(key)
15
+ raise(
16
+ SmartCore::Container::ArgumentError,
17
+ 'Namespace/Dependency name should be a symbol or a string'
18
+ ) unless key.is_a?(String) || key.is_a?(Symbol)
19
+ end
20
+
21
+ # @param key [Symbol, String]
22
+ # @return [String]
23
+ #
24
+ # @api private
25
+ # @since 0.5.0
26
+ def indifferently_accessable_key(key)
27
+ prevent_incomparabilities!(key)
28
+ key.to_s
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.5.0
5
+ class SmartCore::Container::MemoizedDependency < SmartCore::Container::Dependency
6
+ # @param external_name [String]
7
+ # @param dependency_definition [Proc]
8
+ # @param options [Hash<Symbol,Any>]
9
+ # @return [void]
10
+ #
11
+ # @todo option list
12
+ # @see [SmartCore::Container::Dependency]
13
+ #
14
+ # @api private
15
+ # @since 0.5.0
16
+ def initialize(external_name, dependency_definition, **options)
17
+ @memoized_call = nil
18
+ super
19
+ end
20
+
21
+ # @return [Any]
22
+ #
23
+ # @api private
24
+ # @since 0.5.0
25
+ def call
26
+ thread_safe { @memoized_call ||= dependency_definition.call }
27
+ end
28
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api public
4
+ # @since 0.5.0
5
+ module SmartCore::Container::Mixin
6
+ class << self
7
+ # @param base_klass [Class]
8
+ # @return [void]
9
+ #
10
+ # @api private
11
+ # @since 0.5.0
12
+ def included(base_klass)
13
+ # rubocop:disable Metrics/LineLength
14
+ base_klass.instance_variable_set(:@__smart_core_container_access_lock__, Mutex.new)
15
+ base_klass.instance_variable_set(:@__smart_core_container_definition_lock__, Mutex.new)
16
+ base_klass.instance_variable_set(:@__smart_core_container_klass__, Class.new(SmartCore::Container))
17
+ base_klass.instance_variable_set(:@__smart_core_container__, nil)
18
+ # rubocop:enable Metrics/LineLength
19
+
20
+ base_klass.extend(ClassMethods)
21
+ base_klass.include(InstanceMethods)
22
+ base_klass.singleton_class.prepend(ClassInheritance)
23
+ end
24
+ end
25
+
26
+ # @api private
27
+ # @since 0.5.0
28
+ module ClassInheritance
29
+ # @param child_klass [CLass]
30
+ # @return [void]
31
+ #
32
+ # @api private
33
+ # @since 0.5.0
34
+ def inherited(child_klass)
35
+ inherited_container_klass = Class.new(@__smart_core_container_klass__)
36
+
37
+ child_klass.instance_variable_set(:@__smart_core_container_access_lock__, Mutex.new)
38
+ child_klass.instance_variable_set(:@__smart_core_container_definition_lock__, Mutex.new)
39
+ child_klass.instance_variable_set(:@__smart_core_container_klass__, inherited_container_klass)
40
+ child_klass.instance_variable_set(:@__smart_core_container__, nil)
41
+
42
+ super
43
+ end
44
+ end
45
+
46
+ # @api private
47
+ # @since 0.5.0
48
+ module ClassMethods
49
+ # @param block [Proc]
50
+ # @return [void]
51
+ #
52
+ # @api public
53
+ # @since 0.5.0
54
+ def dependencies(&block)
55
+ @__smart_core_container_definition_lock__.synchronize do
56
+ @__smart_core_container_klass__.instance_eval(&block) if block_given?
57
+ end
58
+ end
59
+
60
+ # @return [SmartCore::Container]
61
+ #
62
+ # @api public
63
+ # @since 0.5.0
64
+ def container
65
+ @__smart_core_container_definition_lock__.synchronize do
66
+ @__smart_core_container__ ||= @__smart_core_container_klass__.new
67
+ end
68
+ end
69
+ end
70
+
71
+ # @api private
72
+ # @since 0.5.0
73
+ module InstanceMethods
74
+ # @return [SmartCore::Container]
75
+ #
76
+ # @api public
77
+ # @since 0.5.0
78
+ def container
79
+ self.class.container
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.5.0
5
+ class SmartCore::Container::Namespace < SmartCore::Container::Entity
6
+ # @param external_name [String]
7
+ # @return [void]
8
+ #
9
+ # @api private
10
+ # @since 0.5.0
11
+ def initialize(external_name)
12
+ @container = Class.new(SmartCore::Container)
13
+ @container_instance = nil
14
+ @access_lock = Mutex.new
15
+ super(external_name)
16
+ end
17
+
18
+ # @param dependency_definitions [Proc]
19
+ # @return [void]
20
+ #
21
+ # @api private
22
+ # @since 0.5.0
23
+ def append_definitions(dependency_definitions)
24
+ container.instance_eval(&dependency_definitions)
25
+ end
26
+
27
+ # @return [SmartCore::Container]
28
+ #
29
+ # @api private
30
+ # @since 0.5.0
31
+ def call
32
+ thread_safe { @container_instance ||= container.new }
33
+ end
34
+
35
+ private
36
+
37
+ # @return [Class<SmartCore::Container>]
38
+ #
39
+ # @api private
40
+ # @since 0.5.0
41
+ attr_reader :container
42
+
43
+ # @param block [Proc]
44
+ # @return [Any]
45
+ #
46
+ # @api private
47
+ # @since 0.5.0
48
+ def thread_safe(&block)
49
+ @access_lock.synchronize(&block)
50
+ end
51
+ end
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SmartCore::Container
4
+ # @api private
5
+ # @since 0.5.0
6
+ class Registry
7
+ # @since 0.5.0
8
+ include Enumerable
9
+
10
+ # @return [void]
11
+ #
12
+ # @api private
13
+ # @since 0.5.0
14
+ def initialize
15
+ @access_lock = Mutex.new
16
+ @registry = {}
17
+ end
18
+
19
+ # @return [void]
20
+ #
21
+ # @api private
22
+ # @since 0.5.0
23
+ def freeze
24
+ thread_safe { freeze_state }
25
+ end
26
+
27
+ # @return [void]
28
+ #
29
+ # @api private
30
+ # @since 0.5.0
31
+ def frozen?
32
+ thread_safe { state_frozen? }
33
+ end
34
+
35
+ # @param dependency_path [String, Symbol]
36
+ # @return [SmartCore::Container::Namespace, SmartCore::Container::Dependnecy]
37
+ #
38
+ # @api private
39
+ # @since 0.5.0
40
+ def resolve(dependency_path)
41
+ thread_safe { fetch_dependency(dependency_path) }
42
+ end
43
+
44
+ # @param name [String, Symbol]
45
+ # @param options [Hash<Symbol,Any>]
46
+ # @param dependency_definition [Block]
47
+ # @return [void]
48
+ #
49
+ # @todo option list
50
+ #
51
+ # @api private
52
+ # @since 0.5.0
53
+ def register(name, **options, &dependency_definition)
54
+ thread_safe do
55
+ raise(
56
+ SmartCore::Container::FrozenRegistryError,
57
+ 'Can not modify frozen registry'
58
+ ) if state_frozen?
59
+
60
+ append_dependency(name, dependency_definition, **options)
61
+ end
62
+ end
63
+
64
+ # @param name [String, Symbol]
65
+ # @param dependency_definitions [Block]
66
+ # @return [void]
67
+ #
68
+ # @api private
69
+ # @since 0.5.0
70
+ def namespace(name, &dependency_definitions)
71
+ thread_safe do
72
+ raise(
73
+ SmartCore::Container::FrozenRegistryError,
74
+ 'Can not modify frozen registry'
75
+ ) if state_frozen?
76
+
77
+ append_namespace(name, dependency_definitions)
78
+ end
79
+ end
80
+
81
+ # @param block [Block]
82
+ # @return [Enumerable]
83
+ #
84
+ # @api private
85
+ # @since 0.5.0
86
+ def each(&block)
87
+ thread_safe { block_given? ? registry.each_value(&block) : registry.each_value }
88
+ end
89
+
90
+ # @return [Hash<Symbol,Any>]
91
+ #
92
+ # @api private
93
+ # @since 0.5.0
94
+ def to_h
95
+ # TODO: implement :)
96
+ end
97
+
98
+ private
99
+
100
+ # @return [Mutex]
101
+ #
102
+ # @api private
103
+ # @since 0.5.0
104
+ attr_reader :access_lock
105
+
106
+ # @return [Hash<Symbol,SmartCore::Container::Entity>]
107
+ #
108
+ # @api private
109
+ # @since 0.5.0
110
+ attr_reader :registry
111
+
112
+ # @return [Boolean]
113
+ #
114
+ # @api private
115
+ # @since 0.5.0
116
+ def state_frozen?
117
+ registry.frozen?
118
+ end
119
+
120
+ # @return [void]
121
+ #
122
+ # @api private
123
+ # @since 0.5.0
124
+ def freeze_state
125
+ registry.freeze
126
+ end
127
+
128
+ # @paramm dependency_path [String, Symbol]
129
+ # @return [SmartCore::Container::Namespace, SmartCore::Container::Dependency]
130
+ #
131
+ # @api private
132
+ # @since 0.5.0
133
+ def fetch_dependency(dependency_path)
134
+ # TODO: rabbit style dependnecy path
135
+ name = indifferently_accessable_name(dependency_path)
136
+ registry.fetch(name)
137
+ rescue KeyError
138
+ raise(
139
+ SmartCore::Container::UnexistentDependencyError,
140
+ "Dependencny with '#{name}' name does not exist!"
141
+ )
142
+ end
143
+
144
+ # @param dependency_name [String, Symbol]
145
+ # @param dependency_definition [Proc]
146
+ # @param options [Hash<Symbol,Any>]
147
+ # @return [void]
148
+ #
149
+ # @todo option list
150
+ # @raise [SmartCore::Container::NamespaceOverlapError]
151
+ #
152
+ # @api private
153
+ # @since 0.5.0
154
+ def append_dependency(dependency_name, dependency_definition, **options)
155
+ dependency_name = indifferently_accessable_name(dependency_name)
156
+ prevent_namespace_overlap!(dependency_name)
157
+ dependency = DependencyBuilder.build(dependency_name, dependency_definition, **options)
158
+
159
+ registry[dependency_name] = dependency
160
+ end
161
+
162
+ # @param namespace_name [String, Symbol]
163
+ # @param dependency_definitions [Proc]
164
+ # @return [void]
165
+ #
166
+ # @raise [SmartCore::Container::DependencyOverlapError]
167
+ #
168
+ # @api private
169
+ # @since 0.5.0
170
+ def append_namespace(namespace_name, dependency_definitions)
171
+ namespace_name = indifferently_accessable_name(namespace_name)
172
+ prevent_dependency_overlap!(namespace_name)
173
+
174
+ # rubocop:disable Layout/RescueEnsureAlignment
175
+ namespace = begin
176
+ registry.fetch(namespace_name)
177
+ rescue KeyError
178
+ registry[namespace_name] = SmartCore::Container::Namespace.new(namespace_name)
179
+ end
180
+ # rubocop:enable Layout/RescueEnsureAlignment
181
+
182
+ namespace.append_definitions(dependency_definitions)
183
+ end
184
+
185
+ # @param name [String, Symbol]
186
+ # @return [void]
187
+ #
188
+ # @see [SmartCore::Container::KeyGuard]
189
+ #
190
+ # @api private
191
+ # @since 0.5.0
192
+ def indifferently_accessable_name(name)
193
+ SmartCore::Container::KeyGuard.indifferently_accessable_key(name)
194
+ end
195
+
196
+ # @param dependency_name [String]
197
+ # @retrun [void]
198
+ #
199
+ # @api private
200
+ # @since 0.5.0
201
+ def prevent_namespace_overlap!(dependency_name)
202
+ DependencyCompatability::Registry.prevent_namespace_overlap!(
203
+ self, dependency_name
204
+ )
205
+ end
206
+
207
+ # @param namespace_name [String]
208
+ # @return [void]
209
+ #
210
+ # @api private
211
+ # @since 0.5.0
212
+ def prevent_dependency_overlap!(namespace_name)
213
+ DependencyCompatability::Registry.prevent_dependency_overlap!(
214
+ self, namespace_name
215
+ )
216
+ end
217
+
218
+ # @param block [Proc]
219
+ # @return [Any]
220
+ #
221
+ # @api private
222
+ # @since 0.5.0
223
+ def thread_safe(&block)
224
+ @access_lock.synchronize(&block)
225
+ end
226
+ end
227
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.5.0
5
+ module SmartCore::Container::RegistryBuilder
6
+ class << self
7
+ # @param commands [SmartCore::Container::CommandSet]
8
+ # @return [SmartCore::Container::Registry]
9
+ #
10
+ # @api private
11
+ # @since 0.5.0
12
+ def build(commands)
13
+ SmartCore::Container::Registry.new.tap do |registry|
14
+ commands.each { |command| command.call(registry) }
15
+ end
16
+ end
17
+ end
18
+ end