flows 0.5.1 → 0.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e6f029a3e03c0cd3ac484cb703f67a1c4910a10c8395e7b1024518dc84a14cb5
4
- data.tar.gz: 23f44992d336cd637c02ff6d96c82a99f7e6af2a2affc82bcdc2c3f4ee1153a2
3
+ metadata.gz: dd10b19c7b749fd46c6b8897db3c9912603b4111af7793449d5ff49f69adfcb1
4
+ data.tar.gz: b18271b7321f199e978ebd4f66f2d21e532c20dd37cd61b6b641b00d875c2f4c
5
5
  SHA512:
6
- metadata.gz: 17015a5eb9868ced9c18872706625df9aeb51aa90717be36929fe223006b5e28fa439fcc63db49b43a047c0375b3d6493f4640da6164be21815b3df95291ae96
7
- data.tar.gz: b2170ec9d8a710b70fd676772ba7a5556ab23b9a50313bf34f321afb440f07d9fe66bc76634a9e53f02dd7063ebf92e27a94e162e6470e2b22a87703a6856f48
6
+ metadata.gz: 6b3e90d9b76911f1e0a0539bd89eee76a68dcba04f4136858412e4d0e0e1ca51ff8f35027963ef52fdd870f2b74ffe9a326aca4802cd443af2e55f8bf18f67b1
7
+ data.tar.gz: 5a6306a15a6024a1c9412f325ab3454b029abcd7d80defee6a9d5b286e8664981b572fb0ecf6f91d06b98c7d5f3040169fe02aae43b8ee8261fa7b65c469fe54
@@ -1,10 +1,12 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
1
3
  require:
2
4
  - rubocop-rspec
3
5
  - rubocop-performance
4
6
  - rubocop-md
5
7
 
6
8
  AllCops:
7
- TargetRubyVersion: 2.7
9
+ TargetRubyVersion: 2.5
8
10
  NewCops: enable
9
11
 
10
12
  Style/HashEachMethods:
