sequel-privacy 0.7.0 → 0.7.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c7ef079bb3498aa1006aa327a138bcb1735b78ae02a65b11d2ee4976d9871978
4
- data.tar.gz: 95000199db0cbb08908c1a261152c641b71ceb7898875a9f732936662df9334e
3
+ metadata.gz: 3a775f6a706825d2606e667f2b83076f00bed29b079e18581be62a6452bbff32
4
+ data.tar.gz: 538b8ca6f3eb9c748a2697dab1a0d9f5b013c2a0cabaa4f06702513def743bd9
5
5
  SHA512:
6
- metadata.gz: 6ea2e6410976a3e325264faa698a2faaf323c1aafdfe9eb3444c8cc18f51c887155227a1fe68ec119408d9679b1c4a39cc70e69611db4e6ed94edf9b93857ef2
7
- data.tar.gz: 0271f5b12a6d376e04b5feae0eb9ee604da1258871092578878571c877b86b9ef388a60441962b31b36588c9c3e4fc9642c9b71cd0f90a8890030903e2a1bb21
6
+ metadata.gz: 8c1e2f5aad69229373e401207e4e3aa664f8c9f8a5e615d147208c24690dbe32155647a8d7e6dd3a81543e8f838dc1b449a993be4a2d24511c11418cddc94bc2
7
+ data.tar.gz: f5397fe792ad89555aed6e81bb40be6ced0cfdc0e88ebd9c20e09c6466351498dc83293a6a5a2f41dd86d978721ad702ca8f8e87e4e9d999bc970b21e6e278d7
data/CHANGELOG.md CHANGED
@@ -3,6 +3,15 @@
3
3
 
4
4
  ## [Unreleased]
5
5
 
6
+ ## [0.7.2] – 2026-09-21
7
+
8
+ - Reduced the overhead of operation-scoped cache access.
9
+
10
+ ## [0.7.1] – 2026-09-21
11
+
12
+ - Added `Sequel::Privacy.with_cache_scope` for nested, exception-safe policy
13
+ caches isolated between concurrent threads and fibers.
14
+
6
15
  ## [0.7.0] – 2026-08-26
7
16
  - Removed `#for_vc` on `Sequel::Model` instances. Instead, call `#reset_viewer_context(vc, reason)`.
8
17
  - Removed `#viewer_context=` from `Sequel::Model` instances.
data/README.md CHANGED
@@ -402,7 +402,9 @@ Sequel::Privacy.logger = SemanticLogger['Privacy']
402
402
 
403
403
  ## Cache Management
404
404
 
405
- Policy results are cached per-request to avoid redundant evaluation. Clear between requests:
405
+ Policy results are cached per operation to avoid redundant evaluation. Wrap
406
+ each request, job, or other independent operation in a cache scope. Scopes are
407
+ isolated between concurrent threads and fibers:
406
408
 
407
409
  ```ruby
408
410
  # In Rack middleware
@@ -412,13 +414,14 @@ class PrivacyCacheMiddleware
412
414
  end
413
415
 
414
416
  def call(env)
415
- Sequel::Privacy.clear_cache!
416
- @app.call(env)
417
+ Sequel::Privacy.with_cache_scope { @app.call(env) }
417
418
  end
418
419
  end
419
420
  ```
420
421
 
421
- Or somewhere manually, like at the top of your Roda or Sinatra app:
422
+ Scopes may be nested and are restored after exceptions. `clear_cache!` clears
423
+ only the active scope. Applications not yet using scopes retain the legacy
424
+ process-global cache and can clear it manually:
422
425
 
423
426
  ```ruby
424
427
  Sequel::Privacy.clear_cache!
@@ -2,28 +2,52 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Sequel
5
+ # Privacy enforcement and operation-scoped policy caching.
5
6
  module Privacy
6
- # In-memory cache for policy evaluation results. Clear between
7
- # requests (e.g. via Rack middleware).
7
+ # In-memory cache for policy evaluation results. Concurrent applications
8
+ # should wrap each independent operation in with_cache_scope.
8
9
  class << self
9
10
  extend T::Sig
10
11
 
12
+ CACHE_SCOPE_KEY = :sequel_privacy_cache_scope
13
+ private_constant :CACHE_SCOPE_KEY
14
+
15
+ sig do
16
+ type_parameters(:U)
17
+ .params(block: T.proc.returns(T.type_parameter(:U)))
18
+ .returns(T.type_parameter(:U))
19
+ end
20
+ def with_cache_scope(&block)
21
+ previous = Thread.current[CACHE_SCOPE_KEY]
22
+ Thread.current[CACHE_SCOPE_KEY] = [{}, {}]
23
+ block.()
24
+ ensure
25
+ Thread.current[CACHE_SCOPE_KEY] = previous
26
+ end
27
+
11
28
  sig { returns(T::Hash[Integer, Symbol]) }
12
29
  def cache
13
- @cache ||= T.let({}, T.nilable(T::Hash[Integer, Symbol]))
30
+ scope = Thread.current[CACHE_SCOPE_KEY]
31
+ scope ? scope[0] : (@cache ||= T.let({}, T.nilable(T::Hash[Integer, Symbol])))
14
32
  end
15
33
 
16
34
  # Tracks single-match optimization state.
17
35
  # Key: [policy, actor, viewer_context].hash → Value: subject.hash
18
36
  sig { returns(T::Hash[Integer, Integer]) }
19
37
  def single_matches
20
- @single_matches ||= T.let({}, T.nilable(T::Hash[Integer, Integer]))
38
+ scope = Thread.current[CACHE_SCOPE_KEY]
39
+ scope ? scope[1] : (@single_matches ||= T.let({}, T.nilable(T::Hash[Integer, Integer])))
21
40
  end
22
41
 
23
42
  sig { void }
24
43
  def clear_cache!
25
- @cache = {}
26
- @single_matches = {}
44
+ if (scope = Thread.current[CACHE_SCOPE_KEY])
45
+ scope[0] = {}
46
+ scope[1] = {}
47
+ else
48
+ @cache = {}
49
+ @single_matches = {}
50
+ end
27
51
  end
28
52
  end
29
53
  end
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Sequel
5
5
  module Privacy
6
- VERSION = '0.7.0'
6
+ VERSION = '0.7.2'
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel-privacy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Austin Bales
@@ -152,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
152
152
  - !ruby/object:Gem::Version
153
153
  version: '0'
154
154
  requirements: []
155
- rubygems_version: 4.0.14
155
+ rubygems_version: 4.0.21
156
156
  specification_version: 4
157
157
  summary: Privacy enforcement plugin for Sequel models
158
158
  test_files: []