teckel 0.2.0 → 0.7.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +111 -0
  3. data/LICENSE_LOGO +4 -0
  4. data/README.md +4 -4
  5. data/lib/teckel.rb +9 -4
  6. data/lib/teckel/chain.rb +31 -341
  7. data/lib/teckel/chain/config.rb +275 -0
  8. data/lib/teckel/chain/result.rb +38 -0
  9. data/lib/teckel/chain/runner.rb +62 -0
  10. data/lib/teckel/chain/step.rb +18 -0
  11. data/lib/teckel/config.rb +25 -28
  12. data/lib/teckel/contracts.rb +19 -0
  13. data/lib/teckel/operation.rb +84 -302
  14. data/lib/teckel/operation/config.rb +396 -0
  15. data/lib/teckel/operation/result.rb +92 -0
  16. data/lib/teckel/operation/runner.rb +74 -0
  17. data/lib/teckel/result.rb +52 -53
  18. data/lib/teckel/version.rb +1 -1
  19. data/spec/chain/around_hook_spec.rb +100 -0
  20. data/spec/chain/default_settings_spec.rb +39 -0
  21. data/spec/chain/inheritance_spec.rb +116 -0
  22. data/spec/chain/none_input_spec.rb +36 -0
  23. data/spec/chain/results_spec.rb +53 -0
  24. data/spec/chain_spec.rb +180 -0
  25. data/spec/config_spec.rb +26 -0
  26. data/spec/doctest_helper.rb +8 -0
  27. data/spec/operation/contract_trace_spec.rb +116 -0
  28. data/spec/operation/default_settings_spec.rb +94 -0
  29. data/spec/operation/fail_on_input_spec.rb +103 -0
  30. data/spec/operation/inheritance_spec.rb +94 -0
  31. data/spec/operation/result_spec.rb +55 -0
  32. data/spec/operation/results_spec.rb +117 -0
  33. data/spec/operation_spec.rb +483 -0
  34. data/spec/rb27/pattern_matching_spec.rb +193 -0
  35. data/spec/result_spec.rb +22 -0
  36. data/spec/spec_helper.rb +28 -0
  37. data/spec/support/dry_base.rb +8 -0
  38. data/spec/support/fake_db.rb +12 -0
  39. data/spec/support/fake_models.rb +20 -0
  40. data/spec/teckel_spec.rb +7 -0
  41. metadata +68 -28
  42. data/.codeclimate.yml +0 -3
  43. data/.github/workflows/ci.yml +0 -92
  44. data/.github/workflows/pages.yml +0 -50
  45. data/.gitignore +0 -15
  46. data/.rspec +0 -3
  47. data/.rubocop.yml +0 -12
  48. data/.ruby-version +0 -1
  49. data/DEVELOPMENT.md +0 -32
  50. data/Gemfile +0 -16
  51. data/Rakefile +0 -35
  52. data/bin/console +0 -15
  53. data/bin/rake +0 -29
  54. data/bin/rspec +0 -29
  55. data/bin/rubocop +0 -18
  56. data/bin/setup +0 -8
  57. data/lib/teckel/none.rb +0 -18
  58. data/lib/teckel/operation/results.rb +0 -72
  59. data/teckel.gemspec +0 -32
