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.
Files changed (166) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/{build.yml → test.yml} +5 -10
  3. data/.gitignore +9 -1
  4. data/.mdlrc +1 -1
  5. data/.reek.yml +54 -0
  6. data/.rubocop.yml +26 -7
  7. data/.rubocop_todo.yml +27 -0
  8. data/.ruby-version +1 -1
  9. data/.yardopts +1 -0
  10. data/CHANGELOG.md +81 -0
  11. data/Gemfile +0 -6
  12. data/README.md +167 -363
  13. data/Rakefile +35 -1
  14. data/bin/.rubocop.yml +5 -0
  15. data/bin/all_the_errors +55 -0
  16. data/bin/benchmark +73 -105
  17. data/bin/benchmark_cli/compare.rb +118 -0
  18. data/bin/benchmark_cli/compare/a_plus_b.rb +22 -0
  19. data/bin/benchmark_cli/compare/base.rb +45 -0
  20. data/bin/benchmark_cli/compare/command.rb +47 -0
  21. data/bin/benchmark_cli/compare/ten_steps.rb +22 -0
  22. data/bin/benchmark_cli/examples.rb +23 -0
  23. data/bin/benchmark_cli/examples/.rubocop.yml +22 -0
  24. data/bin/benchmark_cli/examples/a_plus_b/dry_do.rb +23 -0
  25. data/bin/benchmark_cli/examples/a_plus_b/dry_transaction.rb +17 -0
  26. data/bin/benchmark_cli/examples/a_plus_b/flows_do.rb +22 -0
  27. data/bin/benchmark_cli/examples/a_plus_b/flows_railway.rb +13 -0
  28. data/bin/benchmark_cli/examples/a_plus_b/flows_scp.rb +13 -0
  29. data/bin/benchmark_cli/examples/a_plus_b/flows_scp_mut.rb +13 -0
  30. data/bin/benchmark_cli/examples/a_plus_b/flows_scp_oc.rb +21 -0
  31. data/bin/benchmark_cli/examples/a_plus_b/trailblazer.rb +15 -0
  32. data/bin/benchmark_cli/examples/ten_steps/dry_do.rb +70 -0
  33. data/bin/benchmark_cli/examples/ten_steps/dry_transaction.rb +64 -0
  34. data/bin/benchmark_cli/examples/ten_steps/flows_do.rb +69 -0
  35. data/bin/benchmark_cli/examples/ten_steps/flows_railway.rb +58 -0
  36. data/bin/benchmark_cli/examples/ten_steps/flows_scp.rb +58 -0
  37. data/bin/benchmark_cli/examples/ten_steps/flows_scp_mut.rb +58 -0
  38. data/bin/benchmark_cli/examples/ten_steps/flows_scp_oc.rb +66 -0
  39. data/bin/benchmark_cli/examples/ten_steps/trailblazer.rb +60 -0
  40. data/bin/benchmark_cli/helpers.rb +12 -0
  41. data/bin/benchmark_cli/ruby.rb +15 -0
  42. data/bin/benchmark_cli/ruby/command.rb +38 -0
  43. data/bin/benchmark_cli/ruby/method_exec.rb +71 -0
  44. data/bin/benchmark_cli/ruby/self_class.rb +69 -0
  45. data/bin/benchmark_cli/ruby/structs.rb +90 -0
  46. data/bin/console +1 -0
  47. data/bin/docserver +7 -0
  48. data/bin/errors +138 -0
  49. data/bin/errors_cli/contract_error_demo.rb +49 -0
  50. data/bin/errors_cli/di_error_demo.rb +38 -0
  51. data/bin/errors_cli/flow_error_demo.rb +22 -0
  52. data/bin/errors_cli/flows_router_error_demo.rb +15 -0
  53. data/bin/errors_cli/interface_error_demo.rb +17 -0
  54. data/bin/errors_cli/oc_error_demo.rb +40 -0
  55. data/bin/errors_cli/railway_error_demo.rb +10 -0
  56. data/bin/errors_cli/result_error_demo.rb +13 -0
  57. data/bin/errors_cli/scp_error_demo.rb +17 -0
  58. data/docs/README.md +3 -187
  59. data/docs/_sidebar.md +0 -24
  60. data/docs/index.html +1 -1
  61. data/flows.gemspec +27 -2
  62. data/forspell.dict +9 -0
  63. data/lefthook.yml +9 -0
  64. data/lib/flows.rb +11 -5
  65. data/lib/flows/contract.rb +402 -0
  66. data/lib/flows/contract/array.rb +55 -0
  67. data/lib/flows/contract/case_eq.rb +43 -0
  68. data/lib/flows/contract/compose.rb +77 -0
  69. data/lib/flows/contract/either.rb +53 -0
  70. data/lib/flows/contract/error.rb +24 -0
  71. data/lib/flows/contract/hash.rb +75 -0
  72. data/lib/flows/contract/hash_of.rb +70 -0
  73. data/lib/flows/contract/helpers.rb +22 -0
  74. data/lib/flows/contract/predicate.rb +34 -0
  75. data/lib/flows/contract/transformer.rb +50 -0
  76. data/lib/flows/contract/tuple.rb +70 -0
  77. data/lib/flows/flow.rb +96 -7
  78. data/lib/flows/flow/errors.rb +29 -0
  79. data/lib/flows/flow/node.rb +132 -0
  80. data/lib/flows/flow/router.rb +29 -0
  81. data/lib/flows/flow/router/custom.rb +59 -0
  82. data/lib/flows/flow/router/errors.rb +11 -0
  83. data/lib/flows/flow/router/simple.rb +25 -0
  84. data/lib/flows/plugin.rb +15 -0
  85. data/lib/flows/plugin/dependency_injector.rb +170 -0
  86. data/lib/flows/plugin/dependency_injector/dependency.rb +24 -0
  87. data/lib/flows/plugin/dependency_injector/dependency_definition.rb +16 -0
  88. data/lib/flows/plugin/dependency_injector/dependency_list.rb +55 -0
  89. data/lib/flows/plugin/dependency_injector/errors.rb +58 -0
  90. data/lib/flows/plugin/implicit_init.rb +45 -0
  91. data/lib/flows/plugin/interface.rb +84 -0
  92. data/lib/flows/plugin/output_contract.rb +85 -0
  93. data/lib/flows/plugin/output_contract/dsl.rb +48 -0
  94. data/lib/flows/plugin/output_contract/errors.rb +74 -0
  95. data/lib/flows/plugin/output_contract/wrapper.rb +55 -0
  96. data/lib/flows/plugin/profiler.rb +114 -0
  97. data/lib/flows/plugin/profiler/injector.rb +35 -0
  98. data/lib/flows/plugin/profiler/report.rb +48 -0
  99. data/lib/flows/plugin/profiler/report/events.rb +43 -0
  100. data/lib/flows/plugin/profiler/report/flat.rb +41 -0
  101. data/lib/flows/plugin/profiler/report/flat/method_report.rb +80 -0
  102. data/lib/flows/plugin/profiler/report/raw.rb +15 -0
  103. data/lib/flows/plugin/profiler/report/tree.rb +98 -0
  104. data/lib/flows/plugin/profiler/report/tree/calculated_node.rb +116 -0
  105. data/lib/flows/plugin/profiler/report/tree/node.rb +34 -0
  106. data/lib/flows/plugin/profiler/wrapper.rb +53 -0
  107. data/lib/flows/railway.rb +140 -34
  108. data/lib/flows/railway/dsl.rb +8 -18
  109. data/lib/flows/railway/errors.rb +8 -12
  110. data/lib/flows/railway/step.rb +24 -0
  111. data/lib/flows/railway/step_list.rb +38 -0
  112. data/lib/flows/result.rb +188 -2
  113. data/lib/flows/result/do.rb +158 -16
  114. data/lib/flows/result/err.rb +12 -6
  115. data/lib/flows/result/errors.rb +29 -17
  116. data/lib/flows/result/helpers.rb +25 -3
  117. data/lib/flows/result/ok.rb +12 -6
  118. data/lib/flows/shared_context_pipeline.rb +342 -0
  119. data/lib/flows/shared_context_pipeline/dsl.rb +12 -0
  120. data/lib/flows/shared_context_pipeline/dsl/callbacks.rb +35 -0
  121. data/lib/flows/shared_context_pipeline/dsl/tracks.rb +52 -0
  122. data/lib/flows/shared_context_pipeline/errors.rb +17 -0
  123. data/lib/flows/shared_context_pipeline/mutation_step.rb +30 -0
  124. data/lib/flows/shared_context_pipeline/router_definition.rb +21 -0
  125. data/lib/flows/shared_context_pipeline/step.rb +55 -0
  126. data/lib/flows/shared_context_pipeline/track.rb +54 -0
  127. data/lib/flows/shared_context_pipeline/track_list.rb +51 -0
  128. data/lib/flows/shared_context_pipeline/wrap.rb +73 -0
  129. data/lib/flows/util.rb +17 -0
  130. data/lib/flows/util/inheritable_singleton_vars.rb +86 -0
  131. data/lib/flows/util/inheritable_singleton_vars/dup_strategy.rb +100 -0
  132. data/lib/flows/util/inheritable_singleton_vars/isolation_strategy.rb +91 -0
  133. data/lib/flows/util/prepend_to_class.rb +191 -0
  134. data/lib/flows/version.rb +1 -1
  135. metadata +253 -38
  136. data/Gemfile.lock +0 -174
  137. data/bin/demo +0 -66
  138. data/bin/examples.rb +0 -195
  139. data/bin/profile_10steps +0 -106
  140. data/bin/ruby_benchmarks +0 -26
  141. data/docs/CNAME +0 -1
  142. data/docs/contributing/benchmarks_profiling.md +0 -3
  143. data/docs/contributing/local_development.md +0 -3
  144. data/docs/flow/direct_usage.md +0 -3
  145. data/docs/flow/general_idea.md +0 -3
  146. data/docs/operation/basic_usage.md +0 -1
  147. data/docs/operation/inject_steps.md +0 -3
  148. data/docs/operation/lambda_steps.md +0 -3
  149. data/docs/operation/result_shapes.md +0 -3
  150. data/docs/operation/routing_tracks.md +0 -3
  151. data/docs/operation/wrapping_steps.md +0 -3
  152. data/docs/overview/performance.md +0 -336
  153. data/docs/railway/basic_usage.md +0 -232
  154. data/docs/result_objects/basic_usage.md +0 -196
  155. data/docs/result_objects/do_notation.md +0 -139
  156. data/lib/flows/node.rb +0 -27
  157. data/lib/flows/operation.rb +0 -52
  158. data/lib/flows/operation/builder.rb +0 -130
  159. data/lib/flows/operation/builder/build_router.rb +0 -37
  160. data/lib/flows/operation/dsl.rb +0 -93
  161. data/lib/flows/operation/errors.rb +0 -75
  162. data/lib/flows/operation/executor.rb +0 -78
  163. data/lib/flows/railway/builder.rb +0 -68
  164. data/lib/flows/railway/executor.rb +0 -23
  165. data/lib/flows/result_router.rb +0 -14
  166. data/lib/flows/router.rb +0 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eed3e3ba742be2b25c1ae3ca7b52d2db3418183e0950616409ef8d05232365de
