briefly 0.2.0 → 0.2.1
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 +44 -22
- data/README.md +106 -56
- data/lib/briefly/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f6c561612c29ff83aaaccdf483e6e563633e54d3dc81c92856857db2f26978b3
|
|
4
|
+
data.tar.gz: a257e3be54bb8e1bb076009dc5982a7c15a6add60f16225bb774e9e3100f3db4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4496b43e5d42fb6b90d0b8f528092207ff867dfbab5febe1d36b69451c307527bd1a056da118a20a48770b77601c49c8965018fa76c904fc203642a4bd5dc178
|
|
7
|
+
data.tar.gz: 430188265df3b33c124f90cbf28de0b34f27fa3090174e74d60a94a63d32a97a4bb905bfb46a72e50263977a44c89cecd29a66799f25847e520ee268071ea18e
|
data/CHANGELOG.md
CHANGED
|
@@ -6,38 +6,60 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## v0.2.1 (2026-07-31)
|
|
10
|
+
|
|
11
|
+
No change to `lib/` or `sig/`: this release carries the documentation the gem ships and the process
|
|
12
|
+
that publishes it. `Briefly::VERSION` is the only Ruby file that differs from v0.2.0.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- The README declares the public API that Semantic Versioning covers, and what falls outside it, so
|
|
16
|
+
a MINOR bump before `1.0.0` now has a stated scope rather than an implied one.
|
|
17
|
+
- CONTRIBUTING.md states the deprecation policy: a public item ships deprecated in one release, with
|
|
18
|
+
a runtime warning naming its replacement and the earliest removal version, before a later release
|
|
19
|
+
removes it. v0.2.0's three removals predate the policy and are named there.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- Development now resolves through committed lockfiles. `gemfiles/` holds one file per Rails line,
|
|
23
|
+
each pinning that line and evaluating the root `Gemfile`, and 7.2, 8.0 and 8.1 commit a lockfile
|
|
24
|
+
beside theirs; CI installs from them frozen instead of deleting `Gemfile.lock` and re-resolving.
|
|
25
|
+
Contributors testing another Rails version now pass `BUNDLE_GEMFILE` rather than `RAILS_VERSION`.
|
|
26
|
+
Nothing an application installs changed.
|
|
27
|
+
- Releases are published by CI from a pushed tag, authenticating to RubyGems through OIDC, gated on
|
|
28
|
+
a manual approval, and signed with sigstore. No publishing credential exists on any machine.
|
|
29
|
+
`bundle exec rake release` no longer publishes; it prints the tag steps and exits.
|
|
30
|
+
|
|
9
31
|
## v0.2.0 (2026-07-14)
|
|
10
32
|
|
|
11
33
|
### Added
|
|
12
|
-
- `rails g briefly:install
|
|
34
|
+
- `rails g briefly:install`, a Rails generator that writes `config/initializers/briefly.rb`: a
|
|
13
35
|
working `App` facade plus a commented, concern-grouped map of every shortcut `use "rails"` installs.
|
|
14
36
|
Pass a name (`rails g briefly:install Facade`) to rename the constant; re-run it after upgrading to
|
|
15
37
|
refresh the map, as Rails prompts before overwriting. It loads only under `rails generate`, so no
|
|
16
38
|
Rails runtime dependency is added.
|
|
17
39
|
- `Briefly::Rails::Config` gains `error` (`Rails.error`, the framework's handled-error reporter) and
|
|
18
40
|
`config_for` (per-environment YAML via `Rails.application.config_for`). Both are live lookups;
|
|
19
|
-
`config_for` takes an argument, so it never memoizes
|
|
41
|
+
`config_for` takes an argument, so it never memoizes. Compose one that does by chaining `.memoize`
|
|
20
42
|
onto a shortcut: `shortcut(:x) { config_for(:x) }.memoize`.
|
|
21
43
|
- `Briefly::Rails::DB` gains `connected_to`, `reading` and `writing` for multi-database routing.
|
|
22
44
|
`connected_to` forwards the full Rails surface (`role:`, `shard:`, `prevent_writes:`, custom roles);
|
|
23
45
|
`reading`/`writing` are sugar that pin their role and forward the rest. `base` must be
|
|
24
46
|
`ActiveRecord::Base` or an abstract connection class; a concrete model raises `NotImplementedError`.
|
|
25
|
-
- `Briefly::Rails::DB` gains `select
|
|
47
|
+
- `Briefly::Rails::DB` gains `select`, a raw-SQL read through `select_all`, returning an
|
|
26
48
|
`ActiveRecord::Result` on the query-cache-preserving path, with the same positional and named
|
|
27
49
|
bind-safety as `query`. `query` keeps running arbitrary SQL (writes and DDL included) via `exec_query`.
|
|
28
50
|
- `Briefly::Rails::Env` gains `dev?` and `prod?`, aliases of `development?` and `production?`.
|
|
29
|
-
- `Briefly::Rails::Instrument
|
|
51
|
+
- `Briefly::Rails::Instrument`, a new `"rails/instrument"` pack with one `instrument` shortcut over
|
|
30
52
|
`ActiveSupport::Notifications.instrument(name, payload) { }`. Usable on its own; `use "rails"`
|
|
31
53
|
includes it, so `App.instrument` comes for free.
|
|
32
54
|
|
|
33
55
|
### Changed
|
|
34
|
-
- **BREAKING:** Facade management moved behind a single `App.briefly` accessor
|
|
56
|
+
- **BREAKING:** Facade management moved behind a single `App.briefly` accessor:
|
|
35
57
|
`App.briefly.configure`, `App.briefly.shortcuts`, `App.briefly.shortcut?` and
|
|
36
58
|
`App.briefly.clear_memos!`. This frees `configure`, `shortcuts`, `shortcut?` and `clear_memos!` for
|
|
37
59
|
use as your own shortcut names; only `briefly`, `inspect` and `to_s` stay reserved on the facade's
|
|
38
60
|
public surface.
|
|
39
61
|
- **BREAKING:** `Briefly::Rails::DB#connection`/`#conn` is now an auto-releasing block that forwards to
|
|
40
|
-
`base.with_connection`, yielding the leased connection and releasing it at block exit
|
|
62
|
+
`base.with_connection`, yielding the leased connection and releasing it at block exit, the shape of
|
|
41
63
|
`transaction`. It requires a block; the old bare `base.lease_connection` accessor (held on the thread
|
|
42
64
|
and never released, a leak outside a request) is gone, with no compatibility shim. Anyone needing a
|
|
43
65
|
held raw lease calls `lease_connection` on their model directly.
|
|
@@ -45,8 +67,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
45
67
|
calls are verified rather than mocked. `activerecord` and `sqlite3` join `activesupport` as dev-only
|
|
46
68
|
dependencies; the gem still declares no Rails runtime dependency.
|
|
47
69
|
- **BREAKING:** `shortcut` now returns the `Briefly::Shortcut` it declares instead of the canonical
|
|
48
|
-
name Symbol. Refine it in place
|
|
49
|
-
in any order
|
|
70
|
+
name Symbol. Refine it in place (`shortcut(:x) { ... }.memoize`, `.rescue_from(Error) { fallback }`,
|
|
71
|
+
in any order) so a shortcut's name is never written twice to annotate it. A bodiless `shortcut(:x)`
|
|
50
72
|
fetches an already-declared shortcut (canonical or alias) to refine, raising
|
|
51
73
|
`Briefly::UnknownShortcutError` on an unknown name; it never re-declares. A shortcut's memoization
|
|
52
74
|
and its own error handlers live on the shortcut itself, so refining it after a redeclaration affects
|
|
@@ -56,51 +78,51 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
56
78
|
Scope a handler to a shortcut by chaining `.rescue_from` onto it. Global `Briefly.rescue_from` is
|
|
57
79
|
unchanged.
|
|
58
80
|
- **BREAKING:** the rescue-handler registry is now `Briefly::Rescues` (was
|
|
59
|
-
`Briefly::ErrorRegistry`), reached through `Briefly.rescues` (was `Briefly.errors`)
|
|
81
|
+
`Briefly::ErrorRegistry`), reached through `Briefly.rescues` (was `Briefly.errors`), because it holds
|
|
60
82
|
`rescue_from` handlers, not errors. `Briefly.rescues.clear` still resets globally registered
|
|
61
83
|
handlers; the internal `#wide` enumerator is gone, replaced by `#size` for counting registrations.
|
|
62
84
|
|
|
63
85
|
### Removed
|
|
64
|
-
- **BREAKING:** `App.reset
|
|
86
|
+
- **BREAKING:** `App.reset!`. Use `App.briefly.clear_memos!`. It was a pure alias for `clear_memos!`
|
|
65
87
|
with no internal callers.
|
|
66
|
-
- **BREAKING:** The top-level `memoize` DSL verb
|
|
88
|
+
- **BREAKING:** The top-level `memoize` DSL verb. Chain `.memoize` onto the shortcut `shortcut`
|
|
67
89
|
returns (`shortcut(:x) { ... }.memoize`), or `shortcut(:x).memoize` for one declared elsewhere.
|
|
68
|
-
- **BREAKING:** Scoping `rescue_from` by shortcut name on the top-level verb
|
|
90
|
+
- **BREAKING:** Scoping `rescue_from` by shortcut name on the top-level verb, both single-name
|
|
69
91
|
`rescue_from(Error, :x)` and multi-name `rescue_from(Error, :a, :b)`. Scope on the shortcut instead:
|
|
70
92
|
`shortcut(:x).rescue_from(Error) { ... }`, chaining onto each of several.
|
|
71
93
|
|
|
72
94
|
## v0.1.0 (2026-07-10)
|
|
73
95
|
|
|
74
96
|
### Added
|
|
75
|
-
- `Briefly.define` builds a facade of real, introspectable, stubbable methods
|
|
97
|
+
- `Briefly.define` builds a facade of real, introspectable, stubbable methods, with no `method_missing`.
|
|
76
98
|
- `shortcut(canonical, *aliases, &body)` with argument, keyword and block forwarding; predicate and
|
|
77
99
|
bang names; last-declaration-wins overrides. Each shortcut compiles to a real method carrying its
|
|
78
|
-
body's `arity` and parameter kinds, with a `source_location` pointing at the block you declared
|
|
79
|
-
|
|
100
|
+
body's `arity` and parameter kinds, with a `source_location` pointing at the block you declared.
|
|
101
|
+
Keyword names are exact, positionals get generated ones (`__p0`, `__r1`, …). A wrong-arity call, a
|
|
80
102
|
missing required keyword and an unknown keyword raise `ArgumentError` at the call site, before any
|
|
81
103
|
`rescue_from` handler is consulted.
|
|
82
|
-
- `namespace(name) { ... }`
|
|
104
|
+
- `namespace(name) { ... }` groups shortcuts behind a child facade, so `App.db.query` works without
|
|
83
105
|
burning root-level names. Takes the whole DSL, including nested namespaces. `clear_memos!` cascades
|
|
84
106
|
into it. `configure` stays atomic across the whole tree: a pass that raises anywhere leaves the root
|
|
85
107
|
and every namespace untouched. A `shortcut` of the same name overrides a namespace and drops the child.
|
|
86
|
-
- `memoize
|
|
87
|
-
- `rescue_from
|
|
108
|
+
- `memoize`, permanent per-process caching, correct for `nil`/`false`, never caching a rescued fallback.
|
|
109
|
+
- `rescue_from`, with facade-scoped, facade-wide and global (`Briefly.rescue_from`) handlers whose return
|
|
88
110
|
value becomes the shortcut's value.
|
|
89
111
|
- `clear_memos!` / `reset!`, `configure`, `shortcuts`, `shortcut?`, `inspect`.
|
|
90
112
|
- Pack protocol: any object responding to `#install(builder, **opts)`. `use` accepts keywords,
|
|
91
113
|
forwarded to the pack's `install`. `Briefly.register(name, pack)` / `Briefly.pack(name)` provide a
|
|
92
114
|
pack registry, so `use "rails/db"` resolves; values may be a pack or a constant path resolved on
|
|
93
115
|
first use, and an unknown name raises `Briefly::UnknownPackError`.
|
|
94
|
-
- Method fabrication
|
|
116
|
+
- Method fabrication, meaning the arity, `parameters` and `source_location` each shortcut reports, is
|
|
95
117
|
provided by [candor](https://github.com/svyatov/candor), the sole runtime dependency (candor itself
|
|
96
118
|
has none). Compiled bodies live under `Candor::BODY_PREFIX`; a shortcut may not take a name there.
|
|
97
|
-
- `Briefly::Rails
|
|
119
|
+
- `Briefly::Rails`, with `config`/`c`, `config_x`/`x`, `env` and its predicates, `root`, `cache`,
|
|
98
120
|
`logger`/`log`, `credentials`/`cred`, `helpers`/`h`, `routes`/`r`, `renderer`, `render`, plus a `db`
|
|
99
121
|
namespace. Nothing in the pack is memoized. Its `Config`, `Env` and `View` groups are usable alone.
|
|
100
|
-
- `Briefly::Rails::DB
|
|
122
|
+
- `Briefly::Rails::DB`, with `connection`/`conn`, `transaction`/`txn`, `query`. `query` takes positional
|
|
101
123
|
(`query(sql, 1)`) or named (`query(sql, id: 1)`) binds, and passes a bindless statement through
|
|
102
124
|
unsanitized. Takes `base:` (default `"ApplicationRecord"`), resolved on every call so a reloaded
|
|
103
125
|
class is never captured. Memoizes nothing and wires no lifecycle hook, so it works without a booted
|
|
104
126
|
application.
|
|
105
|
-
- `Briefly::Rails::Reload`
|
|
127
|
+
- `Briefly::Rails::Reload` clears memos via `Rails.application.reloader.to_prepare`.
|
|
106
128
|
- RBS signatures in `sig/`.
|
data/README.md
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
|
-
# briefly
|
|
1
|
+
# briefly
|
|
2
2
|
|
|
3
|
-
A terse, curated facade over your application's most reached-for objects.
|
|
4
|
-
with a Rails pack included.
|
|
3
|
+
A terse, curated facade over your application's most reached-for objects.
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
[](https://rubygems.org/gems/briefly)
|
|
6
|
+
[](https://github.com/svyatov/briefly/actions/workflows/main.yml)
|
|
7
|
+
[](https://app.codecov.io/gh/svyatov/briefly)
|
|
8
|
+
|
|
9
|
+
- **Ruby 3.2 and up.** Rails is optional: the gem does not declare it, and `Briefly::Rails` is
|
|
10
|
+
autoloaded only when you name it. The Rails packs themselves need Rails 7.2 or newer.
|
|
11
|
+
- **25 shortcuts across six packs.** Config, env, view, db, instrument and reload, each usable on
|
|
12
|
+
its own or taken together through the `"rails"` umbrella.
|
|
13
|
+
- **One runtime dependency.** [candor](https://github.com/svyatov/candor), which is itself
|
|
14
|
+
dependency-free.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
bundle add briefly
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Then, in `config/initializers/app.rb`:
|
|
13
21
|
|
|
14
22
|
```ruby
|
|
15
|
-
# config/initializers/app.rb
|
|
16
23
|
App = Briefly.define do
|
|
17
24
|
use "rails"
|
|
18
25
|
shortcut(:redis) { REDIS_POOL }
|
|
@@ -25,16 +32,16 @@ App.redis # => REDIS_POOL
|
|
|
25
32
|
App.local? # => true in development and test
|
|
26
33
|
```
|
|
27
34
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
Every app grows an `App` module full of `def self.config = Rails.configuration`. `briefly` gives
|
|
36
|
+
you that module without writing it, as real methods. There is no `method_missing`, so `respond_to?`,
|
|
37
|
+
console tab-completion and test stubbing all work. Each shortcut carries its body's `arity` and
|
|
38
|
+
parameter kinds (keyword names are exact, positionals get generated ones), and its `source_location`
|
|
39
|
+
is the block you declared, so jump-to-definition lands in your initializer rather than inside the gem.
|
|
40
|
+
That fabrication is [candor](https://github.com/svyatov/candor), extracted from this gem.
|
|
33
41
|
|
|
34
|
-
|
|
35
|
-
it, and `Briefly::Rails` is autoloaded only when you name it.
|
|
42
|
+
## The Rails generator
|
|
36
43
|
|
|
37
|
-
In a Rails app, `rails g briefly:install` writes `config/initializers/briefly.rb
|
|
44
|
+
In a Rails app, `rails g briefly:install` writes `config/initializers/briefly.rb`: a working `App`
|
|
38
45
|
facade plus a commented, concern-grouped map of every shortcut the `rails` pack gives you. Pass a
|
|
39
46
|
name (`rails g briefly:install Facade`) to call the constant something else. Re-run it after upgrading
|
|
40
47
|
to refresh the map; Rails prompts before overwriting. The generator loads only under `rails generate`,
|
|
@@ -43,7 +50,8 @@ so it adds no runtime dependency.
|
|
|
43
50
|
## Core concepts
|
|
44
51
|
|
|
45
52
|
A **facade** is the object `Briefly.define` returns. You assign it to a constant of your choosing;
|
|
46
|
-
`briefly` never installs one for you. Multiple independent facades share no state
|
|
53
|
+
`briefly` never installs one for you. Multiple independent facades share no state, so an initializer
|
|
54
|
+
can declare as many as it needs:
|
|
47
55
|
|
|
48
56
|
```ruby
|
|
49
57
|
App = Briefly.define { use "rails" }
|
|
@@ -51,7 +59,8 @@ Admin = Briefly.define { shortcut(:audit_log) { AuditLog } }
|
|
|
51
59
|
```
|
|
52
60
|
|
|
53
61
|
A **shortcut** is a name plus a body. The body is always attached to `shortcut`, one block in one
|
|
54
|
-
place, and runs bound to the facade, so it can reach the facade's other shortcuts
|
|
62
|
+
place, and runs bound to the facade, so it can reach the facade's other shortcuts. Inside the
|
|
63
|
+
initializer's `Briefly.define` block:
|
|
55
64
|
|
|
56
65
|
```ruby
|
|
57
66
|
Briefly.define do
|
|
@@ -67,8 +76,8 @@ overrides it silently; that is how you override a pack's shortcut.
|
|
|
67
76
|
|
|
68
77
|
## `memoize`
|
|
69
78
|
|
|
70
|
-
`shortcut` returns the shortcut you refine. Chain `.memoize` onto it to cache the
|
|
71
|
-
then reused for the process lifetime:
|
|
79
|
+
`shortcut` returns the shortcut you refine. Chain `.memoize` onto it in your initializer to cache the
|
|
80
|
+
value, computed once, then reused for the process lifetime:
|
|
72
81
|
|
|
73
82
|
```ruby
|
|
74
83
|
Briefly.define do
|
|
@@ -76,8 +85,9 @@ Briefly.define do
|
|
|
76
85
|
end
|
|
77
86
|
```
|
|
78
87
|
|
|
79
|
-
To memoize a shortcut you didn't declare
|
|
80
|
-
with no block to fetch it, then chain onto that. A bodiless `shortcut` never re-declares
|
|
88
|
+
To memoize a shortcut you didn't declare yourself, one a pack installed, say, call `shortcut(name)`
|
|
89
|
+
with no block to fetch it, then chain onto that. A bodiless `shortcut` never re-declares, so this
|
|
90
|
+
also belongs in the initializer:
|
|
81
91
|
|
|
82
92
|
```ruby
|
|
83
93
|
Briefly.define do
|
|
@@ -95,7 +105,8 @@ retried on next call.
|
|
|
95
105
|
|
|
96
106
|
That guarantee is per-cell, and does not compose. A memoized shortcut whose body *reads* a
|
|
97
107
|
rescue-backed shortcut succeeds, so its own value (containing the fallback) is cached for the
|
|
98
|
-
process lifetime, even after the inner shortcut recovers
|
|
108
|
+
process lifetime, even after the inner shortcut recovers. Two initializer declarations that trap
|
|
109
|
+
you:
|
|
99
110
|
|
|
100
111
|
```ruby
|
|
101
112
|
shortcut(:flaky) { external_call }.rescue_from(SomeError) { "unknown" }.memoize
|
|
@@ -103,13 +114,14 @@ shortcut(:summary) { "build #{flaky}" }.memoize # <- caches "build unknown" fo
|
|
|
103
114
|
```
|
|
104
115
|
|
|
105
116
|
Clearing is a neutral primitive. Management lives behind one accessor, `App.briefly`, so names like
|
|
106
|
-
`configure`, `shortcuts`, `shortcut?` and `clear_memos!` stay yours to use as shortcuts
|
|
117
|
+
`configure`, `shortcuts`, `shortcut?` and `clear_memos!` stay yours to use as shortcuts. Call it
|
|
118
|
+
from application code:
|
|
107
119
|
|
|
108
120
|
```ruby
|
|
109
121
|
App.briefly.clear_memos! # => App (thread-safe)
|
|
110
122
|
```
|
|
111
123
|
|
|
112
|
-
Reclaim one of those names and `App.configure` calls *your* shortcut
|
|
124
|
+
Reclaim one of those names and `App.configure` calls *your* shortcut; the old management call now
|
|
113
125
|
lives only at `App.briefly.configure`. Worth knowing when porting pre-0.2.0 code: a leftover
|
|
114
126
|
`App.configure { ... }` won't raise if a `configure` shortcut exists, it just runs the shortcut and,
|
|
115
127
|
like any non-yielding method, drops the block.
|
|
@@ -121,7 +133,7 @@ like any non-yielding method, drops the block.
|
|
|
121
133
|
The handler's return value becomes the shortcut's return value. To guard one shortcut, chain
|
|
122
134
|
`.rescue_from(error_class) { |e, name| ... }` onto it. The top-level `rescue_from` verb is for what a
|
|
123
135
|
single shortcut can't voice: a facade-wide handler, consulted after each shortcut's own. Error class
|
|
124
|
-
first, always:
|
|
136
|
+
first, always. Both forms belong in the initializer:
|
|
125
137
|
|
|
126
138
|
```ruby
|
|
127
139
|
Briefly.define do
|
|
@@ -131,7 +143,7 @@ Briefly.define do
|
|
|
131
143
|
end
|
|
132
144
|
```
|
|
133
145
|
|
|
134
|
-
The top-level `rescue_from` takes no shortcut names
|
|
146
|
+
The top-level `rescue_from` takes no shortcut names. Pass any and it raises `ArgumentError`, pointing
|
|
135
147
|
you at `shortcut(name).rescue_from(...)`. Scoping a handler to a shortcut lives in one channel, the
|
|
136
148
|
shortcut itself, so its name is never written twice to annotate it. To share one handler across a few
|
|
137
149
|
shortcuts, chain `.rescue_from` onto each; to cover them all, register it facade-wide.
|
|
@@ -140,9 +152,10 @@ Unlike a shortcut body, a handler is not bound to the facade. It is called as
|
|
|
140
152
|
`handler.call(error, name)`, so `self` stays whatever it was where you wrote the block. Reach for
|
|
141
153
|
constants (`Rails.logger`, `Sentry`) rather than bare shortcut names inside a handler.
|
|
142
154
|
|
|
143
|
-
>
|
|
155
|
+
> [!WARNING]
|
|
156
|
+
> A facade-wide `rescue_from(StandardError)` catches your own bugs, not just your app's.
|
|
144
157
|
> `briefly` cannot tell an error raised *by* a shortcut body from one raised *about* the call; a
|
|
145
|
-
> typo and a dead Redis both arrive as a `StandardError
|
|
158
|
+
> typo and a dead Redis both arrive as a `StandardError`. Written in an initializer:
|
|
146
159
|
>
|
|
147
160
|
> ```ruby
|
|
148
161
|
> shortcut(:host) { Rails.aplication.config.host } # typo -> NoMethodError
|
|
@@ -160,8 +173,10 @@ constants (`Rails.logger`, `Sentry`) rather than bare shortcut names inside a ha
|
|
|
160
173
|
> body calling another with a bad argument list is a different matter: that raises *inside* the
|
|
161
174
|
> calling body, where the calling shortcut's own handler sees it like any other error.
|
|
162
175
|
|
|
163
|
-
>
|
|
164
|
-
>
|
|
176
|
+
> [!IMPORTANT]
|
|
177
|
+
> `{}` needs parentheses, on the standalone `rescue_from` only. `rescue_from StandardError { ... }`
|
|
178
|
+
> binds the block to `StandardError`, not to `rescue_from`, and raises `NoMethodError`. Use either
|
|
179
|
+
> form inside your `Briefly.define` block:
|
|
165
180
|
>
|
|
166
181
|
> ```ruby
|
|
167
182
|
> rescue_from StandardError do |e| ... end # do/end, no parens
|
|
@@ -188,11 +203,12 @@ For a raised error, the first `is_a?` match wins, searching in this order:
|
|
|
188
203
|
| 2 | Facade-wide handlers | last registered first |
|
|
189
204
|
| 3 | Global handlers (`Briefly.rescue_from`) | last registered first |
|
|
190
205
|
|
|
191
|
-
|
|
206
|
+
With no match, the error propagates.
|
|
192
207
|
|
|
193
208
|
## `namespace`
|
|
194
209
|
|
|
195
|
-
`namespace` groups shortcuts behind a name, so the root keyspace stays yours
|
|
210
|
+
`namespace` groups shortcuts behind a name, so the root keyspace stays yours. In the initializer,
|
|
211
|
+
and then anywhere in the app:
|
|
196
212
|
|
|
197
213
|
```ruby
|
|
198
214
|
App = Briefly.define do
|
|
@@ -228,7 +244,8 @@ Two limits, both deliberate:
|
|
|
228
244
|
## Packs
|
|
229
245
|
|
|
230
246
|
A pack is any object responding to `#install(builder, **opts)`. Options are optional: Ruby drops an
|
|
231
|
-
empty `**` splat, so a pack taking none needs no keyword parameter.
|
|
247
|
+
empty `**` splat, so a pack taking none needs no keyword parameter. Put the pack in a file your
|
|
248
|
+
app already loads, such as `lib/redis_pack.rb`, and `use` it from the initializer:
|
|
232
249
|
|
|
233
250
|
```ruby
|
|
234
251
|
module RedisPack
|
|
@@ -250,7 +267,7 @@ exactly what `Briefly::Rails::Reload` does. The core stays framework-agnostic; p
|
|
|
250
267
|
### Options
|
|
251
268
|
|
|
252
269
|
Keywords passed to `use` reach the pack's `install`. Ruby drops an empty `**` splat, so a pack that
|
|
253
|
-
takes no options needs no keyword parameter
|
|
270
|
+
takes no options needs no keyword parameter. The same pack file, now taking a `url:`:
|
|
254
271
|
|
|
255
272
|
```ruby
|
|
256
273
|
module RedisPack
|
|
@@ -267,7 +284,8 @@ Api = Briefly.define { use RedisPack, url: "redis://cache:6379" }
|
|
|
267
284
|
### Short names
|
|
268
285
|
|
|
269
286
|
`Briefly.register` maps a name to a pack, so `use` can take a string or symbol. There is no
|
|
270
|
-
inflection and no path guessing; the registry is the only source of truth
|
|
287
|
+
inflection and no path guessing; the registry is the only source of truth. Register in the
|
|
288
|
+
initializer, before the `define` that names it:
|
|
271
289
|
|
|
272
290
|
```ruby
|
|
273
291
|
Briefly.register("myapp/redis", RedisPack) # a pack object
|
|
@@ -315,7 +333,7 @@ Nothing in the pack is memoized. `helpers`, `routes` and `renderer` are live loo
|
|
|
315
333
|
already caches them on objects it refreshes on reload, so caching them again would only go stale.
|
|
316
334
|
It still composes `Briefly::Rails::Reload`, because *your* memoized shortcuts need clearing.
|
|
317
335
|
|
|
318
|
-
Need a custom renderer? Override it; last declaration wins:
|
|
336
|
+
Need a custom renderer? Override it in the initializer; last declaration wins:
|
|
319
337
|
|
|
320
338
|
```ruby
|
|
321
339
|
App = Briefly.define do
|
|
@@ -324,7 +342,7 @@ App = Briefly.define do
|
|
|
324
342
|
end
|
|
325
343
|
```
|
|
326
344
|
|
|
327
|
-
`Briefly::Rails` is an umbrella over
|
|
345
|
+
`Briefly::Rails` is an umbrella over six packs, each usable on its own:
|
|
328
346
|
|
|
329
347
|
| pack | short name | shortcuts |
|
|
330
348
|
|---|---|---|
|
|
@@ -333,6 +351,9 @@ end
|
|
|
333
351
|
| `Briefly::Rails::View` | `"rails/view"` | `helpers`, `routes`, `renderer`, `render` |
|
|
334
352
|
| `Briefly::Rails::DB` | `"rails/db"` | `connection`, `transaction`, `select`, `query`, `connected_to`, `reading`, `writing` |
|
|
335
353
|
| `Briefly::Rails::Instrument` | `"rails/instrument"` | `instrument` |
|
|
354
|
+
| `Briefly::Rails::Reload` | `"rails/reload"` | none; clears memos on every code reload |
|
|
355
|
+
|
|
356
|
+
Take only the parts you want, in the initializer:
|
|
336
357
|
|
|
337
358
|
```ruby
|
|
338
359
|
Worker = Briefly.define do
|
|
@@ -346,14 +367,16 @@ end
|
|
|
346
367
|
|
|
347
368
|
| shortcut | aliases | value |
|
|
348
369
|
|---|---|---|
|
|
349
|
-
| `connection` | `conn` | forwards keywords and the block to `base.with_connection
|
|
370
|
+
| `connection` | `conn` | forwards keywords and the block to `base.with_connection`, yielding the connection and auto-releasing |
|
|
350
371
|
| `transaction` | `txn` | forwards keywords and the block to `base.transaction` |
|
|
351
|
-
| `select` | | `base.with_connection { \|c\| c.select_all(sql) }
|
|
352
|
-
| `query` | | `base.with_connection { \|c\| c.exec_query(sql) }
|
|
372
|
+
| `select` | | `base.with_connection { \|c\| c.select_all(sql) }`, a read (SELECT) on the cache-aware path |
|
|
373
|
+
| `query` | | `base.with_connection { \|c\| c.exec_query(sql) }`, arbitrary SQL, writes and DDL included |
|
|
353
374
|
| `connected_to` | | forwards every argument to `base.connected_to` (`role:`, `shard:`, `prevent_writes:`) |
|
|
354
375
|
| `reading` | | runs the block under the `:reading` role |
|
|
355
376
|
| `writing` | | runs the block under the `:writing` role |
|
|
356
377
|
|
|
378
|
+
Declare the namespace in the initializer, then call it from application code:
|
|
379
|
+
|
|
357
380
|
```ruby
|
|
358
381
|
App = Briefly.define do
|
|
359
382
|
use "rails"
|
|
@@ -365,14 +388,14 @@ App.db.conn { |c| c.select_value("select count(*) from users") }
|
|
|
365
388
|
```
|
|
366
389
|
|
|
367
390
|
`select` and `query` are the two raw-SQL helpers, differing only in which adapter path they take.
|
|
368
|
-
`select(sql, *binds)` runs a read through `select_all
|
|
391
|
+
`select(sql, *binds)` runs a read through `select_all`, the path Rails recommends for a raw SELECT,
|
|
369
392
|
returning an `ActiveRecord::Result` without clearing the query cache. `query(sql, *binds)` runs
|
|
370
393
|
arbitrary SQL through `exec_query`: reads, writes, and DDL all execute. The name tells you which
|
|
371
394
|
you're reaching for; neither polices the SQL it's handed, so `select` will happily run a write you
|
|
372
|
-
give it
|
|
395
|
+
give it. The split is name and cache-path, not a runtime guard.
|
|
373
396
|
|
|
374
397
|
Both sanitize through `base.sanitize_sql_array` when binds are given, and pass the statement through
|
|
375
|
-
untouched when they are not. Positional and named binds both work:
|
|
398
|
+
untouched when they are not. Positional and named binds both work, from application code:
|
|
376
399
|
|
|
377
400
|
```ruby
|
|
378
401
|
App.db.select("select * from users where name like '%ada%'") # no binds, passed through
|
|
@@ -388,12 +411,12 @@ not a convenience: always pass untrusted values as binds. The bindless path is u
|
|
|
388
411
|
merely a read one.
|
|
389
412
|
|
|
390
413
|
`connection`/`conn` mirrors `transaction`: it forwards to `with_connection`, yields the leased
|
|
391
|
-
connection, and auto-releases at block exit, so nothing leaks outside a request. It requires a block
|
|
414
|
+
connection, and auto-releases at block exit, so nothing leaks outside a request. It requires a block;
|
|
392
415
|
there is no bare-lease accessor. Anyone who genuinely needs a held raw lease calls `lease_connection`
|
|
393
416
|
on their model directly.
|
|
394
417
|
|
|
395
|
-
`reading` and `writing` *route* a block
|
|
396
|
-
send specific reads to a replica or pin a write to the primary:
|
|
418
|
+
`reading` and `writing` *route* a block. Everything inside runs under that connection role, so from
|
|
419
|
+
application code you send specific reads to a replica or pin a write to the primary:
|
|
397
420
|
|
|
398
421
|
```ruby
|
|
399
422
|
App.db.reading { App.db.select("select * from reports") } # runs on the replica
|
|
@@ -401,7 +424,8 @@ App.db.writing { Audit.create!(event: "export") } # pinned to the prima
|
|
|
401
424
|
```
|
|
402
425
|
|
|
403
426
|
They're sugar over `connected_to`, which is also a shortcut in its own right and forwards the whole
|
|
404
|
-
Rails surface
|
|
427
|
+
Rails surface: any role, not just reading and writing, plus `shard:` and `prevent_writes:`. Also
|
|
428
|
+
from application code:
|
|
405
429
|
|
|
406
430
|
```ruby
|
|
407
431
|
App.db.connected_to(role: :analytics) { Report.all.to_a } # a custom role
|
|
@@ -413,7 +437,7 @@ The `reading`/`writing` sugar pins its role: a `role:` you pass through it can't
|
|
|
413
437
|
always reads. All of this needs a multi-database app: `base` must be `ActiveRecord::Base` or the
|
|
414
438
|
abstract class that declared `connects_to(database: { ... })`. On a concrete model `connected_to`
|
|
415
439
|
raises `NotImplementedError`, and in a single-database app with no matching connection
|
|
416
|
-
`App.db.reading { ... }` raises `ActiveRecord::ConnectionNotDefined
|
|
440
|
+
`App.db.reading { ... }` raises `ActiveRecord::ConnectionNotDefined`. These are a multi-DB tool, not a
|
|
417
441
|
role flag.
|
|
418
442
|
|
|
419
443
|
**Pass `base:` as a String, not the class.** A pack is `use`d from an initializer, where naming an
|
|
@@ -425,8 +449,8 @@ The pack memoizes nothing and wires no lifecycle hook, so it works without a boo
|
|
|
425
449
|
|
|
426
450
|
### `Briefly::Rails::Reload`
|
|
427
451
|
|
|
428
|
-
`Briefly::Rails` composes it. Use it alone for a facade with no framework
|
|
429
|
-
memoizes objects holding on to reloadable application classes:
|
|
452
|
+
`Briefly::Rails` composes it. Use it alone, in an initializer, for a facade with no framework
|
|
453
|
+
shortcuts that still memoizes objects holding on to reloadable application classes:
|
|
430
454
|
|
|
431
455
|
```ruby
|
|
432
456
|
Admin = Briefly.define do
|
|
@@ -454,7 +478,7 @@ bodies call into each other can deadlock, like any pair of mutually-locking obje
|
|
|
454
478
|
|
|
455
479
|
## Testing
|
|
456
480
|
|
|
457
|
-
Shortcuts are real methods, so nothing special is needed:
|
|
481
|
+
Shortcuts are real methods, so nothing special is needed in a test file:
|
|
458
482
|
|
|
459
483
|
```ruby
|
|
460
484
|
allow(App).to receive(:redis).and_return(fake_redis) # rspec-mocks verifies it
|
|
@@ -481,6 +505,8 @@ We do not fake this with an RBS-only `method_missing`; the gem has none.
|
|
|
481
505
|
|
|
482
506
|
## Migrating a hand-rolled `App`
|
|
483
507
|
|
|
508
|
+
What the initializer replaces, and what it becomes:
|
|
509
|
+
|
|
484
510
|
```ruby
|
|
485
511
|
# before
|
|
486
512
|
module App
|
|
@@ -506,9 +532,33 @@ end
|
|
|
506
532
|
memoized, and `redis` (which you *do* want memoized) is cleared on every dev reload by the Reload
|
|
507
533
|
pack that `Briefly::Rails` composes.
|
|
508
534
|
|
|
535
|
+
## Versioning
|
|
536
|
+
|
|
537
|
+
`briefly` follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html). The public API it
|
|
538
|
+
covers is what an application touches: `Briefly.define`, `Briefly.register`, `Briefly.pack`,
|
|
539
|
+
`Briefly.rescue_from` and `Briefly.rescues`; the `Builder` DSL inside a `define` block (`shortcut`,
|
|
540
|
+
`namespace`, `rescue_from`, `use`); the refinements `shortcut` returns (`memoize`, `rescue_from`);
|
|
541
|
+
the management surface behind `App.briefly`; the canonical names and aliases every shipped pack
|
|
542
|
+
installs; and the error classes under `Briefly::Error`.
|
|
543
|
+
|
|
544
|
+
Outside that promise: anything a shortcut compiles to internally, every `__`-prefixed private method
|
|
545
|
+
on `Facade`, and the RBS signatures in `sig/`, which describe the static surface and are revised
|
|
546
|
+
alongside it.
|
|
547
|
+
|
|
548
|
+
While the version stays below `1.0.0`, an incompatible change ships in a MINOR bump and a compatible
|
|
549
|
+
fix in a PATCH, so a MINOR bump is the one that may need you to read the changelog.
|
|
550
|
+
|
|
509
551
|
## Contributing
|
|
510
552
|
|
|
511
|
-
Bug reports and pull requests are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
553
|
+
Bug reports and pull requests are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md), and
|
|
554
|
+
[CHANGELOG.md](CHANGELOG.md) for what has changed between releases.
|
|
555
|
+
|
|
556
|
+
Questions and bug reports both go to [GitHub Issues](https://github.com/svyatov/briefly/issues).
|
|
557
|
+
|
|
558
|
+
## Maintenance
|
|
559
|
+
|
|
560
|
+
One person maintains `briefly`, actively, in their spare time. Issues and pull requests get a reply,
|
|
561
|
+
and a bug on a supported Ruby or Rails version gets a fix. A large feature request may not.
|
|
512
562
|
|
|
513
563
|
## License
|
|
514
564
|
|
data/lib/briefly/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: briefly
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Leonid Svyatov
|
|
@@ -84,7 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
84
84
|
- !ruby/object:Gem::Version
|
|
85
85
|
version: '0'
|
|
86
86
|
requirements: []
|
|
87
|
-
rubygems_version: 4.0.
|
|
87
|
+
rubygems_version: 4.0.16
|
|
88
88
|
specification_version: 4
|
|
89
89
|
summary: A terse, curated facade over your application's most reached-for objects.
|
|
90
90
|
test_files: []
|