flows 0.2.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Build Status](https://github.com/ffloyd/flows/workflows/
|
3
|
+
[![Build Status](https://github.com/ffloyd/flows/workflows/Test/badge.svg)](https://github.com/ffloyd/flows/actions)
|
4
4
|
[![codecov](https://codecov.io/gh/ffloyd/flows/branch/master/graph/badge.svg)](https://codecov.io/gh/ffloyd/flows)
|
5
5
|
[![Gem Version](https://badge.fury.io/rb/flows.svg)](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
|