@@ -0,0 +1,27 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config --auto-gen-only-exclude`
3
+ # on 2020-08-22 07:49:05 UTC using RuboCop version 0.89.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 27
10
+ #
11
+ # *** NON-GENERATED COMMENT ***
12
+ # I'm not sure about enabling this cop
13
+ # * using super may has performance impact
14
+ # * but for some cases it still can be useful
15
+ # Research needed
16
+ Lint/MissingSuper:
17
+ Enabled: false
18
+
19
+ # Offense count: 5
20
+ # Configuration parameters: AllowSubject, Max.
21
+ #
22
+ # *** NON-GENERATED COMMENT ***
23
+ # Might be it's impossible to satisfy this cop or significant refactoring of these tests needed
24
+ RSpec/MultipleMemoizedHelpers:
25
+ Exclude:
26
+ - 'spec/flows/flow/node_spec.rb'
27
+ - 'spec/flows/util/inheritable_singleton_vars/isolation_strategy_spec.rb'
@@ -1 +1 @@
1
- 2.6.5
1
+ 2.6.6
@@ -16,6 +16,21 @@ Types of changes:
16
16
 
17
17
  ## [Unreleased]
18
18
 
19
+ ## [0.6.0] - 2020-09-23
20
+
21
+ ### Added
22
+
23
+ * `Flows::Plugin::Interface` basic implementation
24
+ * `Flows::SharedContextPipeline` simple sub-pipelines injection
25
+
26
+ ### Changed
27
+
28
+ * `Flows::SharedContextPipeline` callbacks execution method is changed to `instance_exec` (previously was `.call`)
29
+
30
+ ### Fixed
31
+
32
+ * `Flows::Plugin::DependencyInjector` and modules generated by `Flows::Util::InheritableSingletonVars::DupStrategy` now can be safely included several times in the inheritance chain
33
+
19
34
  ## [0.5.1] - 2020-06-29
20
35
 
21
36
  ### Fixed
@@ -59,7 +74,8 @@ Types of changes:
59
74
  * `Flows::Util::PrependToClass` - allows to prepend some module to class even if
60
75
  target module did not included directly into class.
61
76
 
62
- [unreleased]: https://github.com/ffloyd/flows/compare/v0.5.1...HEAD
77
+ [unreleased]: https://github.com/ffloyd/flows/compare/v0.6.0...HEAD
78
+ [0.6.0]: https://github.com/ffloyd/flows/compare/v0.5.1...v0.6.0
63
79
  [0.5.1]: https://github.com/ffloyd/flows/compare/v0.5.0...v0.5.1
64
80
  [0.5.0]: https://github.com/ffloyd/flows/compare/v0.4.0...v0.5.0
65
81
  [0.4.0]: https://github.com/ffloyd/flows/compare/v0.3.0...v0.4.0
@@ -17,3 +17,6 @@ Lint/UselessAssignment:
17
17
 
18
18
  Metrics/MethodLength:
19
19
  Enabled: false
20
+
21
+ Metrics/AbcSize:
22
+ Enabled: false
data/bin/errors CHANGED
@@ -17,6 +17,7 @@ require_relative 'errors_cli/railway_error_demo'
17
17
  require_relative 'errors_cli/result_error_demo'
18
18
  require_relative 'errors_cli/scp_error_demo'
19
19
  require_relative 'errors_cli/flow_error_demo'
20
+ require_relative 'errors_cli/interface_error_demo'
20
21
 
21
22
  class ErrorsCLI
22
23
  extend GLI::App
@@ -30,11 +31,11 @@ class ErrorsCLI
30
31
  ctx.command name do |cmd|
31
32
  cmd.action do |_, _, _|
32
33
  puts title.green
33
- puts(('BEGIN' + '-' * (title.length - 5)).color(:darkgray))
34
+ puts("BEGIN#{'-' * (title.length - 5)}".color(:darkgray))
34
35
  yield
35
36
  rescue StandardError => err
36
37
  puts err.message
37
- puts(('END' + '-' * (title.length - 3)).color(:darkgray))
38
+ puts("END#{'-' * (title.length - 3)}".color(:darkgray))
38
39
  puts
39
40
  end
40
41
  end
@@ -125,6 +126,13 @@ class ErrorsCLI
125
126
  FlowErrorDemo.invalid_node_route
126
127
  end
127
128
  end
129
+
130
+ desc 'Interface errors'
131
+ command :interface do |cmd|
132
+ make_cmd cmd, 'Missing Implementation', :missing_implementation do
133
+ InterfaceErrorDemo.missing_implementation
134
+ end
135
+ end
128
136
  end
129
137
 
130
138
  exit ErrorsCLI.run(ARGV)
@@ -0,0 +1,17 @@
1
+ module InterfaceErrorDemo
2
+ class Parent
3
+ extend Flows::Plugin::Interface
4
+
5
+ defmethod :execute
6
+ defmethod :debug
7
+ end
8
+
9
+ class Child < Parent
10
+ end
11
+
12
+ class << self
13
+ def missing_implementation
14
+ Child.new
15
+ end
16
+ end
17
+ end
@@ -21,6 +21,8 @@ Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
21
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
22
  spec.require_paths = ['lib']
23
23
 
24
+ spec.required_ruby_version = '>= 2.5'
25
+
24
26
  # This library has no production dependencies.
25
27
  # So, it will not block you from updating any dependencies in your project.
26
28
  # So, don't add production dependencies.
@@ -394,9 +394,9 @@ module Flows
394
394
 
395
395
  # :reek:UtilityFunction
396
396
  def merge_nested_errors(description, nested_errors)
397
- shifted = nested_errors.split("\n").map { |str| ' ' + str }.join("\n")
397
+ shifted = nested_errors.split("\n").map { |str| " #{str}" }.join("\n")
398
398
 
399
- description + "\n" + shifted
399
+ "#{description}\n#{shifted}"
400
400
  end
401
401
  end
402
402
  end
@@ -2,8 +2,7 @@ module Flows
2
2
  class Contract
3
3
  # Class for {Type} errors.
4
4
  class Error < ::Flows::Error
5
- attr_reader :value
6
- attr_reader :value_error
5
+ attr_reader :value, :value_error
7
6
 
8
7
  # @param value [Object] checked value
9
8
  # @param value_error [String] error message
@@ -12,3 +12,4 @@ require_relative 'plugin/dependency_injector'
12
12
  require_relative 'plugin/implicit_init'
13
13
  require_relative 'plugin/output_contract'
14
14
  require_relative 'plugin/profiler'
15
+ require_relative 'plugin/interface'
@@ -136,6 +136,17 @@ module Flows
136
136
 
137
137
  InitializerWrapper = Util::PrependToClass.make_module do
138
138
  def initialize(*args, **kwargs, &block) # rubocop:disable Metrics/MethodLength
139
+ if @__dependencies_injected__
140
+ if kwargs.empty? # https://bugs.ruby-lang.org/issues/14415
141
+ super(*args, &block)
142
+ else
143
+ super(*args, **kwargs, &block)
144
+ end
145
+
146
+ return
147
+ end
148
+ @__dependencies_injected__ = true
149
+
139
150
  klass = self.class
140
151
  DependencyList.new(
141
152
  klass: klass,
@@ -5,9 +5,7 @@ module Flows
5
5
  #
6
6
  # @api private
7
7
  class DependencyList
8
- attr_reader :definitions
9
- attr_reader :provided_values
10
- attr_reader :dependencies
8
+ attr_reader :definitions, :provided_values, :dependencies
11
9
 
12
10
  def initialize(klass:, definitions:, provided_values:)
13
11
  @klass = klass
@@ -0,0 +1,84 @@
1
+ module Flows
2
+ module Plugin
3
+ # Class extension to define Java/C#-like interfaces in Ruby.
4
+ #
5
+ # On target class initialization will check defined methods for existence.
6
+ #
7
+ # **Currently interface composition is not supported.** You cannot define
8
+ # 2 interface modules and include it into one class.
9
+ #
10
+ # @example Simple interface
11
+ # class MyAction
12
+ # extend Flows::Plugin::Interface
13
+ #
14
+ # defmethod :perform
15
+ # end
16
+ #
17
+ # class InvalidAction < MyAction; end
18
+ # InvalidAction.new
19
+ # # will raise an error
20
+ #
21
+ # class ValidAction < MyAction
22
+ # def perfrom
23
+ # puts 'Hello!'
24
+ # end
25
+ # end
26
+ # ValidAction.new.perform
27
+ # # => Hello!
28
+ #
29
+ # @example Interface as module
30
+ # module MyBehavior
31
+ # extend Flows::Plugin::Interface
32
+ #
33
+ # defmethod :my_method
34
+ # end
35
+ #
36
+ # class MyImplementation
37
+ # include MyBehaviour
38
+ #
39
+ # def my_method; end
40
+ # end
41
+ module Interface
42
+ # Base error class for interface errors.
43
+ class Error < ::Flows::Error; end
44
+
45
+ # Raised when you're missed some dependency.
46
+ class MissingMethodsError < Error
47
+ def initialize(klass, names)
48
+ @klass = klass
49
+ @names = names
50
+ end
51
+
52
+ def message
53
+ "Methods required by interface for #{@klass} are missing: #{@names.map(&:to_s).join(', ')}"
54
+ end
55
+ end
56
+
57
+ SingletonVarsSetup = Flows::Util::InheritableSingletonVars::DupStrategy.make_module(
58
+ '@interface_methods' => {}
59
+ )
60
+
61
+ include SingletonVarsSetup
62
+
63
+ InitializePatch = Flows::Util::PrependToClass.make_module do
64
+ def initialize(*)
65
+ klass = self.class
66
+
67
+ required_methods = klass.instance_variable_get(:@interface_methods).keys
68
+ missing_methods = required_methods - methods
69
+
70
+ raise MissingMethodsError.new(klass, missing_methods) if missing_methods.any?
71
+
72
+ super
73
+ end
74
+ end
75
+
76
+ include InitializePatch
77
+
78
+ def defmethod(method_name)
79
+ method_list = instance_variable_get(:@interface_methods)
80
+ method_list[method_name.to_sym] = { required_by: self }
81
+ end
82
+ end
83
+ end
84
+ end
@@ -39,7 +39,7 @@ module Flows
39
39
  # Disables contract check and transformation for current class and children.
40
40
  #
41
41
  # @param enable [Boolean] if true - contracts are disabled
42
- def skip_output_contract(enable = true)
42
+ def skip_output_contract(enable: true)
43
43
  @skip_output_contract_flag = enable
44
44
  end
45
45
  end
@@ -24,7 +24,7 @@ module Flows
24
24
  end
25
25
 
26
26
  def message
27
- shifted_error = @error.split("\n").map { |str| ' ' + str }.join("\n")
27
+ shifted_error = @error.split("\n").map { |str| " #{str}" }.join("\n")
28
28
 
29
29
  "Output contract for #{@klass} is violated.\n" \
30
30
  "Result:\n" \
@@ -5,8 +5,7 @@ module Flows
5
5
  class Flat < Tree
6
6
  # @api private
7
7
  class MethodReport
8
- attr_reader :root_node
9
- attr_reader :calculated_nodes
8
+ attr_reader :root_node, :calculated_nodes
10
9
 
11
10
  def initialize(root_node, *calculated_nodes)
12
11
  @root_node = root_node
@@ -106,7 +106,7 @@ module Flows
106
106
  children.map { |node| node.to_s(root_node) }
107
107
  .join("\n")
108
108
  .split("\n")
109
- .map { |str| '| ' + str }
109
+ .map { |str| "| #{str}" }
110
110
  end
111
111
  end
112
112
  end
@@ -5,8 +5,7 @@ module Flows
5
5
  class Tree < Report
6
6
  # @api private
7
7
  class Node
8
- attr_reader :subject
9
- attr_reader :executions
8
+ attr_reader :subject, :executions
10
9
 
11
10
  def initialize(subject:)
12
11
  @subject = subject
@@ -1,15 +1,15 @@
1
1
  module Flows
2
2
  class Railway
3
- # @api private
4
- Step = Struct.new(:name, :lambda, :next_step, keyword_init: true) do
5
- NODE_PREPROCESSOR = ->(input, _, _) { [[], input.unwrap] }
3
+ NODE_PREPROCESSOR = ->(input, _, _) { [[], input.unwrap] }
6
4
 
7
- NODE_POSTPROCESSOR = lambda do |output, context, meta|
8
- context[:last_step] = meta[:name]
5
+ NODE_POSTPROCESSOR = lambda do |output, context, meta|
6
+ context[:last_step] = meta[:name]
9
7
 
10
- output
11
- end
8
+ output
9
+ end
12
10
 
11
+ # @api private
12
+ Step = Struct.new(:name, :lambda, :next_step, keyword_init: true) do
13
13
  def to_node(method_source)
14
14
  Flows::Flow::Node.new(
15
15
  body: lambda || method_source.method(name),
@@ -141,6 +141,46 @@ module Flows
141
141
  # # steps implementations here
142
142
  # end
143
143
  #
144
+ # ## Simple injecting of nested pipelines
145
+ #
146
+ # If you provide some object which responds to `#call` instead of step name - this object will be used as a step body.
147
+ #
148
+ # class SubOperation < Flows::SharedContextPipeline
149
+ # step :hello
150
+ #
151
+ # def hello(**)
152
+ # ok(data: 'some data')
153
+ # end
154
+ # end
155
+ #
156
+ # class MainOperation < Flows::SharedContextPipeline
157
+ # step :init
158
+ # step SubOperation
159
+ #
160
+ # def init(**)
161
+ # ok(generated_by_init: true)
162
+ # end
163
+ # end
164
+ #
165
+ # MainOperation.call
166
+ # # => ok(generated_by_init: true, data: 'some data')
167
+ #
168
+ # You can use the same object multiple times in the same pipeline:
169
+ #
170
+ # step SubOperation
171
+ # step SubOperation
172
+ #
173
+ # If you need any input or output processing - refactor such step definition into normal step.
174
+ #
175
+ # This way has disadvantage: you cannot route to a such step because it has no explicit name.
176
+ # To handle this you can use alternative syntax:
177
+ #
178
+ # step :do_something, body: SubOperation
179
+ #
180
+ # Same features can be used with `mut_step`.
181
+ #
182
+ # This feature is primarily intended to simplify refactoring of big pipelines into smaller ones.
183
+ #
144
184
  # ## Wrappers
