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,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api public
4
+ # @since 0.5.0
5
+ class SmartCore::Container
6
+ require_relative 'container/exceptions'
7
+ require_relative 'container/commands'
8
+ require_relative 'container/command_set'
9
+ require_relative 'container/key_guard'
10
+ require_relative 'container/entity'
11
+ require_relative 'container/dependency'
12
+ require_relative 'container/memoized_dependency'
13
+ require_relative 'container/namespace'
14
+ require_relative 'container/dependency_builder'
15
+ require_relative 'container/registry'
16
+ require_relative 'container/registry_builder'
17
+ require_relative 'container/dependency_compatability'
18
+ require_relative 'container/dependency_resolver'
19
+ require_relative 'container/definition_dsl'
20
+ require_relative 'container/mixin'
21
+
22
+ # TODO: container composition
23
+ # TODO: #merge / #merge!
24
+
25
+ # @since 0.5.0
26
+ include DefinitionDSL
27
+
28
+ # @return [void]
29
+ #
30
+ # @api public
31
+ # @since 0.5.0
32
+ def initialize
33
+ build_registry
34
+ @access_lock = Mutex.new
35
+ end
36
+
37
+ # @param dependency_name [String, Symbol]
38
+ # @param options [Hash<Symbol,Any>]
39
+ # @param dependency_definition [Block]
40
+ # @return [void]
41
+ #
42
+ # @todo option list
43
+ #
44
+ # @api public
45
+ # @since 0.5.0
46
+ def register(dependency_name, **options, &dependency_definition)
47
+ thread_safe do
48
+ registry.register(dependency_name, **options, &dependency_definition)
49
+ end
50
+ end
51
+
52
+ # @return [void]
53
+ #
54
+ # @api public
55
+ # @since 0.5.0
56
+ def freeze
57
+ thread_safe { registry.freeze }
58
+ end
59
+
60
+ # @return [Boolean]
61
+ #
62
+ # @api public
63
+ # @since 0.5.0
64
+ def frozen?
65
+ thread_safe { registry.frozen? }
66
+ end
67
+
68
+ # @param namespace_name [String, Symbol]
69
+ # @param dependency_definitions [Block]
70
+ # @return [void]
71
+ #
72
+ # @api public
73
+ # @since 0.5.0
74
+ def namespace(namespace_name, &dependency_definitions)
75
+ thread_safe do
76
+ registry.namespace(namespace_name, &dependency_definitions)
77
+ end
78
+ end
79
+
80
+ # @param dependency_path [String, Symbol]
81
+ # @return [Any]
82
+ #
83
+ # @api public
84
+ # @since 0.5.0
85
+ def resolve(dependency_path)
86
+ thread_safe do
87
+ DependencyResolver.resolve(registry, dependency_path)
88
+ end
89
+ end
90
+
91
+ # @return [void]
92
+ #
93
+ # @api public
94
+ # @since 0.5.0
95
+ def reload!
96
+ thread_safe { build_registry }
97
+ end
98
+
99
+ private
100
+
101
+ # @return [SmartCore::Container::Registry]
102
+ #
103
+ # @api private
104
+ # @since 0.5.0
105
+ attr_reader :registry
106
+
107
+ # @return [void]
108
+ #
109
+ # @api private
110
+ # @since 0.5.0
111
+ def build_registry
112
+ @registry = RegistryBuilder.build(self.class.__commands__)
113
+ end
114
+
115
+ # @param block [Proc]
116
+ # @return [Any]
117
+ #
118
+ # @api private
119
+ # @since 0.5.0
120
+ def thread_safe(&block)
121
+ @access_lock.synchronize(&block)
122
+ end
123
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmartCore
4
+ # @api public
5
+ # @since 0.5.0
6
+ Error = Class.new(StandardError)
7
+
8
+ # @api public
9
+ # @since 0.5.0
10
+ ArgumentError = Class.new(::ArgumentError)
11
+
12
+ # @api public
13
+ # @since 0.5.0
14
+ FrozenError = begin # rubocop:disable Naming/ConstantName
15
+ # :nocov:
16
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
17
+ Class.new(::FrozenError)
18
+ else
19
+ Class.new(::RuntimeError)
20
+ end
21
+ # :nocov:
22
+ end
23
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.5.0
5
+ class SmartCore::Initializer::Attribute
6
+ module Builder
7
+ class << self
8
+ # @return [Proc]
9
+ #
10
+ # @api private
11
+ # @since 0.5.0
12
+ DEFAULT_FINALIZE = -> (value) { value }.freeze
13
+
14
+ # @param name [String, Symbol]
15
+ # @option type [Symbol]
16
+ # @option privacy [Symbol, String]
17
+ # @param options [Hash<Symbol,Any>]
18
+ # - :default (see SmartCore::Initializer::Attribute#initialize)
19
+ # - :finalize (see SmartCore::Initializer::Attribute#initialize)
20
+ # @return [SmartCore::Initializer::Attribute]
21
+ #
22
+ # @raise [SmartCore::Initializer::IncorrectAttributeNameError]
23
+ # @raise [SmartCore::Initializer::UnregisteredTypeError]
24
+ # @raise [SmartCore::Initializer::AttributeError]
25
+ #
26
+ # @api private
27
+ # @since 0.5.0
28
+ def build(name, type: :__any__, privacy: :default, finalize: DEFAULT_FINALIZE, **options)
29
+ name = represent_name_attr(name)
30
+ type = represent_type_attr(type)
31
+ privacy = represent_privacy_attr(privacy)
32
+ finalizer = represent_finalizer_attr(finalize)
33
+
34
+ SmartCore::Initializer::Attribute.new(name, type, privacy, finalizer, **options)
35
+ end
36
+
37
+ private
38
+
39
+ # @param finalize [Proc, String, Symbol]
40
+ # @return [SmartCore::Initializer::Attribute::ValueFinalizer::Lambda]
41
+ # @return [SmartCore::Initializer::Attribute::ValueFinalizer::Method]
42
+ #
43
+ # @api private
44
+ # @since 0.5.0
45
+ def represent_finalizer_attr(finalize)
46
+ raise(
47
+ SmartCore::Initializer::IncompatibleFinalizerTypeError,
48
+ ':finalize should be a symbol/string or a proc/lambda'
49
+ ) unless finalize.is_a?(Proc) || finalize.is_a?(Symbol) || finalize.is_a?(String)
50
+
51
+ ValueFinalizer.build(finalize)
52
+ end
53
+
54
+ # @param privacy [Symbol, String]
55
+ # @return [Symbol]
56
+ #
57
+ # @api private
58
+ # @since 0.5.0
59
+ def represent_privacy_attr(privacy)
60
+ begin
61
+ PRIVACY_MODES.fetch(privacy.to_sym)
62
+ rescue KeyError
63
+ raise(
64
+ SmartCore::Initializer::UnsupportedAttributePrivacyError,
65
+ "Required :#{privacy} privacy mode is not supported!"
66
+ )
67
+ end
68
+ end
69
+
70
+ # @param name [Symbol, String]
71
+ # @return [Symbol, String]
72
+ #
73
+ # @api private
74
+ # @since 0.5.0
75
+ def represent_name_attr(name)
76
+ name.tap do |value|
77
+ raise(
78
+ SmartCore::Initializer::IncorrectAttributeNameError,
79
+ 'Attribute name should be a symbol or a string'
80
+ ) unless value.is_a?(Symbol) || value.is_a?(String)
81
+ end
82
+ end
83
+
84
+ # @param type [String]
85
+ # @return [Symbol]
86
+ #
87
+ # @api private
88
+ # @since 0.5.0
89
+ def represent_type_attr(type)
90
+ type.tap do |value|
91
+ raise(
92
+ SmartCore::Initializer::UnregisteredTypeError,
93
+ "type :#{value} is not registered!"
94
+ ) unless SmartCore::Initializer.types.has_type?(value)
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.5.0
5
+ class SmartCore::Initializer::Attribute::ValueFinalizer::Lambda
6
+ # @param finalizer [Proc]
7
+ # @return [void]
8
+ #
9
+ # @api private
10
+ # @since 0.5.0
11
+ def initialize(finalizer)
12
+ @finalizer = finalizer
13
+ end
14
+
15
+ # @param value [Any]
16
+ # @param instance [Any]
17
+ # @return [Any]
18
+ #
19
+ # @api private
20
+ # @since 0.5.0
21
+ def finalize(value, instance)
22
+ finalizer.call(value)
23
+ end
24
+
25
+ private
26
+
27
+ # @return [Proc]
28
+ #
29
+ # @api private
30
+ # @since 0.5.0
31
+ attr_reader :finalizer
32
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.5.0
5
+ class SmartCore::Initializer::Attribute::ValueFinalizer::Method
6
+ # @param finalizer [String, Symbol]
7
+ # @return [void]
8
+ #
9
+ # @api private
10
+ # @since 0.5.0
11
+ def initialize(finalizer)
12
+ @finalizer = finalizer
13
+ end
14
+
15
+ # @param value [Any]
16
+ # @param instance [Any]
17
+ # @return [Any]
18
+ #
19
+ # @api private
20
+ # @since 0.5.0
21
+ def finalize(value, instance)
22
+ instance.send(finalizer, value)
23
+ end
24
+
25
+ private
26
+
27
+ # @return [String, Symbol]
28
+ #
29
+ # @api private
30
+ # @since 0.5.0
31
+ attr_reader :finalizer
32
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmartCore::Initializer::Attribute::ValueFinalizer
4
+ require_relative 'value_finalizer/lambda'
5
+ require_relative 'value_finalizer/method'
6
+
7
+ class << self
8
+ # @param finalize [Proc, String, Symbol]
9
+ # @return [SmartCore::Initializer::Attribute::ValueFinalizer::Lambda]
10
+ # @return [SmartCore::Initializer::Attribute::ValueFinalizer::Method]
11
+ #
12
+ # @api private
13
+ # @since 0.5.0
14
+ def build(finalize)
15
+ case finalize
16
+ when Symbol, String
17
+ Method.new(finalize)
18
+ when Proc
19
+ Lambda.new(finalize)
20
+ end
21
+ # NOTE: other variants are impossible (by SmartCore::Initializer::Attribute::Builder)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.5.0
5
+ class SmartCore::Initializer::Attribute
6
+ require_relative 'attribute/builder'
7
+ require_relative 'attribute/value_finalizer'
8
+
9
+ # @return [Hash<Symbol,Symbol>]
10
+ #
11
+ # @api private
12
+ # @since 0.5.0
13
+ PRIVACY_MODES = {
14
+ private: :private,
15
+ protected: :protected,
16
+ public: :public,
17
+ default: :public
18
+ }.freeze
19
+
20
+ # @return [Symbol]
21
+ #
22
+ # @api private
23
+ # @since 0.5.0
24
+ attr_reader :name
25
+
26
+ # @return [String, Symbol]
27
+ #
28
+ # @api private
29
+ # @since 0.5.0
30
+ attr_reader :type
31
+
32
+ # @return [Symbol]
33
+ #
34
+ # @api private
35
+ # @since 0.5.0
36
+ attr_reader :privacy
37
+
38
+ # @return [SmartCore::Initializer::Attribute::ValueFinalizer::Lambda]
39
+ # @return [SmartCore::Initializer::Attribute::ValueFinalizer::Method]
40
+ #
41
+ # @api private
42
+ # @since 0.5.0
43
+ attr_reader :finalizer
44
+
45
+ # @return [Hash<Symbol,Any>]
46
+ #
47
+ # @api private
48
+ # @since 0.5.0
49
+ attr_reader :options
50
+
51
+ # @param name [String, Symbol]
52
+ # @param type [Symbol] (see SmartCore::Initializer::TypeSet, SmartCore::Initializer::Type)
53
+ # @param privacy [Symbol
54
+ # @param finalizer [SmartCore::Initializer::Attribute::ValueFinalizer::Lambda/Method]
55
+ # @param options [HAsh<Symbol,Any>] Supported options:
56
+ # - :default [Proc] see #default_value
57
+ # @return [void]
58
+ #
59
+ # @api private
60
+ # @since 0.5.0
61
+ def initialize(name, type, privacy, finalizer, **options)
62
+ @name = name
63
+ @type = type
64
+ @privacy = privacy
65
+ @finalizer = finalizer
66
+ @options = options
67
+ end
68
+
69
+ # @return [Boolean]
70
+ #
71
+ # @api private
72
+ # @since 0.5.0
73
+ def has_default_value?
74
+ options.key?(:default)
75
+ end
76
+
77
+ # @param value [Any]
78
+ # @return [void]
79
+ #
80
+ # @api private
81
+ # @since 0.5.0
82
+ def validate_value_type!(value)
83
+ type_checker = SmartCore::Initializer.get_type(type)
84
+
85
+ raise(
86
+ SmartCore::Initializer::ArgumentError,
87
+ "Incorrect type of <#{name}> attribute " \
88
+ "(given: #{value.class}, expected: :#{type_checker.name})"
89
+ ) unless type_checker.comparable?(value)
90
+ end
91
+
92
+ # @return [Any]
93
+ #
94
+ # @raise [SmartCore::Initializer::ArgumentError]
95
+ #
96
+ # @api private
97
+ # @since 0.5.0
98
+ def default_value
99
+ default_value = options.fetch(:default) do
100
+ raise(SmartCore::Initializer::ArgumentError, 'Default value is not provided.')
101
+ end
102
+
103
+ default_value.is_a?(Proc) ? default_value.call : default_value
104
+ end
105
+
106
+ # @param value [Any]
107
+ # @param instance [Any]
108
+ # @return [Any]
109
+ #
110
+ # @api private
111
+ # @since 0.5.0
112
+ def finalize(value, instance)
113
+ finalizer.finalize(value, instance)
114
+ end
115
+
116
+ # @return [SmartCore::Intializer::Attribute]
117
+ #
118
+ # @api private
119
+ # @since 0.5.0
120
+ def dup
121
+ self.class.new(name, type, privacy, finalizer, **options)
122
+ end
123
+ end
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.5.0
5
+ class SmartCore::Initializer::AttributeDefiner
6
+ # @param processed_klass [Class]
7
+ # @return [void]
8
+ #
9
+ # @api private
10
+ # @since 0.5.0
11
+ def initialize(processed_klass)
12
+ @processed_klass = processed_klass
13
+ @definition_lock = Mutex.new
14
+ end
15
+
16
+ # @param param_name [Symbol, String]
17
+ # @param param_type [String, Symbol]
18
+ # @return [void]
19
+ #
20
+ # @api private
21
+ # @since 0.5.0
22
+ def define_param(param_name, param_type = :__any__, **options)
23
+ thread_safe do
24
+ parameter = build_attribute(param_name, param_type, **options)
25
+ prevent_intersection_with_already_defined_option(parameter)
26
+ append_parameter(parameter)
27
+ end
28
+ end
29
+
30
+ # @param param_names [Array<String, Symbol>]
31
+ # @return [void]
32
+ #
33
+ # @api private
34
+ # @since 0.5.0
35
+ def define_params(*param_names)
36
+ thread_safe do
37
+ parameters = param_names.map do |param_name|
38
+ build_attribute(param_name).tap do |parameter|
39
+ prevent_intersection_with_already_defined_option(parameter)
40
+ end
41
+ end
42
+
43
+ parameters.each { |parameter| append_parameter(parameter) }
44
+ end
45
+ end
46
+
47
+ # @param option_name [Symbol, String]
48
+ # @param option_type [String, Symbol]
49
+ # @return [void]
50
+ #
51
+ # @api private
52
+ # @since 0.5.0
53
+ def define_option(option_name, option_type = :__any__, **options)
54
+ thread_safe do
55
+ option = build_attribute(option_name, option_type, **options)
56
+ prevent_intersection_with_already_defined_param(option)
57
+
58
+ append_option(option)
59
+ end
60
+ end
61
+
62
+ # @param option_names [Array<String, Symbol>]
63
+ # @return [void]
64
+ #
65
+ # @api private
66
+ # @since 0.5.0
67
+ def define_options(*option_names)
68
+ thread_safe do
69
+ options = option_names.map do |option_name|
70
+ build_attribute(option_name).tap do |option|
71
+ prevent_intersection_with_already_defined_param(option)
72
+ end
73
+ end
74
+
75
+ options.each { |option| append_option(option) }
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ # @return [Class]
82
+ #
83
+ # @api private
84
+ # @since 0.5.0
85
+ attr_reader :processed_klass
86
+
87
+ # @param attribute_name [Symbol, String]
88
+ # @param attribute_type [String, Symbol]
89
+ # @param attribute_options [Hash<Symbol,Any>]
90
+ # @return [SmartCore::Initializer::Attribute]
91
+ #
92
+ # @api private
93
+ # @since 0.5.0
94
+ def build_attribute(attribute_name, attribute_type = :__any__, **attribute_options)
95
+ SmartCore::Initializer::Attribute::Builder.build(
96
+ attribute_name, type: attribute_type, **attribute_options
97
+ )
98
+ end
99
+
100
+ # @param parameter [SmartCore::Initializer::Attribute]
101
+ # @return [void]
102
+ #
103
+ # @api private
104
+ # @since 0.5.0
105
+ def append_parameter(parameter)
106
+ processed_klass.__params__ << parameter
107
+ processed_klass.send(:attr_reader, parameter.name)
108
+ processed_klass.send(parameter.privacy, parameter.name)
109
+ end
110
+
111
+ # @param option [SmartCore::Initializer::Attribute]
112
+ # @return [void]
113
+ #
114
+ # @api private
115
+ # @since 0.5.0
116
+ def append_option(option)
117
+ processed_klass.__options__ << option
118
+ processed_klass.send(:attr_reader, option.name)
119
+ processed_klass.send(option.privacy, option.name)
120
+ end
121
+
122
+ # @param parameter [SmartCore::Initializer::Attribute]
123
+ # @return [void]
124
+ #
125
+ # @raise [SmartCore::Initializer::OptionOverlapError]
126
+ #
127
+ # @api private
128
+ # @since 0.5.0
129
+ def prevent_intersection_with_already_defined_option(parameter)
130
+ if processed_klass.__options__.conflicts_with?(parameter)
131
+ raise(
132
+ SmartCore::Initializer::OptionOverlapError,
133
+ "You have already defined option with name :#{parameter.name}"
134
+ )
135
+ end
136
+ end
137
+
138
+ # @param option [SmartCore::Initializer::Attribute]
139
+ # @return [void]
140
+ #
141
+ # @raise [SmartCore::Initializer::ParamOverlapError]
142
+ #
143
+ # @api private
144
+ # @since 0.5.0
145
+ def prevent_intersection_with_already_defined_param(option)
146
+ if processed_klass.__params__.conflicts_with?(option)
147
+ raise(
148
+ SmartCore::Initializer::ParamOverlapError,
149
+ "You have already defined param with name :#{option.name}"
150
+ )
151
+ end
152
+ end
153
+
154
+ # @param block [Proc]
155
+ # @return [Any]
156
+ #
157
+ # @api private
158
+ # @since 0.5.0
159
+ def thread_safe(&block)
160
+ @definition_lock.synchronize(&block)
161
+ end
162
+ end