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.
- 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
data/.github/workflows/pages.yml
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
name: pages
|
2
|
-
|
3
|
-
on:
|
4
|
-
push:
|
5
|
-
branches:
|
6
|
-
- master
|
7
|
-
paths:
|
8
|
-
- .github/workflows/pages.yml
|
9
|
-
- lib/**
|
10
|
-
- README.md
|
11
|
-
- .yardopts
|
12
|
-
- Rakefile
|
13
|
-
|
14
|
-
jobs:
|
15
|
-
build-docs:
|
16
|
-
runs-on: ubuntu-latest
|
17
|
-
steps:
|
18
|
-
- uses: actions/checkout@v2
|
19
|
-
- name: Set up Ruby
|
20
|
-
uses: actions/setup-ruby@v1
|
21
|
-
with:
|
22
|
-
ruby-version: 2.6.x
|
23
|
-
- name: Bundle install
|
24
|
-
run: |
|
25
|
-
gem install bundler
|
26
|
-
bundle install --jobs 4 --retry 3 --without tools docs benchmarks
|
27
|
-
- name: Build docs
|
28
|
-
run: 'bin/rake docs:yard'
|
29
|
-
- name: config git
|
30
|
-
run: |
|
31
|
-
git config --local user.email "action@github.com"
|
32
|
-
git config --local user.name "GitHub Action"
|
33
|
-
- name: Checkout Pages
|
34
|
-
run: 'git fetch origin gh-pages && git checkout gh-pages'
|
35
|
-
- name: Replace and commit docs
|
36
|
-
run: |
|
37
|
-
rm -rf doc && mv _yardoc doc
|
38
|
-
git add -A doc
|
39
|
-
git commit -m"Update API docs"
|
40
|
-
- name: Replace index
|
41
|
-
run: |
|
42
|
-
git checkout master README.md
|
43
|
-
mv -f README.md index.md
|
44
|
-
if [ ! -z "$(git diff --name-only -- index.md)" ]; then
|
45
|
-
git commit index.md -m"update index"
|
46
|
-
fi
|
47
|
-
- name: Push
|
48
|
-
env:
|
49
|
-
INPUT_GITHUB_TOKEN: "${{ secrets.DEPLOY_TOKEN }}"
|
50
|
-
run: git push https://git:${INPUT_GITHUB_TOKEN}@github.com/fnordfish/teckel.git gh-pages
|
data/.gitignore
DELETED
data/.rspec
DELETED
data/.rubocop.yml
DELETED
data/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
ruby-2.6.5
|
data/DEVELOPMENT.md
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
# Development Guidelines
|
2
|
-
|
3
|
-
- Keep it simple.
|
4
|
-
- Favor easy debug-ability over clever solutions.
|
5
|
-
- Aim to be a 0-dependency lib (at runtime)
|
6
|
-
|
7
|
-
## Roadmap
|
8
|
-
|
9
|
-
- Add "Settings"/Dependency injection for Operations and Chains
|
10
|
-
|
11
|
-
```
|
12
|
-
MyOp.with(foo: "bar").call("input")
|
13
|
-
|
14
|
-
class MyOp
|
15
|
-
settings Types::Hash.schema(foo: Types::String)
|
16
|
-
|
17
|
-
def call(input)
|
18
|
-
input == "input"
|
19
|
-
settings.foo == "bar"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
MyCain.with(:step1) { { foo: "bar" } }.with(:stepX) { { another: :setting} }.call(params)
|
24
|
-
```
|
25
|
-
- Add a dry-monads mixin to wrap Operations and Chains result/error into a Result Monad (for example see https://dry-rb.org/gems/dry-types/master/extensions/monads/)
|
26
|
-
```
|
27
|
-
MyOp.call("input").to_monad do
|
28
|
-
end
|
29
|
-
```
|
30
|
-
- Check if/how to deal with inheritance
|
31
|
-
- ...
|
32
|
-
|
data/Gemfile
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
source "https://rubygems.org"
|
4
|
-
|
5
|
-
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
6
|
-
|
7
|
-
# Specify your gem's dependencies in teckel.gemspec
|
8
|
-
gemspec
|
9
|
-
|
10
|
-
group :development, :test do
|
11
|
-
gem "dry-struct", ">= 1.1.1", "< 2"
|
12
|
-
end
|
13
|
-
|
14
|
-
group :test do
|
15
|
-
gem "simplecov", require: false
|
16
|
-
end
|
data/Rakefile
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "bundler/gem_tasks"
|
4
|
-
require "rspec/core/rake_task"
|
5
|
-
require "yard"
|
6
|
-
require "yard/doctest/rake"
|
7
|
-
|
8
|
-
RSpec::Core::RakeTask.new(:spec)
|
9
|
-
|
10
|
-
task :docs do
|
11
|
-
Rake::Task["docs:yard"].invoke
|
12
|
-
Rake::Task["docs:yard:doctest"].invoke
|
13
|
-
end
|
14
|
-
|
15
|
-
namespace :docs do
|
16
|
-
YARD::Rake::YardocTask.new do |t|
|
17
|
-
t.files = ['lib/**/*.rb']
|
18
|
-
t.options = []
|
19
|
-
t.stats_options = ['--list-undoc']
|
20
|
-
end
|
21
|
-
|
22
|
-
task :fswatch do
|
23
|
-
sh 'fswatch -0 lib | while read -d "" e; do rake docs:yard; done'
|
24
|
-
end
|
25
|
-
|
26
|
-
YARD::Doctest::RakeTask.new do |task|
|
27
|
-
task.doctest_opts = %w[-v]
|
28
|
-
task.pattern = Dir.glob('lib/**/*.rb')
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
task :default do
|
33
|
-
Rake::Task["spec"].invoke
|
34
|
-
Rake::Task["docs:yard:doctest"].invoke
|
35
|
-
end
|
data/bin/console
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "bundler/setup"
|
5
|
-
require "teckel"
|
6
|
-
|
7
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
-
# with your gem easier. You can also use a different console, if you like.
|
9
|
-
|
10
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
-
# require "pry"
|
12
|
-
# Pry.start
|
13
|
-
|
14
|
-
require "irb"
|
15
|
-
IRB.start(__FILE__)
|
data/bin/rake
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
#
|
5
|
-
# This file was generated by Bundler.
|
6
|
-
#
|
7
|
-
# The application 'rake' is installed as part of a gem, and
|
8
|
-
# this file is here to facilitate running it.
|
9
|
-
#
|
10
|
-
|
11
|
-
require "pathname"
|
12
|
-
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
-
Pathname.new(__FILE__).realpath)
|
14
|
-
|
15
|
-
bundle_binstub = File.expand_path('bundle', __dir__)
|
16
|
-
|
17
|
-
if File.file?(bundle_binstub)
|
18
|
-
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
-
load(bundle_binstub)
|
20
|
-
else
|
21
|
-
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
-
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
require "rubygems"
|
27
|
-
require "bundler/setup"
|
28
|
-
|
29
|
-
load Gem.bin_path("rake", "rake")
|
data/bin/rspec
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
#
|
5
|
-
# This file was generated by Bundler.
|
6
|
-
#
|
7
|
-
# The application 'rspec' is installed as part of a gem, and
|
8
|
-
# this file is here to facilitate running it.
|
9
|
-
#
|
10
|
-
|
11
|
-
require "pathname"
|
12
|
-
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
-
Pathname.new(__FILE__).realpath)
|
14
|
-
|
15
|
-
bundle_binstub = File.expand_path('bundle', __dir__)
|
16
|
-
|
17
|
-
if File.file?(bundle_binstub)
|
18
|
-
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
-
load(bundle_binstub)
|
20
|
-
else
|
21
|
-
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
-
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
require "rubygems"
|
27
|
-
require "bundler/setup"
|
28
|
-
|
29
|
-
load Gem.bin_path("rspec-core", "rspec")
|
data/bin/rubocop
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require 'bundler/inline'
|
5
|
-
require 'bundler'
|
6
|
-
|
7
|
-
# We need the `Bundler.settings.temporary` for a bundler bug:
|
8
|
-
# https://github.com/bundler/bundler/issues/7114
|
9
|
-
# Will get fixed in bundler version 2.1.0
|
10
|
-
Bundler.settings.temporary(frozen: false) do
|
11
|
-
gemfile do
|
12
|
-
source 'https://rubygems.org'
|
13
|
-
gem 'rubocop', '~> 0.78.0'
|
14
|
-
gem 'relaxed-rubocop', '2.4'
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
load Gem.bin_path("rubocop", "rubocop")
|
data/bin/setup
DELETED
data/lib/teckel/none.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Teckel
|
4
|
-
# Simple type object for enforcing +input+, +output+ or +error+ data to be
|
5
|
-
# not set (or +nil+)
|
6
|
-
class None
|
7
|
-
class << self
|
8
|
-
# Always return nil
|
9
|
-
# @return nil
|
10
|
-
# @raise [ArgumentError] when called with any non-nil arguments
|
11
|
-
def [](*args)
|
12
|
-
raise ArgumentError, "None called with arguments" if args.any?(&:itself)
|
13
|
-
end
|
14
|
-
|
15
|
-
alias :new :[]
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,72 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Teckel
|
4
|
-
module Operation
|
5
|
-
# Works just like {Teckel::Operation}, but wraps +output+ and +error+ into a
|
6
|
-
# {Teckel::Result Teckel::Result}.
|
7
|
-
#
|
8
|
-
# If a {Teckel::Result Teckel::Result} is given as +input+, it will get unwrapped,
|
9
|
-
# so that the original {Teckel::Result#value} gets passed to your Operation code.
|
10
|
-
#
|
11
|
-
# @example
|
12
|
-
#
|
13
|
-
# class CreateUser
|
14
|
-
# include Teckel::Operation::Results
|
15
|
-
#
|
16
|
-
# input Types::Hash.schema(name: Types::String, age: Types::Coercible::Integer)
|
17
|
-
# output Types.Instance(User)
|
18
|
-
# error Types::Hash.schema(message: Types::String, errors: Types::Array.of(Types::Hash))
|
19
|
-
#
|
20
|
-
# # @param [Hash<name: String, age: Integer>]
|
21
|
-
# # @return [User,Hash<message: String, errors: [Hash]>]
|
22
|
-
# def call(input)
|
23
|
-
# user = User.new(name: input[:name], age: input[:age])
|
24
|
-
# if user.save
|
25
|
-
# success!(user) # exits early with success, prevents any further execution
|
26
|
-
# else
|
27
|
-
# fail!(message: "Could not save User", errors: user.errors)
|
28
|
-
# end
|
29
|
-
# end
|
30
|
-
# end
|
31
|
-
#
|
32
|
-
# # A success call:
|
33
|
-
# CreateUser.call(name: "Bob", age: 23).is_a?(Teckel::Result) #=> true
|
34
|
-
# CreateUser.call(name: "Bob", age: 23).success.is_a?(User) #=> true
|
35
|
-
#
|
36
|
-
# # A failure call:
|
37
|
-
# CreateUser.call(name: "Bob", age: 10).is_a?(Teckel::Result) #=> true
|
38
|
-
# CreateUser.call(name: "Bob", age: 10).failure.is_a?(Hash) #=> true
|
39
|
-
#
|
40
|
-
# # Unwrapping success input:
|
41
|
-
# CreateUser.call(Teckel::Result.new({name: "Bob", age: 23}, true)).success.is_a?(User) #=> true
|
42
|
-
#
|
43
|
-
# # Unwrapping failure input:
|
44
|
-
# CreateUser.call(Teckel::Result.new({name: "Bob", age: 23}, false)).success.is_a?(User) #=> true
|
45
|
-
#
|
46
|
-
# @!visibility public
|
47
|
-
module Results
|
48
|
-
# Overwrites the defaults {Teckel::Operation::Runner} to un/wrap input, output and error
|
49
|
-
class Runner < Teckel::Operation::Runner
|
50
|
-
private
|
51
|
-
|
52
|
-
def build_input(input)
|
53
|
-
input = input.value if input.is_a?(Teckel::Result)
|
54
|
-
super(input)
|
55
|
-
end
|
56
|
-
|
57
|
-
def build_output(*args)
|
58
|
-
Teckel::Result.new(super, true)
|
59
|
-
end
|
60
|
-
|
61
|
-
def build_error(*args)
|
62
|
-
Teckel::Result.new(super, false)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def self.included(receiver)
|
67
|
-
receiver.send :include, Teckel::Operation unless Teckel::Operation >= receiver
|
68
|
-
receiver.send :runner, Runner
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
data/teckel.gemspec
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
lib = File.expand_path('lib', __dir__)
|
4
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
require "teckel/version"
|
6
|
-
|
7
|
-
Gem::Specification.new do |spec|
|
8
|
-
spec.name = "teckel"
|
9
|
-
spec.version = Teckel::VERSION
|
10
|
-
spec.authors = ["Robert Schulze"]
|
11
|
-
spec.email = ["robert@dotless.de"]
|
12
|
-
spec.licenses = ['Apache-2.0']
|
13
|
-
|
14
|
-
spec.summary = 'Operations with enforced in/out/err data structures'
|
15
|
-
spec.description = 'Wrap your business logic into a common interface with enforced input, output and error data structures'
|
16
|
-
spec.homepage = "https://github.com/dotless-de/teckel"
|
17
|
-
|
18
|
-
# Specify which files should be added to the gem when it is released.
|
19
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
20
|
-
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
21
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
22
|
-
end
|
23
|
-
spec.bindir = "exe"
|
24
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
|
-
spec.require_paths = ["lib"]
|
26
|
-
|
27
|
-
spec.add_development_dependency "bundler", "~> 2.0"
|
28
|
-
spec.add_development_dependency "rake", ">= 10.0"
|
29
|
-
spec.add_development_dependency "rspec", "~> 3.0"
|
30
|
-
spec.add_development_dependency "yard"
|
31
|
-
spec.add_development_dependency "yard-doctest"
|
32
|
-
end
|