thread_local_var_accessors 1.0.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +11 -1
- data/README.md +112 -62
- data/lib/thread_local_var_accessors/version.rb +1 -1
- data/lib/thread_local_var_accessors.rb +113 -39
- data/thread_local_var_accessors.gemspec +1 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4537e0db3960367d75a6ab4d731026c6a425e315816bf0523d078b563b90a09a
|
4
|
+
data.tar.gz: d270efc557c3b04f41ae9adf7850157d98c793a7d57cd025e8c88a40d2aaa154
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b1ad3ff8ce74641cdb6956fe084d8bd69e2712b002805302c2ca3550d82591e2874e94322dbf3ba4123f70f8185318fb05c6ef51e8527ce6145361f72347fb64
|
7
|
+
data.tar.gz: 16ad94b606dfc4249bf344fcd859c1db7929bd026680fc8ef885e6768097c20cfbbc4753543c4989fea1095ce2876fbd525b5ba9090570fd117d79936446838b
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
thread_local_var_accessors (1.
|
4
|
+
thread_local_var_accessors (1.3.0)
|
5
5
|
concurrent-ruby
|
6
6
|
|
7
7
|
GEM
|
@@ -9,6 +9,8 @@ GEM
|
|
9
9
|
specs:
|
10
10
|
ast (2.4.2)
|
11
11
|
builder (3.2.4)
|
12
|
+
byebug (11.1.3)
|
13
|
+
coderay (1.1.3)
|
12
14
|
concurrent-ruby (1.2.2)
|
13
15
|
diff-lcs (1.5.0)
|
14
16
|
docile (1.4.0)
|
@@ -16,9 +18,16 @@ GEM
|
|
16
18
|
rspec-core (~> 3.0)
|
17
19
|
ruby-progressbar (~> 1.4)
|
18
20
|
json (2.6.3)
|
21
|
+
method_source (1.0.0)
|
19
22
|
parallel (1.22.1)
|
20
23
|
parser (3.2.1.1)
|
21
24
|
ast (~> 2.4.1)
|
25
|
+
pry (0.14.2)
|
26
|
+
coderay (~> 1.1)
|
27
|
+
method_source (~> 1.0)
|
28
|
+
pry-byebug (3.10.1)
|
29
|
+
byebug (~> 11.0)
|
30
|
+
pry (>= 0.13, < 0.15)
|
22
31
|
rainbow (3.1.1)
|
23
32
|
rake (13.0.6)
|
24
33
|
redcarpet (3.6.0)
|
@@ -74,6 +83,7 @@ PLATFORMS
|
|
74
83
|
DEPENDENCIES
|
75
84
|
bundler
|
76
85
|
fuubar
|
86
|
+
pry-byebug
|
77
87
|
rake
|
78
88
|
redcarpet
|
79
89
|
rspec
|
data/README.md
CHANGED
@@ -28,10 +28,10 @@ variables that use `ThreadLocalVar` (TLV) objects.
|
|
28
28
|
the instance variable names '@name', which is expected to be either nil,
|
29
29
|
or already have a `Concurrent::ThreadLocalVar` instance.
|
30
30
|
|
31
|
-
- `tlv_writer` creates an instance method with the name `name=`, which accepts a
|
32
|
-
argument that is the new value.
|
33
|
-
on the instance variable named `@name`, which should be a
|
34
|
-
`Concurrent::ThreadLocalVar` instance.
|
31
|
+
- `tlv_writer` creates an instance method with the name `name=`, which accepts a
|
32
|
+
single argument that is the new value. This method checks for an existing
|
33
|
+
value on the instance variable named `@name`, which should be a
|
34
|
+
`Concurrent::ThreadLocalVar` instance. If `@name` value is nil, then a new
|
35
35
|
`Concurrent::ThreadLocalVar` instance is assigned to it. In either case, the
|
36
36
|
instance variable's TLV object is assigned the new value, which is returned.
|
37
37
|
|
@@ -41,6 +41,35 @@ For reference, see [ThreadLocalVars](https://ruby-concurrency.github.io/concurre
|
|
41
41
|
|
42
42
|
### Instance Methods
|
43
43
|
|
44
|
+
The following are a brief list of the instance variable in
|
45
|
+
the `ThreadLocalVarAccessors` class.
|
46
|
+
|
47
|
+
These methods interrogate or set thread-local variable values:
|
48
|
+
|
49
|
+
tlv_get NAME # => TLV value
|
50
|
+
tlv_set NAME, VALUE # => TLV value
|
51
|
+
tlv_set NAME { VALUE } # => TLV value
|
52
|
+
tlv_set_once NAME, VALUE # => current TLV value, or new VALUE
|
53
|
+
tlv_set_once NAME { VALUE } # => current TLV value, or new VALUE
|
54
|
+
|
55
|
+
There is a method to fetch the TLV object for a given instance variable name,
|
56
|
+
but only if it is actually a TLV. If the instance variable is unassigned, or
|
57
|
+
contains a non-TLV, then `nil` is returned.
|
58
|
+
|
59
|
+
tlv_get_var NAME # => TLV object or nil
|
60
|
+
|
61
|
+
The methods below manage the default values for thread-local variables:
|
62
|
+
|
63
|
+
tlv_new NAME, DEFAULT # => new TLV
|
64
|
+
tlv_new NAME { DEFAULT } # => new TLV
|
65
|
+
tlv_init NAME, DEFAULT # => DEFAULT
|
66
|
+
tlv_init NAME { DEFAULT } # => DEFAULT
|
67
|
+
tlv_default NAME # => TLV default value for NAME
|
68
|
+
tlv_set_default NAME, VALUE # => VALUE
|
69
|
+
tlv_set_default NAME { VALUE } # => VALUE
|
70
|
+
|
71
|
+
### Instance Variable Details
|
72
|
+
|
44
73
|
With the accessor methods, obtaining values and setting values
|
45
74
|
becomes very simple:
|
46
75
|
|
@@ -49,25 +78,41 @@ becomes very simple:
|
|
49
78
|
timeout # fetches the current TLV value, unique to each thread
|
50
79
|
...
|
51
80
|
self.timeout = 0.5 # stores the TLV value just for this thread
|
52
|
-
|
81
|
+
|
53
82
|
The `tlv_init` method creates a _new_ TLVar and sets its default value.
|
54
83
|
|
55
|
-
Note that the default value is used when any thread evaluates the instance
|
84
|
+
Note that the default value is used when any thread evaluates the instance
|
56
85
|
variable and there has been no thread-specific value assignment.
|
57
86
|
|
58
|
-
|
87
|
+
Note: It's a best practice to use `tlv_init` to set the thread-local
|
88
|
+
instance variables with non-nil defaults that may be inherited across multiple
|
89
|
+
threads.
|
90
|
+
|
91
|
+
On the other hand, if the ruby developer using threads does not rely on
|
92
|
+
any particular value (other than nil) to be inherited, then using `tlv_set`
|
93
|
+
is the right way to go.
|
94
|
+
|
59
95
|
|
60
|
-
|
61
|
-
|
96
|
+
The TLV default value is used across *all* threads, when there is no value
|
97
|
+
specifically assigned for a given thread.
|
98
|
+
|
99
|
+
tlv_init(:timeout, default)
|
100
|
+
tlv_init(:timeout) { default }
|
101
|
+
|
102
|
+
The `tlv_init` method is essentially the same as `tlv_set_default` followed
|
103
|
+
by `tlv_get`: it sets the default value (or block) of a given TLVar, without
|
104
|
+
affecting any possible thread-local variables already assigned.
|
62
105
|
|
63
106
|
Alternative ways to initialize:
|
64
107
|
|
65
|
-
tlv_set(:timeout, 0)
|
108
|
+
tlv_set(:timeout, 0) # => 0
|
66
109
|
|
67
|
-
tlv_set(:timeout)
|
110
|
+
tlv_set(:timeout) # @timeout = Concurrent::ThreadLocalVar.new
|
68
111
|
@timeout.value = 0
|
69
112
|
|
70
|
-
|
113
|
+
### More Details
|
114
|
+
|
115
|
+
The following methods are used within the above reader, writer, and accessor
|
71
116
|
methods:
|
72
117
|
|
73
118
|
tlv_get(name) - fetches the value of TLV `name`
|
@@ -76,29 +121,29 @@ methods:
|
|
76
121
|
There is a block form to `tls_set`:
|
77
122
|
|
78
123
|
tlv_set(name) { |old_val| new_val }
|
79
|
-
|
124
|
+
|
80
125
|
In addition, there is also a `tlv_set_once` method that can be used to set
|
81
126
|
a TLV value only if has not currently be set already.
|
82
127
|
|
83
128
|
tlv_set_once(name, value)
|
84
|
-
|
129
|
+
|
85
130
|
tlv_set_once(name) { |old_val| new_value }
|
86
131
|
|
87
|
-
For `tlv_accessor` instance variables, it's possible to use the assign operators,
|
88
|
-
For example:
|
132
|
+
For `tlv_accessor` instance variables, it's possible to use the assign operators,
|
133
|
+
eg: `+=`, or `||=`. For example:
|
89
134
|
|
90
135
|
tlv_accessor :timeout, :count
|
91
|
-
|
136
|
+
|
92
137
|
self.timeout ||= DEFAULT_TIMEOUT
|
93
|
-
|
138
|
+
|
94
139
|
self.count += 1
|
95
140
|
|
96
141
|
### TLV Name Arguments
|
97
142
|
|
98
143
|
The `name` argument to `tlv_get` and `tlv_set` is the same as given on
|
99
144
|
the accessor, reader, and writer methods: either a string or symbol name,
|
100
|
-
automatically converted as needed to instance-variable syntax (eg:
|
101
|
-
and setter-method name syntax (eg
|
145
|
+
automatically converted as needed to instance-variable syntax (eg: `:@name`),
|
146
|
+
and setter-method name syntax (eg `:name=`).
|
102
147
|
|
103
148
|
|
104
149
|
## Installation
|
@@ -115,14 +160,16 @@ Then:
|
|
115
160
|
|
116
161
|
## Usage
|
117
162
|
|
118
|
-
To use the class methods, they must be included into the current module or
|
163
|
+
To use the class methods, they must be included into the current module or
|
164
|
+
class, with:
|
119
165
|
|
120
166
|
class MyNewClass
|
121
167
|
include ThreadLocalVarAccessors
|
122
168
|
...
|
123
169
|
end
|
124
|
-
|
125
|
-
With the include above, you can use the class methods to declare instance getter
|
170
|
+
|
171
|
+
With the include above, you can use the class methods to declare instance getter
|
172
|
+
and setter methods:
|
126
173
|
|
127
174
|
class MyNewClass
|
128
175
|
include ThreadLocalVarAccessors
|
@@ -130,24 +177,28 @@ With the include above, you can use the class methods to declare instance getter
|
|
130
177
|
tlv_reader :name1
|
131
178
|
tlv_writer :name2
|
132
179
|
tlv_accessor :name3, :name4
|
133
|
-
|
180
|
+
|
134
181
|
end
|
135
|
-
|
136
|
-
The above invocations:
|
182
|
+
|
183
|
+
The above invocations:
|
137
184
|
|
138
185
|
- create reader methods for `name1`, `name3`, and `name4`.
|
139
186
|
- create writer methods for `name2`, `name3`, and `name4`.
|
140
187
|
|
141
|
-
The writer methods accept a value as the second argument, or from the result of
|
188
|
+
The writer methods accept a value as the second argument, or from the result of
|
189
|
+
an optional, associated block.
|
142
190
|
|
143
|
-
Note: to use the read-and-operate operators, eg: `+=`, `-=`, `||=`, etc., the
|
191
|
+
Note: to use the read-and-operate operators, eg: `+=`, `-=`, `||=`, etc., the
|
192
|
+
object must have both a reader and writer method. In other words, it needs to
|
193
|
+
have been created as an `tlv_accessor`.
|
144
194
|
|
145
|
-
When adapting legacy code to become thread-safe, it's sometimes necessary to use
|
195
|
+
When adapting legacy code to become thread-safe, it's sometimes necessary to use
|
196
|
+
the underlying instance methods:
|
146
197
|
|
147
198
|
tlv_get(name)
|
148
199
|
tlv_set(name, value)
|
149
200
|
tlv_set_once(name, value)
|
150
|
-
|
201
|
+
|
151
202
|
Alternative block forms:
|
152
203
|
|
153
204
|
tlv_set(name) { |oldval| newval }
|
@@ -155,14 +206,15 @@ Alternative block forms:
|
|
155
206
|
|
156
207
|
In all cases, the `name` can be a string or symbol, with or without a leading `@`.
|
157
208
|
|
158
|
-
Ultimately, these methods are all
|
209
|
+
Ultimately, these methods are all performing basic accesses of the corresponding
|
210
|
+
instance variables:
|
159
211
|
|
160
212
|
@name1 ||= ThreadLocalVar.new
|
161
213
|
@name1.value = per_thread_value
|
162
214
|
...
|
163
215
|
@name1.value # returns the per_thread_value
|
164
|
-
|
165
|
-
If you prefer the style above, then you don't really need these accessor methods.
|
216
|
+
|
217
|
+
If you prefer the style above, then you don't really need these accessor methods.
|
166
218
|
|
167
219
|
### Example Usage
|
168
220
|
|
@@ -176,12 +228,26 @@ class MyClass
|
|
176
228
|
tlv_reader :sleep_time
|
177
229
|
tlv_writer :locked
|
178
230
|
|
231
|
+
# if the ivars will not be inherited in new threads after initialization
|
179
232
|
def initialize(**args)
|
233
|
+
# set the current thread's local value for each ivar
|
180
234
|
self.limit = args[:limit]
|
181
235
|
self.timeout = args[:timeout]
|
182
236
|
self.max_time = args[:max_time]
|
183
237
|
self.sleep_time = args[:sleep_time]
|
184
238
|
end
|
239
|
+
|
240
|
+
# if the ivars might possibly be inherited in new threads after
|
241
|
+
# initialization, then the defaults should be set for each thread to
|
242
|
+
# inherit.
|
243
|
+
|
244
|
+
def alt_initialize(**args)
|
245
|
+
# for each ivar, set the default value, which is inherited across all threads
|
246
|
+
tlv_init :limit, args[:limit]
|
247
|
+
tlv_init :timeout, args[:timeout]
|
248
|
+
tlv_init :max_time, args[:max_time]
|
249
|
+
tlv_init :sleep_time, args[:sleep_time]
|
250
|
+
end
|
185
251
|
|
186
252
|
def run
|
187
253
|
while count < limit && delay_time < timeout
|
@@ -196,34 +262,14 @@ class MyClass
|
|
196
262
|
end
|
197
263
|
```
|
198
264
|
|
199
|
-
There may be times where you may want to use the `tlv_`-prefix methods and not use the accessors.
|
200
|
-
|
201
|
-
The following are a set of equivalencies.
|
202
|
-
|
203
|
-
```ruby
|
204
|
-
tlv_accessor :timeout
|
205
|
-
```
|
206
|
-
|
207
|
-
produces both the reader and writer methods, using the `tlv_get` and `tlv_set` methods.
|
208
|
-
|
209
|
-
```ruby
|
210
|
-
def timeout
|
211
|
-
tlv_get(:timeout)
|
212
|
-
end
|
213
|
-
|
214
|
-
def timeout=(val)
|
215
|
-
tlv_set(:timeout, val)
|
216
|
-
end
|
217
|
-
```
|
218
|
-
|
219
|
-
The advantage of the block method, especially for `tlv_set_once`, is that the
|
220
|
-
VALUE is only evaluated when the instance variable value is being set, and, the block will receive the old value as a parameter.
|
221
|
-
|
222
265
|
## Development
|
223
266
|
|
224
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then,
|
267
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then,
|
268
|
+
run `rake spec` to run the tests. You can also run `bin/console` for an
|
269
|
+
interactive prompt that will allow you to experiment.
|
225
270
|
|
226
|
-
For both development and testing, the environment variables described above must
|
271
|
+
For both development and testing, the environment variables described above must
|
272
|
+
be defined.
|
227
273
|
|
228
274
|
## Testing
|
229
275
|
|
@@ -231,12 +277,16 @@ For both development and testing, the environment variables described above must
|
|
231
277
|
|
232
278
|
This repo is configured to the [gitflow](https://datasift.github.io/gitflow/IntroducingGitFlow.html) pattern, with the `develop` branch being the _default_ branch on PRs.
|
233
279
|
|
234
|
-
The `main` branch gets updated with a PR or with a manual merge-and-push from
|
280
|
+
The `main` branch gets updated with a PR or with a manual merge-and-push from
|
281
|
+
the `develop` branch by a repo admin.
|
235
282
|
|
236
|
-
When any branch is pushed, the continuous integration with causes the branch to
|
283
|
+
When any branch is pushed, the continuous integration with causes the branch to
|
284
|
+
be tested with all of the `rspec` tests _(except the integration tests)_.
|
237
285
|
|
238
|
-
When the `main` branch is updated and after its tests pass, the `deploy` action
|
286
|
+
When the `main` branch is updated and after its tests pass, the `deploy` action
|
287
|
+
is invoked which causes the newest build of the gem to be pushed to
|
288
|
+
rubygems.org.
|
239
289
|
|
240
290
|
## Original Author:
|
241
|
-
|
291
|
+
|
242
292
|
Alan K. Stebbens <aks@stebbens.org>
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'concurrent-ruby'
|
4
|
+
|
3
5
|
# This module has methods making it easy to use the Concurrent::ThreadLocalVar
|
4
6
|
# as instance variables. This makes instance variables using this code as
|
5
7
|
# actually thread-local, without also leaking memory over time.
|
@@ -7,6 +9,8 @@
|
|
7
9
|
# See [Why Concurrent::ThreadLocalVar](https://github.com/ruby-concurrency/concurrent-ruby/blob/master/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb#L10-L17)
|
8
10
|
# to understand why we use TLVs instead of `Thread.current.thread_variable_(set|get)`
|
9
11
|
#
|
12
|
+
# Class Methods
|
13
|
+
#
|
10
14
|
# The following class methods declare `Concurrent::ThreadLocalVar` reader,
|
11
15
|
# writer, and accessors with these class methods:
|
12
16
|
#
|
@@ -32,58 +36,85 @@
|
|
32
36
|
#
|
33
37
|
# tlv_accessor :timeout
|
34
38
|
#
|
39
|
+
# Instance Methods
|
40
|
+
#
|
35
41
|
# Create a new TLV instance with an associated default, applied across all threads.
|
36
42
|
#
|
37
|
-
#
|
43
|
+
# tlv_new :timeout, default_timeout
|
44
|
+
# tlv_new :timeout { default_timeout }
|
45
|
+
#
|
46
|
+
# This method _always_ assignes the instance variable. It is equivalent to:
|
38
47
|
#
|
39
|
-
#
|
48
|
+
# @instance_var = ThreadLocalVar.new(default)
|
49
|
+
# @instance_var = ThreadLocalVar.new { default }
|
50
|
+
#
|
51
|
+
# Assign the current thread value for the TLV variable:
|
52
|
+
#
|
53
|
+
# self.timeout = 0.5 # stores the TLV value just for this thread
|
40
54
|
#
|
55
|
+
# which is equivalent to:
|
56
|
+
#
|
57
|
+
# @timeout ||= ThreadLocalVar.new
|
58
|
+
# @timeout.value = 0.5
|
41
59
|
#
|
42
60
|
# Reference the current thread value for the TLV variable:
|
43
61
|
#
|
44
62
|
# timeout # fetches the current TLV value, unique to each thread
|
45
63
|
#
|
46
|
-
#
|
64
|
+
# which is the same as:
|
47
65
|
#
|
48
|
-
#
|
66
|
+
# @timeout ||= ThreadLocalVar.new
|
67
|
+
# @timeout.value
|
49
68
|
#
|
50
69
|
# Alternative ways to initialize the thread-local value:
|
51
70
|
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
# @timeout.value = 0
|
71
|
+
# tlv_set(:timeout, 0)
|
72
|
+
# tlv_set(:timeout) # ensure that @timeout is initialized to an LTV
|
73
|
+
# tlv_set_once(:timeout, value) # set timeout only if it isn't already set
|
57
74
|
#
|
58
75
|
# Each thread-local instance can be independently assigned a value, which defaults
|
59
76
|
# to the _default_ value, or _block_, that was associated with the original
|
60
|
-
# `ThreadLocalVar.new` method. This module also provides an easy way to do this
|
77
|
+
# `ThreadLocalVar.new` method. This module also provides an easy way to do this.
|
61
78
|
#
|
62
79
|
# Initializes a TLV on the `@timeout` instance variable with a default value of
|
63
80
|
# 0.15 seconds:
|
64
81
|
#
|
65
|
-
#
|
82
|
+
# tlv_new(:timeout, 0.15)
|
66
83
|
#
|
67
84
|
# This does the same, but uses a block (a Proc object) to possibly return a
|
68
85
|
# dynamic default value, as the proc is invoked each time the TLV instance is
|
69
86
|
# evaluted in a Thread.
|
70
87
|
#
|
71
|
-
#
|
88
|
+
# tlv_new(:sleep_time) { computed_sleep_time }
|
72
89
|
#
|
73
90
|
# The block-proc is evaluated at the time the default value is needed, not when
|
74
91
|
# the TLV is assigned to the instance variable. In other words, much later
|
75
92
|
# during process, when the instance variable value is evaluated, _that_ is when
|
76
93
|
# the default block is evaluated.
|
77
94
|
#
|
95
|
+
# The `tlv_new` method always assigns a new TLVar to the named instance variable.
|
96
|
+
#
|
97
|
+
# There is a corresponding method to either create a new TLVar instance or
|
98
|
+
# assign the default value to the existing TLVar instance: `tlv_init`.
|
99
|
+
#
|
100
|
+
# tlv_init(IVAR, DEFAULT)
|
101
|
+
# tlv_init(IVAR) { DEFAULT }
|
102
|
+
#
|
78
103
|
# Note that `tlv_init` does not assign the thread-local value; it assigns the
|
79
104
|
# _instance variable_ to a new TLV with the given default. If any thread
|
80
105
|
# evaluates that instance variable, the default value will be returned unless
|
81
|
-
# and until
|
106
|
+
# and until the current thread associates a new, thread-local value with the TLV
|
107
|
+
# instance.
|
108
|
+
#
|
109
|
+
# The purpose of `tlv_init` is to make the initialization of a TLVar be idempotent:
|
110
|
+
# a. if the TLVar does not yet exist, it is created with the default value.
|
111
|
+
# b. if the TLVar already exists, it's default value is set.
|
112
|
+
#
|
113
|
+
# In neither case are any thread-local value set; only the default.
|
82
114
|
#
|
83
115
|
# The default for an existing TLV can be redefined, using either an optional
|
84
116
|
# default value, or an optional default block.
|
85
117
|
#
|
86
|
-
#
|
87
118
|
# tlv_set_default(:timeout, new_default)
|
88
119
|
# tlv_set_default(:timeout) { new_default }
|
89
120
|
#
|
@@ -145,10 +176,7 @@
|
|
145
176
|
# To assign a new value to an TLV instance:
|
146
177
|
#
|
147
178
|
# @timeout.value = new_value
|
148
|
-
|
149
|
-
require 'concurrent-ruby'
|
150
|
-
|
151
|
-
# methods for making usage of ThreadLocalVars easy
|
179
|
+
#
|
152
180
|
module ThreadLocalVarAccessors
|
153
181
|
# @!visibility private
|
154
182
|
module MyRefinements
|
@@ -200,45 +228,89 @@ module ThreadLocalVarAccessors
|
|
200
228
|
end
|
201
229
|
|
202
230
|
# instance methods
|
231
|
+
# Returns the value of the TLV instance variable, if any.
|
203
232
|
def tlv_get(name)
|
204
|
-
|
233
|
+
tlv_get_var(name)&.value
|
205
234
|
end
|
206
235
|
|
236
|
+
# @return [ThreadLocalVar] the TLV, if any, of the named instance variable.
|
237
|
+
def tlv_get_var(name)
|
238
|
+
var = instance_variable_get(name.to_ivar)
|
239
|
+
var if var.is_a?(Concurrent::ThreadLocalVar)
|
240
|
+
end
|
241
|
+
|
242
|
+
# If the instance variable is already a TLV, then set it's value to the
|
243
|
+
# given value, or the value returned by the block, if any. If the
|
244
|
+
# instance variable is not a TLV, then create a new TLV, initializing
|
245
|
+
# it's default value with the given value, or the value returned by the block,
|
246
|
+
# if any, then, returning it's local value -- which returns the default value.
|
247
|
+
#
|
248
|
+
# This method is equivalent to:
|
249
|
+
# if @name.is_a?(ThreadLocalVar)
|
250
|
+
# @name.value = block_given? ? yield : value
|
251
|
+
# else
|
252
|
+
# @name = ThreadLocalVar(value, &block)
|
253
|
+
# end
|
254
|
+
# @return [Object] the value of the TLV instance variable
|
207
255
|
def tlv_set(name, value = nil, &block)
|
208
|
-
var =
|
209
|
-
|
256
|
+
if (var = tlv_get_var(name))
|
257
|
+
tlv_set_var(var, value, &block)
|
258
|
+
else
|
259
|
+
tlv_new(name, value, &block).value
|
260
|
+
end
|
210
261
|
end
|
211
262
|
|
263
|
+
# Sets the thread-local value of the TLV instance variable, but only
|
264
|
+
# if it is not already set. It is equivalent to:
|
265
|
+
# @name ||= ThreadLocalVar.new
|
266
|
+
# @name.value ||= block_given? yield : value
|
212
267
|
def tlv_set_once(name, value = nil, &block)
|
213
|
-
if (var =
|
268
|
+
if (var = tlv_get_var(name))&.value
|
214
269
|
var.value
|
215
270
|
elsif var # var is set, but its value is nil
|
216
271
|
tlv_set_var(var, value, &block)
|
217
|
-
else # var is not set
|
218
|
-
|
272
|
+
else # var is not set, initialize it
|
273
|
+
tlv_new(name, value, &block).value
|
219
274
|
end
|
220
275
|
end
|
221
276
|
|
222
|
-
#
|
223
|
-
#
|
224
|
-
#
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
277
|
+
# Creates a new TLVar with the given default value or block.
|
278
|
+
# Equivalent to:
|
279
|
+
# @name = ThreadLocalVar.new(block_given? ? yield : default)
|
280
|
+
def tlv_new(name, default=nil, &block)
|
281
|
+
instance_variable_set(
|
282
|
+
name.to_ivar,
|
283
|
+
Concurrent::ThreadLocalVar.new(default, &block)
|
284
|
+
)
|
285
|
+
end
|
286
|
+
|
287
|
+
# Creates a new TLVar with a default, or assigns the default value to an
|
288
|
+
# existing TLVar, without affecting any existing thread-local values.
|
289
|
+
# Returns the value of the new TLVar.
|
290
|
+
# Equivalent to:
|
291
|
+
# @name ||= ThreadLocalVar.new
|
292
|
+
# @name.instance_variable_set(:@default, block_given? ? yield : default)
|
293
|
+
# @name.value
|
230
294
|
def tlv_init(name, default=nil, &block)
|
231
|
-
|
295
|
+
tlv_set_default(name, default, &block)
|
296
|
+
tlv_get(name)
|
232
297
|
end
|
233
|
-
alias tlv_new tlv_init
|
234
298
|
|
235
|
-
# Fetches the default value for the TLVar
|
299
|
+
# Fetches the default value for the TLVar at the ivar
|
236
300
|
def tlv_default(name)
|
237
|
-
|
301
|
+
tlv_get_default(tlv_get_var(name))
|
302
|
+
end
|
303
|
+
|
304
|
+
# gets the default value from a TLV
|
305
|
+
# this masks the private ThreadLocalVar#default method
|
306
|
+
def tlv_get_default(tlv)
|
307
|
+
tlv&.send(:default)
|
238
308
|
end
|
239
309
|
|
240
|
-
# Sets the default value or block for the TLV _(which is applied across all threads)_
|
241
|
-
|
310
|
+
# Sets the default value or block for the TLV _(which is applied across all threads)_.
|
311
|
+
# Creates a new TLV if the instance variable is not initialized.
|
312
|
+
# @return [Object] the effective default value of the TLV instance variable
|
313
|
+
def tlv_set_default(name, default=nil, &block)1
|
242
314
|
tlv = instance_variable_get(name.to_ivar)
|
243
315
|
if tlv
|
244
316
|
raise ArgumentError, "tlv_set_default: can only use a default or a block, not both" if default && block
|
@@ -250,9 +322,11 @@ module ThreadLocalVarAccessors
|
|
250
322
|
tlv.instance_variable_set(:@default_block, nil)
|
251
323
|
tlv.instance_variable_set(:@default, default)
|
252
324
|
end
|
325
|
+
tlv
|
253
326
|
else
|
254
|
-
|
327
|
+
tlv = tlv_new(name, default, &block)
|
255
328
|
end
|
329
|
+
tlv_get_default(tlv)
|
256
330
|
end
|
257
331
|
|
258
332
|
# @!visibility private
|
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
|
20
20
|
s.add_development_dependency 'bundler'
|
21
21
|
s.add_development_dependency 'fuubar'
|
22
|
+
s.add_development_dependency 'pry-byebug'
|
22
23
|
s.add_development_dependency 'rake'
|
23
24
|
s.add_development_dependency 'redcarpet'
|
24
25
|
s.add_development_dependency 'rspec'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thread_local_var_accessors
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alan Stebbens
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry-byebug
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rake
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|