4
- data.tar.gz: c2f340f63e6e55178426c28fcfe29aa6d6c9af7d9a4b539d43461f068f4a6082
3
+ metadata.gz: dd10b19c7b749fd46c6b8897db3c9912603b4111af7793449d5ff49f69adfcb1
4
+ data.tar.gz: b18271b7321f199e978ebd4f66f2d21e532c20dd37cd61b6b641b00d875c2f4c
5
5
  SHA512:
6
- metadata.gz: 44a6ac9cdcedb674aab92373647582e038602c9459361d1ccbc6502cd78556835042fb81004c2329f1c1a95108371040dddb829fbd11a8eaa20bf310817163f3
7
- data.tar.gz: 333ff1f85e39a794c5e4073e7deeca887562a0568497cd480d75b26338aff242dc8c5d1a8f67ce822083e894beecfe033b7abcffe25e931490de53db7e52cf29
6
+ metadata.gz: 6b3e90d9b76911f1e0a0539bd89eee76a68dcba04f4136858412e4d0e0e1ca51ff8f35027963ef52fdd870f2b74ffe9a326aca4802cd443af2e55f8bf18f67b1
7
+ data.tar.gz: 5a6306a15a6024a1c9412f325ab3454b029abcd7d80defee6a9d5b286e8664981b572fb0ecf6f91d06b98c7d5f3040169fe02aae43b8ee8261fa7b65c469fe54
@@ -1,4 +1,4 @@
1
- name: Build
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
- build:
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: Rubocop
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 rspec
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"
@@ -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
@@ -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.6
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/UncommunicativeMethodParamName:
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
- Lint/UnreachableCode:
54
+ Style/CaseEquality:
42
55
  Exclude:
