flows 0.2.0 → 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 +4 -4
- data/.github/workflows/{build.yml → test.yml} +5 -10
- data/.gitignore +9 -1
- data/.mdlrc +1 -1
- data/.reek.yml +54 -0
- data/.rubocop.yml +26 -7
- data/.rubocop_todo.yml +27 -0
- data/.ruby-version +1 -1
- data/.yardopts +1 -0
- data/CHANGELOG.md +81 -0
- data/Gemfile +0 -6
- data/README.md +167 -363
- data/Rakefile +35 -1
- data/bin/.rubocop.yml +5 -0
- data/bin/all_the_errors +55 -0
- data/bin/benchmark +73 -105
- data/bin/benchmark_cli/compare.rb +118 -0
- data/bin/benchmark_cli/compare/a_plus_b.rb +22 -0
- data/bin/benchmark_cli/compare/base.rb +45 -0
- data/bin/benchmark_cli/compare/command.rb +47 -0
- data/bin/benchmark_cli/compare/ten_steps.rb +22 -0
- data/bin/benchmark_cli/examples.rb +23 -0
- data/bin/benchmark_cli/examples/.rubocop.yml +22 -0
- data/bin/benchmark_cli/examples/a_plus_b/dry_do.rb +23 -0
- data/bin/benchmark_cli/examples/a_plus_b/dry_transaction.rb +17 -0
- data/bin/benchmark_cli/examples/a_plus_b/flows_do.rb +22 -0
- data/bin/benchmark_cli/examples/a_plus_b/flows_railway.rb +13 -0
- data/bin/benchmark_cli/examples/a_plus_b/flows_scp.rb +13 -0
- data/bin/benchmark_cli/examples/a_plus_b/flows_scp_mut.rb +13 -0
- data/bin/benchmark_cli/examples/a_plus_b/flows_scp_oc.rb +21 -0
- data/bin/benchmark_cli/examples/a_plus_b/trailblazer.rb +15 -0
- data/bin/benchmark_cli/examples/ten_steps/dry_do.rb +70 -0
- data/bin/benchmark_cli/examples/ten_steps/dry_transaction.rb +64 -0
- data/bin/benchmark_cli/examples/ten_steps/flows_do.rb +69 -0
- data/bin/benchmark_cli/examples/ten_steps/flows_railway.rb +58 -0
- data/bin/benchmark_cli/examples/ten_steps/flows_scp.rb +58 -0
- data/bin/benchmark_cli/examples/ten_steps/flows_scp_mut.rb +58 -0
- data/bin/benchmark_cli/examples/ten_steps/flows_scp_oc.rb +66 -0
- data/bin/benchmark_cli/examples/ten_steps/trailblazer.rb +60 -0
- data/bin/benchmark_cli/helpers.rb +12 -0
- data/bin/benchmark_cli/ruby.rb +15 -0
- data/bin/benchmark_cli/ruby/command.rb +38 -0
- data/bin/benchmark_cli/ruby/method_exec.rb +71 -0
- data/bin/benchmark_cli/ruby/self_class.rb +69 -0
- data/bin/benchmark_cli/ruby/structs.rb +90 -0
- data/bin/console +1 -0
- data/bin/docserver +7 -0
- data/bin/errors +138 -0
- data/bin/errors_cli/contract_error_demo.rb +49 -0
- data/bin/errors_cli/di_error_demo.rb +38 -0
- data/bin/errors_cli/flow_error_demo.rb +22 -0
- data/bin/errors_cli/flows_router_error_demo.rb +15 -0
- data/bin/errors_cli/interface_error_demo.rb +17 -0
- data/bin/errors_cli/oc_error_demo.rb +40 -0
- data/bin/errors_cli/railway_error_demo.rb +10 -0
- data/bin/errors_cli/result_error_demo.rb +13 -0
- data/bin/errors_cli/scp_error_demo.rb +17 -0
- data/docs/README.md +3 -187
- data/docs/_sidebar.md +0 -24
- data/docs/index.html +1 -1
- data/flows.gemspec +27 -2
- data/forspell.dict +9 -0
- data/lefthook.yml +9 -0
- data/lib/flows.rb +11 -5
- data/lib/flows/contract.rb +402 -0
- data/lib/flows/contract/array.rb +55 -0
- data/lib/flows/contract/case_eq.rb +43 -0
- data/lib/flows/contract/compose.rb +77 -0
- data/lib/flows/contract/either.rb +53 -0
- data/lib/flows/contract/error.rb +24 -0
- data/lib/flows/contract/hash.rb +75 -0
- data/lib/flows/contract/hash_of.rb +70 -0
- data/lib/flows/contract/helpers.rb +22 -0
- data/lib/flows/contract/predicate.rb +34 -0
- data/lib/flows/contract/transformer.rb +50 -0
- data/lib/flows/contract/tuple.rb +70 -0
- data/lib/flows/flow.rb +96 -7
- data/lib/flows/flow/errors.rb +29 -0
- data/lib/flows/flow/node.rb +132 -0
- data/lib/flows/flow/router.rb +29 -0
- data/lib/flows/flow/router/custom.rb +59 -0
- data/lib/flows/flow/router/errors.rb +11 -0
- data/lib/flows/flow/router/simple.rb +25 -0
- data/lib/flows/plugin.rb +15 -0
- data/lib/flows/plugin/dependency_injector.rb +170 -0
- data/lib/flows/plugin/dependency_injector/dependency.rb +24 -0
- data/lib/flows/plugin/dependency_injector/dependency_definition.rb +16 -0
- data/lib/flows/plugin/dependency_injector/dependency_list.rb +55 -0
- data/lib/flows/plugin/dependency_injector/errors.rb +58 -0
- data/lib/flows/plugin/implicit_init.rb +45 -0
- data/lib/flows/plugin/interface.rb +84 -0
- data/lib/flows/plugin/output_contract.rb +85 -0
- data/lib/flows/plugin/output_contract/dsl.rb +48 -0
- data/lib/flows/plugin/output_contract/errors.rb +74 -0
- data/lib/flows/plugin/output_contract/wrapper.rb +55 -0
- data/lib/flows/plugin/profiler.rb +114 -0
- data/lib/flows/plugin/profiler/injector.rb +35 -0
- data/lib/flows/plugin/profiler/report.rb +48 -0
- data/lib/flows/plugin/profiler/report/events.rb +43 -0
- data/lib/flows/plugin/profiler/report/flat.rb +41 -0
- data/lib/flows/plugin/profiler/report/flat/method_report.rb +80 -0
- data/lib/flows/plugin/profiler/report/raw.rb +15 -0
- data/lib/flows/plugin/profiler/report/tree.rb +98 -0
- data/lib/flows/plugin/profiler/report/tree/calculated_node.rb +116 -0
- data/lib/flows/plugin/profiler/report/tree/node.rb +34 -0
- data/lib/flows/plugin/profiler/wrapper.rb +53 -0
- data/lib/flows/railway.rb +140 -34
- data/lib/flows/railway/dsl.rb +8 -18
- data/lib/flows/railway/errors.rb +8 -12
- data/lib/flows/railway/step.rb +24 -0
- data/lib/flows/railway/step_list.rb +38 -0
- data/lib/flows/result.rb +188 -2
- data/lib/flows/result/do.rb +158 -16
- data/lib/flows/result/err.rb +12 -6
- data/lib/flows/result/errors.rb +29 -17
- data/lib/flows/result/helpers.rb +25 -3
- data/lib/flows/result/ok.rb +12 -6
- data/lib/flows/shared_context_pipeline.rb +342 -0
- data/lib/flows/shared_context_pipeline/dsl.rb +12 -0
- data/lib/flows/shared_context_pipeline/dsl/callbacks.rb +35 -0
- data/lib/flows/shared_context_pipeline/dsl/tracks.rb +52 -0
- data/lib/flows/shared_context_pipeline/errors.rb +17 -0
- data/lib/flows/shared_context_pipeline/mutation_step.rb +30 -0
- data/lib/flows/shared_context_pipeline/router_definition.rb +21 -0
- data/lib/flows/shared_context_pipeline/step.rb +55 -0
- data/lib/flows/shared_context_pipeline/track.rb +54 -0
- data/lib/flows/shared_context_pipeline/track_list.rb +51 -0
- data/lib/flows/shared_context_pipeline/wrap.rb +73 -0
- data/lib/flows/util.rb +17 -0
- data/lib/flows/util/inheritable_singleton_vars.rb +86 -0
- data/lib/flows/util/inheritable_singleton_vars/dup_strategy.rb +100 -0
- data/lib/flows/util/inheritable_singleton_vars/isolation_strategy.rb +91 -0
- data/lib/flows/util/prepend_to_class.rb +191 -0
- data/lib/flows/version.rb +1 -1
- metadata +253 -38
- data/Gemfile.lock +0 -174
- data/bin/demo +0 -66
- data/bin/examples.rb +0 -195
- data/bin/profile_10steps +0 -106
- data/bin/ruby_benchmarks +0 -26
- data/docs/CNAME +0 -1
- data/docs/contributing/benchmarks_profiling.md +0 -3
- data/docs/contributing/local_development.md +0 -3
- data/docs/flow/direct_usage.md +0 -3
- data/docs/flow/general_idea.md +0 -3
- data/docs/operation/basic_usage.md +0 -1
- data/docs/operation/inject_steps.md +0 -3
- data/docs/operation/lambda_steps.md +0 -3
- data/docs/operation/result_shapes.md +0 -3
- data/docs/operation/routing_tracks.md +0 -3
- data/docs/operation/wrapping_steps.md +0 -3
- data/docs/overview/performance.md +0 -336
- data/docs/railway/basic_usage.md +0 -232
- data/docs/result_objects/basic_usage.md +0 -196
- data/docs/result_objects/do_notation.md +0 -139
- data/lib/flows/node.rb +0 -27
- data/lib/flows/operation.rb +0 -52
- data/lib/flows/operation/builder.rb +0 -130
- data/lib/flows/operation/builder/build_router.rb +0 -37
- data/lib/flows/operation/dsl.rb +0 -93
- data/lib/flows/operation/errors.rb +0 -75
- data/lib/flows/operation/executor.rb +0 -78
- data/lib/flows/railway/builder.rb +0 -68
- data/lib/flows/railway/executor.rb +0 -23
- data/lib/flows/result_router.rb +0 -14
- data/lib/flows/router.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd10b19c7b749fd46c6b8897db3c9912603b4111af7793449d5ff49f69adfcb1
|
4
|
+
data.tar.gz: b18271b7321f199e978ebd4f66f2d21e532c20dd37cd61b6b641b00d875c2f4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b3e90d9b76911f1e0a0539bd89eee76a68dcba04f4136858412e4d0e0e1ca51ff8f35027963ef52fdd870f2b74ffe9a326aca4802cd443af2e55f8bf18f67b1
|
7
|
+
data.tar.gz: 5a6306a15a6024a1c9412f325ab3454b029abcd7d80defee6a9d5b286e8664981b572fb0ecf6f91d06b98c7d5f3040169fe02aae43b8ee8261fa7b65c469fe54
|
@@ -1,4 +1,4 @@
|
|
1
|
-
name:
|
1
|
+
name: Test
|
2
2
|
|
3
3
|
on:
|
4
4
|
push:
|
@@ -11,7 +11,7 @@ on:
|
|
11
11
|
- cron: 0 2 * * 1-5
|
12
12
|
|
13
13
|
jobs:
|
14
|
-
|
14
|
+
test:
|
15
15
|
runs-on: ubuntu-latest
|
16
16
|
strategy:
|
17
17
|
fail-fast: false
|
@@ -19,6 +19,7 @@ jobs:
|
|
19
19
|
ruby:
|
20
20
|
- 2.5.x
|
21
21
|
- 2.6.x
|
22
|
+
# - 2.7.x
|
22
23
|
steps:
|
23
24
|
- uses: actions/checkout@v1
|
24
25
|
- name: Set up Ruby
|
@@ -31,13 +32,7 @@ jobs:
|
|
31
32
|
run: sudo apt-get install hunspell
|
32
33
|
- name: Install Deps
|
33
34
|
run: bundle install --jobs 4 --retry 3
|
34
|
-
- name:
|
35
|
-
run: bundle exec rubocop
|
36
|
-
- name: RSpec
|
35
|
+
- name: Test
|
37
36
|
env:
|
38
37
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
39
|
-
run: bundle exec
|
40
|
-
- name: Markdown Linter (docs)
|
41
|
-
run: bundle exec mdl docs/**/*.md README.md
|
42
|
-
- name: Spelling Check (docs & code)
|
43
|
-
run: bundle exec forspell
|
38
|
+
run: bundle exec rake
|
data/.gitignore
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
/.bundle/
|
2
|
+
/.vendor/
|
2
3
|
/.yardoc
|
3
4
|
/_yardoc/
|
4
5
|
/coverage/
|
@@ -12,4 +13,11 @@
|
|
12
13
|
|
13
14
|
# profile results folder
|
14
15
|
/profile/
|
15
|
-
!/profile/.keep
|
16
|
+
!/profile/.keep
|
17
|
+
|
18
|
+
# OS specific stuff
|
19
|
+
.DS_Store
|
20
|
+
|
21
|
+
# In case of gem development we don't need to track Gemfile.lock
|
22
|
+
# All the constraints must be expressed in gemspec
|
23
|
+
/Gemfile.lock
|
data/.mdlrc
CHANGED
@@ -1 +1 @@
|
|
1
|
-
rules "~MD013", "~MD033"
|
1
|
+
rules "~MD013", "~MD033", "~MD024"
|
data/.reek.yml
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# default config: https://github.com/troessner/reek/blob/master/docs/defaults.reek.yml
|
2
|
+
# detectors' docs: https://github.com/troessner/reek/tree/master/docs
|
3
|
+
---
|
4
|
+
exclude_paths:
|
5
|
+
- 'bin/'
|
6
|
+
- 'spec/'
|
7
|
+
|
8
|
+
detectors:
|
9
|
+
LongParameterList:
|
10
|
+
enabled: true
|
11
|
+
exclude: []
|
12
|
+
max_params: 4 # +1 to default value
|
13
|
+
overrides:
|
14
|
+
initialize:
|
15
|
+
max_params: 6 # +1 to default value
|
16
|
+
ModuleInitialize:
|
17
|
+
enabled: false
|
18
|
+
TooManyInstanceVariables:
|
19
|
+
enabled: true
|
20
|
+
exclude: []
|
21
|
+
max_instance_variables: 5 # +1 to default value
|
22
|
+
DataClump:
|
23
|
+
exclude:
|
24
|
+
- Flows::Result::Helpers
|
25
|
+
- Flows::SharedContextPipeline::DSL::Tracks
|
26
|
+
IrresponsibleModule:
|
27
|
+
exclude:
|
28
|
+
- Flows::SharedContextPipeline
|
29
|
+
FeatureEnvy:
|
30
|
+
exclude:
|
31
|
+
- Flows::SharedContextPipeline#call
|
32
|
+
- Flows::Contract # too many false positives here
|
33
|
+
- Flows::Plugin::Profiler::Report#events
|
34
|
+
TooManyStatements:
|
35
|
+
exclude:
|
36
|
+
- Flows::SharedContextPipeline#call
|
37
|
+
- Flows::Plugin::Profiler::Wrapper#make_instance_wrapper
|
38
|
+
- Flows::Plugin::Profiler::Wrapper#make_singleton_wrapper
|
39
|
+
- initialize
|
40
|
+
DuplicateMethodCall:
|
41
|
+
exclude:
|
42
|
+
- Flows::SharedContextPipeline#call
|
43
|
+
- Flows::Plugin::Profiler::Wrapper#make_instance_wrapper
|
44
|
+
- Flows::Plugin::Profiler::Wrapper#make_singleton_wrapper
|
45
|
+
- 'length'
|
46
|
+
MissingSafeMethod:
|
47
|
+
exclude:
|
48
|
+
- Flows::Contract
|
49
|
+
BooleanParameter:
|
50
|
+
exclude:
|
51
|
+
- Flows::Plugin::OutputContract::DSL
|
52
|
+
TooManyMethods:
|
53
|
+
exclude:
|
54
|
+
- Flows::Plugin::Profiler::Report::Tree::Node
|
data/.rubocop.yml
CHANGED
@@ -1,12 +1,24 @@
|
|
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.
|
9
|
+
TargetRubyVersion: 2.5
|
10
|
+
NewCops: enable
|
11
|
+
|
12
|
+
Style/HashEachMethods:
|
13
|
+
Enabled: true
|
14
|
+
|
15
|
+
Style/HashTransformKeys:
|
16
|
+
Enabled: true
|
17
|
+
|
18
|
+
Style/HashTransformValues:
|
19
|
+
Enabled: true
|
8
20
|
|
9
|
-
LineLength:
|
21
|
+
Layout/LineLength:
|
10
22
|
Max: 120
|
11
23
|
|
12
24
|
Metrics/BlockLength:
|
@@ -22,9 +34,10 @@ Metrics/ParameterLists:
|
|
22
34
|
Style/WhileUntilModifier:
|
23
35
|
Enabled: false
|
24
36
|
|
25
|
-
Naming/
|
37
|
+
Naming/MethodParameterName:
|
26
38
|
Exclude:
|
27
39
|
- '**/*.md'
|
40
|
+
- 'spec/**/*'
|
28
41
|
|
29
42
|
Style/MixinUsage:
|
30
43
|
Exclude:
|
@@ -38,10 +51,16 @@ Lint/UnusedBlockArgument:
|
|
38
51
|
Exclude:
|
39
52
|
- '**/*.md'
|
40
53
|
|
41
|
-
|
54
|
+
Style/CaseEquality:
|
42
55
|
Exclude:
|
43
|
-
- '
|
56
|
+
- 'lib/flows/contract/**/*'
|
57
|
+
|
58
|
+
Naming/RescuedExceptionsVariableName:
|
59
|
+
PreferredName: err
|
44
60
|
|
45
|
-
|
61
|
+
Style/Documentation:
|
46
62
|
Exclude:
|
47
|
-
- '
|
63
|
+
- 'lib/flows/shared_context_pipeline/step.rb' # false positive here
|
64
|
+
|
65
|
+
Style/SlicingWithRange:
|
66
|
+
Enabled: false # to support Ruby 2.5
|
data/.rubocop_todo.yml
ADDED
@@ -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'
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.6.
|
1
|
+
2.6.6
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
Types of changes:
|
9
|
+
|
10
|
+
* _Added_ - for new features.
|
11
|
+
* _Changed_ - for changes in existing functionality.
|
12
|
+
* _Deprecated_ - for soon-to-be removed features.
|
13
|
+
* _Removed_ - for now removed features.
|
14
|
+
* _Fixed_ -- for any bug fixes.
|
15
|
+
* _Security_ - in case of vulnerabilities.
|
16
|
+
|
17
|
+
## [Unreleased]
|
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
|
+
|
34
|
+
## [0.5.1] - 2020-06-29
|
35
|
+
|
36
|
+
### Fixed
|
37
|
+
|
38
|
+
* `Flows::SharedContextPipeline` wrap DSL failure in case of inheritance, [issue](https://github.com/ffloyd/flows/issues/18)
|
39
|
+
|
40
|
+
## [0.5.0] - 2020-05-18
|
41
|
+
|
42
|
+
### Added
|
43
|
+
|
44
|
+
* `Flows::SharedContextPipeline` wrap DSL, [issue](https://github.com/ffloyd/flows/issues/7)
|
45
|
+
* `Flows::Flow` routing integrity check on initialization
|
46
|
+
* `Flows::Plugin::OutputContract` skip contract DSL method
|
47
|
+
* `Flows::Plugin::Profiler` introduced. Report types: raw, tree and flat.
|
48
|
+
|
49
|
+
### Changed
|
50
|
+
|
51
|
+
* `Flows::SharedContextPipeline` callback API, [issue](https://github.com/ffloyd/flows/issues/6)
|
52
|
+
* `Flows::Util` modules API, [issue](https://github.com/ffloyd/flows/issues/11)
|
53
|
+
* `Flows::Contract::CaseEq` default error expanded to present more context
|
54
|
+
|
55
|
+
## [0.4.0] - 2020-04-21
|
56
|
+
|
57
|
+
### Added
|
58
|
+
|
59
|
+
* `Flows::Contract` - type contracts with specific transformation feature.
|
60
|
+
* `Flows::Flow` - fast and lightweight logic execution engine, designed for
|
61
|
+
internal usage and library writers.
|
62
|
+
* `Flows::Plugin::DependencyInjector` - simple dependency injection plugin for your classes
|
63
|
+
* `Flows::Plugin::ImplicitInit` - allows to use `MyClass.call` instead of
|
64
|
+
`MyClass.new.call`, an class instance will be created once.
|
65
|
+
* `Flows::Plugin::OutputContract` - plugin for output type checks and
|
66
|
+
transformations for service objects which return `Flows::Result`.
|
67
|
+
* `Flows::Railway` - stupid simple implementation of the railway pattern.
|
68
|
+
* `Flows::Result` - general purpose Result Object designed after Rust Result type.
|
69
|
+
* `Flows::Result::Do` - do-notation for Result Objects.
|
70
|
+
* `Flows::SharedContextPipeline` - much more flexible adoption of the railway
|
71
|
+
pattern for Ruby.
|
72
|
+
* `Flows::Util::InheritableSingletonVars` - allows to define behavior for
|
73
|
+
singleton variables in the context of inheritance.
|
74
|
+
* `Flows::Util::PrependToClass` - allows to prepend some module to class even if
|
75
|
+
target module did not included directly into class.
|
76
|
+
|
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
|
79
|
+
[0.5.1]: https://github.com/ffloyd/flows/compare/v0.5.0...v0.5.1
|
80
|
+
[0.5.0]: https://github.com/ffloyd/flows/compare/v0.4.0...v0.5.0
|
81
|
+
[0.4.0]: https://github.com/ffloyd/flows/compare/v0.3.0...v0.4.0
|
data/Gemfile
CHANGED
@@ -2,9 +2,3 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in flows.gemspec
|
4
4
|
gemspec
|
5
|
-
|
6
|
-
# temporary dev dependencies,
|
7
|
-
# move it to gemspec after PRs will be merged into realesed version of a gem
|
8
|
-
group :development do
|
9
|
-
gem 'mdl', github: 'ffloyd/markdownlint', branch: 'update-kramdown-dep'
|
10
|
-
end
|
data/README.md
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
# Flows
|
2
2
|
|
3
|
-
[](https://github.com/ffloyd/flows/actions)
|
4
4
|
[](https://codecov.io/gh/ffloyd/flows)
|
5
5
|
[](https://badge.fury.io/rb/flows)
|
6
6
|
|
7
7
|
Small and fast ruby framework for implementing railway-like operations.
|
8
|
-
By design it is close to
|
9
|
-
|
8
|
+
By design it is close to
|
9
|
+
[Trailblazer::Operation](http://trailblazer.to/gems/operation/2.0/),
|
10
|
+
[Dry::Transaction](https://dry-rb.org/gems/dry-transaction/) and Rust control
|
11
|
+
flow style.
|
12
|
+
Flows has simple and flexible DSL for defining operations and matching results.
|
13
|
+
Also `flows` is faster than Ruby's alternatives.
|
10
14
|
|
11
15
|
`flows` has no production dependencies so it can be used with any framework.
|
12
16
|
|
@@ -15,7 +19,7 @@ but has simpler and flexible DSL for defining operations and matching results. A
|
|
15
19
|
Add this line to your application's Gemfile:
|
16
20
|
|
17
21
|
```ruby
|
18
|
-
gem 'flows'
|
22
|
+
gem 'flows', '~> 0.5'
|
19
23
|
```
|
20
24
|
|
21
25
|
And then execute:
|
@@ -30,436 +34,236 @@ Or install it yourself as:
|
|
30
34
|
gem install flows
|
31
35
|
```
|
32
36
|
|
33
|
-
##
|
37
|
+
## Supported Ruby versions
|
34
38
|
|
35
|
-
|
39
|
+
CI tests against last patch versions every day:
|
36
40
|
|
37
|
-
|
38
|
-
|
41
|
+
* `MRI 2.5.x`
|
42
|
+
* `MRI 2.6.x`
|
39
43
|
|
40
|
-
|
44
|
+
`MRI 2.7.x` will be added later, right now (`2.7.1`) this version of MRI Ruby is too
|
45
|
+
unstable and produce segmentation faults inside RSpec internals.
|
41
46
|
|
42
|
-
|
43
|
-
[Rust Result Objects](https://doc.rust-lang.org/1.30.0/book/2018-edition/ch09-02-recoverable-errors-with-result.html).
|
47
|
+
## Usage & Documentation
|
44
48
|
|
45
|
-
|
49
|
+
* [YARD documentation](https://rubydoc.info/github/ffloyd/flows/master) - this
|
50
|
+
link is for master branch. You can also find YARD documentation for any released
|
51
|
+
version after `v0.4.0`. This documentation has a lot of examples, describes
|
52
|
+
motivation behind each abstraction, but lacks some guides and defined conventions.
|
53
|
+
* [Guides](https://ffloyd.github.io/flows/#/) - guides, conventions, integration
|
54
|
+
and migration notes. Will be done before `v1.0.0` release. Right now is under development.
|
46
55
|
|
47
|
-
|
48
|
-
* both classes has same parent class `Flows::Result`
|
49
|
-
* result data should be a `Hash` with symbol keys and any values
|
50
|
-
* result has a status
|
51
|
-
* default status for successful results is `:success`
|
52
|
-
* default status for failure results is `:failure`
|
56
|
+
## Development
|
53
57
|
|
54
|
-
|
58
|
+
`Flows` is designed to be framework for your business logic. It is a big
|
59
|
+
responsibility. That's why `flows` has near to be sadistic development
|
60
|
+
conventions and linter setup.
|
55
61
|
|
56
|
-
|
57
|
-
# create successful result with data {a: 1, b: 2}
|
58
|
-
result_ok = Flows::Result::Ok.new(a: 1, b: 2)
|
62
|
+
### Anyone can make Flows even better
|
59
63
|
|
60
|
-
|
61
|
-
|
64
|
+
If you see some typos or unclear things in documentation or code - feel free to open
|
65
|
+
an issue. Even if you don't have plans to implement a solution - a problem reporting
|
66
|
+
will help development much. We cannot fix what we don't know.
|
62
67
|
|
63
|
-
|
64
|
-
result_ok.error[:a] # raises exception
|
68
|
+
### [Lefthook](https://github.com/Arkweid/lefthook) as a git hook manager
|
65
69
|
|
66
|
-
|
67
|
-
result_ok.status # :success
|
70
|
+
Installation on MacOS via Homebrew:
|
68
71
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
+
```sh
|
73
|
+
brew install Arkweid/lefthook/lefthook
|
74
|
+
```
|
72
75
|
|
73
|
-
|
74
|
-
result_ok_custom = Flows::Result::Ok.new({ a: 1, b: 2 }, status: :custom)
|
76
|
+
Activation (in the root of the repo):
|
75
77
|
|
76
|
-
|
77
|
-
|
78
|
+
```sh
|
79
|
+
lefthook install
|
80
|
+
```
|
78
81
|
|
79
|
-
|
80
|
-
result_err = Flows::Result::Err.new(a: 1, b: 2)
|
82
|
+
Run hooks manually:
|
81
83
|
|
82
|
-
|
83
|
-
|
84
|
+
```sh
|
85
|
+
lefthook run pre-commit
|
86
|
+
lefthook run pre-push
|
87
|
+
```
|
84
88
|
|
85
|
-
|
86
|
-
result_err.error[:a] # 1
|
89
|
+
Please, never turn off the pre-commit and pre-push hooks.
|
87
90
|
|
88
|
-
|
89
|
-
result_err.status # :failure
|
91
|
+
### Rubocop linter
|
90
92
|
|
91
|
-
|
92
|
-
result_ok.ok? # false
|
93
|
-
result_ok.err? # true
|
93
|
+
[Rubocop](https://docs.rubocop.org/en/stable/) in this setup is responsible for:
|
94
94
|
|
95
|
-
|
96
|
-
|
95
|
+
* defining code style (indentation, etc.)
|
96
|
+
* suggest performance improvements ([rubocop-performance](https://docs.rubocop.org/projects/performance/en/stable/))
|
97
|
+
* forces all that stuff (with some exceptions) to snippets in Markdown files ([rubocop-md](https://github.com/rubocop-hq/rubocop-md))
|
98
|
+
* forces unit-testing best practices ([rubocop-rspec](https://docs.rubocop.org/projects/rspec/en/latest/))
|
97
99
|
|
98
|
-
|
99
|
-
|
100
|
-
```
|
100
|
+
Rubocop config for library and RSpec files should be close to standard one only
|
101
|
+
with minor amount of exceptions.
|
101
102
|
|
102
|
-
|
103
|
+
Code in Markdown snippets and `/bin` folder can ignore more rules. `/bin` folder
|
104
|
+
contains only development-related scripts and tools so it's ok to ease linter requirements.
|
103
105
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
# create successful result with data {a: 1, b: 2}
|
108
|
-
result_ok = ok(a: 1, b: 2)
|
109
|
-
|
110
|
-
# create successful result with data {a: 1, b: 2} and status `:custom`
|
111
|
-
result_ok_custom = ok(:custom, a: 1, b: 2)
|
112
|
-
|
113
|
-
# create failure result with data {a: 1, b: 2}
|
114
|
-
result_err = err(a: 1, b: 2)
|
115
|
-
|
116
|
-
# create failure result with data {a: 1, b: 2} and status `:custom`
|
117
|
-
result_err_custom = err(:custom, a: 1, b: 2)
|
118
|
-
|
119
|
-
# matching helpers
|
120
|
-
result = SomeOperation.new.call
|
121
|
-
|
122
|
-
case result
|
123
|
-
when match_ok(:custom)
|
124
|
-
# matches only successful results with status :custom
|
125
|
-
do_something
|
126
|
-
when match_ok
|
127
|
-
# matches only successful results with any status
|
128
|
-
do_something
|
129
|
-
when match_err(:custom)
|
130
|
-
# matches only failure results with status :custom
|
131
|
-
do_something
|
132
|
-
when match_err
|
133
|
-
# matches only failure results with any status
|
134
|
-
do_something
|
135
|
-
end
|
136
|
-
```
|
106
|
+
Rubocop Metrics (ABC-size, method/class length, etc) must not be eased
|
107
|
+
globally. Never.
|
137
108
|
|
138
|
-
###
|
109
|
+
### Reek linter
|
139
110
|
|
140
|
-
|
111
|
+
[Ruby Reek](https://github.com/troessner/reek) is a very aggressive linter that
|
112
|
+
forces you to do a clean OOP design.
|
141
113
|
|
142
|
-
|
143
|
-
|
144
|
-
|
114
|
+
You will be tempted to just shut up this linter many times. But believe me, in 9
|
115
|
+
of 10 cases it worth to refactor. And after each such refactoring you will
|
116
|
+
understand OOP design better and better.
|
145
117
|
|
146
|
-
|
147
|
-
class Summator
|
148
|
-
# Make this class an operation by including this module.
|
149
|
-
# It adds DSL, initializer and call method.
|
150
|
-
# Also it includes Flows::Result::Helper both on DSL and instance level.
|
151
|
-
include Flows::Operation
|
152
|
-
|
153
|
-
# This is step definitions.
|
154
|
-
# In simplest form step defined by its name and
|
155
|
-
# step implementation expected to be in a method
|
156
|
-
# with same name.
|
157
|
-
#
|
158
|
-
# Steps will be executed in a definition order.
|
159
|
-
step :validate
|
160
|
-
step :calc_sum
|
161
|
-
step :calc_square
|
162
|
-
|
163
|
-
# Which keys of operation data we want to expose on success
|
164
|
-
ok_shape :sum, :sum_square
|
165
|
-
|
166
|
-
# Which keys of operation data we want to expose on failure
|
167
|
-
err_shape :message
|
168
|
-
|
169
|
-
# Step implementation receives execution context as keyword arguments.
|
170
|
-
# For the first step context equals to operation arguments.
|
171
|
-
#
|
172
|
-
# Step implementation must return Result Object.
|
173
|
-
# Result Objects's data will be merged into operation context.
|
174
|
-
#
|
175
|
-
# If result is successful - next step will be executed.
|
176
|
-
# If not - operation terminates and returns failure.
|
177
|
-
def validate(a:, b:, **)
|
178
|
-
err(message: 'a is not a number') unless a.is_a?(Number)
|
179
|
-
err(message: 'b is not a number') unless b.is_a?(Number)
|
180
|
-
|
181
|
-
ok
|
182
|
-
end
|
183
|
-
|
184
|
-
def calc_sum(a:, b:, **)
|
185
|
-
ok(sum: a + b)
|
186
|
-
end
|
187
|
-
|
188
|
-
# We may get data from previous steps because all results' data are merged to context.
|
189
|
-
def calc_square(sum:, **)
|
190
|
-
ok(sum_square: sum * sum)
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
# prepare operation
|
195
|
-
operation = Summator.new
|
196
|
-
|
197
|
-
# execute operation
|
198
|
-
result = operation.call(a: 1, b: 2)
|
199
|
-
|
200
|
-
result.ok? # true
|
201
|
-
result.unwrap # { sum: 3, sum_square: 9 } - only keys from success shape present
|
202
|
-
|
203
|
-
result = operation.call(a: nil, b: nil)
|
204
|
-
|
205
|
-
result.ok? # false
|
206
|
-
result.error # { message: 'a is not a number' } - only keys from error shape present
|
207
|
-
```
|
118
|
+
### Rest of the linters
|
208
119
|
|
209
|
-
|
120
|
+
* [MDL](https://github.com/markdownlint/markdownlint) - for consistent format of Markdown files
|
121
|
+
* [forspell](https://github.com/kkuprikov/forspell) - for spellchecking in comments and markdown files
|
122
|
+
* [inch](http://rrrene.org/inch/) - for documentation coverage suggestions (the
|
123
|
+
only optional linter)
|
210
124
|
|
211
|
-
|
125
|
+
### Default Rake task and CI
|
212
126
|
|
213
|
-
|
214
|
-
# Set exposed keys for :success status of successful result.
|
215
|
-
#
|
216
|
-
# Success result will have shape like { key1: ..., key2: ... }
|
217
|
-
#
|
218
|
-
# If one of keys is missing in the final operation context an exception will be raised.
|
219
|
-
ok_shape :key1, :key2
|
220
|
-
|
221
|
-
# Set different exposed keys for different statuses.
|
222
|
-
#
|
223
|
-
# Operation result status is a status of last executed step result.
|
224
|
-
ok_shape status1: %i[key1 key2],
|
225
|
-
status2: [:key3]
|
226
|
-
|
227
|
-
# Failure shapes defined in the same way:
|
228
|
-
err_shape :key1, :key2
|
229
|
-
err_shape status1: %i[key1 key2],
|
230
|
-
status2: [:key3]
|
231
|
-
```
|
127
|
+
Default rake task (`bundle exec rake`) executes the following checks:
|
232
128
|
|
233
|
-
|
234
|
-
|
129
|
+
* Rubocop
|
130
|
+
* Ruby Reek
|
131
|
+
* RSpec
|
132
|
+
* Spellcheck (forspell)
|
133
|
+
* MarkdownLint (mdl)
|
235
134
|
|
236
|
-
|
135
|
+
CI is also performing default Rake task. So, if you want to reproduce CI error
|
136
|
+
locally - just run `bundle exec rake`.
|
237
137
|
|
238
|
-
|
138
|
+
Default Rake task is also executed as a pre-push git hook.
|
239
139
|
|
240
|
-
|
241
|
-
step :outer_1 # next step is outer_2
|
242
|
-
|
243
|
-
track :some_track do
|
244
|
-
step :inner_1 # next step is inner_2
|
245
|
-
track :inner_track do
|
246
|
-
step :deep_1 # next step is deep_2
|
247
|
-
step :deep_2 # next step is inner_2
|
248
|
-
end
|
249
|
-
step :inner_2 # next step in outer_2
|
250
|
-
end
|
251
|
-
|
252
|
-
step :outer_2
|
253
|
-
```
|
140
|
+
### Error reporting
|
254
141
|
|
255
|
-
|
142
|
+
I hope no one will argue that clear errors makes development noticeably faster.
|
143
|
+
That's why _each_ exception in `flows` should be clear and easy to read.
|
256
144
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
step :outer_1, routes(
|
261
|
-
when_ok(:to_some_track) => :some_track
|
262
|
-
)
|
263
|
-
|
264
|
-
track :some_track do
|
265
|
-
step :inner * 1, routes(when_err => :inner_track) # redirect to inner_track on any failure result
|
266
|
-
track :inner_track do
|
267
|
-
step :deep_1, routes(when_ok(:some_status) => :outer_2) # you may redirect to steps too
|
268
|
-
step :deep_2
|
269
|
-
end
|
270
|
-
step :inner_2
|
271
|
-
end
|
272
|
-
|
273
|
-
step :outer_2
|
274
|
-
```
|
145
|
+
This cannot be tested automatically: you only can test correctness
|
146
|
+
automatically, convenience can only be tested manually. That's why when you
|
147
|
+
introduce any new `raise` you have to:
|
275
148
|
|
276
|
-
|
149
|
+
* make an error message clear and descriptive
|
150
|
+
* add this error to _errors demo CLI_ (`bin/errors`)
|
151
|
+
* add this errors to _all the errors demo_ (`bin/all_the_errors`)
|
152
|
+
* make sure that error is displayed correctly and follows a style of the rest
|
153
|
+
of implemented errors
|
277
154
|
|
278
|
-
|
279
|
-
|
280
|
-
match_ok(:status) => :track_name,
|
281
|
-
match_ok => :track_name
|
282
|
-
```
|
155
|
+
`bin/errors` is done using [GLI](https://davetron5000.github.io/gli/) library,
|
156
|
+
run `bin/errors -h` to explore possibilities.
|
283
157
|
|
284
|
-
|
158
|
+
### Performance
|
285
159
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
```
|
160
|
+
Ruby is slow. Moreover, Ruby is very slow. Yes, again. In the past time we had
|
161
|
+
to compare Ruby with Python. Python was faster and that's why people started to
|
162
|
+
complain about Ruby performance. That was fixed. But is Ruby fast nowadays? No.
|
163
|
+
Because languages like Clojure, Go, Rust, Elixir appeared and in comparison
|
164
|
+
with any of these languages Ruby is very very slow.
|
292
165
|
|
293
|
-
|
166
|
+
That's why you **must** be extra careful with performance. Some business
|
167
|
+
operations can be executed hundreds or even thousands times per request. Each
|
168
|
+
line of code in your abstraction will slow down such request a bit. That's why
|
169
|
+
you should think about each line performance.
|
294
170
|
|
295
|
-
|
171
|
+
Also, it's nearly impossible to make zero-cost abstractions in Ruby. The best
|
172
|
+
thing you can do - to offload calculations to a class loading or initialization
|
173
|
+
step. Sacrifice some warm-up time to make runtime performance better.
|
296
174
|
|
297
|
-
|
175
|
+
And to compare performance overhead between different `flows` abstractions
|
176
|
+
and another alternatives a benchmarking CLI was done: `bin/benchmark`.
|
298
177
|
|
299
|
-
|
300
|
-
step :name, ->(a:, b:, **) { ok(sum: a + b) }
|
301
|
-
```
|
178
|
+
This CLI is done using GLI, run `bin/benchmark -h` to explore possibilities.
|
302
179
|
|
303
|
-
|
180
|
+
So far, `flows` offers the best performance among alternatives. And this CLI
|
181
|
+
is made to simplify comparison with alternatives and keep `flows` the fastest solution.
|
304
182
|
|
305
|
-
|
183
|
+
### Documentation
|
306
184
|
|
307
|
-
|
308
|
-
|
309
|
-
include Flows::Operation
|
185
|
+
Each public API method or module **must** be properly documented with examples
|
186
|
+
and motivation behind.
|
310
187
|
|
311
|
-
|
188
|
+
To run documentation server locally run `bin/docserver`.
|
312
189
|
|
313
|
-
|
314
|
-
|
190
|
+
Respect `@since` YARD documentation tag. When some module, class or method has any
|
191
|
+
API change - you have to provide correct `@since` tag value to the documentation.
|
315
192
|
|
316
|
-
|
317
|
-
sum: ->(a:, b:, **) { ok(sum: a + b) }
|
318
|
-
})
|
193
|
+
### Documentation Driven Development
|
319
194
|
|
320
|
-
|
321
|
-
|
195
|
+
When you about to do some work, the following guideline can lead to the best
|
196
|
+
results:
|
322
197
|
|
323
|
-
|
198
|
+
* first, write needed class and method structure without implementation
|
199
|
+
* write YARD documentation with motivation and usage examples for each public
|
200
|
+
class, method, module.
|
201
|
+
* write unit tests, check that tests are failing
|
202
|
+
* write implementation until tests are green
|
324
203
|
|
325
|
-
|
204
|
+
Yes, it's TDD approach with documentation step prepended.
|
326
205
|
|
327
|
-
|
328
|
-
step :first
|
329
|
-
|
330
|
-
wrap :wrapper do
|
331
|
-
step :wrapped
|
332
|
-
end
|
333
|
-
|
334
|
-
def wrapper(**_context)
|
335
|
-
# do smth
|
336
|
-
result = yield # execute wrapped steps
|
337
|
-
# do smth or modify result
|
338
|
-
result
|
339
|
-
end
|
340
|
-
```
|
206
|
+
### Unit test
|
341
207
|
|
342
|
-
|
208
|
+
Each public API method or module **must** be properly tested. Internal modules
|
209
|
+
can be tested indirectly through public API.
|
343
210
|
|
344
|
-
|
345
|
-
* you may route wrapped steps only to wrapped steps in the same wrap block
|
346
|
-
* you cannot route to wrapped steps from outside
|
211
|
+
Test coverage **must** be higher than 95%.
|
347
212
|
|
348
|
-
|
213
|
+
### Commit naming
|
349
214
|
|
350
|
-
You
|
215
|
+
You **must** follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
|
351
216
|
|
352
|
-
|
217
|
+
Allowed prefixes since `v0.4.0`:
|
353
218
|
|
354
|
-
|
219
|
+
* `feat:` - for new features
|
220
|
+
* `fix:` - for bugfixes
|
221
|
+
* `perf:` - for performance improvements
|
222
|
+
* `refactor:` - for refactoring work
|
223
|
+
* `ci:` - updates for CI configuration
|
224
|
+
* `docs:` - for documentation update
|
355
225
|
|
356
|
-
|
357
|
-
|
226
|
+
Sometimes commit can have several responsibilities. As example: when you write
|
227
|
+
documentation, test and implementation for a feature in the one commit. You can do
|
228
|
+
extra effort to split and rearrange commits to make it atomic. But does it
|
229
|
+
really provide significant value if we already have a strong convention for
|
230
|
+
changelog (see the next section)?
|
358
231
|
|
359
|
-
|
360
|
-
|
232
|
+
So, when you in such situation use the first applicable prefix in the list:
|
233
|
+
between `docs` and `refactor` - pick `refactor`.
|
361
234
|
|
362
|
-
|
235
|
+
Also, there is one more special prefix for release commits. Release commit
|
236
|
+
messages **must** look like: `release: v0.4.0`.
|
363
237
|
|
364
|
-
|
365
|
-
10_000.times { OperationClass.new.call }
|
366
|
-
```
|
238
|
+
### Changelog
|
367
239
|
|
368
|
-
|
369
|
-
|
370
|
-
### Benchmark Results
|
371
|
-
|
372
|
-
Host:
|
373
|
-
|
374
|
-
* MacBook Pro (13-inch, 2017, Four Thunderbolt 3 Ports)
|
375
|
-
* 3.1 GHz Intel Core i5
|
376
|
-
* 8 GB 2133 MHz LPDDR3
|
377
|
-
|
378
|
-
Results:
|
379
|
-
|
380
|
-
```text
|
381
|
-
--------------------------------------------------
|
382
|
-
- task: A + B, one step implementation
|
383
|
-
--------------------------------------------------
|
384
|
-
Warming up --------------------------------------
|
385
|
-
Flows::Operation (build each time)
|
386
|
-
9.147k i/100ms
|
387
|
-
Flows::Operation (build once)
|
388
|
-
25.738k i/100ms
|
389
|
-
Dry::Transaction (build each time)
|
390
|
-
2.294k i/100ms
|
391
|
-
Dry::Transaction (build once)
|
392
|
-
21.836k i/100ms
|
393
|
-
Trailblazer::Operation
|
394
|
-
5.057k i/100ms
|
395
|
-
Calculating -------------------------------------
|
396
|
-
Flows::Operation (build each time)
|
397
|
-
96.095k (± 2.3%) i/s - 484.791k in 5.047684s
|
398
|
-
Flows::Operation (build once)
|
399
|
-
281.248k (± 1.7%) i/s - 1.416M in 5.034728s
|
400
|
-
Dry::Transaction (build each time)
|
401
|
-
23.683k (± 1.7%) i/s - 119.288k in 5.038506s
|
402
|
-
Dry::Transaction (build once)
|
403
|
-
237.379k (± 3.3%) i/s - 1.201M in 5.066073s
|
404
|
-
Trailblazer::Operation
|
405
|
-
52.676k (± 1.5%) i/s - 268.021k in 5.089306s
|
406
|
-
|
407
|
-
Comparison:
|
408
|
-
Flows::Operation (build once): 281248.4 i/s
|
409
|
-
Dry::Transaction (build once): 237378.7 i/s - 1.18x slower
|
410
|
-
Flows::Operation (build each time): 96094.9 i/s - 2.93x slower
|
411
|
-
Trailblazer::Operation: 52676.3 i/s - 5.34x slower
|
412
|
-
Dry::Transaction (build each time): 23682.9 i/s - 11.88x slower
|
413
|
-
|
414
|
-
|
415
|
-
--------------------------------------------------
|
416
|
-
- task: ten steps returns successful result
|
417
|
-
--------------------------------------------------
|
418
|
-
Warming up --------------------------------------
|
419
|
-
Flows::Operation (build each time)
|
420
|
-
1.496k i/100ms
|
421
|
-
Flows::Operation (build once)
|
422
|
-
3.847k i/100ms
|
423
|
-
Dry::Transaction (build each time)
|
424
|
-
274.000 i/100ms
|
425
|
-
Dry::Transaction (build once)
|
426
|
-
2.992k i/100ms
|
427
|
-
Trailblazer::Operation
|
428
|
-
1.082k i/100ms
|
429
|
-
Calculating -------------------------------------
|
430
|
-
Flows::Operation (build each time)
|
431
|
-
15.013k (± 3.8%) i/s - 76.296k in 5.089734s
|
432
|
-
Flows::Operation (build once)
|
433
|
-
39.239k (± 1.6%) i/s - 196.197k in 5.001538s
|
434
|
-
Dry::Transaction (build each time)
|
435
|
-
2.743k (± 3.7%) i/s - 13.700k in 5.002847s
|
436
|
-
Dry::Transaction (build once)
|
437
|
-
30.441k (± 1.8%) i/s - 152.592k in 5.014565s
|
438
|
-
Trailblazer::Operation
|
439
|
-
11.022k (± 1.4%) i/s - 55.182k in 5.007543s
|
440
|
-
|
441
|
-
Comparison:
|
442
|
-
Flows::Operation (build once): 39238.6 i/s
|
443
|
-
Dry::Transaction (build once): 30440.5 i/s - 1.29x slower
|
444
|
-
Flows::Operation (build each time): 15012.7 i/s - 2.61x slower
|
445
|
-
Trailblazer::Operation: 11022.1 i/s - 3.56x slower
|
446
|
-
Dry::Transaction (build each time): 2743.0 i/s - 14.30x slower
|
447
|
-
```
|
240
|
+
Starting from `v0.4.0` [keep a changelog](https://keepachangelog.com/en/1.0.0/)
|
241
|
+
guideline must be met.
|
448
242
|
|
449
|
-
|
243
|
+
If you adding something - provide some lines to the unreleased section of the `CHANGELOG.md`.
|
244
|
+
|
245
|
+
### Versioning
|
450
246
|
|
451
|
-
|
247
|
+
The project strictly follows [SemVer](https://semver.org/spec/v2.0.0.html).
|
452
248
|
|
453
|
-
|
249
|
+
After `v1.0.0` even smallest backward incompatible change will bump major
|
250
|
+
version. _No exceptions._
|
454
251
|
|
455
|
-
|
252
|
+
Commit with a version bump should contain _only_ version bump and CHANGELOG.md update.
|
456
253
|
|
457
|
-
|
254
|
+
### GitHub Flow
|
458
255
|
|
459
|
-
|
256
|
+
Since `v0.4.0` this repo strictly follow [GitHub
|
257
|
+
Flow](https://guides.github.com/introduction/flow/) with some additions:
|
460
258
|
|
461
|
-
|
259
|
+
* branch naming using dash: `improved-contexts`
|
260
|
+
* use [references to
|
261
|
+
issues](https://help.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword)
|
262
|
+
in commit messages and make links to issues in CHANGELOG.md
|
462
263
|
|
463
|
-
|
264
|
+
### Planned features for v1.0.0
|
464
265
|
|
465
|
-
|
266
|
+
* validation framework
|
267
|
+
* error reporting improvements
|
268
|
+
* various plugins for SCP (tracing, benchmarking, logging, etc)
|
269
|
+
* site with guides and conventions
|