145
185
  #
146
186
  # Sometimes you have to execute some steps inside SQL-transaction or something like this.
@@ -224,6 +264,9 @@ module Flows
224
264
  #
225
265
  # You may want to have some logic to execute before all steps, or after all, or before each, or after each.
226
266
  # For example to inject generalized execution process logging.
267
+ #
268
+ # These callbacks are executed via `instance_exec` (in the context of instance).
269
+ #
227
270
  # To achieve this you can use callbacks:
228
271
  #
229
272
  # class MySCP < Flows::SharedContextPipeline
@@ -277,10 +320,10 @@ module Flows
277
320
  def call(**data) # rubocop:disable Metrics/MethodLength
278
321
  klass = self.class
279
322
  meta = {}
280
- context = { data: data, meta: meta, class: klass }
323
+ context = { data: data, meta: meta, class: klass, instance: self }
281
324
 
282
325
  klass.before_all_callbacks.each do |callback|
283
- callback.call(klass, data, meta)
326
+ instance_exec(klass, data, meta, &callback)
284
327
  end
285
328
 
286
329
  flow_result = @__flow.call(nil, context: context)
@@ -292,7 +335,7 @@ module Flows
292
335
  )
293
336
 
294
337
  klass.after_all_callbacks.reduce(final_result) do |result, callback|