43
- - 'docs/result_objects/do_notation.md'
56
+ - 'lib/flows/contract/**/*'
57
+
58
+ Naming/RescuedExceptionsVariableName:
59
+ PreferredName: err
44
60
 
45
- Lint/Void:
61
+ Style/Documentation:
46
62
  Exclude:
47
- - 'docs/result_objects/do_notation.md'
63
+ - 'lib/flows/shared_context_pipeline/step.rb' # false positive here
64
+
65
+ Style/SlicingWithRange:
66
+ Enabled: false # to support Ruby 2.5
@@ -0,0 +1,27 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config --auto-gen-only-exclude`
3
+ # on 2020-08-22 07:49:05 UTC using RuboCop version 0.89.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 27
10
+ #
11
+ # *** NON-GENERATED COMMENT ***
12
+ # I'm not sure about enabling this cop
13
+ # * using super may has performance impact
14
+ # * but for some cases it still can be useful
15
+ # Research needed
16
+ Lint/MissingSuper:
17
+ Enabled: false
18
+
19
+ # Offense count: 5
20
+ # Configuration parameters: AllowSubject, Max.
21
+ #
22
+ # *** NON-GENERATED COMMENT ***
23
+ # Might be it's impossible to satisfy this cop or significant refactoring of these tests needed
24
+ RSpec/MultipleMemoizedHelpers:
25
+ Exclude:
26
+ - 'spec/flows/flow/node_spec.rb'
27
+ - 'spec/flows/util/inheritable_singleton_vars/isolation_strategy_spec.rb'
@@ -1 +1 @@
1
- 2.6.2
1
+ 2.6.6
@@ -0,0 +1 @@
1
+ --markup markdown
@@ -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/Build/badge.svg)](https://github.com/ffloyd/flows/actions)
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 [Trailblazer::Operation](http://trailblazer.to/gems/operation/2.0/) and [Dry::Transaction](https://dry-rb.org/gems/dry-transaction/),
9
- but has simpler and flexible DSL for defining operations and matching results. Also `flows` is faster, see [Performance](#performance).
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
- ## Usage
37
+ ## Supported Ruby versions
34
38
 
35
- ### `Flows::Flow`
39
+ CI tests against last patch versions every day:
36
40
 
37
- Low-level instrument for defining execution flows. Used internally as execution engine for `Flows::Operation`.
38
- Check out source code and specs for details.
41
+ * `MRI 2.5.x`
42
+ * `MRI 2.6.x`
39
43
 
40
- ### `Flows::Result`
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
- Result Object implementation. Inspired by [Dry::Monads::Result](https://dry-rb.org/gems/dry-monads/1.0/result/) and
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
- Main concepts & conventions:
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
- * separate classes for successful (`Flows::Result::Ok`) and failure (`Flows::Result::Err`) results
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
- Basic usage:
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
- ```ruby
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
- # get `:a` from result
61
- result_ok.unwrap[:a] # 1
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
- # get error data from result
64
- result_ok.error[:a] # raises exception
68
+ ### [Lefthook](https://github.com/Arkweid/lefthook) as a git hook manager
65
69
 
