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 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.