@@ -0,0 +1,193 @@
1
+ # frozen_string_literal: true
2
+ @warning = Warning[:experimental]
3
+ Warning[:experimental] = false
4
+
5
+ require 'support/dry_base'
6
+ require 'support/fake_models'
7
+
8
+ RSpec.describe "Ruby 2.7 pattern matches for Result and Chain" do
9
+ module TeckelChainPatternMatchingTest
10
+ class CreateUser
11
+ include ::Teckel::Operation
12
+
13
+ result!
14
+
15
+ input Types::Hash.schema(name: Types::String, age: Types::Coercible::Integer.optional)
16
+ output Types.Instance(User)
17
+ error Types::Hash.schema(message: Types::String, errors: Types::Array.of(Types::Hash))
18
+
19
+ def call(input)
20
+ user = User.new(name: input[:name], age: input[:age])
21
+ if user.save
22
+ success!(user)
23
+ else
24
+ fail!(message: "Could not save User", errors: user.errors)
25
+ end
26
+ end
27
+ end
28
+
29
+ class LogUser
30
+ include ::Teckel::Operation
31
+
32
+ result!
33
+
34
+ input Types.Instance(User)
35
+ error none
36
+ output input
37
+
38
+ def call(usr)
39
+ Logger.new(File::NULL).info("User #{usr.name} created")
40
+ success! usr
41
+ end
42
+ end
43
+
44
+ class AddFriend
45
+ include ::Teckel::Operation
46
+
47
+ result!
48
+
49
+ settings Struct.new(:fail_befriend)
50
+
51
+ input Types.Instance(User)
52
+ output Types::Hash.schema(user: Types.Instance(User), friend: Types.Instance(User))
53
+ error Types::Hash.schema(message: Types::String)
54
+
55
+ def call(user)
56
+ if settings&.fail_befriend
57
+ fail!(message: "Did not find a friend.")
58
+ else
59
+ success!(user: user, friend: User.new(name: "A friend", age: 42))
60
+ end
61
+ end
62
+ end
63
+
64
+ class Chain
65
+ include Teckel::Chain
66
+
67
+ step :create, CreateUser
68
+ step :log, LogUser
69
+ step :befriend, AddFriend
70
+ end
71
+ end
72
+
73
+ describe "Operation Result" do
74
+ context "success" do
75
+ specify "pattern matching with keys" do
76
+ x =
77
+ case TeckelChainPatternMatchingTest::AddFriend.call(User.new(name: "bob", age: 23))
78
+ in { success: false, value: value }
79
+ ["Failed", value]
80
+ in { success: true, value: value }
81
+ ["Success result", value]
82
+ else
83
+ raise "Unexpected Result"
84
+ end
85
+
86
+ expect(x).to contain_exactly("Success result", hash_including(:friend, :user))
87
+ end
88
+ end
89
+
90
+ context "failure" do
91
+ specify "pattern matching with keys" do
92
+ result =
93
+ TeckelChainPatternMatchingTest::AddFriend.
94
+ with(befriend: :fail).
95
+ call(User.new(name: "bob", age: 23))
96
+
97
+ x =
98
+ case result
99
+ in { success: false, value: value }
100
+ ["Failed", value]
101
+ in { success: true, value: value }
102
+ ["Success result", value]
103
+ else
104
+ raise "Unexpected Result"
105
+ end
106
+
107
+ expect(x).to contain_exactly("Failed", hash_including(:message))
108
+ end
109
+
110
+ specify "pattern matching array" do
111
+ result =
112
+ TeckelChainPatternMatchingTest::AddFriend.
113
+ with(befriend: :fail).
114
+ call(User.new(name: "bob", age: 23))
115
+
116
+ x =
117
+ case result
118
+ in [false, value]
119
+ ["Failed", value]
120
+ in [true, value]
121
+ ["Success result", value]
122
+ else
123
+ raise "Unexpected Result"
124
+ end
125
+ expect(x).to contain_exactly("Failed", hash_including(:message))
126
+ end
127
+ end
128
+ end
129
+
130
+ describe "Chain" do
131
+ context "success" do
132
+ specify "pattern matching with keys" do
133
+ x =
134
+ case TeckelChainPatternMatchingTest::Chain.call(name: "Bob", age: 23)
135
+ in { success: false, step: :befriend, value: value }
136
+ ["Failed", value]
137
+ in { success: true, value: value }
138
+ ["Success result", value]
139
+ else
140
+ raise "Unexpected Result"
141
+ end
142
+ expect(x).to contain_exactly("Success result", hash_including(:friend, :user))
143
+ end
144
+
145
+ specify "pattern matching array" do
146
+ x =
147
+ case TeckelChainPatternMatchingTest::Chain.call(name: "Bob", age: 23)
148
+ in [false, :befriend, value]
149
+ "Failed in befriend with #{value}"
150
+ in [true, _, value]
151
+ "Success result"
152
+ end
153
+ expect(x).to eq("Success result")
154
+ end
155
+ end
156
+
157
+ context "failure" do
158
+ specify "pattern matching with keys" do
159
+ result =
160
+ TeckelChainPatternMatchingTest::Chain.
161
+ with(befriend: :fail).
162
+ call(name: "bob", age: 23)
163
+
164
+ x =
165
+ case result
166
+ in { success: false, step: :befriend, value: value }
167
+ "Failed in befriend with #{value}"
168
+ in { success: true, value: value }
169
+ "Success result"
170
+ end
171
+ expect(x).to eq("Failed in befriend with #{ {message: "Did not find a friend."} }")
172
+ end
173
+
174
+ specify "pattern matching array" do
175
+ result =
176
+ TeckelChainPatternMatchingTest::Chain.
177
+ with(befriend: :fail).
178
+ call(name: "Bob", age: 23)
179
+
180
+ x =
181
+ case result
182
+ in [false, :befriend, value]
183
+ "Failed in befriend with #{value}"
184
+ in [true, value]
185
+ "Success result"
186
+ end
187
+ expect(x).to eq("Failed in befriend with #{ {message: "Did not find a friend."} }")
188
+ end
189
+ end
190
+ end
191
+ end
192
+
193
+ Warning[:experimental] = @warning
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'support/dry_base'
4
+ require 'support/fake_models'
5
+
6
+ module TeckelResultTest
7
+ class MissingResultImplementation
8
+ include Teckel::Result
9
+ def initialize(value, success); end
10
+ end
11
+ end
12
+
13
+ RSpec.describe Teckel::Result do
14
+ describe "missing initialize" do
15
+ specify "raises NotImplementedError" do
16
+ result = TeckelResultTest::MissingResultImplementation["value", true]
17
+ expect { result.successful? }.to raise_error(NotImplementedError, "Result object does not implement `successful?`")
18
+ expect { result.failure? }.to raise_error(NotImplementedError, "Result object does not implement `successful?`")
19
+ expect { result.value }.to raise_error(NotImplementedError, "Result object does not implement `value`")
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ if ENV['COVERAGE'] == 'true'
5
+ require 'simplecov'
6
+ require 'simplecov_json_formatter'
7
+ SimpleCov.formatter = SimpleCov::Formatter::JSONFormatter
8
+ SimpleCov.start do
9
+ add_filter %r{^/spec/}
10
+ end
11
+ end
12
+
13
+ require "teckel"
14
+ require "teckel/chain"
15
+
16
+ RSpec.configure do |config|
17
+ # Enable flags like --only-failures and --next-failure
18
+ config.example_status_persistence_file_path = ".rspec_status"
19
+
20
+ # Disable RSpec exposing methods globally on `Module` and `main`
21
+ config.disable_monkey_patching!
22
+
23
+ config.formatter = config.files_to_run.size > 1 ? :progress : :documentation
24
+
25
+ config.expect_with :rspec do |c|
26
+ c.syntax = :expect
27
+ end
28
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-types'
4
+ require 'dry-struct'
5
+
6
+ module Types
7
+ include Dry.Types()
8
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FakeDB
4
+ Rollback = Class.new(RuntimeError)
5
+
6
+ def self.transaction
7
+ yield
8
+ rescue Rollback
9
+ # doing rollback ...
10
+ raise
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class User
4
+ def initialize(name:, age:)
5
+ @name, @age = name, age
6
+ end
7
+ attr_reader :name, :age
8
+
9
+ def save
10
+ !underage?
11
+ end
12
+
13
+ def errors
14
+ underage? ? [{ age: "underage" }] : nil
15
+ end
16
+
17
+ def underage?
18
+ @age <= 18
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Teckel do
4
+ it "has a version number" do
5
+ expect(Teckel::VERSION).not_to be nil
6
+ end
7
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: teckel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Schulze
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-01-14 00:00:00.000000000 Z
11
+ date: 2021-01-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -88,39 +88,57 @@ executables: []
88
88
  extensions: []