66
- # get status from result
67
- result_ok.status # :success
70
+ Installation on MacOS via Homebrew:
68
71
 
69
- # boolean flags
70
- result_ok.ok? # true
71
- result_ok.err? # false
72
+ ```sh
73
+ brew install Arkweid/lefthook/lefthook
74
+ ```
72
75
 
73
- # create successful result with data {a: 1, b: 2} and status `:custom`
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
- # get status from result
77
- result_ok_custom.status # :custom
78
+ ```sh
79
+ lefthook install
80
+ ```
78
81
 
79
- # create failure result with data {a: 1, b: 2}
80
- result_err = Flows::Result::Err.new(a: 1, b: 2)
82
+ Run hooks manually:
81
83
 
82
- # get `:a` from result
83
- result_err.unwrap[:a] # raises exception
84
+ ```sh
85
+ lefthook run pre-commit
86
+ lefthook run pre-push
87
+ ```
84
88
 
85
- # get error data from result
86
- result_err.error[:a] # 1
89
+ Please, never turn off the pre-commit and pre-push hooks.
87
90
 
88
- # get status from result
89
- result_err.status # :failure
91
+ ### Rubocop linter
90
92
 
91
- # boolean flags
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
- # create failure result with data {a: 1, b: 2} and status `:custom`
96
- result_err_custom = Flows::Result::Err.new({ a: 1, b: 2 }, status: :custom)
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
- # get status from result
99
- result_err_custom.status # :custom
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
- Mixin `Flows::Result::Helpers` contains tools for simpler generating and matching Result Objects:
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
- ```ruby
105
- include Flows::Result::Helpers
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
- ### `Flows::Operation`
109
+ ### Reek linter
139
110
 
140
- Let's solve simple task using operation:
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
- * given numbers `a` and `b`
143
- * result should contain sum of this numbers
144
- * result should contain square of this sum
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
- ```ruby
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
- #### Result Shapes
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
- You may limit list of exposed fields by defining success and failure shapes. _After_ step definitions use `ok_shape` to define shapes of success result, and `err_shape` to define shapes of failure result. Examples:
125
+ ### Default Rake task and CI
212
126
 
