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,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.5.0
5
+ class SmartCore::Initializer::InstanceBuilder
6
+ class << self
7
+ # @param processed_object [Any]
8
+ # @param processed_klass [Class]
9
+ # @param parameters [Array<Any>]
10
+ # @param options [Hash<Symbol,Any>]
11
+ # @return [Any] Fully allocated processed_object
12
+ #
13
+ # @api private
14
+ # @since 0.5.0
15
+ def call(processed_object, processed_klass, parameters, options)
16
+ new(processed_object, processed_klass, parameters, options).call
17
+ end
18
+ end
19
+
20
+ # @param processed_object [Any]
21
+ # @param processed_klass [Class]
22
+ # @param parameters [Array<Any>]
23
+ # @param options [Hash<Symbol,Any>]
24
+ # @return [void]
25
+ #
26
+ # @api private
27
+ # @since 0.5.0
28
+ def initialize(processed_object, processed_klass, parameters, options)
29
+ @processed_object = processed_object
30
+ @processed_klass = processed_klass
31
+ @parameters = parameters
32
+ @options = options
33
+ end
34
+
35
+ # @return [Any]
36
+ #
37
+ # @api private
38
+ # @since 0.5.0
39
+ def call
40
+ processed_object.tap do
41
+ prevent_parameters_incomparability
42
+ initialize_parameters
43
+ initialize_options
44
+ call_original_methods
45
+ invoke_additional_initialization_steps
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ # @return [Any]
52
+ #
53
+ # @api private
54
+ # @since 0.5.0
55
+ attr_reader :processed_object
56
+
57
+ # @return [Class]
58
+ #
59
+ # @api private
60
+ # @since 0.5.0
61
+ attr_reader :processed_klass
62
+
63
+ # @return [Array<Any>]
64
+ #
65
+ # @api private
66
+ # @since 0.5.0
67
+ attr_reader :parameters
68
+
69
+ # @return [Hash<Symbol,Any>]
70
+ #
71
+ # @api private
72
+ # @since 0.5.0
73
+ attr_reader :options
74
+
75
+ # An array of the required option attribute names (parameters without default values)
76
+ #
77
+ # @return [Array<Symbol>]
78
+ #
79
+ # @api private
80
+ # @since 0.5.0
81
+ def required_options
82
+ processed_klass.__options__.each.reject(&:has_default_value?).map(&:name)
83
+ end
84
+
85
+ # @return [Integer]
86
+ #
87
+ # @api private
88
+ # @since 0.5.0
89
+ def required_attributes_count
90
+ processed_klass.__params__.size
91
+ end
92
+
93
+ # @return [void]
94
+ #
95
+ # @raise [SmartCore::Initializer::AttributeError]
96
+ # @raise [SmartCore::Initializer::ParameterError]
97
+ # @raise [SmartCore::Initializer::OptionError]
98
+ #
99
+ # @api private
100
+ # @since 0.5.0
101
+ def prevent_parameters_incomparability
102
+ raise(
103
+ SmartCore::Initializer::ParameterError,
104
+ "Wrong number of parameters " \
105
+ "(given #{parameters.size}, expected #{required_attributes_count})"
106
+ ) unless parameters.size == required_attributes_count
107
+
108
+ missing_options = required_options.reject { |option| options.key?(option) }
109
+
110
+ raise(
111
+ SmartCore::Initializer::OptionError,
112
+ "Missing options: :#{missing_options.join(', :')}"
113
+ ) unless missing_options.empty?
114
+ end
115
+
116
+ # @return [void]
117
+ #
118
+ # @api private
119
+ # @since 0.5.0
120
+ def initialize_parameters
121
+ parameter_objects = processed_klass.__params__
122
+ parameter_pairs = Hash[parameter_objects.zip(parameters)]
123
+
124
+ parameter_pairs.each_pair do |parameter_object, parameter_value|
125
+ parameter_object.validate_value_type!(parameter_value)
126
+ final_parameter_value = parameter_object.finalize(parameter_value, processed_object)
127
+ processed_object.instance_variable_set("@#{parameter_object.name}", final_parameter_value)
128
+ end
129
+ end
130
+
131
+ # @return [void]
132
+ #
133
+ # @api private
134
+ # @since 0.5.0
135
+ def initialize_options
136
+ processed_klass.__options__.each do |option|
137
+ option_name = option.name
138
+ option_value = options.fetch(option_name) { option.default_value }.tap do |value|
139
+ option.validate_value_type!(value)
140
+ end
141
+ final_option_value = option.finalize(option_value, processed_object)
142
+
143
+ processed_object.instance_variable_set("@#{option_name}", final_option_value)
144
+ end
145
+ end
146
+
147
+ # @return [void]
148
+ #
149
+ # @api private
150
+ # @since 0.5.0
151
+ def invoke_additional_initialization_steps
152
+ processed_klass.__initialization_extensions__.each do |extension|
153
+ extension.call(processed_object)
154
+ end
155
+ end
156
+
157
+ # @return [void]
158
+ #
159
+ # @api private
160
+ # @since 0.5.0
161
+ def call_original_methods
162
+ processed_object.send(:initialize, *parameters, **options)
163
+ end
164
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.5.0
5
+ class SmartCore::Initializer::Type
6
+ # @return [String]
7
+ #
8
+ # @api private
9
+ # @since 0.5.0
10
+ attr_reader :name
11
+
12
+ # @name [Symbol, String]
13
+ # @param checker [Proc]
14
+ # @return [void]
15
+ #
16
+ # @api private
17
+ # @since 0.5.0
18
+ def initialize(name, checker)
19
+ unless name.is_a?(String) || name.is_a?(Symbol)
20
+ raise(SmartCore::Initializer::ArgumentError, 'Type name should be a symbol or a string')
21
+ end
22
+
23
+ unless checker.is_a?(Proc)
24
+ raise(SmartCore::Initializer::ArgumentError, 'Checker should be a proc')
25
+ end
26
+
27
+ @name = name.to_sym
28
+ @checker = checker
29
+ end
30
+
31
+ # @param value [Any]
32
+ # @return [Boolean]
33
+ #
34
+ # @raise [SmartCore::Initializer::TypeError]
35
+ #
36
+ # @api private
37
+ # @since 0.5.0
38
+ def comparable?(value)
39
+ checker.call(value)
40
+ end
41
+
42
+ private
43
+
44
+ # @return [Proc]
45
+ #
46
+ # @api private
47
+ # @since 0.5.0
48
+ attr_reader :checker
49
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.5.0
5
+ class SmartCore::Initializer::TypeSet
6
+ # @return [void]
7
+ #
8
+ # @api private
9
+ # @since 0.5.0
10
+ def initialize
11
+ @types = {}
12
+ end
13
+
14
+ # @param name [String, Symbol]
15
+ # @return [Boolean]
16
+ #
17
+ # @api private
18
+ # @since 0.5.0
19
+ def has_type?(name)
20
+ types.key?(name)
21
+ end
22
+
23
+ # @param name [String, Symbol]
24
+ # @param checker [Proc]
25
+ # @return [void]
26
+ #
27
+ # @api private
28
+ # @since 0.5.0
29
+ def register(name, checker)
30
+ type = SmartCore::Initializer::Type.new(name, checker)
31
+ types[type.name] = type
32
+ end
33
+
34
+ # @param name [String, Symbol]
35
+ # @return [SmartCore::Initializer::Type]
36
+ #
37
+ # @api private
38
+ # @since 0.5.0
39
+ def resolve(name)
40
+ types.fetch(name.to_sym)
41
+ rescue KeyError
42
+ raise SmartCore::Initializer::UnregisteredTypeError, "type :#{name} is not registered!"
43
+ end
44
+
45
+ private
46
+
47
+ # @return [Hash<Symbol,SmartCore::Initializer::Type>]
48
+ #
49
+ # @api private
50
+ # @since 0.5.0
51
+ attr_reader :types
52
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api public
4
+ # @since 0.5.0
5
+ module SmartCore::Initializer
6
+ require_relative 'initializer/exceptions'
7
+ require_relative 'initializer/attribute'
8
+ require_relative 'initializer/attribute_set'
9
+ require_relative 'initializer/attribute_definer'
10
+ require_relative 'initializer/extension'
11
+ require_relative 'initializer/extension_set'
12
+ require_relative 'initializer/extension_definer'
13
+ require_relative 'initializer/instance_builder'
14
+ require_relative 'initializer/type'
15
+ require_relative 'initializer/type_set'
16
+ require_relative 'initializer/initialization_dsl'
17
+ require_relative 'initializer/instance_attribute_accessing'
18
+
19
+ @__type_set__ = SmartCore::Initializer::TypeSet.new
20
+ # TODO: convertable attributes and converters (in typeset manner)
21
+
22
+ class << self
23
+ # @param child_klass [Class]
24
+ # @return [void]
25
+ #
26
+ # @api public
27
+ # @since 0.5.0
28
+ def included(child_klass)
29
+ child_klass.include(InitializationDSL)
30
+ child_klass.include(InstanceAttributeAccessing)
31
+ end
32
+
33
+ # @param name [String, Symbol]
34
+ # @param checker [Block]
35
+ # @return [void]
36
+ #
37
+ # @api public
38
+ # @since 0.5.0
39
+ def register_type(name, &checker)
40
+ types.register(name, checker)
41
+ end
42
+
43
+ # @param name [String, Symbol]
44
+ # @return [SmartCore::Initializer::Type]
45
+ #
46
+ # @api private
47
+ # @since 0.5.0
48
+ def get_type(name)
49
+ types.resolve(name)
50
+ end
51
+
52
+ # @return [SmartCore::Initializer::TypeSet]
53
+ #
54
+ # @api private
55
+ # @since 0.5.0
56
+ def types
57
+ @__type_set__
58
+ end
59
+ end
60
+
61
+ # @return [void]
62
+ #
63
+ # @api public
64
+ # @since 0.5.0
65
+ def initialize(*, **); end
66
+
67
+ # @since 0.5.0
68
+ register_type(:array) { |value| value.is_a?(Array) }
69
+ # @since 0.5.0
70
+ register_type(:hash) { |value| value.is_a?(Hash) }
71
+ # @since 0.5.0
72
+ register_type(:string) { |value| value.is_a?(String) }
73
+ # @since 0.5.0
74
+ register_type(:integer) { |value| value.is_a?(Integer) }
75
+ # @since 0.5.0
76
+ register_type(:float) { |value| value.is_a?(Float) }
77
+ # @since 0.5.0
78
+ register_type(:proc) { |value| value.is_a?(Proc) }
79
+ # @since 0.5.0
80
+ register_type(:numeric) { |value| value.is_a?(Numeric) }
81
+ # @since 0.5.0
82
+ register_type(:big_decimal) { |value| value.is_a?(BigDecimal) }
83
+ # @since 0.5.0
84
+ register_type(:class) { |value| value.is_a?(Class) }
85
+ # @since 0.5.0
86
+ register_type(:boolean) { |value| value.is_a?(TrueClass) || value.is_a?(FalseClass) }
87
+ # @since 0.5.0
88
+ register_type(:__any__) { true }
89
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api public
4
+ # @since 0.5.0
5
+ module SmartCore::Injector
6
+ end
@@ -3,7 +3,7 @@
3
3
  class SmartCore::Operation