295
- callback.call(klass, result, data, meta)
338
+ instance_exec(klass, result, data, meta, &callback)
296
339
  end
297
340
  end
298
341
  end
@@ -12,10 +12,7 @@ module Flows
12
12
 
13
13
  include SingletonVarsSetup
14
14
 
15
- attr_reader :before_all_callbacks
16
- attr_reader :after_all_callbacks
17
- attr_reader :before_each_callbacks
18
- attr_reader :after_each_callbacks
15
+ attr_reader :before_all_callbacks, :after_all_callbacks, :before_each_callbacks, :after_each_callbacks
19
16
 
20
17
  def before_all(&callback)
21
18
  before_all_callbacks << callback
@@ -16,15 +16,15 @@ module Flows
16
16
 
17
17
  attr_reader :tracks
18
18
 
19
- def step(name, router_def = DEFAULT_ROUTER_DEF, &lambda)
19
+ def step(name, router_def = DEFAULT_ROUTER_DEF, body: nil)
20
20
  tracks.add_step(
21
- Step.new(name: name, lambda: lambda, router_def: router_def)
21
+ Step.new(name: name, body: body, router_def: router_def)
22
22
  )
23
23
  end
24
24
 
25
- def mut_step(name, router_def = DEFAULT_ROUTER_DEF, &lambda)
25
+ def mut_step(name, router_def = DEFAULT_ROUTER_DEF, body: nil)
26
26
  tracks.add_step(
27
- MutationStep.new(name: name, lambda: lambda, router_def: router_def)
27
+ MutationStep.new(name: name, body: body, router_def: router_def)
28
28
  )