89
89
  extra_rdoc_files: []
90
90
  files:
91
- - ".codeclimate.yml"
92
- - ".github/workflows/ci.yml"
93
- - ".github/workflows/pages.yml"
94
- - ".gitignore"
95
- - ".rspec"
96
- - ".rubocop.yml"
97
- - ".ruby-version"
98
91
  - ".yardopts"
99
92
  - CHANGELOG.md
100
- - DEVELOPMENT.md
101
- - Gemfile
102
93
  - LICENSE
94
+ - LICENSE_LOGO
103
95
  - README.md
104
- - Rakefile
105
- - bin/console
106
- - bin/rake
107
- - bin/rspec
108
- - bin/rubocop
109
- - bin/setup
110
96
  - lib/teckel.rb
111
97
  - lib/teckel/chain.rb
98
+ - lib/teckel/chain/config.rb
99
+ - lib/teckel/chain/result.rb
100
+ - lib/teckel/chain/runner.rb
101
+ - lib/teckel/chain/step.rb
112
102
  - lib/teckel/config.rb
113
- - lib/teckel/none.rb
103
+ - lib/teckel/contracts.rb
114
104
  - lib/teckel/operation.rb
115
- - lib/teckel/operation/results.rb
105
+ - lib/teckel/operation/config.rb
106
+ - lib/teckel/operation/result.rb
107
+ - lib/teckel/operation/runner.rb
116
108
  - lib/teckel/result.rb