4
4
  # @api public
5
5
  # @since 0.2.0
6
- Error = Class.new(StandardError)
6
+ Error = Class.new(SmartCore::Error)
7
7
 
8
8
  # @api public
9
9
  # @since 0.3.0
@@ -27,27 +27,11 @@ class SmartCore::Operation
27
27
 
28
28
  # @api public
29
29
  # @since 0.2.0
30
- ArgumentError = Class.new(::ArgumentError)
31
-
32
- # @api public
33
- # @since 0.2.0
34
- ParameterError = Class.new(ArgumentError)
35
-
36
- # @api public
37
- # @since 0.2.0
38
- ParamOverlapError = Class.new(ParameterError)
39
-
40
- # @api public
41
- # @since 0.2.0
42
- OptionError = Class.new(ArgumentError)
43
-
44
- # @api public
45
- # @since 0.2.0
46
- OptionOverlapError = Class.new(OptionError)
30
+ IncorrectAttributeNameError = Class.new(Error)
47
31
 
48
32
  # @api public
49
- # @since 0.2.0
50
- IncorrectAttributeNameError = Class.new(Error)
33
+ # @since 0.5.0
34
+ IncorrectStepNameError = Class.new(Error)
51
35
 
52
36
  # @api public
53
37
  # @since 0.2.0