29
29
  end
30
30
 
@@ -8,7 +8,7 @@ module Flows
8
8
  class MutationStep < Step
9
9
  NODE_PREPROCESSOR = lambda do |_input, context, node_meta|
10
10
  context[:class].before_each_callbacks.each do |callback|
11
- callback.call(context[:class], node_meta[:name], context[:data], context[:meta])
11
+ context[:instance].instance_exec(context[:class], node_meta[:name], context[:data], context[:meta], &callback)
12
12
  end
13
13
 
14
14
  [[context[:data]], EMPTY_HASH]
@@ -20,7 +20,8 @@ module Flows
20
20
  else output ? EMPTY_OK : EMPTY_ERR
21
21
  end.tap do |result|
22
22
  context[:class].after_each_callbacks.each do |callback|
23
- callback.call(context[:class], node_meta[:name], result, context[:data], context[:meta])
23
+ context[:instance]
24
+ .instance_exec(context[:class], node_meta[:name], result, context[:data], context[:meta], &callback)
24
25
  end
25
26
  end
26
27
  end
@@ -3,12 +3,22 @@ module Flows
3
3
  EMPTY_ARRAY = [].freeze
4
4
 
5
5
  # @api private
6
- Step = Struct.new(:name, :lambda, :router_def, :next_step, keyword_init: true) do
6
+ Step = Struct.new(:name, :body, :router_def, :next_step, keyword_init: true) do
7
+ # :reek:ManualDispatch
8
+ def initialize(name:, body: nil, **rest)
9
+ if name.respond_to?(:call)
10
+ body = name
11
+ name = "#{body}+Step-Object-ID-#{object_id}"
12
+ end
13
+
14
+ super(name: name, body: body, **rest)
15
+ end
16
+
7
17
  def to_node(pipeline_class)