117
109
  - lib/teckel/version.rb
118
- - teckel.gemspec
119
- homepage: https://github.com/dotless-de/teckel
110
+ - spec/chain/around_hook_spec.rb
111
+ - spec/chain/default_settings_spec.rb
112
+ - spec/chain/inheritance_spec.rb
113
+ - spec/chain/none_input_spec.rb
114
+ - spec/chain/results_spec.rb
115
+ - spec/chain_spec.rb
116
+ - spec/config_spec.rb
117
+ - spec/doctest_helper.rb
118
+ - spec/operation/contract_trace_spec.rb
119
+ - spec/operation/default_settings_spec.rb
120
+ - spec/operation/fail_on_input_spec.rb
121
+ - spec/operation/inheritance_spec.rb
122
+ - spec/operation/result_spec.rb
123
+ - spec/operation/results_spec.rb
124
+ - spec/operation_spec.rb
125
+ - spec/rb27/pattern_matching_spec.rb
126
+ - spec/result_spec.rb
127
+ - spec/spec_helper.rb
128
+ - spec/support/dry_base.rb
129
+ - spec/support/fake_db.rb
130
+ - spec/support/fake_models.rb
131
+ - spec/teckel_spec.rb
132
+ homepage: https://github.com/fnordfish/teckel
120
133
  licenses:
121
134
  - Apache-2.0
122
- metadata: {}
123
- post_install_message:
135
+ metadata:
136
+ changelog_uri: https://github.com/fnordfish/teckel/blob/master/CHANGELOG.md
137
+ source_code_uri: https://github.com/fnordfish/teckel
138
+ bug_tracker_uri: https://github.com/fnordfish/teckel/issues
139
+ documentation_uri: https://www.rubydoc.info/gems/teckel/0.7.0
140
+ user_docs_uri: https://fnordfish.github.io/teckel/
141
+ post_install_message:
124
142
  rdoc_options: []
125
143
  require_paths:
126
144
  - lib
@@ -128,15 +146,37 @@ required_ruby_version: !ruby/object:Gem::Requirement
128
146
  requirements:
129
147
  - - ">="
130
148
  - !ruby/object:Gem::Version
131
- version: '0'
149
+ version: 2.4.0
132
150
  required_rubygems_version: !ruby/object:Gem::Requirement
133
151
  requirements:
134
152
  - - ">="
135
153
  - !ruby/object:Gem::Version
136
154
  version: '0'
137
155
  requirements: []
138
- rubygems_version: 3.1.2
139
- signing_key:
156
+ rubygems_version: 3.2.3
157
+ signing_key:
140
158
  specification_version: 4
141
159
  summary: Operations with enforced in/out/err data structures