@@ -1,49 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # @api private
4
- # @since 0.2.0
4
+ # @since 0.5.0
5
5
  class SmartCore::Operation::InstanceBuilder
6
6
  class << self
7
- # @param operation_object [SmartCore::Operation]
8
- # @param operation_klass [Class<SmartCore::Operation>]
9
- # @param parameters [Array<Any>]
10
- # @param options [Hash<Symbol,Any>]
11
- # @return [SmartCore::Operation]
7
+ # @param operation [SmartCore::Operation]
8
+ # @return [void]
12
9
  #
13
10
  # @api private
14
- # @since 0.2.0
15
- def call(operation_object, operation_klass, parameters, options)
16
- new(operation_object, operation_klass, parameters, options).call
11
+ # @since 0.5.0
12
+ def call(operation)
13
+ new(operation).call
17
14
  end
18
15
  end
19
16
 
20
- # @param operation_object [SmartCore::Operation]
21
- # @param operation_klass [Class<SmartCore::Operation>]
22
- # @param parameters [Array<Any>]
23
- # @param options [Hash<Symbol,Any>]
17
+ # @param operation [SmartCore::Operation]
24
18
  # @return [void]
25
19
  #
26
20
  # @api private
27
- # @since 0.2.0
28
- def initialize(operation_object, operation_klass, parameters, options)
29
- @operation_object = operation_object
30
- @operation_klass = operation_klass
31
- @parameters = parameters
32
- @options = options
21
+ # @since 0.5.0
22
+ def initialize(operation)
23
+ @operation = operation
33
24
  end
