flowy 0.1.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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +873 -0
- data/lib/flowy/concern/step_runner.rb +135 -0
- data/lib/flowy/concern.rb +144 -0
- data/lib/flowy/enumerable.rb +29 -0
- data/lib/flowy/error.rb +47 -0
- data/lib/flowy/failure.rb +120 -0
- data/lib/flowy/pipeline.rb +194 -0
- data/lib/flowy/result.rb +63 -0
- data/lib/flowy/success.rb +74 -0
- data/lib/flowy/version.rb +3 -0
- data/lib/flowy.rb +11 -0
- metadata +70 -0
data/lib/flowy/result.rb
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module Flowy
|
|
2
|
+
# Tag module + factory namespace. Both Flowy::Success and Flowy::Failure
|
|
3
|
+
# include this module so `result.is_a?(Flowy::Result)` matches either.
|
|
4
|
+
# The instance interface is defined on Success and Failure — see the README.
|
|
5
|
+
module Result
|
|
6
|
+
def self._deep_merge(a, b)
|
|
7
|
+
a.merge(b) do |_, va, vb|
|
|
8
|
+
if va.is_a?(Hash) && vb.is_a?(Hash)
|
|
9
|
+
_deep_merge(va, vb)
|
|
10
|
+
else
|
|
11
|
+
vb
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self._collect_results(enumerable)
|
|
17
|
+
enumerable.map do |item|
|
|
18
|
+
result = yield item
|
|
19
|
+
unless result.is_a?(Flowy::Result)
|
|
20
|
+
raise TypeError,
|
|
21
|
+
"all_success/any_success block must return a Flowy::Result, got #{result.class}"
|
|
22
|
+
end
|
|
23
|
+
result
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.success(data: {}, warnings: [])
|
|
28
|
+
Flowy::Success.new(data: data, warnings: warnings)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.failure(error_code:, error_data: {}, error_title: nil, error_description: nil, parent_failure: nil)
|
|
32
|
+
Flowy::Failure.new(
|
|
33
|
+
error_code: error_code,
|
|
34
|
+
error_data: error_data,
|
|
35
|
+
error_title: error_title,
|
|
36
|
+
error_description: error_description,
|
|
37
|
+
parent_failure: parent_failure
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# `rescue:` is captured via **opts because `rescue` is a Ruby reserved word
|
|
42
|
+
# and cannot be referenced as a bare local variable inside a method body.
|
|
43
|
+
def self.wrap(error_code: :wrapped_error, error_title: nil, **opts)
|
|
44
|
+
unknown = opts.keys - [:rescue]
|
|
45
|
+
raise ArgumentError, "unknown keyword: #{unknown.first}" if unknown.any?
|
|
46
|
+
|
|
47
|
+
rescued_classes = Array(opts.fetch(:rescue, [StandardError]))
|
|
48
|
+
|
|
49
|
+
value = yield
|
|
50
|
+
|
|
51
|
+
return value if value.is_a?(Flowy::Result)
|
|
52
|
+
|
|
53
|
+
Flowy::Success.new(data: { value: value })
|
|
54
|
+
rescue *rescued_classes => e
|
|
55
|
+
Flowy::Failure.new(
|
|
56
|
+
error_code: error_code,
|
|
57
|
+
error_data: { error_class: e.class.name, message: e.message },
|
|
58
|
+
error_title: error_title,
|
|
59
|
+
error_description: e.message
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Flowy
|
|
2
|
+
class Success
|
|
3
|
+
include Flowy::Result
|
|
4
|
+
|
|
5
|
+
attr_reader :data, :warnings
|
|
6
|
+
|
|
7
|
+
def initialize(data: {}, warnings: [])
|
|
8
|
+
@data = data
|
|
9
|
+
@warnings = warnings
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def +(other)
|
|
13
|
+
self.class.new(data: Flowy::Result._deep_merge(data, other.data))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_hash
|
|
17
|
+
{
|
|
18
|
+
success: true,
|
|
19
|
+
data: data,
|
|
20
|
+
warnings: warnings
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def success?
|
|
25
|
+
true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def failure?
|
|
29
|
+
false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def on_success
|
|
33
|
+
yield self
|
|
34
|
+
self
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def on_failure
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def and_then
|
|
42
|
+
result = yield self
|
|
43
|
+
unless result.is_a?(Flowy::Success) || result.is_a?(Flowy::Failure)
|
|
44
|
+
raise TypeError, "and_then block must return a Flowy::Success or Flowy::Failure, got #{result.class}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
result
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def or_else
|
|
51
|
+
self
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def map_failure(**)
|
|
55
|
+
self
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def raise!
|
|
59
|
+
self
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def tap
|
|
63
|
+
yield self
|
|
64
|
+
self
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def merge_data(extra = nil)
|
|
68
|
+
extra = block_given? ? yield(data) : extra
|
|
69
|
+
raise ArgumentError, 'merge_data requires a Hash' unless extra.is_a?(Hash)
|
|
70
|
+
|
|
71
|
+
self.class.new(data: Flowy::Result._deep_merge(data, extra), warnings: warnings)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
data/lib/flowy.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: flowy
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Leanbit
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-05-25 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rspec
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '3.13'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '3.13'
|
|
27
|
+
description: Flowy provides Success/Failure result objects and a composable step-based
|
|
28
|
+
concern for clean, functional-style service objects in Ruby.
|
|
29
|
+
email:
|
|
30
|
+
- support@leanbit.eu
|
|
31
|
+
executables: []
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- LICENSE.txt
|
|
36
|
+
- README.md
|
|
37
|
+
- lib/flowy.rb
|
|
38
|
+
- lib/flowy/concern.rb
|
|
39
|
+
- lib/flowy/concern/step_runner.rb
|
|
40
|
+
- lib/flowy/enumerable.rb
|
|
41
|
+
- lib/flowy/error.rb
|
|
42
|
+
- lib/flowy/failure.rb
|
|
43
|
+
- lib/flowy/pipeline.rb
|
|
44
|
+
- lib/flowy/result.rb
|
|
45
|
+
- lib/flowy/success.rb
|
|
46
|
+
- lib/flowy/version.rb
|
|
47
|
+
homepage: https://github.com/leanbit/flowy
|
|
48
|
+
licenses:
|
|
49
|
+
- MIT
|
|
50
|
+
metadata: {}
|
|
51
|
+
post_install_message:
|
|
52
|
+
rdoc_options: []
|
|
53
|
+
require_paths:
|
|
54
|
+
- lib
|
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '3.2'
|
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
61
|
+
requirements:
|
|
62
|
+
- - ">="
|
|
63
|
+
- !ruby/object:Gem::Version
|
|
64
|
+
version: '0'
|
|
65
|
+
requirements: []
|
|
66
|
+
rubygems_version: 3.4.20
|
|
67
|
+
signing_key:
|
|
68
|
+
specification_version: 4
|
|
69
|
+
summary: Lightweight Railway Oriented Programming for Ruby
|
|
70
|
+
test_files: []
|