142
- test_files: []
160
+ test_files:
161
+ - spec/chain/around_hook_spec.rb
162
+ - spec/chain/default_settings_spec.rb
163
+ - spec/chain/inheritance_spec.rb
164
+ - spec/chain/none_input_spec.rb
165
+ - spec/chain/results_spec.rb
166
+ - spec/chain_spec.rb
167
+ - spec/config_spec.rb
168
+ - spec/doctest_helper.rb
169
+ - spec/operation/contract_trace_spec.rb
170
+ - spec/operation/default_settings_spec.rb
171
+ - spec/operation/fail_on_input_spec.rb
172
+ - spec/operation/inheritance_spec.rb
173
+ - spec/operation/result_spec.rb
174
+ - spec/operation/results_spec.rb
175
+ - spec/operation_spec.rb
176
+ - spec/rb27/pattern_matching_spec.rb
177
+ - spec/result_spec.rb
178
+ - spec/spec_helper.rb
179
+ - spec/support/dry_base.rb
180
+ - spec/support/fake_db.rb
181
+ - spec/support/fake_models.rb
182
+ - spec/teckel_spec.rb
@@ -1,3 +0,0 @@
1
- plugins:
2
- rubocop:
3
- enabled: true
@@ -1,92 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- paths:
6
- - .github/workflows/ci.yml
7
- - lib/**
8
- - spec/**
9
- - Gemfile
10
- - "*.gemspec"
11
- - ".rubocop.yml"
12
-
13
- jobs:
14
- rubocop:
15
- runs-on: ubuntu-latest
16
- steps:
17
- - uses: actions/checkout@v1
18
- - name: Set up Ruby
19
- uses: actions/setup-ruby@v1
20
- with:
21
- ruby-version: 2.4.x
22
- - name: Install bundler
23
- run: gem install bundler
24
- - name: Run rubocop
25
- run: bin/rubocop -ESD
26
-
27
- cruby:
28
- runs-on: ubuntu-latest
29
- strategy:
30
- fail-fast: false
31
- matrix:
32
- ruby: ["2.6.x", "2.5.x", "2.4.x"]
33
-
34
- steps:
35
- - uses: actions/checkout@v1
36
- - name: Set up Ruby
37
- uses: actions/setup-ruby@v1
38
- with:
39
- ruby-version: ${{matrix.ruby}}
40
- - name: Bundle install
41
- run: |
42
- gem install bundler
43
- bundle install --jobs 4 --retry 3 --without tools docs benchmarks
44
- - name: Run all tests
45
- run: bundle exec rake
46
-
47
- other-ruby:
48
- runs-on: ubuntu-latest
49
- strategy:
50
- fail-fast: false
51
- matrix:
52
- image: ["jruby:9.2.9", "ruby:2.7"]
53
- include:
54
- - image: "ruby:2.7"
55
- coverage: "true"
56
- container:
57
- image: ${{matrix.image}}
58
-
59
- steps:
60
- - uses: actions/checkout@v1
61
- - name: Install git
62
- run: |
63
- apt-get update
64
- apt-get install -y --no-install-recommends git
65
- - name: Download test reporter
66
- if: "matrix.coverage == 'true'"
67
- run: |
68
- mkdir -p tmp/
69
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./tmp/cc-test-reporter
70
- chmod +x ./tmp/cc-test-reporter
71
- ./tmp/cc-test-reporter before-build
72
- - name: Bundle install
73
- env:
74
- COVERAGE: ${{matrix.coverage}}
75
- run: |
76
- gem install bundler
77
- bundle install --jobs 4 --retry 3 --without tools docs benchmarks
78
- - name: Run all tests
79
- env:
80
- COVERAGE: ${{matrix.coverage}}
81
- run: bundle exec rake
82
- - name: Send coverage results
83
- if: "matrix.coverage == 'true'"
84
- env:
85
- CC_TEST_REPORTER_ID: ${{secrets.CC_TEST_REPORTER_ID}}
86
- GIT_COMMIT_SHA: ${{github.sha}}
87
- GIT_BRANCH: ${{github.ref}}
88
- GIT_COMMITTED_AT: ${{github.event.head_commit.timestamp}}
89
- run: |
90
- GIT_BRANCH=`ruby -e "puts ENV['GITHUB_REF'].split('/', 3).last"` \
91
- GIT_COMMITTED_AT=`ruby -r time -e "puts Time.iso8601(ENV['GIT_COMMITTED_AT']).to_i"` \
92
- ./tmp/cc-test-reporter after-build -t simplecov