smart_core 0.4.0 → 0.5.0

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