teckel 0.3.0 → 0.4.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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +53 -0
  3. data/LICENSE_LOGO +4 -0
  4. data/README.md +4 -4
  5. data/lib/teckel.rb +9 -3
  6. data/lib/teckel/chain.rb +99 -271
  7. data/lib/teckel/chain/result.rb +38 -0
  8. data/lib/teckel/chain/runner.rb +51 -0
  9. data/lib/teckel/chain/step.rb +18 -0
  10. data/lib/teckel/config.rb +1 -23
  11. data/lib/teckel/contracts.rb +19 -0
  12. data/lib/teckel/operation.rb +309 -215
  13. data/lib/teckel/operation/result.rb +92 -0
  14. data/lib/teckel/operation/runner.rb +70 -0
  15. data/lib/teckel/result.rb +52 -53
  16. data/lib/teckel/version.rb +1 -1
  17. data/spec/chain/inheritance_spec.rb +116 -0
  18. data/spec/chain/results_spec.rb +53 -0
  19. data/spec/chain_around_hook_spec.rb +100 -0
  20. data/spec/chain_spec.rb +180 -0
  21. data/spec/config_spec.rb +26 -0
  22. data/spec/doctest_helper.rb +7 -0
  23. data/spec/operation/inheritance_spec.rb +94 -0
  24. data/spec/operation/result_spec.rb +34 -0
  25. data/spec/operation/results_spec.rb +117 -0
  26. data/spec/operation_spec.rb +485 -0
  27. data/spec/rb27/pattern_matching_spec.rb +193 -0
  28. data/spec/result_spec.rb +20 -0
  29. data/spec/spec_helper.rb +25 -0
  30. data/spec/support/dry_base.rb +8 -0
  31. data/spec/support/fake_db.rb +12 -0
  32. data/spec/support/fake_models.rb +20 -0
  33. data/spec/teckel_spec.rb +7 -0
  34. metadata +52 -25
  35. data/.codeclimate.yml +0 -3
  36. data/.github/workflows/ci.yml +0 -92
  37. data/.github/workflows/pages.yml +0 -50
  38. data/.gitignore +0 -15
  39. data/.rspec +0 -3
  40. data/.rubocop.yml +0 -12
  41. data/.ruby-version +0 -1
  42. data/DEVELOPMENT.md +0 -32
  43. data/Gemfile +0 -16
  44. data/Rakefile +0 -35
  45. data/bin/console +0 -15
  46. data/bin/rake +0 -29
  47. data/bin/rspec +0 -29
  48. data/bin/rubocop +0 -18
  49. data/bin/setup +0 -8
  50. data/lib/teckel/none.rb +0 -18
  51. data/lib/teckel/operation/results.rb +0 -72
  52. 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
