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