213
- ```ruby
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
- Operation definition should have exact one `ok_shape` DSL-call and zero or one `err_shape` DSL-call. If you want to disable shaping
234
- you can write `no_shape` DSL-call instead of shape definitions.
129
+ * Rubocop
130
+ * Ruby Reek
131
+ * RSpec
132
+ * Spellcheck (forspell)
133
+ * MarkdownLint (mdl)
235
134
 
236
- #### Routing & Tracks
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
- You define side tracks, even nested ones:
138
+ Default Rake task is also executed as a pre-push git hook.
239
139
 
240
- ```ruby
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
- In definition above tracks will not be used because there is no routes to this tracks. You may define routing like this:
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
- ```ruby
258
- # if result is successful and has status :to_some_track - next step will be inner_1
259
- # for any other successful results - outer_2
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
- You also can use less verbose, but shorter form of definition:
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
- ```ruby
279
- step :name,
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
- Step has default routes:
158
+ ### Performance
285
159
 
286
- ```ruby
287
- routes(
288
- when_ok => next_step_name,
289
- when_err => :term
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
- Custom routes have bigger priority than default ones. Moreover, default routes can be overriden.
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
- #### Lambda Steps
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
- You can use lambda for in-place step implementation:
175
+ And to compare performance overhead between different `flows` abstractions
176
+ and another alternatives a benchmarking CLI was done: `bin/benchmark`.
298
177
 
299
- ```ruby
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
- #### Dependency Injection
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
- You can override or inject step implementation on initialization:
183
+ ### Documentation
306
184
 
