hubbado-sequence 0.3.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 +166 -0
- data/LICENSE +21 -0
- data/README.md +562 -0
- data/config/locales/en.yml +8 -0
- data/hubbado-sequence.gemspec +41 -0
- data/lib/hubbado/sequence/controls/contract.rb +45 -0
- data/lib/hubbado/sequence/controls/model.rb +33 -0
- data/lib/hubbado/sequence/controls/policy.rb +48 -0
- data/lib/hubbado/sequence/controls.rb +3 -0
- data/lib/hubbado/sequence/ctx.rb +19 -0
- data/lib/hubbado/sequence/errors.rb +16 -0
- data/lib/hubbado/sequence/macros/contract/build.rb +48 -0
- data/lib/hubbado/sequence/macros/contract/deserialize.rb +43 -0
- data/lib/hubbado/sequence/macros/contract/persist.rb +50 -0
- data/lib/hubbado/sequence/macros/contract/validate.rb +53 -0
- data/lib/hubbado/sequence/macros/model/build.rb +57 -0
- data/lib/hubbado/sequence/macros/model/find.rb +59 -0
- data/lib/hubbado/sequence/macros/policy/check.rb +65 -0
- data/lib/hubbado/sequence/path.rb +35 -0
- data/lib/hubbado/sequence/pipeline.rb +141 -0
- data/lib/hubbado/sequence/result.rb +83 -0
- data/lib/hubbado/sequence/run_sequence.rb +26 -0
- data/lib/hubbado/sequence/runner.rb +184 -0
- data/lib/hubbado/sequence/sequencer.rb +109 -0
- data/lib/hubbado/sequence.rb +30 -0
- metadata +227 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: bf9d0a82e802923d5f0a04043400578eb8c90ad8c78a00073a30c99cab8784e6
|
|
4
|
+
data.tar.gz: 91e67b7ed3f8fc5fe428bcfea0eb359d4f8c03edb090ccba56d1a16799d2a48d
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 902c46876f2df354ce46f340ef0fa6432d34c867eaff764c14e5ab54f54a8bb1dc466601c1c3da8f3823d5c0637af3d5c279e7bdb3596c8ff6a785488a333d31
|
|
7
|
+
data.tar.gz: 9edff4e5aa8e45dbdb20a31f7cfe360c6f03f8751986cef344146be16e5d5e771c1c8318ef89ab80baef61b89927aabe327ac29c175bbdc7246e00257db8086d
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
All notable changes to this project will be documented in this file.
|
|
3
|
+
|
|
4
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
|
5
|
+
and this project adheres to [Semantic Versioning](http://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [0.3.0] - Contract::Deserialize macro, Runner extraction, Path helper
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`Macros::Contract::Deserialize`** — new macro for populating
|
|
12
|
+
`ctx[:contract]` from submitted params.
|
|
13
|
+
- Calls `contract.deserialize(params)` with params read from a
|
|
14
|
+
configurable ctx path.
|
|
15
|
+
- No-op when the path is absent — safe for fresh-form GETs before any
|
|
16
|
+
params have been posted.
|
|
17
|
+
- Configure name is `:deserialize_to_contract` rather than the generic
|
|
18
|
+
`:deserialize`, to avoid colliding with sequencer-local methods of
|
|
19
|
+
the same name.
|
|
20
|
+
|
|
21
|
+
- **`Hubbado::Sequence::Runner`** — outcome-dispatch + safety-net logic
|
|
22
|
+
extracted from `RunSequence` into a standalone object.
|
|
23
|
+
- Ships with its own `Substitute` for unit-testing the dispatch
|
|
24
|
+
behaviour in isolation.
|
|
25
|
+
- `Runner.build` factory lets `Runner.configure` install it as a
|
|
26
|
+
dependency on any consumer.
|
|
27
|
+
- `RunSequence` is now a thin delegator that retains its existing
|
|
28
|
+
controller-side API; no migration is needed for controllers already
|
|
29
|
+
using `run_sequence`.
|
|
30
|
+
|
|
31
|
+
- **`Hubbado::Sequence::Path.resolve`** — shared ctx-path helper used by
|
|
32
|
+
every macro that reads a configurable location from ctx.
|
|
33
|
+
- Accepts a Symbol (one-key shorthand) or an Array of Symbols (nested
|
|
34
|
+
fetch); walks via `fetch`.
|
|
35
|
+
- Explicit `missing:` policy:
|
|
36
|
+
- `:raise` (default) — propagates `KeyError`. Right for
|
|
37
|
+
Find/Validate/Build, where a missing path is a wiring bug.
|
|
38
|
+
- `:nil` — returns nil. Right for Deserialize, which may legitimately
|
|
39
|
+
run before any params have been posted.
|
|
40
|
+
- Falling back to `send` on a non-hash was considered and rejected: it
|
|
41
|
+
would silently overlap path traversal with method calls and undermine
|
|
42
|
+
the strict-Ctx contract.
|
|
43
|
+
|
|
44
|
+
- **`Macros::Contract::Validate`** — `from:` is now optional.
|
|
45
|
+
- Omit it when the contract has already been deserialised (e.g. via
|
|
46
|
+
`Contract::Deserialize`) to validate as-is and skip re-deserialising.
|
|
47
|
+
- When supplied, behaviour is unchanged.
|
|
48
|
+
|
|
49
|
+
### Changed (breaking)
|
|
50
|
+
|
|
51
|
+
- **Controls factory method renamed to `example_class`**, matching the
|
|
52
|
+
Eventide convention (`example` returns a configured instance,
|
|
53
|
+
`example_class` returns the configurable class).
|
|
54
|
+
- `Controls::Contract.klass` → `Controls::Contract.example_class`.
|
|
55
|
+
- `Controls::Model.example` → `Controls::Model.example_class`.
|
|
56
|
+
- `Controls::Policy.example` → `Controls::Policy.example_class`.
|
|
57
|
+
- The previous shapes returned classes despite being named `example` /
|
|
58
|
+
`klass`. Consumer tests need their call sites updated.
|
|
59
|
+
|
|
60
|
+
- **`Macros::Model::Find`** — `id_key:` is now a single ctx-path argument
|
|
61
|
+
resolved via `Path.resolve`.
|
|
62
|
+
- Accepts a Symbol (e.g. `id_key: :user_id`) or an Array of Symbols
|
|
63
|
+
(e.g. `id_key: %i[params id]`).
|
|
64
|
+
- Default remains `%i[params id]`.
|
|
65
|
+
- Callers passing the previous shape need to switch to the path form.
|
|
66
|
+
|
|
67
|
+
- **`Macros::Contract::Build`** — the model attribute is now optional and
|
|
68
|
+
accepts a ctx-path.
|
|
69
|
+
- Symbol or Array of Symbols, resolved via `Path.resolve`.
|
|
70
|
+
- Omit it for contract-first flows where there is no model yet.
|
|
71
|
+
- Previous form (single Symbol naming a top-level ctx key, required)
|
|
72
|
+
continues to work because a Symbol is a valid path.
|
|
73
|
+
|
|
74
|
+
### Removed
|
|
75
|
+
|
|
76
|
+
- **`I18n.default_locale = :en` override** no longer set by
|
|
77
|
+
`lib/hubbado/sequence.rb` on require.
|
|
78
|
+
- Host apps are responsible for their own I18n configuration.
|
|
79
|
+
- The gem's translation registration (`I18n.load_path += …`) is
|
|
80
|
+
unchanged — `config/locales/en.yml` still ships and is still loaded.
|
|
81
|
+
|
|
82
|
+
## [0.2.0] - Sequencer mixin moved off the namespace
|
|
83
|
+
|
|
84
|
+
### Changed (breaking)
|
|
85
|
+
|
|
86
|
+
- The sequencer mixin moved from `Hubbado::Sequence` to a dedicated
|
|
87
|
+
`Hubbado::Sequence::Sequencer` submodule. Clients now write
|
|
88
|
+
`include Hubbado::Sequence::Sequencer` instead of `include Hubbado::Sequence`.
|
|
89
|
+
The top-level `Hubbado::Sequence` module is now a pure namespace, leaving
|
|
90
|
+
`Sequence::Pipeline`, `Sequence::Ctx`, `Sequence::Result`, etc. unaffected by
|
|
91
|
+
including the sequencer machinery and avoiding constant-lookup leakage from
|
|
92
|
+
the namespace into including classes. No deprecation shim — call sites must
|
|
93
|
+
be updated in lockstep with the gem upgrade.
|
|
94
|
+
|
|
95
|
+
## [0.1.0] - Initial release
|
|
96
|
+
|
|
97
|
+
Initial public surface, building on `evt-dependency`, `evt-configure`,
|
|
98
|
+
`evt-template_method`, `evt-record_invocation`, `evt-casing`, `i18n`, and
|
|
99
|
+
`hubbado-log`.
|
|
100
|
+
|
|
101
|
+
### Added
|
|
102
|
+
|
|
103
|
+
- **Core types**
|
|
104
|
+
- `Hubbado::Sequence::Result` — value object wrapping a `Ctx`, an ok/fail
|
|
105
|
+
flag, a structured error payload, an i18n scope, and a `trail` of
|
|
106
|
+
completed step names. `Result#message` resolves through a per-error
|
|
107
|
+
scope → result scope → framework default → inline message → humanized
|
|
108
|
+
code chain.
|
|
109
|
+
- `Hubbado::Sequence::Ctx` — `Hash` subclass that raises `KeyError` on
|
|
110
|
+
missing keys via `[]`, leaves `fetch` alone for opt-in optional reads.
|
|
111
|
+
- `Hubbado::Sequence::Pipeline` — railway-style step orchestrator with
|
|
112
|
+
block form (`pipeline(ctx) { |p| ... }`) returning the final `Result`
|
|
113
|
+
automatically. Three call shapes: `p.step(:foo)` for local methods,
|
|
114
|
+
`p.step(:foo) { ... }` for inline blocks, `p.invoke(:foo, *args,
|
|
115
|
+
**kwargs)` for declared dependencies. Lenient return convention — only
|
|
116
|
+
explicitly returned failed Results short-circuit. `p.transaction { |t|
|
|
117
|
+
... }` wraps inner steps in `ActiveRecord::Base.transaction`.
|
|
118
|
+
|
|
119
|
+
- **Sequencer mixin** (`include Hubbado::Sequence`)
|
|
120
|
+
- Brings `dependency` (evt-dependency) and `configure` (evt-configure).
|
|
121
|
+
- Class-level `.()` accepts kwargs *or* an existing `Ctx`.
|
|
122
|
+
- Instance `pipeline(ctx)` helper sets `self` as auto-dispatch target.
|
|
123
|
+
- `failure(ctx, **err)` helper auto-applies the sequencer's i18n scope.
|
|
124
|
+
- Auto-derived i18n scope (`Seqs::UpdateUser` → `seqs.update_user`).
|
|
125
|
+
- Default `Substitute` module installed on every including class with
|
|
126
|
+
`succeed_with(**ctx_writes)` / `fail_with(**error)` / `called?(...)`,
|
|
127
|
+
so any sequencer used as a dependency is substitutable without bespoke
|
|
128
|
+
test scaffolding.
|
|
129
|
+
|
|
130
|
+
- **Six framework macros** — declared dependencies that return `Result`s.
|
|
131
|
+
Each ships an inline `Substitute` with `succeed_with` / `fail_with` and a
|
|
132
|
+
past-tense semantic predicate (`fetched?`, `built?`, `validated?`,
|
|
133
|
+
`persisted?`, `checked?`):
|
|
134
|
+
- `Macros::Model::Find` — DB lookup; fails with `:not_found`.
|
|
135
|
+
- `Macros::Model::Build` — instantiate a new (unsaved) record.
|
|
136
|
+
- `Macros::Contract::Build` — wrap a model in a contract.
|
|
137
|
+
- `Macros::Contract::Validate` — run validations against params; fails
|
|
138
|
+
with `:validation_failed`.
|
|
139
|
+
- `Macros::Contract::Persist` — save the contract; fails with
|
|
140
|
+
`:persist_failed`.
|
|
141
|
+
- `Macros::Policy::Check` — authorise via hubbado-policy; fails with
|
|
142
|
+
`:forbidden`, carrying the policy and policy result on `error[:data]`.
|
|
143
|
+
|
|
144
|
+
- **Errors**
|
|
145
|
+
- `Hubbado::Sequence::Errors::Failed` — generic unhandled failure.
|
|
146
|
+
- `Hubbado::Sequence::Errors::NotFound` — unhandled `:not_found`.
|
|
147
|
+
- `Hubbado::Sequence::Errors::Unauthorized` — unhandled `:forbidden`,
|
|
148
|
+
carries the failed `Result`.
|
|
149
|
+
|
|
150
|
+
- **Controller integration** (`include Hubbado::Sequence::RunSequence`)
|
|
151
|
+
- `run_sequence` dispatcher with `success` / `policy_failed` /
|
|
152
|
+
`not_found` / `validation_failed` / `otherwise` outcome blocks.
|
|
153
|
+
- Safety-net raises when `:forbidden` / `:not_found` / any other failure
|
|
154
|
+
isn't handled. `otherwise` deliberately doesn't catch policy denials
|
|
155
|
+
or not_found.
|
|
156
|
+
- Per-handler logging via `Hubbado::Log::Dependency`. Unhandled paths
|
|
157
|
+
log at `:error` before raising.
|
|
158
|
+
|
|
159
|
+
- **Controls** (shipped in `lib/`, à la `hubbado-policy`)
|
|
160
|
+
- `Controls::Model` / `Controls::Contract` / `Controls::Policy` — fake
|
|
161
|
+
AR / Reform / hubbado-policy stand-ins for use in consumer tests.
|
|
162
|
+
|
|
163
|
+
- **i18n**
|
|
164
|
+
- Framework default locale at `config/locales/en.yml` for the standard
|
|
165
|
+
error codes (`:not_found`, `:forbidden`, `:validation_failed`,
|
|
166
|
+
`:persist_failed`, `:conflict`).
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hubbado
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|