8
18
  klass = self.class
9
19
 
10
20
  Flows::Flow::Node.new(
11
- body: lambda || pipeline_class.method(name),
21
+ body: body || pipeline_class.method(name),
12
22
  router: router_def.to_router(next_step),
13
23
  meta: { name: name },
14
24
  preprocessor: klass::NODE_PREPROCESSOR,
@@ -21,7 +31,7 @@ module Flows
21
31
  :NODE_PREPROCESSOR,
22
32
  lambda do |_input, context, node_meta|
23
33
  context[:class].before_each_callbacks.each do |callback|
24
- callback.call(context[:class], node_meta[:name], context[:data], context[:meta])
34
+ context[:instance].instance_exec(context[:class], node_meta[:name], context[:data], context[:meta], &callback)
25
35
  end
26
36
 
27
37
  [EMPTY_ARRAY, context[:data]]
@@ -34,7 +44,8 @@ module Flows
34
44
  context[:data].merge!(result.instance_variable_get(:@data))
35
45
 
36
46
  context[:class].after_each_callbacks.each do |callback|
37
- callback.call(context[:class], node_meta[:name], result, context[:data], context[:meta])
47
+ context[:instance]
48
+ .instance_exec(context[:class], node_meta[:name], result, context[:data], context[:meta], &callback)
38
49
  end
39
50
 
40
51
  result
@@ -44,7 +44,7 @@ module Flows
44
44
  def make_track_entry_node
45
45
  MutationStep.new(
46
46
  name: @name,
47
- lambda: proc { true },
47
+ body: proc { true },
48
48
  router_def: TRACK_ENTRY_ROUTER_DEF,
49
49
  next_step: first_step_name
50
50
  ).to_node(nil)
@@ -2,8 +2,7 @@ module Flows
2
2
  class SharedContextPipeline
3
3
  # @api private
4
4
  class Wrap
5
- attr_reader :router_def
6
- attr_reader :tracks_definitions
5
+ attr_reader :router_def, :tracks_definitions
7
6
 
8
7
  # :reek:Attribute
9
8
  attr_accessor :next_step
@@ -24,14 +24,16 @@ module Flows
24
24
 
25
25
  # @api private
26
26
  module Migrator
27
- def self.call(from, to)
28
- parent_var_list = from.instance_variable_get(VAR_LIST_VAR_NAME)
29
- child_var_list = to.instance_variable_get(VAR_LIST_VAR_NAME) || []
27
+ # :reek:TooManyStatements is allowed here because it's impossible to split to smaller methods
28
+ def self.call(src_mod, dst_mod)
29
+ parent_var_list = src_mod.instance_variable_get(VAR_LIST_VAR_NAME)
30
+ child_var_list = dst_mod.instance_variable_get(VAR_LIST_VAR_NAME) || []
31
+ skip_list = parent_var_list & child_var_list
30
32
 
31
- to.instance_variable_set(VAR_LIST_VAR_NAME, child_var_list + parent_var_list)
33
+ dst_mod.instance_variable_set(VAR_LIST_VAR_NAME, (child_var_list + parent_var_list).uniq)
32
34
 
33
- parent_var_list.each do |name|
34
- to.instance_variable_set(name, from.instance_variable_get(name).dup)
35
+ (parent_var_list - skip_list).each do |name|
36
+ dst_mod.instance_variable_set(name, src_mod.instance_variable_get(name).dup)
35
37
  end
36
38
  end
37
39
  end
@@ -138,7 +138,8 @@ module Flows
138
138
  # * you can include `Mod` into `Mod2`, then include `Mod2` into `Mod3` -
139
139
  # desribed behavior works for include chain of any length.
140
140
  #
141
- # Moreover, this behaviour also works with `extend`, not only `include`.
141
+ # Each `include` generates a new prepend. Be careful about this when including
142
+ # generated module several times in the inheritance chain.
142
143
  #
143
144
  # @yield body for module which will be prepended
144
145
  # @return [Module] module to be included or extended into your module
@@ -160,7 +161,8 @@ module Flows
160
161
  mod.singleton_class.prepend(injector)
161
162
  end
162
163
 
163
- def make_injector_mod(module_to_prepend)
164
+ # :reek:TooManyStatements :reek:DuplicateMethodCall
165
+ def make_injector_mod(module_to_prepend) # rubocop:disable Metrics/MethodLength
164
166
  Module.new.tap do |injector|
165
167
  injector.define_method(:included) do |target_mod|
166
168
  if target_mod.class == Class
@@ -171,6 +173,16 @@ module Flows
171
173
 
172
174
  super(target_mod)
173
175
  end
176
+
177
+ injector.define_method(:extended) do |target_mod|
178
+ if target_mod.class == Class
179
+ target_mod.prepend(module_to_prepend)
180
+ else # Module
181
+ target_mod.singleton_class.prepend injector
182
+ end
183
+
184
+ super(target_mod)
185
+ end
174
186
  end
175
187
  end
176
188
  end
@@ -1,3 +1,3 @@
1
1
  module Flows
2
- VERSION = '0.5.1'.freeze
2
+ VERSION = '0.6.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flows
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roman Kolesnev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-06-29 00:00:00.000000000 Z
11
+ date: 2020-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -387,6 +387,7 @@ files:
387
387
  - ".reek.yml"
388
388
  - ".rspec"
389
389
  - ".rubocop.yml"
390
+ - ".rubocop_todo.yml"
390
391
  - ".ruby-version"
391
392
  - ".yardopts"
392
393
  - CHANGELOG.md
@@ -434,6 +435,7 @@ files:
434
435
  - bin/errors_cli/di_error_demo.rb
435
436
  - bin/errors_cli/flow_error_demo.rb
436
437
  - bin/errors_cli/flows_router_error_demo.rb
438
+ - bin/errors_cli/interface_error_demo.rb
437
439
  - bin/errors_cli/oc_error_demo.rb
438
440
  - bin/errors_cli/railway_error_demo.rb
439
441
  - bin/errors_cli/result_error_demo.rb
@@ -473,6 +475,7 @@ files:
473
475
  - lib/flows/plugin/dependency_injector/dependency_list.rb
474
476
  - lib/flows/plugin/dependency_injector/errors.rb
475
477
  - lib/flows/plugin/implicit_init.rb
478
+ - lib/flows/plugin/interface.rb
476
479
  - lib/flows/plugin/output_contract.rb
477
480
  - lib/flows/plugin/output_contract/dsl.rb
478
481
  - lib/flows/plugin/output_contract/errors.rb
@@ -529,7 +532,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
529
532
  requirements:
530
533
  - - ">="
531
534
  - !ruby/object:Gem::Version
532
- version: '0'
535
+ version: '2.5'
533
536
  required_rubygems_version: !ruby/object:Gem::Requirement
534
537
  requirements:
535
538
  - - ">="