307
- ```ruby
308
- class Summator
309
- include Flows::Operation
185
+ Each public API method or module **must** be properly documented with examples
186
+ and motivation behind.
310
187
 
311
- step :sum
188
+ To run documentation server locally run `bin/docserver`.
312
189
 
313
- ok_shape :sum
314
- end
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
- summator = Summator.new(deps: {
317
- sum: ->(a:, b:, **) { ok(sum: a + b) }
318
- })
193
+ ### Documentation Driven Development
319
194
 
320
- summator.call(a: 1, b: 2).unwrap[:sum] # 3
321
- ```
195
+ When you about to do some work, the following guideline can lead to the best
196
+ results:
322
197
 
323
- #### Wrapping steps
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
- You can wrap several steps with some logic:
204
+ Yes, it's TDD approach with documentation step prepended.
326
205
 
327
- ```ruby
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
- There is routing limitation when you use wrap:
208
+ Each public API method or module **must** be properly tested. Internal modules
209
+ can be tested indirectly through public API.
343
210
 
344
- * outside `wrap` block you may route to wrapped block by wrapper name (`:wrapper` in the provided example)
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
- ## Performance
213
+ ### Commit naming
349
214
 
350
- You can compare performance for some cases by executing `bin/benchmark`. Examples for benchmark are presented in `bin/examples.rb`.
215
+ You **must** follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
351
216
 
352
- `Flows::Operation` and `Dry::Trancation` may be executed in two ways:
217
+ Allowed prefixes since `v0.4.0`:
353
218
 
354
- _Build once:_ when we create operation instance once (build operation):
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
- ```ruby
357
- operation = OperationClass.new
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
- 10_000.times { operation.call }
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
- _Build each time:_ when we create operation instance each execution:
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
- ```ruby
365
- 10_000.times { OperationClass.new.call }
366
- ```
238
+ ### Changelog
367
239
 
368
- `flows` and `dry` are much faster in _build once_ way of using. Note that Trailblazer gives you only one way to execute operation.
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
- ## Development
243
+ If you adding something - provide some lines to the unreleased section of the `CHANGELOG.md`.
244
+
245
+ ### Versioning
450
246
 
451
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
247
+ The project strictly follows [SemVer](https://semver.org/spec/v2.0.0.html).
452
248
 
453
- To install this gem onto your local machine, run `bundle exec rake install`.
249
+ After `v1.0.0` even smallest backward incompatible change will bump major
250
+ version. _No exceptions._
454
251
 
455
- ## Contributing
252
+ Commit with a version bump should contain _only_ version bump and CHANGELOG.md update.
456
253
 
457
- Bug reports and pull requests are welcome on GitHub at [ffloyd/fflows](https://github.com/ffloyd/flows). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
254
+ ### GitHub Flow
458
255
 
459
- ## License
256
+ Since `v0.4.0` this repo strictly follow [GitHub
257
+ Flow](https://guides.github.com/introduction/flow/) with some additions:
460
258
 
461
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
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
- ## Code of Conduct
264
+ ### Planned features for v1.0.0
464
265
 
465
- Everyone interacting in the Flows project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/ffloyd/flows/blob/master/CODE_OF_CONDUCT.md).
266
+ * validation framework
267
+ * error reporting improvements
268
+ * various plugins for SCP (tracing, benchmarking, logging, etc)
269
+ * site with guides and conventions