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,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