query_stash 0.1.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/LICENSE.txt +21 -0
- data/README.md +477 -0
- data/lib/query_stash/active_record.rb +188 -0
- data/lib/query_stash/base.rb +275 -0
- data/lib/query_stash/mongo.rb +152 -0
- data/lib/query_stash/railtie.rb +150 -0
- data/lib/query_stash/registry/sql.rb +17 -0
- data/lib/query_stash/registry.rb +176 -0
- data/lib/query_stash/sequel.rb +155 -0
- data/lib/query_stash/version.rb +6 -0
- data/lib/query_stash.rb +47 -0
- data/lib/sequel/extensions/query_stash.rb +5 -0
- data/sig/query_stash/active_record.rbs +118 -0
- data/sig/query_stash/base.rbs +151 -0
- data/sig/query_stash/mongo.rbs +90 -0
- data/sig/query_stash/railtie.rbs +24 -0
- data/sig/query_stash/registry/sql.rbs +11 -0
- data/sig/query_stash/registry.rbs +114 -0
- data/sig/query_stash/sequel.rbs +97 -0
- data/sig/query_stash/version.rbs +6 -0
- data/sig/query_stash.rbs +41 -0
- data/sig/sequel/extensions/query_stash.rbs +2 -0
- metadata +82 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Generated from lib/query_stash/base.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module QueryStash
|
|
4
|
+
class Base
|
|
5
|
+
# Where `enable!` and `disable!` keep what they set, when no `isolated_state` is
|
|
6
|
+
# given: a hash in a thread variable, so every fiber of a thread sees the same values and
|
|
7
|
+
# other threads see none.
|
|
8
|
+
#
|
|
9
|
+
# An isolated state is any object that responds to `[]`, `[]=`, `key?` and `delete`, like
|
|
10
|
+
# `ActiveSupport::IsolatedExecutionState`.
|
|
11
|
+
class ThreadVariableState
|
|
12
|
+
THREAD_VARIABLE: Symbol
|
|
13
|
+
|
|
14
|
+
# : (untyped key) -> untyped
|
|
15
|
+
def []: (untyped key) -> untyped
|
|
16
|
+
|
|
17
|
+
# : (untyped key, untyped value) -> untyped
|
|
18
|
+
def []=: (untyped key, untyped value) -> untyped
|
|
19
|
+
|
|
20
|
+
# : (untyped key) -> bool
|
|
21
|
+
def key?: (untyped key) -> bool
|
|
22
|
+
|
|
23
|
+
# : (untyped key) -> untyped
|
|
24
|
+
def delete: (untyped key) -> untyped
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
# : () -> Hash[untyped, untyped]
|
|
29
|
+
def values: () -> Hash[untyped, untyped]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Used when no `instrumenter` is given: runs the block and tells no one.
|
|
33
|
+
class NullInstrumenter
|
|
34
|
+
# : (String _name, ?Hash[Symbol, untyped] payload) ?{ (Hash[Symbol, untyped]) -> untyped } -> untyped
|
|
35
|
+
def instrument: (String _name, ?Hash[Symbol, untyped] payload) ?{ (Hash[Symbol, untyped]) -> untyped } -> untyped
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Used when no `error_reporter` is given.
|
|
39
|
+
class WarnErrorReporter
|
|
40
|
+
# : (Exception error, ?handled: bool) -> void
|
|
41
|
+
def report: (Exception error, ?handled: bool) -> void
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
self.@instances: Concurrent::Map[untyped, untyped]
|
|
45
|
+
|
|
46
|
+
# : (Class subclass) -> void
|
|
47
|
+
def self.inherited: (Class subclass) -> void
|
|
48
|
+
|
|
49
|
+
# : (untyped app) -> instance
|
|
50
|
+
def self.[]: (untyped app) -> instance
|
|
51
|
+
|
|
52
|
+
# : (untyped app) -> bool
|
|
53
|
+
def self.key?: (untyped app) -> bool
|
|
54
|
+
|
|
55
|
+
# Makes `stash` the instance `app` uses.
|
|
56
|
+
# : (untyped app, instance stash) -> instance
|
|
57
|
+
def self.set: (untyped app, instance stash) -> instance
|
|
58
|
+
|
|
59
|
+
@adapter: Symbol
|
|
60
|
+
|
|
61
|
+
@cache: _CacheStore
|
|
62
|
+
|
|
63
|
+
@registry: Registry
|
|
64
|
+
|
|
65
|
+
@raise_on_cache_error: bool
|
|
66
|
+
|
|
67
|
+
@error_reporter: _ErrorReporter
|
|
68
|
+
|
|
69
|
+
@instrumenter: _Instrumenter
|
|
70
|
+
|
|
71
|
+
@isolated_state: _IsolatedState
|
|
72
|
+
|
|
73
|
+
@enabled: bool
|
|
74
|
+
|
|
75
|
+
attr_reader registry: Registry
|
|
76
|
+
|
|
77
|
+
attr_reader cache: _CacheStore
|
|
78
|
+
|
|
79
|
+
attr_reader enabled: bool
|
|
80
|
+
|
|
81
|
+
# What each stash is given:
|
|
82
|
+
#
|
|
83
|
+
# - `enabled`: whether queries are stashed outside of `enable!` and `disable!`.
|
|
84
|
+
# - `isolated_state`: where those keep what they set. By default, a thread variable.
|
|
85
|
+
# - `instrumenter`: told about what the stash does. By default, no one.
|
|
86
|
+
# - `error_reporter`: told about the errors it handles. By default, a warning on stderr.
|
|
87
|
+
# - `raise_on_cache_error`: raise the errors of the cache store, instead of reporting them.
|
|
88
|
+
# - `cache_store`: where results are kept, any object that responds to `read(key)`,
|
|
89
|
+
# `write(key, value, expires_in:)` and `clear`, like an ActiveSupport cache store.
|
|
90
|
+
#
|
|
91
|
+
# @rbs cache_store: _CacheStore
|
|
92
|
+
# @rbs enabled: bool
|
|
93
|
+
# @rbs isolated_state: _IsolatedState?
|
|
94
|
+
# @rbs instrumenter: _Instrumenter?
|
|
95
|
+
# @rbs error_reporter: _ErrorReporter?
|
|
96
|
+
# @rbs raise_on_cache_error: bool
|
|
97
|
+
# @rbs return: void
|
|
98
|
+
def initialize: (cache_store: _CacheStore, ?enabled: bool, ?isolated_state: _IsolatedState?, ?instrumenter: _Instrumenter?, ?error_reporter: _ErrorReporter?, ?raise_on_cache_error: bool) -> void
|
|
99
|
+
|
|
100
|
+
# : () { (Registry registry) -> void } -> void
|
|
101
|
+
def configure: () { (Registry registry) -> void } -> void
|
|
102
|
+
|
|
103
|
+
# Turn the stash on or off for the block. Outside of either, it follows `enabled`.
|
|
104
|
+
# : [T] () { () -> T } -> T
|
|
105
|
+
def enable!: [T] () { () -> T } -> T
|
|
106
|
+
|
|
107
|
+
# : [T] () { () -> T } -> T
|
|
108
|
+
def disable!: [T] () { () -> T } -> T
|
|
109
|
+
|
|
110
|
+
# : (Registry::Rule rule) -> _CacheStore
|
|
111
|
+
def cache_for: (Registry::Rule rule) -> _CacheStore
|
|
112
|
+
|
|
113
|
+
# The rule of the registry the query is to be stashed under, or nil when it isn't to be: the stash is off
|
|
114
|
+
# (see `stashing?`), the query is not one that can be, or no rule matches it.
|
|
115
|
+
# : (*untyped) -> Registry::Rule?
|
|
116
|
+
def rule_for: (*untyped) -> Registry::Rule?
|
|
117
|
+
|
|
118
|
+
# The key the query's result is stored under.
|
|
119
|
+
# : (*untyped) -> String
|
|
120
|
+
def cache_key_for: (*untyped) -> String
|
|
121
|
+
|
|
122
|
+
# The result stored for the query, or nil when there is none (or the cache store failed: that is
|
|
123
|
+
# reported, or raised when `raise_on_cache_error` is set). Emits `cache_read` and, on a hit,
|
|
124
|
+
# `cache_hit`.
|
|
125
|
+
# : (Registry::Rule rule, *untyped args) -> untyped
|
|
126
|
+
def read: (Registry::Rule rule, *untyped args) -> untyped
|
|
127
|
+
|
|
128
|
+
# Stores `result` for the query when the rule keeps results for any time (else it is skipped), and
|
|
129
|
+
# returns it. Emits `cache_miss` or, when skipped, `cache_skip`.
|
|
130
|
+
# A failure of the cache store is reported (or raised), and does not lose the result.
|
|
131
|
+
# : (Registry::Rule rule, untyped result, *untyped args) -> untyped
|
|
132
|
+
def write: (Registry::Rule rule, untyped result, *untyped args) -> untyped
|
|
133
|
+
|
|
134
|
+
private
|
|
135
|
+
|
|
136
|
+
# Whether the stash is on for the current execution context: what `enable!` or `disable!` set
|
|
137
|
+
# there, or else `enabled`.
|
|
138
|
+
# : () -> bool
|
|
139
|
+
def stashing?: () -> bool
|
|
140
|
+
|
|
141
|
+
# Sets the stash on or off in the isolated state for the block, then puts back what was there.
|
|
142
|
+
# : [T] (bool value) { () -> T } -> T
|
|
143
|
+
def isolate: [T] (bool value) { () -> T } -> T
|
|
144
|
+
|
|
145
|
+
# : () -> Registry
|
|
146
|
+
def build_registry: () -> Registry
|
|
147
|
+
|
|
148
|
+
# : [T] () { () -> T } -> untyped
|
|
149
|
+
def rescue_cache_error: [T] () { () -> T } -> untyped
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Generated from lib/query_stash/mongo.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module QueryStash
|
|
4
|
+
class Mongo < Base
|
|
5
|
+
# Has a name: a collection, or a database.
|
|
6
|
+
interface _Named
|
|
7
|
+
def name: () -> String
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# A `Mongo::Collection::View`, or an aggregation view.
|
|
11
|
+
interface _View
|
|
12
|
+
def collection: () -> _Named
|
|
13
|
+
|
|
14
|
+
def database: () -> _Named
|
|
15
|
+
|
|
16
|
+
def filter: () -> untyped
|
|
17
|
+
|
|
18
|
+
def options: () -> Hash[Symbol, untyped]
|
|
19
|
+
|
|
20
|
+
def each: () ?{ (untyped document) -> untyped } -> untyped
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# A selector to match rules against.
|
|
24
|
+
type selector = Hash[Symbol, untyped]
|
|
25
|
+
|
|
26
|
+
# ---------------- Constants ----------------
|
|
27
|
+
# Wildcard sentinel for selector patterns
|
|
28
|
+
ANY: Object
|
|
29
|
+
|
|
30
|
+
class MongoFilterRegistry < Registry
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
# Pattern semantics:
|
|
34
|
+
#
|
|
35
|
+
# - ANY sentinel:
|
|
36
|
+
# pattern.equal?(ANY) => match anything as long as the key exists
|
|
37
|
+
# : (untyped actual, untyped pattern) -> bool
|
|
38
|
+
def value_matches_pattern?: (untyped actual, untyped pattern) -> bool
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Enables the stash for the duration of each request. `application` is the Rails application the
|
|
42
|
+
# middleware stack belongs to; `app` is the next middleware.
|
|
43
|
+
class Middleware
|
|
44
|
+
@app: _RackApp
|
|
45
|
+
|
|
46
|
+
@application: untyped
|
|
47
|
+
|
|
48
|
+
# : (_RackApp app, untyped application) -> void
|
|
49
|
+
def initialize: (_RackApp app, untyped application) -> void
|
|
50
|
+
|
|
51
|
+
# : (Hash[String, untyped] env) -> untyped
|
|
52
|
+
def call: (Hash[String, untyped] env) -> untyped
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @rbs module-self _View
|
|
56
|
+
module AdapterPatch : _View
|
|
57
|
+
# : () ?{ (untyped document) -> untyped } -> untyped
|
|
58
|
+
def each: () ?{ (untyped document) -> untyped } -> untyped
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Runs the view's cursor through the stash: `cursor` iterates the view's documents into the block
|
|
62
|
+
# it is given, and `consumer` is the block `each` was called with. (RBS can't say that a block takes
|
|
63
|
+
# a block, so the parameters of `cursor` are not typed.)
|
|
64
|
+
# : (_View view, ?(^(untyped) -> untyped)? consumer) { (?) -> untyped } -> untyped
|
|
65
|
+
def wrap: (_View view, ?(^(untyped) -> untyped)? consumer) { (?) -> untyped } -> untyped
|
|
66
|
+
|
|
67
|
+
# The rule the view is stashed under, or nil when it isn't to be stashed: the stash is off, or no rule
|
|
68
|
+
# matches its selector.
|
|
69
|
+
# : (_View view) -> Registry::Rule?
|
|
70
|
+
def rule_for: (_View view) -> Registry::Rule?
|
|
71
|
+
|
|
72
|
+
# Based off of #cache_key from Mongo::QueryCache
|
|
73
|
+
# https://github.com/mongodb/mongo-ruby-driver/blob/master/lib/mongo/query_cache.rb#L19
|
|
74
|
+
# : (_View view) -> String
|
|
75
|
+
def cache_key_for: (_View view) -> String
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
# : () -> MongoFilterRegistry
|
|
80
|
+
def build_registry: () -> MongoFilterRegistry
|
|
81
|
+
|
|
82
|
+
# Based off of #cache_options in Mongo::Collection::View::Iterable
|
|
83
|
+
# https://github.com/mongodb/mongo-ruby-driver/blob/v2.19.3/lib/mongo/collection/view/iterable.rb
|
|
84
|
+
# : (_View view) -> selector
|
|
85
|
+
def selector_for: (_View view) -> selector
|
|
86
|
+
|
|
87
|
+
# : () { (?) -> untyped } -> Array[untyped]
|
|
88
|
+
def load_documents: () { (?) -> untyped } -> Array[untyped]
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Generated from lib/query_stash/railtie.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
# The railtie is the only part of QueryStash that expects ActiveSupport: it hands the rest what it
|
|
4
|
+
# needs from it, as options of `config.query_stash`.
|
|
5
|
+
module QueryStash
|
|
6
|
+
class Railtie < Rails::Railtie
|
|
7
|
+
# The options an adapter can set for itself, in `config.query_stash.active_record` and
|
|
8
|
+
# `config.query_stash.mongo`. What it does not set, it takes from `config.query_stash`.
|
|
9
|
+
ADAPTER_OPTIONS: untyped
|
|
10
|
+
|
|
11
|
+
# The adapters' sections of `config.query_stash`, which are not options of their own.
|
|
12
|
+
ADAPTER_SECTIONS: untyped
|
|
13
|
+
|
|
14
|
+
# The `adapter` (QueryStash::ActiveRecord or QueryStash::Mongo) for the app's `config`, with
|
|
15
|
+
# its own options over the ones of `config.query_stash`, and the cache store they name.
|
|
16
|
+
# : (singleton(Base) adapter, untyped config) -> Base
|
|
17
|
+
def self.build: (singleton(Base) adapter, untyped config) -> Base
|
|
18
|
+
|
|
19
|
+
# `config.query_stash`, with what the adapter's own section sets on top of it. A section only
|
|
20
|
+
# holds what the user set, so an option it has, even a `false` or a `nil`, is the user's choice.
|
|
21
|
+
# : (singleton(Base) adapter, untyped config) -> untyped
|
|
22
|
+
def self.options_for: (singleton(Base) adapter, untyped config) -> untyped
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Generated from lib/query_stash/registry/sql.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module QueryStash
|
|
4
|
+
class Registry
|
|
5
|
+
# The rules of the adapters that stash SQL: a rule's selector is a regexp, matched against the query.
|
|
6
|
+
class Sql < Registry
|
|
7
|
+
# @rbs override
|
|
8
|
+
def add_rule: ...
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Generated from lib/query_stash/registry.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module QueryStash
|
|
4
|
+
class Registry
|
|
5
|
+
# How long a result is kept: a number of seconds, or something that works it out from the result.
|
|
6
|
+
type ttl = _ToI | ^(untyped result) -> _ToI
|
|
7
|
+
|
|
8
|
+
class Rule < Data
|
|
9
|
+
attr_reader selector(): untyped
|
|
10
|
+
|
|
11
|
+
attr_reader name(): String
|
|
12
|
+
|
|
13
|
+
attr_reader ttl_seconds(): ttl
|
|
14
|
+
|
|
15
|
+
attr_reader ttl_jitter_seconds(): ttl
|
|
16
|
+
|
|
17
|
+
attr_reader enabled(): (^() -> boolish)?
|
|
18
|
+
|
|
19
|
+
attr_reader store(): _CacheStore?
|
|
20
|
+
|
|
21
|
+
def self.new: (untyped selector, String name, ttl ttl_seconds, ttl ttl_jitter_seconds, (^() -> boolish)? enabled, _CacheStore? store) -> instance
|
|
22
|
+
| (selector: untyped, name: String, ttl_seconds: ttl, ttl_jitter_seconds: ttl, enabled: (^() -> boolish)?, store: _CacheStore?) -> instance
|
|
23
|
+
|
|
24
|
+
def self.members: () -> [ :selector, :name, :ttl_seconds, :ttl_jitter_seconds, :enabled, :store ]
|
|
25
|
+
|
|
26
|
+
def members: () -> [ :selector, :name, :ttl_seconds, :ttl_jitter_seconds, :enabled, :store ]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class Rule
|
|
30
|
+
# The seconds a result is kept for, for `result`.
|
|
31
|
+
# : (untyped result) -> Integer
|
|
32
|
+
def ttl_seconds_for: (untyped result) -> Integer
|
|
33
|
+
|
|
34
|
+
# The jitter added to that, for `result`.
|
|
35
|
+
# : (untyped result) -> Integer
|
|
36
|
+
def ttl_jitter_seconds_for: (untyped result) -> Integer
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# What `set_rule_defaults` can set: the options of `add_rule` other than the selector and the name.
|
|
40
|
+
RULE_DEFAULTS: Array[Symbol]
|
|
41
|
+
|
|
42
|
+
# What an option of `add_rule` is when it is not given (`nil` is a value too: `enabled: nil`).
|
|
43
|
+
UNSET: Object
|
|
44
|
+
|
|
45
|
+
@rules: Array[Rule]
|
|
46
|
+
|
|
47
|
+
@rule_defaults: Hash[Symbol, untyped]
|
|
48
|
+
|
|
49
|
+
attr_reader rules: Array[Rule]
|
|
50
|
+
|
|
51
|
+
# : () -> void
|
|
52
|
+
def initialize: () -> void
|
|
53
|
+
|
|
54
|
+
# Sets what the rules added from now on take for the options they don't give, so that a block of rules
|
|
55
|
+
# needs to say only what is different about each one:
|
|
56
|
+
#
|
|
57
|
+
# registry.set_rule_defaults(ttl_seconds: 60, ttl_jitter_seconds: 10)
|
|
58
|
+
# registry.add_rule(/SELECT .*users/, name: "users") # 60 seconds, 10 of jitter
|
|
59
|
+
# registry.add_rule(/SELECT .*orders/, name: "orders", ttl_seconds: 5) # 5 seconds, 10 of jitter
|
|
60
|
+
#
|
|
61
|
+
# The rules that exist are left as they are. A later call changes only the options it names, and `nil`
|
|
62
|
+
# takes a default away.
|
|
63
|
+
#
|
|
64
|
+
# @rbs **defaults: untyped
|
|
65
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
66
|
+
def set_rule_defaults: (**untyped defaults) -> Hash[Symbol, untyped]
|
|
67
|
+
|
|
68
|
+
# `ttl_seconds` and `ttl_jitter_seconds` are required, unless `set_rule_defaults` gave them.
|
|
69
|
+
#
|
|
70
|
+
# @rbs selector: untyped
|
|
71
|
+
# @rbs name: String
|
|
72
|
+
# @rbs ttl_seconds: ttl | Object
|
|
73
|
+
# @rbs ttl_jitter_seconds: ttl | Object
|
|
74
|
+
# @rbs enabled: (^() -> boolish)? | Object
|
|
75
|
+
# @rbs store: _CacheStore? | Object
|
|
76
|
+
# @rbs return: Array[Rule]
|
|
77
|
+
def add_rule: (untyped selector, name: String, ?ttl_seconds: ttl | Object, ?ttl_jitter_seconds: ttl | Object, ?enabled: (^() -> boolish)? | Object, ?store: _CacheStore? | Object) -> Array[Rule]
|
|
78
|
+
|
|
79
|
+
# : (String name) -> Array[Rule]
|
|
80
|
+
def delete_rule: (String name) -> Array[Rule]
|
|
81
|
+
|
|
82
|
+
# Selector / options / collection-based lookup
|
|
83
|
+
# : (untyped selector) -> Rule?
|
|
84
|
+
def match_rule: (untyped selector) -> Rule?
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
# Pattern matching for hashes.
|
|
89
|
+
#
|
|
90
|
+
# `pattern` is a subset / pattern-form of `full`:
|
|
91
|
+
# - wildcards and special values are interpreted via value_matches_pattern?
|
|
92
|
+
#
|
|
93
|
+
# : (Hash[untyped, untyped] full, Hash[untyped, untyped] pattern) -> bool
|
|
94
|
+
def hash_matches_pattern?: (Hash[untyped, untyped] full, Hash[untyped, untyped] pattern) -> bool
|
|
95
|
+
|
|
96
|
+
# Pattern semantics:
|
|
97
|
+
#
|
|
98
|
+
# - Proc:
|
|
99
|
+
# pattern.call(actual) must be truthy
|
|
100
|
+
# - Regexp:
|
|
101
|
+
# actual.to_s must match pattern
|
|
102
|
+
# - Hash:
|
|
103
|
+
# pattern is a nested hash pattern, recurse
|
|
104
|
+
# - Array:
|
|
105
|
+
# pattern is a "prefix" array pattern:
|
|
106
|
+
# - pattern length <= actual length
|
|
107
|
+
# - each element is matched via value_matches_pattern?
|
|
108
|
+
# - Fallback:
|
|
109
|
+
# actual == pattern
|
|
110
|
+
#
|
|
111
|
+
# : (untyped actual, untyped pattern) -> bool
|
|
112
|
+
def value_matches_pattern?: (untyped actual, untyped pattern) -> bool
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Generated from lib/query_stash/sequel.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module QueryStash
|
|
4
|
+
# Stashes the `SELECT`s of Sequel datasets, matched by regexp like the ActiveRecord ones.
|
|
5
|
+
#
|
|
6
|
+
# A database stashes once it loads the extension, which gives it a stash of its own, from the options in its
|
|
7
|
+
# `:query_stash` option:
|
|
8
|
+
#
|
|
9
|
+
# DB = Sequel.connect(url, query_stash: { cache_store: store, enabled: true })
|
|
10
|
+
# DB.extension(:query_stash)
|
|
11
|
+
# QueryStash::Sequel[DB].configure { |rules| ... }
|
|
12
|
+
#
|
|
13
|
+
# (`cache_store:` and the options of `QueryStash::Base#initialize`.) With sequel-rails, the railtie makes it
|
|
14
|
+
# the app's stash instead: `Sequel::Model.db.extension(:query_stash)` is all it takes for another database.
|
|
15
|
+
#
|
|
16
|
+
# (Inside this class `Sequel` is this class: the library is `::Sequel`.)
|
|
17
|
+
class Sequel < Base
|
|
18
|
+
# A dataset, as far as the patch and the stash look at it.
|
|
19
|
+
interface _Dataset
|
|
20
|
+
def fetch_rows: (String sql) { (Hash[Symbol, untyped] row) -> untyped } -> untyped
|
|
21
|
+
|
|
22
|
+
def db: () -> ::Sequel::Database
|
|
23
|
+
|
|
24
|
+
def opts: () -> Hash[Symbol, untyped]
|
|
25
|
+
|
|
26
|
+
def is_a?: (Module mod) -> bool
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
CACHEABLE_SQL_REGEX: Regexp
|
|
30
|
+
|
|
31
|
+
# Enables the stash for the duration of each request. `application` is the Rails application the
|
|
32
|
+
# middleware stack belongs to; `app` is the next middleware.
|
|
33
|
+
class Middleware
|
|
34
|
+
@app: _RackApp
|
|
35
|
+
|
|
36
|
+
@application: untyped
|
|
37
|
+
|
|
38
|
+
# : (_RackApp app, untyped application) -> void
|
|
39
|
+
def initialize: (_RackApp app, untyped application) -> void
|
|
40
|
+
|
|
41
|
+
# : (Hash[String, untyped] env) -> untyped
|
|
42
|
+
def call: (Hash[String, untyped] env) -> untyped
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Included in the dataset class of each database that loads the extension. Every query a dataset runs
|
|
46
|
+
# goes through `fetch_rows`, which each adapter defines for its own dataset class, so this has to sit
|
|
47
|
+
# in front of those rather than in `Sequel::Dataset`.
|
|
48
|
+
#
|
|
49
|
+
# A database's stash is the one set for the database itself, which the extension does (see
|
|
50
|
+
# `QueryStash::Sequel.stash_builder`).
|
|
51
|
+
# @rbs module-self _Dataset
|
|
52
|
+
module DatasetPatch : _Dataset
|
|
53
|
+
# : (String sql) { (Hash[Symbol, untyped] row) -> untyped } -> untyped
|
|
54
|
+
def fetch_rows: (String sql) { (Hash[Symbol, untyped] row) -> untyped } -> untyped
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# What builds the stash of a database that loads the extension without one: from its `:query_stash`
|
|
58
|
+
# option. (The railtie replaces it, to hand out the app's stash.)
|
|
59
|
+
DEFAULT_STASH_BUILDER: ^(::Sequel::Database) -> QueryStash::Sequel
|
|
60
|
+
|
|
61
|
+
self.@stash_builder: ^(::Sequel::Database) -> QueryStash::Sequel
|
|
62
|
+
|
|
63
|
+
# Builds the stash of a database that loads the extension without having one.
|
|
64
|
+
# : () -> (^(::Sequel::Database) -> QueryStash::Sequel)
|
|
65
|
+
def self.stash_builder: () -> ^(::Sequel::Database) -> QueryStash::Sequel
|
|
66
|
+
|
|
67
|
+
# : (^(::Sequel::Database) -> QueryStash::Sequel builder) -> void
|
|
68
|
+
attr_writer stash_builder: untyped
|
|
69
|
+
|
|
70
|
+
# Runs the dataset's query through the stash: `cursor` fetches its rows into the block it is given,
|
|
71
|
+
# and `consumer` is the block `fetch_rows` was called with. (RBS can't say that a block takes a block,
|
|
72
|
+
# so the parameters of `cursor` are not typed.)
|
|
73
|
+
# : (_Dataset dataset, String sql, (^(Hash[Symbol, untyped]) -> untyped)? consumer) { (?) -> untyped } -> untyped
|
|
74
|
+
def wrap: (_Dataset dataset, String sql, (^(Hash[Symbol, untyped]) -> untyped)? consumer) { (?) -> untyped } -> untyped
|
|
75
|
+
|
|
76
|
+
# The rule the query is stashed under, or nil when it isn't to be stashed: the stash is off, it has
|
|
77
|
+
# binds or runs through a cursor (not supported), it is not a SELECT, or no rule matches it.
|
|
78
|
+
# : (_Dataset dataset, String sql) -> Registry::Rule?
|
|
79
|
+
def rule_for: (_Dataset dataset, String sql) -> Registry::Rule?
|
|
80
|
+
|
|
81
|
+
# The same SQL is not the same query on another database, or on another server of the same one.
|
|
82
|
+
# : (_Dataset dataset, String sql) -> String
|
|
83
|
+
def cache_key_for: (_Dataset dataset, String sql) -> String
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
# : () -> Registry::Sql
|
|
88
|
+
def build_registry: () -> Registry::Sql
|
|
89
|
+
|
|
90
|
+
# Whether the dataset is a prepared statement, or has bind variables.
|
|
91
|
+
# : (_Dataset dataset) -> bool
|
|
92
|
+
def bound?: (_Dataset dataset) -> bool
|
|
93
|
+
|
|
94
|
+
# : () { (?) -> untyped } -> Array[Hash[Symbol, untyped]]
|
|
95
|
+
def load_rows: () { (?) -> untyped } -> Array[Hash[Symbol, untyped]]
|
|
96
|
+
end
|
|
97
|
+
end
|
data/sig/query_stash.rbs
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Generated from lib/query_stash.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module QueryStash
|
|
4
|
+
# Where `enable!` and `disable!` keep what they set, like `ActiveSupport::IsolatedExecutionState`.
|
|
5
|
+
interface _IsolatedState
|
|
6
|
+
def []: (untyped key) -> untyped
|
|
7
|
+
|
|
8
|
+
def []=: (untyped key, untyped value) -> untyped
|
|
9
|
+
|
|
10
|
+
def key?: (untyped key) -> bool
|
|
11
|
+
|
|
12
|
+
def delete: (untyped key) -> untyped
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Where results are kept, like an `ActiveSupport::Cache::Store`. `write` is given `expires_in:`.
|
|
16
|
+
interface _CacheStore
|
|
17
|
+
def read: (String key) -> untyped
|
|
18
|
+
|
|
19
|
+
def write: (String key, untyped value, ?Hash[Symbol, untyped] options) -> untyped
|
|
20
|
+
|
|
21
|
+
def clear: () -> untyped
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Told about what the stash does, like `ActiveSupport::Notifications`.
|
|
25
|
+
interface _Instrumenter
|
|
26
|
+
def instrument: (String name, ?Hash[Symbol, untyped] payload) ?{ (Hash[Symbol, untyped]) -> untyped } -> untyped
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Told about the errors the stash handles, like `ActiveSupport.error_reporter`.
|
|
30
|
+
interface _ErrorReporter
|
|
31
|
+
def report: (Exception error, ?handled: bool) -> untyped
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# The next middleware.
|
|
35
|
+
interface _RackApp
|
|
36
|
+
def call: (Hash[String, untyped] env) -> untyped
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class Error < StandardError
|
|
40
|
+
end
|
|
41
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: query_stash
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Persona
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: concurrent-ruby
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.0'
|
|
26
|
+
description: Caches the results of allowlisted SELECT queries (and MongoDB finds)
|
|
27
|
+
across requests, below the ORM, with per-rule TTLs computed from the result.
|
|
28
|
+
email:
|
|
29
|
+
- rubygems@withpersona.com
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- LICENSE.txt
|
|
35
|
+
- README.md
|
|
36
|
+
- lib/query_stash.rb
|
|
37
|
+
- lib/query_stash/active_record.rb
|
|
38
|
+
- lib/query_stash/base.rb
|
|
39
|
+
- lib/query_stash/mongo.rb
|
|
40
|
+
- lib/query_stash/railtie.rb
|
|
41
|
+
- lib/query_stash/registry.rb
|
|
42
|
+
- lib/query_stash/registry/sql.rb
|
|
43
|
+
- lib/query_stash/sequel.rb
|
|
44
|
+
- lib/query_stash/version.rb
|
|
45
|
+
- lib/sequel/extensions/query_stash.rb
|
|
46
|
+
- sig/query_stash.rbs
|
|
47
|
+
- sig/query_stash/active_record.rbs
|
|
48
|
+
- sig/query_stash/base.rbs
|
|
49
|
+
- sig/query_stash/mongo.rbs
|
|
50
|
+
- sig/query_stash/railtie.rbs
|
|
51
|
+
- sig/query_stash/registry.rbs
|
|
52
|
+
- sig/query_stash/registry/sql.rbs
|
|
53
|
+
- sig/query_stash/sequel.rbs
|
|
54
|
+
- sig/query_stash/version.rbs
|
|
55
|
+
- sig/sequel/extensions/query_stash.rbs
|
|
56
|
+
homepage: https://github.com/persona-id/query_stash
|
|
57
|
+
licenses:
|
|
58
|
+
- MIT
|
|
59
|
+
metadata:
|
|
60
|
+
allowed_push_host: https://rubygems.org
|
|
61
|
+
homepage_uri: https://github.com/persona-id/query_stash
|
|
62
|
+
source_code_uri: https://github.com/persona-id/query_stash
|
|
63
|
+
changelog_uri: https://github.com/persona-id/query_stash/blob/main/CHANGELOG.md
|
|
64
|
+
rubygems_mfa_required: 'true'
|
|
65
|
+
rdoc_options: []
|
|
66
|
+
require_paths:
|
|
67
|
+
- lib
|
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: 3.3.0
|
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
74
|
+
requirements:
|
|
75
|
+
- - ">="
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: '0'
|
|
78
|
+
requirements: []
|
|
79
|
+
rubygems_version: 4.0.20
|
|
80
|
+
specification_version: 4
|
|
81
|
+
summary: Cross-request, allowlisted query result caching for ActiveRecord and MongoDB.
|
|
82
|
+
test_files: []
|