Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning and to Conventional Commits.
Unreleased
v0.2.1 (2026-07-31)
No change to lib/ or sig/: this release carries the documentation the gem ships and the process
that publishes it. Briefly::VERSION is the only Ruby file that differs from v0.2.0.
Added
- The README declares the public API that Semantic Versioning covers, and what falls outside it, so
a MINOR bump before
1.0.0now has a stated scope rather than an implied one. - CONTRIBUTING.md states the deprecation policy: a public item ships deprecated in one release, with a runtime warning naming its replacement and the earliest removal version, before a later release removes it. v0.2.0's three removals predate the policy and are named there.
Changed
- Development now resolves through committed lockfiles.
gemfiles/holds one file per Rails line, each pinning that line and evaluating the rootGemfile, and 7.2, 8.0 and 8.1 commit a lockfile beside theirs; CI installs from them frozen instead of deletingGemfile.lockand re-resolving. Contributors testing another Rails version now passBUNDLE_GEMFILErather thanRAILS_VERSION. Nothing an application installs changed. - Releases are published by CI from a pushed tag, authenticating to RubyGems through OIDC, gated on
a manual approval, and signed with sigstore. No publishing credential exists on any machine.
bundle exec rake releaseno longer publishes; it prints the tag steps and exits.
v0.2.0 (2026-07-14)
Added
rails g briefly:install, a Rails generator that writesconfig/initializers/briefly.rb: a workingAppfacade plus a commented, concern-grouped map of every shortcutuse "rails"installs. Pass a name (rails g briefly:install Facade) to rename the constant; re-run it after upgrading to refresh the map, as Rails prompts before overwriting. It loads only underrails generate, so no Rails runtime dependency is added.Briefly::Rails::Configgainserror(Rails.error, the framework's handled-error reporter) andconfig_for(per-environment YAML viaRails.application.config_for). Both are live lookups;config_fortakes an argument, so it never memoizes. Compose one that does by chaining.memoizeonto a shortcut:shortcut(:x) { config_for(:x) }.memoize.Briefly::Rails::DBgainsconnected_to,readingandwritingfor multi-database routing.connected_toforwards the full Rails surface (role:,shard:,prevent_writes:, custom roles);reading/writingare sugar that pin their role and forward the rest.basemust beActiveRecord::Baseor an abstract connection class; a concrete model raisesNotImplementedError.Briefly::Rails::DBgainsselect, a raw-SQL read throughselect_all, returning anActiveRecord::Resulton the query-cache-preserving path, with the same positional and named bind-safety asquery.querykeeps running arbitrary SQL (writes and DDL included) viaexec_query.Briefly::Rails::Envgainsdev?andprod?, aliases ofdevelopment?andproduction?.Briefly::Rails::Instrument, a new"rails/instrument"pack with oneinstrumentshortcut overActiveSupport::Notifications.instrument(name, payload) { }. Usable on its own;use "rails"includes it, soApp.instrumentcomes for free.
Changed
- BREAKING: Facade management moved behind a single
App.brieflyaccessor:App.briefly.configure,App.briefly.shortcuts,App.briefly.shortcut?andApp.briefly.clear_memos!. This freesconfigure,shortcuts,shortcut?andclear_memos!for use as your own shortcut names; onlybriefly,inspectandto_sstay reserved on the facade's public surface. - BREAKING:
Briefly::Rails::DB#connection/#connis now an auto-releasing block that forwards tobase.with_connection, yielding the leased connection and releasing it at block exit, the shape oftransaction. It requires a block; the old barebase.lease_connectionaccessor (held on the thread and never released, a leak outside a request) is gone, with no compatibility shim. Anyone needing a held raw lease callslease_connectionon their model directly. - The DB pack's tests now run against real Active Record on in-memory SQLite, so its Active Record
calls are verified rather than mocked.
activerecordandsqlite3joinactivesupportas dev-only dependencies; the gem still declares no Rails runtime dependency. - BREAKING:
shortcutnow returns theBriefly::Shortcutit declares instead of the canonical name Symbol. Refine it in place (shortcut(:x) { ... }.memoize,.rescue_from(Error) { fallback }, in any order) so a shortcut's name is never written twice to annotate it. A bodilessshortcut(:x)fetches an already-declared shortcut (canonical or alias) to refine, raisingBriefly::UnknownShortcutErroron an unknown name; it never re-declares. A shortcut's memoization and its own error handlers live on the shortcut itself, so refining it after a redeclaration affects the declaration you named, exactly as its body does. - BREAKING: The top-level
rescue_from(error_class)verb is now facade-wide only and takes no shortcut names; passing any raisesArgumentErrorpointing atshortcut(name).rescue_from(...). Scope a handler to a shortcut by chaining.rescue_fromonto it. GlobalBriefly.rescue_fromis unchanged. - BREAKING: the rescue-handler registry is now
Briefly::Rescues(wasBriefly::ErrorRegistry), reached throughBriefly.rescues(wasBriefly.errors), because it holdsrescue_fromhandlers, not errors.Briefly.rescues.clearstill resets globally registered handlers; the internal#wideenumerator is gone, replaced by#sizefor counting registrations.
Removed
- BREAKING:
App.reset!. UseApp.briefly.clear_memos!. It was a pure alias forclear_memos!with no internal callers. - BREAKING: The top-level
memoizeDSL verb. Chain.memoizeonto the shortcutshortcutreturns (shortcut(:x) { ... }.memoize), orshortcut(:x).memoizefor one declared elsewhere. - BREAKING: Scoping
rescue_fromby shortcut name on the top-level verb, both single-namerescue_from(Error, :x)and multi-namerescue_from(Error, :a, :b). Scope on the shortcut instead:shortcut(:x).rescue_from(Error) { ... }, chaining onto each of several.
v0.1.0 (2026-07-10)
Added
Briefly.definebuilds a facade of real, introspectable, stubbable methods, with nomethod_missing.shortcut(canonical, *aliases, &body)with argument, keyword and block forwarding; predicate and bang names; last-declaration-wins overrides. Each shortcut compiles to a real method carrying its body'sarityand parameter kinds, with asource_locationpointing at the block you declared. Keyword names are exact, positionals get generated ones (__p0,__r1, …). A wrong-arity call, a missing required keyword and an unknown keyword raiseArgumentErrorat the call site, before anyrescue_fromhandler is consulted.namespace(name) { ... }groups shortcuts behind a child facade, soApp.db.queryworks without burning root-level names. Takes the whole DSL, including nested namespaces.clear_memos!cascades into it.configurestays atomic across the whole tree: a pass that raises anywhere leaves the root and every namespace untouched. Ashortcutof the same name overrides a namespace and drops the child.memoize, permanent per-process caching, correct fornil/false, never caching a rescued fallback.rescue_from, with facade-scoped, facade-wide and global (Briefly.rescue_from) handlers whose return value becomes the shortcut's value.clear_memos!/reset!,configure,shortcuts,shortcut?,inspect.- Pack protocol: any object responding to
#install(builder, **opts).useaccepts keywords, forwarded to the pack'sinstall.Briefly.register(name, pack)/Briefly.pack(name)provide a pack registry, souse "rails/db"resolves; values may be a pack or a constant path resolved on first use, and an unknown name raisesBriefly::UnknownPackError. - Method fabrication, meaning the arity,
parametersandsource_locationeach shortcut reports, is provided by candor, the sole runtime dependency (candor itself has none). Compiled bodies live underCandor::BODY_PREFIX; a shortcut may not take a name there. Briefly::Rails, withconfig/c,config_x/x,envand its predicates,root,cache,logger/log,credentials/cred,helpers/h,routes/r,renderer,render, plus adbnamespace. Nothing in the pack is memoized. ItsConfig,EnvandViewgroups are usable alone.Briefly::Rails::DB, withconnection/conn,transaction/txn,query.querytakes positional (query(sql, 1)) or named (query(sql, id: 1)) binds, and passes a bindless statement through unsanitized. Takesbase:(default"ApplicationRecord"), resolved on every call so a reloaded class is never captured. Memoizes nothing and wires no lifecycle hook, so it works without a booted application.Briefly::Rails::Reloadclears memos viaRails.application.reloader.to_prepare.- RBS signatures in
sig/.