flows 0.1.0 → 0.2.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +43 -0
  3. data/.mdlrc +1 -0
  4. data/.rubocop.yml +25 -0
  5. data/Gemfile +6 -0
  6. data/Gemfile.lock +80 -25
  7. data/README.md +170 -44
  8. data/bin/benchmark +65 -42
  9. data/bin/examples.rb +37 -1
  10. data/bin/profile_10steps +48 -6
  11. data/docs/.nojekyll +0 -0
  12. data/docs/CNAME +1 -0
  13. data/docs/README.md +197 -0
  14. data/docs/_sidebar.md +26 -0
  15. data/docs/contributing/benchmarks_profiling.md +3 -0
  16. data/docs/contributing/local_development.md +3 -0
  17. data/docs/flow/direct_usage.md +3 -0
  18. data/docs/flow/general_idea.md +3 -0
  19. data/docs/index.html +30 -0
  20. data/docs/operation/basic_usage.md +1 -0
  21. data/docs/operation/inject_steps.md +3 -0
  22. data/docs/operation/lambda_steps.md +3 -0
  23. data/docs/operation/result_shapes.md +3 -0
  24. data/docs/operation/routing_tracks.md +3 -0
  25. data/docs/operation/wrapping_steps.md +3 -0
  26. data/docs/overview/performance.md +336 -0
  27. data/docs/railway/basic_usage.md +232 -0
  28. data/docs/result_objects/basic_usage.md +196 -0
  29. data/docs/result_objects/do_notation.md +139 -0
  30. data/flows.gemspec +2 -0
  31. data/forspell.dict +8 -0
  32. data/lefthook.yml +12 -0
  33. data/lib/flows.rb +2 -0
  34. data/lib/flows/flow.rb +1 -1
  35. data/lib/flows/operation.rb +1 -3
  36. data/lib/flows/operation/builder.rb +2 -2
  37. data/lib/flows/operation/dsl.rb +21 -0
  38. data/lib/flows/railway.rb +48 -0
  39. data/lib/flows/railway/builder.rb +68 -0
  40. data/lib/flows/railway/dsl.rb +28 -0
  41. data/lib/flows/railway/errors.rb +21 -0
  42. data/lib/flows/railway/executor.rb +23 -0
  43. data/lib/flows/result.rb +1 -0
  44. data/lib/flows/result/do.rb +30 -0
  45. data/lib/flows/result_router.rb +1 -1
  46. data/lib/flows/version.rb +1 -1
  47. metadata +59 -3
  48. data/.travis.yml +0 -8
