request_store_rails 2.0.0 → 3.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7bd1299319b0c1e8167b6d68d55e69d4099439824beeac03f8a9fc0c715ee71b
4
- data.tar.gz: 7d5a0b52520b5f09405ae262f5eb9c984d8faad49886e343331e76b7b1c88249
3
+ metadata.gz: f8fce44e58aadca2344b59aa90defca178623e6d9c85d112d93bf565dfd5201c
4
+ data.tar.gz: d02fb9bd71f920fcad84e3a21be15c4d2c2333e517bc84b8158c398d80a0e11e
5
5
  SHA512:
6
- metadata.gz: 1a8fd3b6848bf8b92a74610e1803349388b93cbd64f1454c5df608483fb2283022db0210c4cf7d8c797059dd3aebdd038eedad4214672a6c4eabda6c19bb019d
7
- data.tar.gz: 1ff7ff9d67e0d00447a904e2e3b80b130fca8a24b92759f6d2fd0d4cdcc8878fb28d958e58e7dd9eb1272faa1e5f7e2f47c99ff9d2d8f6c13446528256f4218b
6
+ metadata.gz: dcd870787ebc47f6e5db5b3e20052e793dc2b2930435d2b159b8e1e9d6737a2cfbe765691f27986cbb5656c40ecd79b7a72ede46753f4dc8e2d1a886daf97612
7
+ data.tar.gz: d5c90ad7949b83d53b1fa3b36fe2e59e80c65488d2be2ec341ad44d74e4b12885995971a7824806d37c4ad16c1278481de32f42ab860cbc9e1aa06cbd241099d
data/README.md CHANGED
@@ -1,28 +1,17 @@
1
1
  RequestLocals
2
2
  =====================
