briefly 0.1.0 → 0.2.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 +4 -4
- data/CHANGELOG.md +63 -0
- data/README.md +167 -97
- data/briefly.gemspec +1 -1
- data/lib/briefly/builder.rb +58 -42
- data/lib/briefly/errors.rb +1 -1
- data/lib/briefly/facade.rb +79 -31
- data/lib/briefly/rails/config.rb +35 -0
- data/lib/briefly/rails/db.rb +33 -8
- data/lib/briefly/rails/env.rb +27 -0
- data/lib/briefly/rails/instrument.rb +29 -0
- data/lib/briefly/rails/reload.rb +1 -1
- data/lib/briefly/rails/view.rb +27 -0
- data/lib/briefly/rails.rb +7 -50
- data/lib/briefly/rescues.rb +47 -0
- data/lib/briefly/shortcut.rb +104 -0
- data/lib/briefly/version.rb +1 -1
- data/lib/briefly.rb +26 -9
- data/lib/generators/briefly/install/install_generator.rb +29 -0
- data/lib/generators/briefly/install/templates/briefly.rb.tt +47 -0
- data/sig/briefly/builder.rbs +18 -8
- data/sig/briefly/facade.rbs +26 -12
- data/sig/briefly/rails.rbs +7 -2
- data/sig/briefly/rescues.rbs +21 -0
- data/sig/briefly/{definition.rbs → shortcut.rbs} +8 -4
- data/sig/briefly.rbs +7 -10
- metadata +11 -5
- data/lib/briefly/definition.rb +0 -64
- data/lib/briefly/error_registry.rb +0 -40
- data/sig/briefly/error_registry.rbs +0 -22
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Briefly
|
|
4
|
+
# An ordered list of facade-wide and global +rescue_from+ registrations, queried at dispatch time.
|
|
5
|
+
# A shortcut's own handlers live on the {Briefly::Shortcut}, not here — this holds only the
|
|
6
|
+
# unscoped handlers a single shortcut cannot voice.
|
|
7
|
+
#
|
|
8
|
+
# Entries are stored oldest-first and matched newest-first, so the most recent registration for a
|
|
9
|
+
# given error class wins. Reads are lock-free against a frozen array; writes rebind it under a
|
|
10
|
+
# mutex, because rebinding alone would drop concurrent registrations.
|
|
11
|
+
class Rescues
|
|
12
|
+
# A single registration.
|
|
13
|
+
Entry = Struct.new(:klass, :handler)
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
@entries = [].freeze
|
|
17
|
+
@mutex = Mutex.new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @api private
|
|
21
|
+
# @param klass [Class] matched against the raised error and its subclasses
|
|
22
|
+
# @param handler [Proc] called with +(error, shortcut_name)+
|
|
23
|
+
# @return [self]
|
|
24
|
+
def add(klass, handler)
|
|
25
|
+
@mutex.synchronize { @entries = [*@entries, Entry.new(klass, handler)].freeze }
|
|
26
|
+
self
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @api private
|
|
30
|
+
# @param error [Exception]
|
|
31
|
+
# @return [Proc, nil] the most recently registered handler whose class matches +error+, or +nil+
|
|
32
|
+
def handler_for(error) = @entries.reverse_each.find { |entry| error.is_a?(entry.klass) }&.handler
|
|
33
|
+
|
|
34
|
+
# @api private
|
|
35
|
+
# @return [Integer] how many registrations the registry holds
|
|
36
|
+
def size = @entries.size
|
|
37
|
+
|
|
38
|
+
# Drops every registration. Reach it as +Briefly.rescues.clear+ to reset globally registered
|
|
39
|
+
# handlers between test examples, so a handler from one example cannot leak into the next.
|
|
40
|
+
#
|
|
41
|
+
# @return [self]
|
|
42
|
+
def clear
|
|
43
|
+
@mutex.synchronize { @entries = [].freeze }
|
|
44
|
+
self
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Briefly
|
|
4
|
+
# One shortcut — and the object {Briefly::Builder#shortcut} hands back so you can refine it. A
|
|
5
|
+
# declaration's memoization and its scoped error handlers both live here, on the shortcut itself, so
|
|
6
|
+
# refining after a redeclaration affects the declaration you named, exactly as its body and aliases do.
|
|
7
|
+
#
|
|
8
|
+
# shortcut(:catalog) { Catalog.load }.memoize
|
|
9
|
+
# shortcut(:redis) { REDIS_POOL }.rescue_from(Redis::BaseError) { nil }
|
|
10
|
+
#
|
|
11
|
+
# The name, aliases, body and location are internal; {#memoize} and {#rescue_from} are its public
|
|
12
|
+
# refinement surface. It holds no {Briefly::Builder} reference — both refinements are self-contained.
|
|
13
|
+
class Shortcut
|
|
14
|
+
# @api private
|
|
15
|
+
# @return [Symbol] the primary method name
|
|
16
|
+
attr_reader :canonical
|
|
17
|
+
|
|
18
|
+
# @api private
|
|
19
|
+
# @return [Array<Symbol>] additional method names delegating to the same body and memo cell
|
|
20
|
+
attr_reader :aliases
|
|
21
|
+
|
|
22
|
+
# @api private
|
|
23
|
+
# @return [Proc] the implementation, bound to the facade at call time
|
|
24
|
+
attr_reader :body
|
|
25
|
+
|
|
26
|
+
# @api private
|
|
27
|
+
# @return [Symbol] the private singleton method the body compiles to
|
|
28
|
+
attr_reader :raw_name
|
|
29
|
+
|
|
30
|
+
# @api private
|
|
31
|
+
# @return [Array<Array(Class, Proc)>] this shortcut's own error handlers, oldest first
|
|
32
|
+
attr_reader :rescues
|
|
33
|
+
|
|
34
|
+
# The +[file, line]+ the compiled methods report. {Briefly::Builder#shortcut} resolves it — the body's
|
|
35
|
+
# own location, or the +shortcut+ call site for a location-less Proc like +&:upcase+ — and
|
|
36
|
+
# {Briefly::Builder#namespace} overwrites it: its child-returning proc literal lives in +builder.rb+,
|
|
37
|
+
# and a namespace that pointed there would reintroduce, for namespaces, the lie this field exists to
|
|
38
|
+
# remove.
|
|
39
|
+
#
|
|
40
|
+
# @api private
|
|
41
|
+
# @return [Array(String, Integer)]
|
|
42
|
+
attr_accessor :source_location
|
|
43
|
+
|
|
44
|
+
# @param canonical [Symbol]
|
|
45
|
+
# @param aliases [Array<Symbol>]
|
|
46
|
+
# @param body [Proc]
|
|
47
|
+
# @param source_location [Array(String, Integer)]
|
|
48
|
+
def initialize(canonical, aliases, body, source_location)
|
|
49
|
+
@canonical = canonical
|
|
50
|
+
@aliases = aliases
|
|
51
|
+
@body = body
|
|
52
|
+
@raw_name = Candor.body_name(canonical)
|
|
53
|
+
@memoized = false
|
|
54
|
+
@rescues = []
|
|
55
|
+
@source_location = source_location
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Builder copies the facade's shortcuts before mutating them, so an aborted pass cannot leave the
|
|
59
|
+
# live facade half-configured. Hash#dup would share these arrays.
|
|
60
|
+
#
|
|
61
|
+
# @param other [Briefly::Shortcut]
|
|
62
|
+
# @return [void]
|
|
63
|
+
def initialize_copy(other)
|
|
64
|
+
super
|
|
65
|
+
@aliases = other.aliases.dup
|
|
66
|
+
@rescues = other.rescues.dup
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Memoizes the value: computed once, cached for the process lifetime. A body that takes any
|
|
70
|
+
# argument is refused at build time (in {Briefly::Builder#compile!}); the compiled method takes none.
|
|
71
|
+
#
|
|
72
|
+
# @return [self]
|
|
73
|
+
def memoize
|
|
74
|
+
@memoized = true
|
|
75
|
+
self
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @api private
|
|
79
|
+
# @return [Boolean]
|
|
80
|
+
def memoized? = @memoized
|
|
81
|
+
|
|
82
|
+
# Registers an error handler scoped to this shortcut. The handler's return value becomes the
|
|
83
|
+
# shortcut's value; it is +call+ed with +(error, shortcut_name)+, never bound to the facade. Later
|
|
84
|
+
# registrations for the same error class win.
|
|
85
|
+
#
|
|
86
|
+
# @param error_class [Class] matched against the raised error and its subclasses
|
|
87
|
+
# @yield [error, shortcut_name] the recovery block
|
|
88
|
+
# @return [self]
|
|
89
|
+
def rescue_from(error_class, &handler)
|
|
90
|
+
Briefly.send(:validate_rescue!, error_class, handler)
|
|
91
|
+
@rescues << [error_class, handler]
|
|
92
|
+
self
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# @api private
|
|
96
|
+
# @param error [Exception]
|
|
97
|
+
# @return [Proc, nil] this shortcut's most recently registered handler whose class matches, or +nil+
|
|
98
|
+
def handler_for(error) = @rescues.reverse_each.find { |klass, _| error.is_a?(klass) }&.last
|
|
99
|
+
|
|
100
|
+
# @api private
|
|
101
|
+
# @return [Array<Symbol>] canonical name followed by every alias
|
|
102
|
+
def names = [@canonical, *@aliases]
|
|
103
|
+
end
|
|
104
|
+
end
|
data/lib/briefly/version.rb
CHANGED
data/lib/briefly.rb
CHANGED
|
@@ -4,8 +4,8 @@ require "candor"
|
|
|
4
4
|
|
|
5
5
|
require "briefly/version"
|
|
6
6
|
require "briefly/errors"
|
|
7
|
-
require "briefly/
|
|
8
|
-
require "briefly/
|
|
7
|
+
require "briefly/shortcut"
|
|
8
|
+
require "briefly/rescues"
|
|
9
9
|
require "briefly/facade"
|
|
10
10
|
require "briefly/builder"
|
|
11
11
|
|
|
@@ -18,7 +18,7 @@ require "briefly/builder"
|
|
|
18
18
|
module Briefly
|
|
19
19
|
autoload :Rails, "briefly/rails"
|
|
20
20
|
|
|
21
|
-
@
|
|
21
|
+
@rescues = Rescues.new
|
|
22
22
|
|
|
23
23
|
# The packs this gem ships, under the short names +use+ accepts. Values are constant paths, never
|
|
24
24
|
# constants: naming +Briefly::Rails::DB+ here would resolve it at load and defeat the autoload above.
|
|
@@ -28,6 +28,7 @@ module Briefly
|
|
|
28
28
|
"rails/env" => "Briefly::Rails::Env",
|
|
29
29
|
"rails/view" => "Briefly::Rails::View",
|
|
30
30
|
"rails/db" => "Briefly::Rails::DB",
|
|
31
|
+
"rails/instrument" => "Briefly::Rails::Instrument",
|
|
31
32
|
"rails/reload" => "Briefly::Rails::Reload"
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -36,7 +37,7 @@ module Briefly
|
|
|
36
37
|
#
|
|
37
38
|
# @yield [] the shortcut declarations
|
|
38
39
|
# @return [Briefly::Facade]
|
|
39
|
-
def define(&) = Facade.new.configure(&)
|
|
40
|
+
def define(&) = Facade.new.briefly.configure(&)
|
|
40
41
|
|
|
41
42
|
# Registers a pack under a short name, so +use "myapp/redis"+ resolves it. Re-registering a name
|
|
42
43
|
# overrides it. There is no inflection and no path guessing: this table is the only source of truth.
|
|
@@ -79,16 +80,32 @@ module Briefly
|
|
|
79
80
|
# @yield [error, shortcut_name] the recovery block; its return value becomes the shortcut's value
|
|
80
81
|
# @return [self]
|
|
81
82
|
def rescue_from(error_class, &handler)
|
|
83
|
+
validate_rescue!(error_class, handler)
|
|
84
|
+
@rescues.add(error_class, handler)
|
|
85
|
+
self
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# The one +rescue_from+ argument check, shared by the global verb here, the facade-wide verb
|
|
89
|
+
# ({Briefly::Builder#rescue_from}) and the per-shortcut form ({Briefly::Shortcut#rescue_from}), so
|
|
90
|
+
# the error class must come first, a block is required, and the message never diverges between them.
|
|
91
|
+
# Private, so it stays off +Briefly+'s public surface; the two cross-class callers reach it with
|
|
92
|
+
# +Briefly.send(:validate_rescue!, ...)+, the same +send+-to-internal seam {Briefly::Facade::Control} uses.
|
|
93
|
+
#
|
|
94
|
+
# @api private
|
|
95
|
+
# @param error_class [Object] must be a Class
|
|
96
|
+
# @param handler [Proc, nil] must be present
|
|
97
|
+
# @return [void]
|
|
98
|
+
# @raise [ArgumentError] if +error_class+ is not a Class, or no block was given
|
|
99
|
+
def validate_rescue!(error_class, handler)
|
|
82
100
|
unless error_class.is_a?(Class)
|
|
83
101
|
raise ArgumentError, "rescue_from expects the error class first, got #{error_class.inspect}"
|
|
84
102
|
end
|
|
85
|
-
raise ArgumentError, "rescue_from(#{error_class}) requires a block" unless handler
|
|
86
103
|
|
|
87
|
-
|
|
88
|
-
self
|
|
104
|
+
raise ArgumentError, "rescue_from(#{error_class}) requires a block" unless handler
|
|
89
105
|
end
|
|
106
|
+
private :validate_rescue!
|
|
90
107
|
|
|
91
|
-
# @return [Briefly::
|
|
92
|
-
attr_reader :
|
|
108
|
+
# @return [Briefly::Rescues] the global registry
|
|
109
|
+
attr_reader :rescues
|
|
93
110
|
end
|
|
94
111
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators/base"
|
|
4
|
+
|
|
5
|
+
module Briefly
|
|
6
|
+
# Namespace for briefly's Rails generators, discovered by `rails generate`.
|
|
7
|
+
module Generators
|
|
8
|
+
# Writes +config/initializers/briefly.rb+: a working +Briefly.define { use "rails" }+ facade plus a
|
|
9
|
+
# commented, concern-grouped map of every shortcut the +rails+ pack installs. Re-run it to update —
|
|
10
|
+
# Rails prompts before overwriting, so the map refreshes after a gem upgrade.
|
|
11
|
+
#
|
|
12
|
+
# rails generate briefly:install # => App = Briefly.define { use "rails" }
|
|
13
|
+
# rails generate briefly:install Facade # => Facade = Briefly.define { use "rails" }
|
|
14
|
+
#
|
|
15
|
+
# +::Rails+ is fully qualified throughout: a bare +Rails+ here would resolve to {Briefly::Rails},
|
|
16
|
+
# the pack, not the framework. There is deliberately no +sig/+ entry: generators load only under
|
|
17
|
+
# railties, outside the gem's typed runtime surface.
|
|
18
|
+
class InstallGenerator < ::Rails::Generators::Base
|
|
19
|
+
source_root File.expand_path("templates", __dir__)
|
|
20
|
+
|
|
21
|
+
argument :name, type: :string, default: "App", banner: "ConstantName"
|
|
22
|
+
|
|
23
|
+
# @return [void]
|
|
24
|
+
def create_initializer
|
|
25
|
+
template "briefly.rb.tt", "config/initializers/briefly.rb"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# briefly — a terse, curated facade over your app's most reached-for objects.
|
|
4
|
+
# Re-run `rails g briefly:install` after upgrading to refresh the map below. It rewrites this whole
|
|
5
|
+
# file (Rails shows a diff first), so keep any shortcuts you add here under version control.
|
|
6
|
+
<%= name %> = Briefly.define do
|
|
7
|
+
use "rails"
|
|
8
|
+
|
|
9
|
+
# Your own shortcuts go here (a body can call the other shortcuts). `shortcut` returns the shortcut
|
|
10
|
+
# you refine: chain `.memoize` to compute once, or `.rescue_from(Error) { fallback }` to recover — in
|
|
11
|
+
# any order. `rescue_from` on its own line stays for facade-wide handlers.
|
|
12
|
+
# shortcut(:redis) { REDIS_POOL }
|
|
13
|
+
# shortcut(:stripe) { Stripe::Client.new(x.stripe_key) }
|
|
14
|
+
# shortcut(:payments) { config_for(:payments) }.memoize # compute once (cleared on reload)
|
|
15
|
+
# shortcut(:cache) { Redis.new }.rescue_from(Redis::CannotConnectError) { NullCache.new }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# --- What `use "rails"` gives you (call as `<%= name %>.<shortcut>`), by concern ---
|
|
19
|
+
#
|
|
20
|
+
# config <%= name %>.config, <%= name %>.c -> Rails.configuration
|
|
21
|
+
# <%= name %>.config_x, <%= name %>.x -> Rails.configuration.x
|
|
22
|
+
# <%= name %>.root -> Rails.root
|
|
23
|
+
# <%= name %>.cache -> Rails.cache
|
|
24
|
+
# <%= name %>.logger, <%= name %>.log -> Rails.logger
|
|
25
|
+
# <%= name %>.credentials, <%= name %>.cred -> Rails.application.credentials
|
|
26
|
+
# <%= name %>.error -> Rails.error (.report(e) / .handle { })
|
|
27
|
+
# <%= name %>.config_for(:key, env:) -> Rails.application.config_for (env: optional)
|
|
28
|
+
#
|
|
29
|
+
# env <%= name %>.env -> Rails.env
|
|
30
|
+
# <%= name %>.production?, <%= name %>.prod? <%= name %>.development?, <%= name %>.dev? <%= name %>.test? <%= name %>.local?
|
|
31
|
+
#
|
|
32
|
+
# view <%= name %>.helpers, <%= name %>.h -> ApplicationController.helpers
|
|
33
|
+
# <%= name %>.routes, <%= name %>.r -> Rails.application.routes.url_helpers
|
|
34
|
+
# <%= name %>.renderer -> ApplicationController.renderer
|
|
35
|
+
# <%= name %>.render(:tmpl, locals:) -> renderer.render
|
|
36
|
+
#
|
|
37
|
+
# notify <%= name %>.instrument(event, payload) { } -> ActiveSupport::Notifications
|
|
38
|
+
#
|
|
39
|
+
# db <%= name %>.db.connection { |c| }, <%= name %>.db.conn { |c| } -> yields a connection, auto-releases
|
|
40
|
+
# <%= name %>.db.transaction { }, <%= name %>.db.txn { } -> wraps the block in a transaction
|
|
41
|
+
# <%= name %>.db.select(sql, id: 1) / select(sql, 1) -> a read (SELECT); named or positional binds
|
|
42
|
+
# <%= name %>.db.query(sql, id: 1) / query(sql, 1) -> arbitrary SQL (writes, DDL); same binds
|
|
43
|
+
# <%= name %>.db.connected_to(role:, shard:, prevent_writes:) { }
|
|
44
|
+
# <%= name %>.db.reading { }, <%= name %>.db.writing { } -> run the block under a role
|
|
45
|
+
#
|
|
46
|
+
# Facades are cheap and share no state — add another for a separate concern:
|
|
47
|
+
# Db = Briefly.define { use "rails/db" }
|
data/sig/briefly/builder.rbs
CHANGED
|
@@ -1,28 +1,38 @@
|
|
|
1
1
|
module Briefly
|
|
2
2
|
# Receives the DSL. Blocks are `instance_eval`'d here, never on the facade.
|
|
3
3
|
class Builder
|
|
4
|
+
# A validated `compile!` pass, ready for a `Facade` to install.
|
|
5
|
+
class Plan < ::Struct[untyped]
|
|
6
|
+
attr_accessor defs: Hash[Symbol, Shortcut]
|
|
7
|
+
attr_accessor rescue_entries: Array[Briefly::rescue_entry]
|
|
8
|
+
attr_accessor children: Hash[Symbol, Facade]
|
|
9
|
+
attr_accessor child_plans: Array[[ Facade, Plan ]]
|
|
10
|
+
|
|
11
|
+
def self.new: (Hash[Symbol, Shortcut] defs, Array[Briefly::rescue_entry] rescue_entries, Hash[Symbol, Facade] children, Array[[ Facade, Plan ]] child_plans) -> Plan
|
|
12
|
+
end
|
|
13
|
+
|
|
4
14
|
@facade: Facade
|
|
5
|
-
@defs: Hash[Symbol,
|
|
15
|
+
@defs: Hash[Symbol, Shortcut]
|
|
6
16
|
@children: Hash[Symbol, Facade]
|
|
7
|
-
@
|
|
17
|
+
@rescues: Array[Briefly::rescue_entry]
|
|
8
18
|
|
|
9
19
|
# One collected namespace pass per name, held until `compile!` has validated the whole tree.
|
|
10
20
|
@pending: Hash[Symbol, Builder]
|
|
11
21
|
|
|
12
22
|
attr_reader facade: Facade
|
|
13
23
|
|
|
14
|
-
def initialize: (Facade facade, Hash[Symbol,
|
|
24
|
+
def initialize: (Facade facade, Hash[Symbol, Shortcut] defs, Hash[Symbol, Facade] children) -> void
|
|
15
25
|
def use: (pack_ref pack, **untyped opts) -> self
|
|
16
26
|
def namespace: (Symbol name) { (?) [self: Builder] -> void } -> Symbol
|
|
17
|
-
def shortcut: (Symbol canonical, *Symbol aliases) { (?) [self: Facade] -> untyped } ->
|
|
18
|
-
def
|
|
19
|
-
def
|
|
20
|
-
def compile!: () -> Briefly::plan
|
|
27
|
+
def shortcut: (Symbol canonical, *Symbol aliases) ?{ (?) [self: Facade] -> untyped } -> Shortcut
|
|
28
|
+
def rescue_from: (Class error_class, *Symbol names) { (StandardError, Symbol) -> untyped } -> self
|
|
29
|
+
def compile!: () -> Plan
|
|
21
30
|
|
|
22
31
|
private
|
|
23
32
|
|
|
33
|
+
def source_location_for: (Proc body) -> [ String, Integer ]
|
|
24
34
|
def validate_name!: (untyped name) -> void
|
|
25
35
|
def purge: (Array[Symbol] names, except: Symbol) -> void
|
|
26
|
-
def fetch: (Symbol name) ->
|
|
36
|
+
def fetch: (Symbol name) -> Shortcut
|
|
27
37
|
end
|
|
28
38
|
end
|
data/sig/briefly/facade.rbs
CHANGED
|
@@ -5,32 +5,46 @@ module Briefly
|
|
|
5
5
|
# Names a shortcut may not take.
|
|
6
6
|
RESERVED: Array[Symbol]
|
|
7
7
|
|
|
8
|
-
@__defs: Hash[Symbol,
|
|
8
|
+
@__defs: Hash[Symbol, Shortcut]
|
|
9
9
|
@__aliases: Hash[Symbol, Symbol]
|
|
10
10
|
@__memos: Hash[Symbol, untyped]
|
|
11
11
|
@__monitor: Monitor
|
|
12
|
-
@
|
|
13
|
-
@
|
|
12
|
+
@__rescues: Rescues
|
|
13
|
+
@__installed: Array[Symbol]
|
|
14
14
|
@__children: Hash[Symbol, Facade]
|
|
15
15
|
|
|
16
16
|
def initialize: () -> void
|
|
17
|
-
def
|
|
18
|
-
def shortcut?: (Symbol name) -> bool
|
|
19
|
-
def clear_memos!: () -> self
|
|
20
|
-
def reset!: () -> self
|
|
21
|
-
def configure: () ?{ (?) [self: Builder] -> void } -> self
|
|
17
|
+
def briefly: () -> Control
|
|
22
18
|
def inspect: () -> String
|
|
23
19
|
def to_s: () -> String
|
|
24
20
|
|
|
21
|
+
# The management surface, forwarded to the facade's private `__`-prefixed methods. Mutators return
|
|
22
|
+
# the facade; readers return their values.
|
|
23
|
+
class Control
|
|
24
|
+
@facade: Facade
|
|
25
|
+
|
|
26
|
+
def initialize: (Facade facade) -> void
|
|
27
|
+
def shortcuts: () -> Array[Symbol]
|
|
28
|
+
def shortcut?: (Symbol name) -> bool
|
|
29
|
+
def clear_memos!: () -> Facade
|
|
30
|
+
def configure: () ?{ (?) [self: Builder] -> void } -> Facade
|
|
31
|
+
def inspect: () -> String
|
|
32
|
+
def to_s: () -> String
|
|
33
|
+
end
|
|
34
|
+
|
|
25
35
|
private
|
|
26
36
|
|
|
37
|
+
def __shortcuts: () -> Array[Symbol]
|
|
38
|
+
def __shortcut?: (Symbol name) -> bool
|
|
39
|
+
def __clear_memos!: () -> self
|
|
40
|
+
def __configure: () ?{ (?) [self: Builder] -> void } -> self
|
|
27
41
|
def __prepare: () -> Builder
|
|
28
|
-
def __commit: (
|
|
29
|
-
def __install: (Hash[Symbol,
|
|
42
|
+
def __commit: (Builder::Plan plan) -> self
|
|
43
|
+
def __install: (Hash[Symbol, Shortcut] defs, Array[Briefly::rescue_entry] rescue_entries) -> self
|
|
30
44
|
def __remove_methods: (Array[Symbol] names) -> void
|
|
31
|
-
def __define: (
|
|
45
|
+
def __define: (Shortcut defn) -> void
|
|
32
46
|
def __call: (Symbol name, *untyped, **untyped) ?{ (?) -> untyped } -> untyped
|
|
33
47
|
def __memo: (Symbol name) { () -> untyped } -> untyped
|
|
34
|
-
def __handle: (StandardError error,
|
|
48
|
+
def __handle: (StandardError error, Shortcut defn) -> untyped
|
|
35
49
|
end
|
|
36
50
|
end
|
data/sig/briefly/rails.rbs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
module Briefly
|
|
2
2
|
# Shortcut pack for Rails applications. Loaded only when `Briefly::Rails` is referenced.
|
|
3
3
|
#
|
|
4
|
-
# An umbrella over `Config`, `Env`, `View` and `Reload`, plus `DB` under the `db`
|
|
5
|
-
# nested pack exposes `install` and nothing else.
|
|
4
|
+
# An umbrella over `Config`, `Env`, `View`, `Instrument` and `Reload`, plus `DB` under the `db`
|
|
5
|
+
# namespace. Each nested pack exposes `install` and nothing else.
|
|
6
6
|
module Rails
|
|
7
7
|
def self?.install: (Builder builder) -> Builder
|
|
8
8
|
|
|
@@ -21,6 +21,11 @@ module Briefly
|
|
|
21
21
|
def self?.install: (Builder builder) -> Builder
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
# ActiveSupport::Notifications instrumentation.
|
|
25
|
+
module Instrument
|
|
26
|
+
def self?.install: (Builder builder) -> Builder
|
|
27
|
+
end
|
|
28
|
+
|
|
24
29
|
# One database, reached through one Active Record class. `base` is a constant path resolved on
|
|
25
30
|
# every call, so a reloaded class is never captured.
|
|
26
31
|
module DB
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Briefly
|
|
2
|
+
# An ordered list of facade-wide and global `rescue_from` registrations, queried at dispatch time.
|
|
3
|
+
class Rescues
|
|
4
|
+
# A single registration.
|
|
5
|
+
class Entry < ::Struct[untyped]
|
|
6
|
+
attr_accessor klass: Class
|
|
7
|
+
attr_accessor handler: Briefly::handler
|
|
8
|
+
|
|
9
|
+
def self.new: (Class klass, Briefly::handler handler) -> Entry
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
@entries: Array[Entry]
|
|
13
|
+
@mutex: Thread::Mutex
|
|
14
|
+
|
|
15
|
+
def initialize: () -> void
|
|
16
|
+
def add: (Class klass, Briefly::handler handler) -> self
|
|
17
|
+
def handler_for: (Exception error) -> Briefly::handler?
|
|
18
|
+
def size: () -> Integer
|
|
19
|
+
def clear: () -> self
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
module Briefly
|
|
2
|
-
# One shortcut
|
|
3
|
-
|
|
2
|
+
# One shortcut, and the object `Builder#shortcut` returns so you can refine it. Its memoization and
|
|
3
|
+
# its own error handlers live here; `#memoize` and `#rescue_from` are the public refinement surface.
|
|
4
|
+
class Shortcut
|
|
4
5
|
attr_reader canonical: Symbol
|
|
5
6
|
attr_reader aliases: Array[Symbol]
|
|
6
7
|
attr_reader body: Proc
|
|
7
8
|
attr_reader raw_name: Symbol
|
|
9
|
+
attr_reader rescues: Array[[ Class, Briefly::handler ]]
|
|
8
10
|
|
|
9
11
|
# The `[file, line]` the compiled methods report; `Builder#namespace` overwrites it.
|
|
10
12
|
attr_accessor source_location: [ String, Integer ]
|
|
@@ -12,9 +14,11 @@ module Briefly
|
|
|
12
14
|
@memoized: bool
|
|
13
15
|
|
|
14
16
|
def initialize: (Symbol canonical, Array[Symbol] aliases, Proc body, [ String, Integer ] source_location) -> void
|
|
15
|
-
def initialize_copy: (
|
|
16
|
-
def memoize
|
|
17
|
+
def initialize_copy: (Shortcut other) -> void
|
|
18
|
+
def memoize: () -> self
|
|
17
19
|
def memoized?: () -> bool
|
|
20
|
+
def rescue_from: (Class error_class) { (StandardError, Symbol) -> untyped } -> self
|
|
21
|
+
def handler_for: (Exception error) -> Briefly::handler?
|
|
18
22
|
def names: () -> Array[Symbol]
|
|
19
23
|
end
|
|
20
24
|
end
|
data/sig/briefly.rbs
CHANGED
|
@@ -9,7 +9,7 @@ module Briefly
|
|
|
9
9
|
class Error < StandardError
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
# Raised when
|
|
12
|
+
# Raised when a bodiless `shortcut` names a shortcut that does not exist.
|
|
13
13
|
class UnknownShortcutError < Error
|
|
14
14
|
end
|
|
15
15
|
|
|
@@ -24,13 +24,8 @@ module Briefly
|
|
|
24
24
|
# A recovery block. Its return value becomes the shortcut's return value.
|
|
25
25
|
type handler = ^(StandardError error, Symbol shortcut_name) -> untyped
|
|
26
26
|
|
|
27
|
-
# A
|
|
28
|
-
type
|
|
29
|
-
|
|
30
|
-
# A validated `configure` pass, ready to install: definitions, error handlers, namespace children,
|
|
31
|
-
# and one `[child, plan]` pair per namespace this pass collected. Recursive, so RBS types the tail
|
|
32
|
-
# as `untyped`.
|
|
33
|
-
type plan = [ Hash[Symbol, Definition], Array[error_entry], Hash[Symbol, Facade], Array[[ Facade, untyped ]] ]
|
|
27
|
+
# A facade-wide rescue handler, before `Rescues` receives it.
|
|
28
|
+
type rescue_entry = [ Class, handler ]
|
|
34
29
|
|
|
35
30
|
# A pack is any object responding to `#install(builder)`. Options are optional: Ruby drops an empty
|
|
36
31
|
# `**` splat, so a pack that takes none needs no `**opts` parameter.
|
|
@@ -44,7 +39,7 @@ module Briefly
|
|
|
44
39
|
# A registry entry: a pack, or a constant path resolved on first use.
|
|
45
40
|
type pack_entry = _Pack | String
|
|
46
41
|
|
|
47
|
-
self.@
|
|
42
|
+
self.@rescues: Rescues
|
|
48
43
|
self.@packs: Hash[String, pack_entry]
|
|
49
44
|
|
|
50
45
|
# Shortcuts are compiled at runtime, so RBS cannot see them. Declare the ones you rely on in
|
|
@@ -53,5 +48,7 @@ module Briefly
|
|
|
53
48
|
def self.register: (String | Symbol name, pack_entry pack) -> Module
|
|
54
49
|
def self.pack: (String | Symbol name) -> _Pack
|
|
55
50
|
def self.rescue_from: (Class error_class) { (StandardError, Symbol) -> untyped } -> Module
|
|
56
|
-
def self.
|
|
51
|
+
def self.rescues: () -> Rescues
|
|
52
|
+
|
|
53
|
+
private def self.validate_rescue!: (untyped error_class, Proc? handler) -> void
|
|
57
54
|
end
|
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.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Leonid Svyatov
|
|
@@ -40,20 +40,26 @@ files:
|
|
|
40
40
|
- briefly.gemspec
|
|
41
41
|
- lib/briefly.rb
|
|
42
42
|
- lib/briefly/builder.rb
|
|
43
|
-
- lib/briefly/definition.rb
|
|
44
|
-
- lib/briefly/error_registry.rb
|
|
45
43
|
- lib/briefly/errors.rb
|
|
46
44
|
- lib/briefly/facade.rb
|
|
47
45
|
- lib/briefly/rails.rb
|
|
46
|
+
- lib/briefly/rails/config.rb
|
|
48
47
|
- lib/briefly/rails/db.rb
|
|
48
|
+
- lib/briefly/rails/env.rb
|
|
49
|
+
- lib/briefly/rails/instrument.rb
|
|
49
50
|
- lib/briefly/rails/reload.rb
|
|
51
|
+
- lib/briefly/rails/view.rb
|
|
52
|
+
- lib/briefly/rescues.rb
|
|
53
|
+
- lib/briefly/shortcut.rb
|
|
50
54
|
- lib/briefly/version.rb
|
|
55
|
+
- lib/generators/briefly/install/install_generator.rb
|
|
56
|
+
- lib/generators/briefly/install/templates/briefly.rb.tt
|
|
51
57
|
- sig/briefly.rbs
|
|
52
58
|
- sig/briefly/builder.rbs
|
|
53
|
-
- sig/briefly/definition.rbs
|
|
54
|
-
- sig/briefly/error_registry.rbs
|
|
55
59
|
- sig/briefly/facade.rbs
|
|
56
60
|
- sig/briefly/rails.rbs
|
|
61
|
+
- sig/briefly/rescues.rbs
|
|
62
|
+
- sig/briefly/shortcut.rbs
|
|
57
63
|
- sig/manifest.yaml
|
|
58
64
|
homepage: https://github.com/svyatov/briefly
|
|
59
65
|
licenses:
|
data/lib/briefly/definition.rb
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Briefly
|
|
4
|
-
# One shortcut declaration: its canonical name, its aliases, its body, and whether it memoizes.
|
|
5
|
-
#
|
|
6
|
-
# @api private
|
|
7
|
-
class Definition
|
|
8
|
-
# @return [Symbol] the primary method name
|
|
9
|
-
attr_reader :canonical
|
|
10
|
-
|
|
11
|
-
# @return [Array<Symbol>] additional method names delegating to the same body and memo cell
|
|
12
|
-
attr_reader :aliases
|
|
13
|
-
|
|
14
|
-
# @return [Proc] the implementation, bound to the facade at call time
|
|
15
|
-
attr_reader :body
|
|
16
|
-
|
|
17
|
-
# @return [Symbol] the private singleton method the body compiles to
|
|
18
|
-
attr_reader :raw_name
|
|
19
|
-
|
|
20
|
-
# The +[file, line]+ the compiled methods report. {Briefly::Builder#shortcut} resolves it — the body's
|
|
21
|
-
# own location, or the +shortcut+ call site for a bodiless-of-location Proc like +&:upcase+ — and
|
|
22
|
-
# {Briefly::Builder#namespace} overwrites it: its child-returning proc literal lives in +builder.rb+,
|
|
23
|
-
# and a namespace that pointed there would reintroduce, for namespaces, the lie this field exists to
|
|
24
|
-
# remove.
|
|
25
|
-
#
|
|
26
|
-
# @return [Array(String, Integer)]
|
|
27
|
-
attr_accessor :source_location
|
|
28
|
-
|
|
29
|
-
# @param canonical [Symbol]
|
|
30
|
-
# @param aliases [Array<Symbol>]
|
|
31
|
-
# @param body [Proc]
|
|
32
|
-
# @param source_location [Array(String, Integer)]
|
|
33
|
-
def initialize(canonical, aliases, body, source_location)
|
|
34
|
-
@canonical = canonical
|
|
35
|
-
@aliases = aliases
|
|
36
|
-
@body = body
|
|
37
|
-
@raw_name = Candor.body_name(canonical)
|
|
38
|
-
@memoized = false
|
|
39
|
-
@source_location = source_location
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# Builder copies the facade's definitions before mutating them, so an aborted pass cannot
|
|
43
|
-
# leave the live facade half-configured. Hash#dup would share this array.
|
|
44
|
-
#
|
|
45
|
-
# @param other [Briefly::Definition]
|
|
46
|
-
# @return [void]
|
|
47
|
-
def initialize_copy(other)
|
|
48
|
-
super
|
|
49
|
-
@aliases = other.aliases.dup
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Marks the value as computed once and cached. A redeclared shortcut is a fresh, unmemoized
|
|
53
|
-
# Definition, so there is no un-memoize.
|
|
54
|
-
#
|
|
55
|
-
# @return [void]
|
|
56
|
-
def memoize! = @memoized = true
|
|
57
|
-
|
|
58
|
-
# @return [Boolean]
|
|
59
|
-
def memoized? = @memoized
|
|
60
|
-
|
|
61
|
-
# @return [Array<Symbol>] canonical name followed by every alias
|
|
62
|
-
def names = [@canonical, *@aliases]
|
|
63
|
-
end
|
|
64
|
-
end
|