34
25
 
35
- # @return [SmartCore::Operation]
26
+ # @return [void]
36
27
  #
37
28
  # @api private
38
- # @since 0.2.0
29
+ # @since 0.5.0
39
30
  def call
40
- operation_object.tap do
41
- prevent_parameters_incomparability
42
- initialize_parameters
43
- initialize_options
44
- call_original_methods
45
- make_operation_caller_yieldable
46
- end
31
+ make_operation_caller_yieldable
47
32
  end
48
33
 
49
34
  private
@@ -51,108 +36,15 @@ class SmartCore::Operation::InstanceBuilder
51
36
  # @return [SmartCore::Operation]
52
37
  #
53
38
  # @api private
54
- # @since 0.2.0
55
- attr_reader :operation_object
56
-
57
- # @return [Class<SmartCore::Operation>]
58
- #
59
- # @api private
60
- # @since 0.2.0
61
- attr_reader :operation_klass
62
-
63
- # @return [Array<Any>]
64
- #
65
- # @api private
66
- # @since 0.2.0
67
- attr_reader :parameters
68
-
69
- # @return [Hash<Symbol,Any>]
70
- #
71
- # @api private
72
- # @since 0.2.0
73
- attr_reader :options
74
-
75
- # An array of the required option attribute names (parameters without default values)
76
- #
77
- # @return [Array<Symbol>]
78
- #
79
- # @api private
80
- # @since 0.2.0
81
- def required_options
82
- operation_klass.__options__.each.reject(&:has_default_value?).map(&:name)
83
- end
84
-
85
- # @return [Integer]
86
- #
87
- # @api private
88
- # @since 0.2.0
89
- def required_attributes_count
90
- operation_klass.__params__.size
91
- end
92
-
93
- # @return [void]
94
- #
95
- # @raise [SmartCore::Operation::AttributeError]
96
- # @raise [SmartCore::Operation::ParameterError]
97
- # @raise [SmartCore::Operation::OptionError]
98
- #
99
- # @api private
100
- # @since 0.2.0
101
- def prevent_parameters_incomparability
102
- raise(
103
- SmartCore::Operation::ParameterError,
104
- "Wrong number of parameters " \
105
- "(given #{parameters.size}, expected #{required_attributes_count})"
106
- ) unless parameters.size == required_attributes_count
107
-
108
- missing_options = required_options.reject { |option| options.key?(option) }
109
-
110
- raise(
111
- SmartCore::Operation::OptionError,
112
- "Missing options: :#{missing_options.join(', :')}"
113
- ) unless missing_options.empty?
114
- end
115
-
116
- # @return [void]
117
- #
118
- # @api private
119
- # @since 0.2.0
120
- def initialize_parameters
121
- parameter_names = operation_klass.__params__.map(&:name)
122
- parameter_pairs = Hash[parameter_names.zip(parameters)]
123
-
124
- parameter_pairs.each_pair do |parameter_name, parameter_value|
125
- operation_object.instance_variable_set("@#{parameter_name}", parameter_value)
126
- end
127
- end
128
-
129
- # @return [void]
130
- #
131
- # @api private
132
- # @since 0.2.0
133
- def initialize_options
134
- operation_klass.__options__.each do |option|
135
- option_name = option.name
136
- option_value = options.fetch(option_name) { option.default_value }
137
-
138
- operation_object.instance_variable_set("@#{option_name}", option_value)
139
- end
140
- end
141
-
142
- # @return [void]
143
- #
144
- # @api private
145
- # @since 0.2.0
146
- def call_original_methods
147
- operation_object.send(:initialize, *parameters, **options)
148
- end
39
+ # @since 0.5.0
40
+ attr_reader :operation
149
41
 