3
- [![Gem Version](https://badge.fury.io/rb/request_store_rails.svg)](http://badge.fury.io/rb/request_store_rails)
4
- [![Build Status](https://travis-ci.org/ElMassimo/request_store_rails.svg)](https://travis-ci.org/ElMassimo/request_store_rails)
5
- [![Test Coverage](https://codeclimate.com/github/ElMassimo/request_store_rails/badges/coverage.svg)](https://codeclimate.com/github/ElMassimo/request_store_rails)
6
- [![Code Climate](https://codeclimate.com/github/ElMassimo/request_store_rails/badges/gpa.svg)](https://codeclimate.com/github/ElMassimo/request_store_rails)
7
- [![Inline docs](http://inch-ci.org/github/ElMassimo/request_store_rails.svg)](http://inch-ci.org/github/ElMassimo/request_store_rails)
3
+ [![Gem Version](https://badge.fury.io/rb/request_store_rails.svg)](https://badge.fury.io/rb/request_store_rails)
4
+ [![CI](https://github.com/ElMassimo/request_store_rails/actions/workflows/ci.yml/badge.svg)](https://github.com/ElMassimo/request_store_rails/actions/workflows/ci.yml)
5
+ [![Code Coverage](https://qlty.sh/gh/ElMassimo/projects/request_store_rails/coverage.svg)](https://qlty.sh/gh/ElMassimo/projects/request_store_rails)
6
+ [![Maintainability](https://qlty.sh/gh/ElMassimo/projects/request_store_rails/maintainability.svg)](https://qlty.sh/gh/ElMassimo/projects/request_store_rails)
8
7
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ElMassimo/request_store_rails/blob/master/LICENSE.txt)
9
8
 
10
9
  If you have ever needed to use a global variable in Rails, you know it sucks.
11
10
 
12
- One of the usual tricks is to go for `Thread.current`, or if you have done your
13
- homework, to use the awesome [`request_store`](https://github.com/steveklabnik/request_store).
11
+ One common solution is the awesome
12
+ [`request_store`](https://github.com/steveklabnik/request_store):
14
13
 
15
14
  ```ruby
16
- # Using Thread.current
17
- def self.foo
18
- Thread.current[:foo] ||= 0
19
- end
20
-
21
- def self.foo=(value)
22
- Thread.current[:foo] = value
23
- end
24
-
25
- # Using RequestStore
26
15
  def self.foo
27
16
  RequestStore.fetch(:foo) { 0 }
28
17
  end
@@ -34,13 +23,8 @@ end
34
23
 
35
24
  ### The problem
36
25
 
37
- - Using `Thread.current`, values can stick around even after the request is over,
38
- since some servers have a pool of Threads that they reuse, which [can cause bugs](https://github.com/steveklabnik/request_store#the-problem).
39
-
40
- - Using `request_store`, the storage is _*not actually*_ request local. Variables
41
- are stored in `Thread.current`, except that the storage is cleared after each
42
- request. However, this does not work when you need to use multiple threads per
43
- request, _different_ threads access _different_ stores.
26
+ `request_store` scopes values to the execution context handling the request.
27
+ When work moves to another thread, that thread accesses a different store.
44
28
 
45
29
  ### The solution
46
30
 
@@ -62,28 +46,18 @@ def self.foo=(value)
62
46
  end
63
47
  ```
64
48
 
65
- Oh yeah, everywhere you used `Thread.current` or `RequestStore.store` just
66
- change it to `RequestLocals.store`. Now your variables will actually be stored
67
- in a true _request-local_ way.
68
-
69
- ### No Rails? No Problem!
70
-
71
- A Railtie is added that configures the Middleware for you, but if you're not
72
- using Rails, no biggie! Just use the Middleware yourself, however you need.
73
- You'll probably have to shove this somewhere:
74
-
75
- ```ruby
76
- use RequestStoreRails::Middleware
77
- ```
49
+ Everywhere you used `RequestStore.store`, change it to `RequestLocals.store`.
50
+ Now your variables will actually be stored in a true _request-local_ way.
78
51
 
79
52
  ## Multi-Threading
80
- The middleware in the gem sets a thread-local variable `:request_store_id` in
81
- `Thread.current` for the main thread that is executing the request.
82
-
83
- If you need to spawn threads within a server that is already using thread-based
84
- concurrency, all you need to do is to make sure that the `:request_store_id`
85
- variable is set for your threads, and you will be able to access the
86
- `RequestLocals` as usual.
53
+ The middleware stores each request's context in
54
+ `ActiveSupport::IsolatedExecutionState`, the same mechanism used by Rails'
55
+ `CurrentAttributes`. Existing `RequestLocals` usage automatically follows the
56
+ thread or fiber isolation level configured by Rails, without application changes.
57
+
58
+ When explicitly spawning a new thread, propagate the current store id with
59
+ `RequestLocals.set_current_store_id` so the new thread can access the same
60
+ request-local values.
87
61
 
88
62
  A good way to apply this pattern is by encapsulating it into a helper class:
89
63
 
@@ -1,12 +1,13 @@
1
1
  require 'singleton'
2
2
  require 'forwardable'
3
3
  require 'concurrent'
4
+ require 'active_support/isolated_execution_state'
4
5
 
5
6
  # Public: Provides per-request global storage, by offering an interface that is
6
7
  # very similar to Rails.cache, or Hash.
7
8
  #
8
- # The store may be shared between threads, as long as the current store id is
9
- # set as a thread-local variable.
9
+ # The store may be shared between execution contexts by propagating the current
10
+ # store id with `RequestLocals.set_current_store_id`.
10
11
  #
11
12
  # Intended to work in collaboration with the RequestStoreRails middleware, that
12
13
  # will clear the request local variables after each request.
@@ -14,9 +15,11 @@ class RequestLocals
14
15
  include Singleton
15
16
  extend Forwardable
16
17
 
17
- # Internal: The key of the thread-local variable the library uses to store the
18
- # identifier of the current store, used during the request lifecycle.
19
- REQUEST_STORE_ID = :request_store_id
18
+ # Internal: The execution-state key used for the request identifier.
19
+ REQUEST_STORE_ID = :request_store_rails_store_id
20
+
21
+ # Internal: The execution-state backend used for the request identifier.
22
+ CONTEXT = ActiveSupport::IsolatedExecutionState
20
23
 
21
24
  class << self
22
25
  extend Forwardable
@@ -79,17 +82,25 @@ class RequestLocals
79
82
  store.compute_if_absent(key, &block)
80
83
  end
81
84
 
82
- # Public: The current request is inferred from the current thread.
85
+ # Public: The current request is inferred from the current execution context.
83
86
  #
84
87
  # NOTE: It's very important to set the current store id when spawning new
85
88
  # threads within a single request, using `RequestLocals.set_current_store_id`.
86
89
  def current_store_id
87
- Thread.current[REQUEST_STORE_ID]
90
+ context[REQUEST_STORE_ID]
88
91
  end
89
92
 
90
- # Public: Changes the store RequestLocals will read from in the current thread.
93
+ # Public: Changes the store RequestLocals will read from in the current execution context.
91
94
  def self.set_current_store_id(id)
92
- Thread.current[REQUEST_STORE_ID] = id
95
+ if id.nil?
96
+ context.delete(REQUEST_STORE_ID)
97
+ else
98
+ context[REQUEST_STORE_ID] = id
99
+ end
100
+ end
101
+
102
+ def self.context
103
+ CONTEXT
93
104
  end
94
105
 
95
106
  protected
@@ -102,6 +113,10 @@ protected
102
113
  @cache.compute_if_absent(current_store_id) { new_store }
103
114
  end
104
115
 
116
+ def context
117
+ self.class.context
118
+ end
119
+
105
120
  # Internal: Returns a new empty structure where the request-local variables
106
121
  # will be stored.
107
122
  def new_store
@@ -2,15 +2,15 @@ require 'securerandom'
2
2
 
3
3
  module RequestStoreRails
4
4
 
5
- # Public: Middleware that takes care of setting a thread-local variable, which
6
- # enables RequestLocals to associate threads with the store for a request.
5
+ # Public: Middleware that sets the isolated execution context, which enables
6
+ # RequestLocals to associate concurrent execution with the store for a request.
7
7
  class Middleware
8
8
 
9
9
  def initialize(app)
10
10
  @app = app
11
11
  end
12
12
 
13
- # Internal: Assigns a thread-local variable to identify the current store,
13
+ # Internal: Assigns the execution context that identifies the current store,
14
14
  # and cleans up all the variables stored for the request once it finishes.
15
15
  def call(env)
16
16
  RequestLocals.set_current_store_id(extract_request_id(env))
@@ -1,4 +1,4 @@
1
1
  module RequestStoreRails
2
2
 
3
- VERSION = '2.0.0'
3
+ VERSION = '3.0.0'
4
4
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: request_store_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Máximo Mussini
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2019-05-20 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rake
@@ -52,6 +51,20 @@ dependencies:
52
51
  - - "~>"
53
52
  - !ruby/object:Gem::Version
54
53
  version: '1.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: activesupport
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '7.0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '7.0'
55
68
  description: RequestLocals gives you per-request global storage in Rails
56
69
  email:
57
70
  - maximomussini@gmail.com
@@ -70,7 +83,6 @@ homepage: https://github.com/ElMassimo/request_store_rails
70
83
  licenses:
71
84
  - MIT
72
85
  metadata: {}
73
- post_install_message:
74
86
  rdoc_options:
75
87
  - "--charset=UTF-8"
76
88
  require_paths:
@@ -79,15 +91,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
79
91
  requirements:
80
92
  - - ">="
81
93
  - !ruby/object:Gem::Version
82
- version: 1.9.3
94
+ version: '2.7'
83
95
  required_rubygems_version: !ruby/object:Gem::Requirement
84
96
  requirements:
85
97
  - - ">="
86
98
  - !ruby/object:Gem::Version
87
99
  version: '0'
88
100
  requirements: []
89
- rubygems_version: 3.0.3
90
- signing_key:
101
+ rubygems_version: 4.0.20
91
102
  specification_version: 4
92
103
  summary: Per-request global storage for Rails
93
104
  test_files: []