teckel 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +53 -0
- data/LICENSE_LOGO +4 -0
- data/README.md +4 -4
- data/lib/teckel.rb +9 -3
- data/lib/teckel/chain.rb +99 -271
- data/lib/teckel/chain/result.rb +38 -0
- data/lib/teckel/chain/runner.rb +51 -0
- data/lib/teckel/chain/step.rb +18 -0
- data/lib/teckel/config.rb +1 -23
- data/lib/teckel/contracts.rb +19 -0
- data/lib/teckel/operation.rb +309 -215
- data/lib/teckel/operation/result.rb +92 -0
- data/lib/teckel/operation/runner.rb +70 -0
- data/lib/teckel/result.rb +52 -53
- data/lib/teckel/version.rb +1 -1
- data/spec/chain/inheritance_spec.rb +116 -0
- data/spec/chain/results_spec.rb +53 -0
- data/spec/chain_around_hook_spec.rb +100 -0
- data/spec/chain_spec.rb +180 -0
- data/spec/config_spec.rb +26 -0
- data/spec/doctest_helper.rb +7 -0
- data/spec/operation/inheritance_spec.rb +94 -0
- data/spec/operation/result_spec.rb +34 -0
- data/spec/operation/results_spec.rb +117 -0
- data/spec/operation_spec.rb +485 -0
- data/spec/rb27/pattern_matching_spec.rb +193 -0
- data/spec/result_spec.rb +20 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/dry_base.rb +8 -0
- data/spec/support/fake_db.rb +12 -0
- data/spec/support/fake_models.rb +20 -0
- data/spec/teckel_spec.rb +7 -0
- metadata +52 -25
- data/.codeclimate.yml +0 -3
- data/.github/workflows/ci.yml +0 -92
- data/.github/workflows/pages.yml +0 -50
- data/.gitignore +0 -15
- data/.rspec +0 -3
- data/.rubocop.yml +0 -12
- data/.ruby-version +0 -1
- data/DEVELOPMENT.md +0 -32
- data/Gemfile +0 -16
- data/Rakefile +0 -35
- data/bin/console +0 -15
- data/bin/rake +0 -29
- data/bin/rspec +0 -29
- data/bin/rubocop +0 -18
- data/bin/setup +0 -8
- data/lib/teckel/none.rb +0 -18
- data/lib/teckel/operation/results.rb +0 -72
- 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
|
data/spec/result_spec.rb
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
@@ -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,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
|
data/spec/teckel_spec.rb
ADDED
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.
|
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-
|
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/
|
102
|
+
- lib/teckel/contracts.rb
|
114
103
|
- lib/teckel/operation.rb
|
115
|
-
- lib/teckel/operation/
|
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
|
-
-
|
119
|
-
|
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:
|
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.
|
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
|
data/.codeclimate.yml
DELETED
data/.github/workflows/ci.yml
DELETED
@@ -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
|