ruby-ode 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/CHANGELOG.md +6 -0
- data/CONTRIBUTING.md +3 -0
- data/PUBLICATION.md +10 -0
- data/README.md +105 -0
- data/USECASE_GUIDE.md +3 -0
- data/lib/ruby_ode/runtime.rb +101 -0
- data/lib/ruby_ode.rb +1 -0
- metadata +52 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 4cbd1b654f43bac29c7be4dd5a330d31fe66d056dcb9760fce801a3674dab572
|
|
4
|
+
data.tar.gz: 575f036138804d7760f9827f9cec5270ea8ead5bccd87462e9cddaf7f81423a1
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 8ed3d1ef2b00b2032182041e1ce1af0af1ca4664bd37eb40f265710c93697ed7d7f1c3f1a37c9286cbf9bf53d1a1533ab63bf42df21dcc794f77166159560a8a
|
|
7
|
+
data.tar.gz: 90a038e33b4e40b57bd53f1fe20cb5199d3777d22d74111333c13d5e1b6a06a07c57049e74930156f33dfe61bf5e1386675e90a33bc921767b285df55c5374f0
|
data/CHANGELOG.md
ADDED
data/CONTRIBUTING.md
ADDED
data/PUBLICATION.md
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# ruby-ode
|
|
2
|
+
|
|
3
|
+
`ruby-ode` is the Ruby member of the ODE runtime family.
|
|
4
|
+
|
|
5
|
+
It provides:
|
|
6
|
+
|
|
7
|
+
- direct `UseCase` execution
|
|
8
|
+
- `ChainUseCase` dependent comparison flow
|
|
9
|
+
- `SequenceUseCase` ordered execution
|
|
10
|
+
- guard-first rejection before business work begins
|
|
11
|
+
|
|
12
|
+
## Narrative Readability
|
|
13
|
+
|
|
14
|
+
ODE is designed so code can be read as narrative. A developer should be able to identify `guard`, `execute`, `chain`, `sequence`, `dispatch` and `publish` steps even in a language they do not use every day.
|
|
15
|
+
|
|
16
|
+
This does not mean every runtime has identical syntax. It means the conceptual reading path remains stable enough that a Java developer can follow a Go or Ruby flow, and a Swift developer can follow a TypeScript or Python flow, without relearning the product story from scratch.
|
|
17
|
+
|
|
18
|
+
## What ODE Does Not Claim
|
|
19
|
+
|
|
20
|
+
ODE does not promise identical syntax across languages, and it does not remove the need to understand native platform idioms. It also does not require every runtime to expose the same UI layer.
|
|
21
|
+
|
|
22
|
+
The reason is pragmatic: forcing textual symmetry across Kotlin, Swift, Go, Ruby, PHP, TypeScript or Python would usually make the libraries feel unnatural inside their host ecosystems. ODE prefers semantic stability over artificial syntactic cloning.
|
|
23
|
+
|
|
24
|
+
## Validation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
ruby -Ilib test/test_runtime.rb
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Repository
|
|
31
|
+
|
|
32
|
+
- source: [github.com/animalab-netizen/ruby-ode](https://github.com/animalab-netizen/ruby-ode)
|
|
33
|
+
|
|
34
|
+
## Coordinates
|
|
35
|
+
|
|
36
|
+
- gem: `ruby-ode`
|
|
37
|
+
- version: `0.1.0`
|
|
38
|
+
|
|
39
|
+
## Public API
|
|
40
|
+
|
|
41
|
+
- `RubyODE::UseCase`
|
|
42
|
+
- `RubyODE::ChainUseCase`
|
|
43
|
+
- `RubyODE::SequenceUseCase`
|
|
44
|
+
- `RubyODE::UseCaseDispatcher`
|
|
45
|
+
- `RubyODE::ValueOutput`, `RubyODE::ErrorOutput`, `RubyODE::EmptyOutput`, `RubyODE::Outputs`
|
|
46
|
+
- `RubyODE::HttpError`, `RubyODE::ConnectionError`, `RubyODE::GuardRejectedError`, `RubyODE::UnexpectedResponseError`
|
|
47
|
+
|
|
48
|
+
## Core Concepts
|
|
49
|
+
|
|
50
|
+
### 1. UseCase
|
|
51
|
+
|
|
52
|
+
`RubyODE::UseCase` keeps guard, execution and output normalization in the same explicit template flow.
|
|
53
|
+
|
|
54
|
+
### 2. ChainUseCase
|
|
55
|
+
|
|
56
|
+
`RubyODE::ChainUseCase` models a two-step dependent narrative.
|
|
57
|
+
|
|
58
|
+
### 3. SequenceUseCase
|
|
59
|
+
|
|
60
|
+
`RubyODE::SequenceUseCase` preserves input order across three or more values.
|
|
61
|
+
|
|
62
|
+
### 4. UseCaseDispatcher
|
|
63
|
+
|
|
64
|
+
`RubyODE::UseCaseDispatcher` triggers a use case and can yield the same output to a block for publication.
|
|
65
|
+
|
|
66
|
+
## Basic Examples
|
|
67
|
+
|
|
68
|
+
### Direct UseCase
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
class LoadPokemonUseCase < RubyODE::UseCase
|
|
72
|
+
def execute(param)
|
|
73
|
+
"spotlight:#{param}"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Guarded UseCase
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
class ComparePokemonUseCase < RubyODE::UseCase
|
|
82
|
+
def guard(param)
|
|
83
|
+
raise RubyODE::GuardRejectedError, "comparison requires distinct entries" if param[0] == param[1]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def execute(param)
|
|
87
|
+
"#{param[0]} vs #{param[1]}"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Publish While Dispatching
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
dispatcher = RubyODE::UseCaseDispatcher.new
|
|
96
|
+
output = dispatcher.dispatch("pikachu", LoadPokemonUseCase.new) do |result|
|
|
97
|
+
puts result.inspect
|
|
98
|
+
end
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Additional Guides
|
|
102
|
+
|
|
103
|
+
- [PUBLICATION.md](/Users/caiosanchezchristino/Desktop/ode-projects/ruby-ode/PUBLICATION.md)
|
|
104
|
+
- [PARITY.md](/Users/caiosanchezchristino/Desktop/ode-projects/ruby-ode/PARITY.md)
|
|
105
|
+
- [USECASE_GUIDE.md](/Users/caiosanchezchristino/Desktop/ode-projects/ruby-ode/USECASE_GUIDE.md)
|
data/USECASE_GUIDE.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
module RubyODE
|
|
2
|
+
class ValueOutput
|
|
3
|
+
attr_reader :value
|
|
4
|
+
|
|
5
|
+
def initialize(value)
|
|
6
|
+
@value = value
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class ErrorOutput
|
|
11
|
+
attr_reader :error
|
|
12
|
+
|
|
13
|
+
def initialize(error)
|
|
14
|
+
@error = error
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class EmptyOutput
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module Outputs
|
|
22
|
+
def self.value(value) = ValueOutput.new(value)
|
|
23
|
+
def self.error(error) = ErrorOutput.new(error)
|
|
24
|
+
def self.empty = EmptyOutput.new
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class GuardRejectedError < StandardError; end
|
|
28
|
+
class ConnectionError < StandardError; end
|
|
29
|
+
class UnexpectedResponseError < StandardError; end
|
|
30
|
+
|
|
31
|
+
class HttpError < StandardError
|
|
32
|
+
attr_reader :status_code
|
|
33
|
+
|
|
34
|
+
def initialize(status_code, message)
|
|
35
|
+
@status_code = status_code
|
|
36
|
+
super("http #{status_code}: #{message}")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class UseCase
|
|
41
|
+
def process(param)
|
|
42
|
+
guard(param)
|
|
43
|
+
on_result(execute(param))
|
|
44
|
+
rescue StandardError => error
|
|
45
|
+
on_error(error)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def guard(_param); end
|
|
49
|
+
|
|
50
|
+
def execute(_param)
|
|
51
|
+
raise NotImplementedError
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def on_result(result)
|
|
55
|
+
Outputs.value(result)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def on_error(error)
|
|
59
|
+
Outputs.error(error)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class ChainUseCase
|
|
64
|
+
def initialize(first:, second:)
|
|
65
|
+
@first = first
|
|
66
|
+
@second = second
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def process(param)
|
|
70
|
+
first_output = @first.process(param)
|
|
71
|
+
return Outputs.error(first_output.error) if first_output.is_a?(ErrorOutput)
|
|
72
|
+
return Outputs.empty if first_output.is_a?(EmptyOutput)
|
|
73
|
+
|
|
74
|
+
Outputs.value(@second.call(first_output.value, param))
|
|
75
|
+
rescue StandardError => error
|
|
76
|
+
Outputs.error(error)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
class SequenceUseCase
|
|
81
|
+
def initialize(step:)
|
|
82
|
+
@step = step
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def process(values)
|
|
86
|
+
return Outputs.empty if values.empty?
|
|
87
|
+
|
|
88
|
+
Outputs.value(values.map { |value| @step.call(value) })
|
|
89
|
+
rescue StandardError => error
|
|
90
|
+
Outputs.error(error)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
class UseCaseDispatcher
|
|
95
|
+
def dispatch(param, use_case)
|
|
96
|
+
output = use_case.process(param)
|
|
97
|
+
yield output if block_given?
|
|
98
|
+
output
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
data/lib/ruby_ode.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require_relative "ruby_ode/runtime"
|
metadata
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ruby-ode
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- ÂnimaLab
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: Ruby runtime for direct, chain, sequence and guard-oriented ODE flows.
|
|
13
|
+
email:
|
|
14
|
+
- animalab.desenvolvimento@gmail.com
|
|
15
|
+
executables: []
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- CHANGELOG.md
|
|
20
|
+
- CONTRIBUTING.md
|
|
21
|
+
- PUBLICATION.md
|
|
22
|
+
- README.md
|
|
23
|
+
- USECASE_GUIDE.md
|
|
24
|
+
- lib/ruby_ode.rb
|
|
25
|
+
- lib/ruby_ode/runtime.rb
|
|
26
|
+
homepage: https://github.com/animalab-netizen/ruby-ode
|
|
27
|
+
licenses:
|
|
28
|
+
- Apache-2.0
|
|
29
|
+
metadata:
|
|
30
|
+
homepage_uri: https://github.com/animalab-netizen/ruby-ode
|
|
31
|
+
source_code_uri: https://github.com/animalab-netizen/ruby-ode
|
|
32
|
+
changelog_uri: https://github.com/animalab-netizen/ruby-ode/blob/main/CHANGELOG.md
|
|
33
|
+
documentation_uri: https://github.com/animalab-netizen/ruby-ode/blob/main/README.md
|
|
34
|
+
bug_tracker_uri: https://github.com/animalab-netizen/ruby-ode/issues
|
|
35
|
+
rdoc_options: []
|
|
36
|
+
require_paths:
|
|
37
|
+
- lib
|
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '3.2'
|
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
requirements: []
|
|
49
|
+
rubygems_version: 4.0.11
|
|
50
|
+
specification_version: 4
|
|
51
|
+
summary: Opinionated delivery engine for Ruby applications.
|
|
52
|
+
test_files: []
|