+ 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
+ { 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,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'support/dry_base'
4
+ require 'support/fake_models'
5
+
6
+ RSpec.describe Teckel::Result do
7
+ describe "missing initialize" do
8
+ class MissingResultImplementation
9
+ include Teckel::Result
10
+ def initialize(value, success); end
11
+ end
12
+
13
+ specify do
14
+ result = MissingResultImplementation["value", true]
15
+ expect { result.successful? }.to raise_error(NotImplementedError)
16
+ expect { result.failure? }.to raise_error(NotImplementedError)
17
+ expect { result.value }.to raise_error(NotImplementedError)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ if ENV['COVERAGE'] == 'true'
5
+ require 'simplecov'
6
+ SimpleCov.start do
7
+ add_filter %r{^/spec/}
8
+ end
9
+ end
10
+
11
+ require "teckel"
12
+
13
+ RSpec.configure do |config|
14
+ # Enable flags like --only-failures and --next-failure
15
+ config.example_status_persistence_file_path = ".rspec_status"
16
+
17
+ # Disable RSpec exposing methods globally on `Module` and `main`
18
+ config.disable_monkey_patching!
19
+
20
+ config.formatter = config.files_to_run.size > 1 ? :progress : :documentation
21
+
22
+ config.expect_with :rspec do |c|
23
+ c.syntax = :expect
24
+ end
25
+ 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.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Schulze
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-01-15 00:00:00.000000000 Z
11
+ date: 2020-02-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -88,38 +88,48 @@ 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/result.rb
99
+ - lib/teckel/chain/runner.rb
100
+ - lib/teckel/chain/step.rb
112
101
  - lib/teckel/config.rb
113
- - lib/teckel/none.rb
102
+ - lib/teckel/contracts.rb
114
103
  - lib/teckel/operation.rb
115
- - lib/teckel/operation/results.rb
104
+ - lib/teckel/operation/result.rb
105
+ - lib/teckel/operation/runner.rb
116
106
  - lib/teckel/result.rb
117
107
  - lib/teckel/version.rb
118
- - teckel.gemspec
119
- homepage: https://github.com/dotless-de/teckel
108
+ - spec/chain/inheritance_spec.rb
109
+ - spec/chain/results_spec.rb
110
+ - spec/chain_around_hook_spec.rb
111
+ - spec/chain_spec.rb
112
+ - spec/config_spec.rb
113
+ - spec/doctest_helper.rb
114
+ - spec/operation/inheritance_spec.rb
115
+ - spec/operation/result_spec.rb
116
+ - spec/operation/results_spec.rb
117
+ - spec/operation_spec.rb
118
+ - spec/rb27/pattern_matching_spec.rb
119
+ - spec/result_spec.rb
120
+ - spec/spec_helper.rb
121
+ - spec/support/dry_base.rb
122
+ - spec/support/fake_db.rb
123
+ - spec/support/fake_models.rb
124
+ - spec/teckel_spec.rb
125
+ homepage: https://github.com/fnordfish/teckel
120
126
  licenses:
121
127
  - Apache-2.0
122
- metadata: {}
128
+ metadata:
129
+ changelog_uri: https://github.com/github.com/fnordfish/blob/master/CHANGELOG.md
130
+ source_code_uri: https://github.com/github.com/fnordfish
131
+ bug_tracker_uri: https://github.com/github.com/fnordfish/issues
132
+ documentation_uri: https://www.rubydoc.info/gems/teckel/0.4.0
123
133
  post_install_message:
124
134
  rdoc_options: []
125
135
  require_paths:
@@ -128,15 +138,32 @@ required_ruby_version: !ruby/object:Gem::Requirement
128
138
  requirements:
129
139
  - - ">="
130
140
  - !ruby/object:Gem::Version
131
- version: '0'
141
+ version: 2.4.0
132
142
  required_rubygems_version: !ruby/object:Gem::Requirement
133
143
  requirements:
134
144
  - - ">="
135
145
  - !ruby/object:Gem::Version
136
146
  version: '0'
137
147
  requirements: []
138
- rubygems_version: 3.0.3
148
+ rubygems_version: 3.1.2
139
149
  signing_key:
140
150
  specification_version: 4
141
151
  summary: Operations with enforced in/out/err data structures
142
- test_files: []
152
+ test_files:
153
+ - spec/chain/inheritance_spec.rb
154
+ - spec/chain/results_spec.rb
155
+ - spec/chain_around_hook_spec.rb
156
+ - spec/chain_spec.rb
157
+ - spec/config_spec.rb
158
+ - spec/doctest_helper.rb
159
+ - spec/operation/inheritance_spec.rb
160
+ - spec/operation/result_spec.rb
161
+ - spec/operation/results_spec.rb
162
+ - spec/operation_spec.rb
163
+ - spec/rb27/pattern_matching_spec.rb
164
+ - spec/result_spec.rb
165
+ - spec/spec_helper.rb
166
+ - spec/support/dry_base.rb
167
+ - spec/support/fake_db.rb
168
+ - spec/support/fake_models.rb
169
+ - 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