@@ -0,0 +1,48 @@
1
+ require_relative './railway/errors'
2
+
3
+ require_relative './railway/dsl'
4
+ require_relative './railway/builder'
5
+ require_relative './railway/executor'
6
+
7
+ module Flows
8
+ # Railway DSL
9
+ module Railway
10
+ def self.included(mod)
11
+ mod.extend ::Flows::Railway::DSL
12
+ end
13
+
14
+ include ::Flows::Result::Helpers
15
+
16
+ def initialize(method_source: nil, deps: {})
17
+ _flows_do_checks
18
+
19
+ flow = _flows_make_flow(method_source || self, deps)
20
+ @_flows_executor = _flows_make_executor(flow)
21
+ end
22
+
23
+ def call(**params)
24
+ @_flows_executor.call(**params)
25
+ end
26
+
27
+ private
28
+
29
+ def _flows_do_checks
30
+ raise NoStepsError if self.class.steps.empty?
31
+ end
32
+
33
+ def _flows_make_flow(method_source, deps)
34
+ ::Flows::Railway::Builder.new(
35
+ steps: self.class.steps,
36
+ method_source: method_source,
37
+ deps: deps
38
+ ).call
39
+ end
40
+
41
+ def _flows_make_executor(flow)
42
+ ::Flows::Railway::Executor.new(
43
+ flow: flow,
44
+ class_name: self.class.name
45
+ )
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,68 @@
1
+ module Flows
2
+ module Railway
3
+ # Flow builder
4
+ class Builder
5
+ attr_reader :steps, :method_source, :deps
6
+
7
+ def initialize(steps:, method_source:, deps:)
8
+ @method_source = method_source
9
+ @steps = steps
10
+ @deps = deps
11
+ end
12
+
13
+ def call
14
+ resolve_bodies_and_wiring!
15
+
16
+ nodes = build_nodes
17
+ Flows::Flow.new(start_node: nodes.first.name, nodes: nodes)
18
+ end
19
+
20
+ private
21
+
22
+ def resolve_bodies_and_wiring!
23
+ index = 0
24
+
25
+ while index < @steps.length
26
+ current_step = @steps[index]
27
+
28
+ current_step[:next_step] = @steps[index + 1]&.fetch(:name) || :term
29
+ current_step[:body] = current_step[:custom_body] || resolve_body_from_source(current_step[:name])
30
+
31
+ index += 1
32
+ end
33
+ end
34
+
35
+ def resolve_body_from_source(name)
36
+ return @deps[name] if @deps.key?(name)
37
+
38
+ raise(::Flows::Railway::NoStepImplementationError, name) unless @method_source.respond_to?(name)
39
+
40
+ @method_source.method(name)
41
+ end
42
+
43
+ def build_nodes
44
+ @nodes = @steps.map do |step|
45
+ Flows::Node.new(
46
+ name: step[:name],
47
+ body: step[:body],
48
+ preprocessor: method(:node_preprocessor),
49
+ postprocessor: method(:node_postprocessor),
50
+ router: Flows::ResultRouter.new(step[:next_step], :term),
51
+
52
+ meta: { name: step[:name] }
53
+ )
54
+ end
55
+ end
56
+
57
+ def node_preprocessor(input, _context, _meta)
58
+ input.unwrap
59
+ end
60
+
61
+ def node_postprocessor(output, context, meta)
62
+ context[:last_step] = meta[:name]
63
+
64
+ output
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,28 @@
1
+ module Flows
2
+ module Railway
3
+ # DSL methods for Railway
4
+ module DSL
5
+ attr_reader :steps
6
+
7
+ def self.extended(mod, steps = nil)
8
+ mod.instance_variable_set(:@steps, steps || [])
9
+
10
+ mod.class_exec do
11
+ def self.inherited(subclass)
12
+ ::Flows::Railway::DSL.extended(subclass, steps.map(&:dup))
13
+ super
14
+ end
15
+ end
16
+ end
17
+
18
+ include Flows::Result::Helpers
19
+
20
+ def step(name, custom_body = nil)
21
+ @steps << {
22
+ name: name,
23
+ custom_body: custom_body
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ module Flows
2
+ module Railway
3
+ # rubocop:disable Style/Documentation
4
+ class NoStepsError < ::Flows::Error
5
+ def message
6
+ 'No steps defined'
7
+ end
8
+ end
9
+
10
+ class NoStepImplementationError < ::Flows::Error
11
+ def initialize(step_name)
12
+ @step_name = step_name
13
+ end
14
+
15
+ def message
16
+ "Missing step definition: #{@step_name}"
17
+ end
18
+ end
19
+ # rubocop:enable Style/Documentation
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ module Flows
2
+ module Railway
3
+ # Runner for railway steps
4
+ class Executor
5
+ include ::Flows::Result::Helpers
6
+
7
+ def initialize(flow:, class_name:)
8
+ @flow = flow
9
+ @railway_class_name = class_name
10
+ end
11
+
12
+ def call(**params)
13
+ context = {}
14
+ last_result = @flow.call(ok(params), context: context)
15
+
16
+ last_result.meta[:railway] = @railway_class_name
17
+ last_result.meta[:last_step] = context[:last_step]
18
+
19
+ last_result
20
+ end
21
+ end
22
+ end
23
+ end
@@ -13,3 +13,4 @@ require_relative 'result/errors'
13
13
  require_relative 'result/ok'
14
14
  require_relative 'result/err'
15
15
  require_relative 'result/helpers'
16
+ require_relative 'result/do'
@@ -0,0 +1,30 @@
1
+ module Flows
2
+ class Result
3
+ # Do-notation for Result Objects
4
+ module Do
5
+ # DSL for Do-notation
6
+ module DSL
7
+ def do_for(method_name)
8
+ @flows_result_do_module.define_method(method_name) do |*args|
9
+ super(*args) do |*fields, result|
10
+ case result
11
+ when Flows::Result::Ok
12
+ fields.any? ? result.unwrap.values_at(*fields) : result.unwrap
13
+ when Flows::Result::Err then return result
14
+ else raise "Unexpected result: #{result.inspect}. Should be a Flows::Result"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ def self.included(mod)
22
+ patch_mod = Module.new
23
+
24
+ mod.instance_variable_set(:@flows_result_do_module, patch_mod)
25
+ mod.prepend(patch_mod)
26
+ mod.extend(DSL)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,6 +1,6 @@
1
1
  module Flows
2
2
  # Node router for simple case when result must be a `Flows::Result`
3
- # and we don't care about resukt status key
3
+ # and we don't care about result status key
4
4
  class ResultRouter
5
5
  def initialize(success_route, failure_route)
6
6
  @success_route = success_route
@@ -1,3 +1,3 @@
1
1
  module Flows
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.2.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.1.0
4
+ version: 0.2.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: 2019-09-05 00:00:00.000000000 Z
11
+ date: 2019-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: forspell
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.0.8
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.0.8
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rubocop
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +80,20 @@ dependencies:
66
80
  - - ">="
67
81
  - !ruby/object:Gem::Version
68
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-md
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: rubocop-performance
71
99
  requirement: !ruby/object:Gem::Requirement
@@ -213,11 +241,12 @@ executables: []
213
241
  extensions: []
214
242
  extra_rdoc_files: []
215
243
  files:
244
+ - ".github/workflows/build.yml"
216
245
  - ".gitignore"
246
+ - ".mdlrc"
217
247
  - ".rspec"
218
248
  - ".rubocop.yml"
219
249
  - ".ruby-version"
220
- - ".travis.yml"
221
250
  - CODE_OF_CONDUCT.md
222
251
  - Gemfile
223
252
  - Gemfile.lock
@@ -231,7 +260,28 @@ files:
231
260
  - bin/profile_10steps
232
261
  - bin/ruby_benchmarks
233
262
  - bin/setup
263
+ - docs/.nojekyll
264
+ - docs/CNAME
265
+ - docs/README.md
266
+ - docs/_sidebar.md
267
+ - docs/contributing/benchmarks_profiling.md
268
+ - docs/contributing/local_development.md
269
+ - docs/flow/direct_usage.md
270
+ - docs/flow/general_idea.md
271
+ - docs/index.html
272
+ - docs/operation/basic_usage.md
273
+ - docs/operation/inject_steps.md
274
+ - docs/operation/lambda_steps.md
275
+ - docs/operation/result_shapes.md
276
+ - docs/operation/routing_tracks.md
277
+ - docs/operation/wrapping_steps.md
278
+ - docs/overview/performance.md
279
+ - docs/railway/basic_usage.md
280
+ - docs/result_objects/basic_usage.md
281
+ - docs/result_objects/do_notation.md
234
282
  - flows.gemspec
283
+ - forspell.dict
284
+ - lefthook.yml
235
285
  - lib/flows.rb
236
286
  - lib/flows/flow.rb
237
287
  - lib/flows/node.rb
@@ -241,7 +291,13 @@ files:
241
291
  - lib/flows/operation/dsl.rb
242
292
  - lib/flows/operation/errors.rb
243
293
  - lib/flows/operation/executor.rb
294
+ - lib/flows/railway.rb
295
+ - lib/flows/railway/builder.rb
296
+ - lib/flows/railway/dsl.rb
297
+ - lib/flows/railway/errors.rb
298
+ - lib/flows/railway/executor.rb
244
299
  - lib/flows/result.rb
300
+ - lib/flows/result/do.rb
245
301
  - lib/flows/result/err.rb
246
302
  - lib/flows/result/errors.rb
247
303
  - lib/flows/result/helpers.rb
@@ -1,8 +0,0 @@
1
- ---
2
- sudo: false
3
- language: ruby
4
- cache: bundler
5
- rvm:
6
- - 2.6.3
7
- - 2.5.5
8
- before_install: gem install bundler -v 2.0.1