150
42
  # @return [void]
151
43
  #
152
44
  # @api private
153
45
  # @since 0.2.0
154
46
  def make_operation_caller_yieldable
155
- operation_object.singleton_class.prepend(Module.new do
47
+ operation.singleton_class.prepend(Module.new do
156
48
  def call
157
49
  super.tap { |result| yield(result) if block_given? }
158
50
  rescue SmartCore::Operation::FatalError => error
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.5.0
5
+ class SmartCore::Operation::State
6
+ # TODO: think about it
7
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.5.0
5
+ class SmartCore::Operation::Step
6
+ # @return [String, Symbol]
7
+ #
8
+ # @api private
9
+ # @since 0.5.0
10
+ attr_reader :method_name
11
+
12
+ # @return [Hash<Symbol,Any>]
13
+ #
14
+ # @api private
15
+ # @since 0.5.0
16
+ attr_reader :options
17
+
18
+ # @param method_name [String, Symbol]
19
+ # @param options [Hash<Symbol,Any>]
20
+ # @return [void]
21
+ #
22
+ # @api private
23
+ # @since 0.5.0
24
+ def initialize(method_name, **options)
25
+ unless method_name.is_a?(Symbol) || method_name.is_a?(String)
26
+ raise(
27
+ SmartCore::Operation::IncorrectStepNameError,
28
+ 'Step name should be a symbol or a string'
29
+ )
30
+ end
31
+
32
+ @method_name = method_name.to_sym
33
+ @options = options # TODO: support for another operations
34
+ end
35
+
36
+ # @return [SmartCore::Operation::Step]
37
+ #
38
+ # @api private
39
+ # @since 0.5.0
40
+ def dup
41
+ self.class.new(method_name, **options)
42
+ end
43
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.5.0
5
+ class SmartCore::Operation::StepSet
6
+ # @since 0.5.0
7
+ include Enumerable
8
+
9
+ # @return [Array<SmartCore:::Operation::Step>]
10
+ #
11
+ # @api private
12
+ # @since 0.5.0
13
+ attr_reader :steps
14
+
15
+ # @return [void]
16
+ #
17
+ # @api private
18
+ # @since 0.5.0
19
+ def initialize
20
+ @steps = []
21
+ @access_lock = Mutex.new
22
+ end
23
+
24
+ # @param step [SmartCore::Operation::Step]
25
+ # @return [void]
26
+ #
27
+ # @api private
28
+ # @since 0.5.0
29
+ def add_step(step)
30
+ thread_safe { steps << step }
31
+ end
32
+ alias_method :<<, :add_step
33
+
34
+ # @param step_set [SmartCore::Operation::Step]
35
+ # @return [void]
36
+ #
37
+ # @api private
38
+ # @sinec 0.5.0
39
+ def concat(step_set)
40
+ thread_safe { steps.concat(step_set.dup.steps) }
41
+ end
42
+
43
+ # @retun [SmartCore::Operation::StepSet]
44
+ #
45
+ # @api private
46
+ # @since 0.5.0
47
+ def dup
48
+ thread_safe do
49
+ self.class.new.tap do |duplicate|
50
+ steps.each { |step| duplicate.add_step(step.dup) }
51
+ end
52
+ end
53
+ end
54
+
55
+ # @return [Enumerable]
56
+ #
57
+ # @api private
58
+ # @since 0.5.0
59
+ def each(&block)
60
+ thread_safe { block_given? ? steps.each(&block) : steps.each }
61
+ end
62
+
63
+ private
64
+
65
+ # @param block [Proc]
66
+ # @return [Any]
67
+ #
68
+ # @api private
69
+ # @since 0.5.0
70
+ def thread_safe(&block)
71
+ @access_lock.synchronize(&block)
72
+ end
73
+ end