flows 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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