thread_local_var_accessors 1.1.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +2 -1
- data/README.md +63 -38
- data/Rakefile +25 -7
- data/lib/thread_local_var_accessors/version.rb +6 -1
- data/lib/thread_local_var_accessors.rb +56 -28
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ed08c93f6e727f6ba7dc57984a05669fbf06bfe99f4fd46b3a6e81f72727128
|
4
|
+
data.tar.gz: b8223cf233fa40fc0749849117c0b6773b2a9723fd98dd80aed9017275f9882f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2387dc90dd67a904b4757a5461ecff8ca4c6b5cbd4ae515f3be15241df0a7e956583b6760ba6b7f6398fdcbdfcd64852f06c62579ea01edba2d3ae0763c670a
|
7
|
+
data.tar.gz: 1942812b37021fdeb8f4f4cf1acf782591e400c229247815f1308553ab37872caca9131aca67b304b1f5fcc3c43238699794ffae431ab19d236827913b6271c1
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
2024-05-09: Version 1.3.1
|
2
|
+
- DRYed up the `tlv_default` method
|
3
|
+
- Changed CI to support "release" task on the main branch
|
4
|
+
- Added more tasks to the Rakefile
|
5
|
+
- Added RELEASES to the version file.
|
6
|
+
- Updated the ruby version to 3.1
|
7
|
+
|
1
8
|
2023-05-04: Version 1.0.0
|
2
9
|
- added support for default values:
|
3
10
|
- renamed `tlv_new` to `tlv_init` (leaving `tlv_new` as an alias)
|
data/Gemfile.lock
CHANGED
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,25 +41,32 @@ 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
|
44
|
+
The following are a brief list of the instance variable in
|
45
|
+
the `ThreadLocalVarAccessors` class.
|
45
46
|
|
46
47
|
These methods interrogate or set thread-local variable values:
|
47
48
|
|
48
|
-
tlv_get NAME
|
49
|
-
tlv_set NAME, VALUE
|
50
|
-
tlv_set NAME { VALUE }
|
51
|
-
tlv_set_once NAME, VALUE
|
52
|
-
tlv_set_once NAME { VALUE }
|
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
|
53
54
|
|
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.
|
55
58
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
63
70
|
|
64
71
|
### Instance Variable Details
|
65
72
|
|
@@ -86,20 +93,21 @@ any particular value (other than nil) to be inherited, then using `tlv_set`
|
|
86
93
|
is the right way to go.
|
87
94
|
|
88
95
|
|
89
|
-
The TLV default value is used across *all* threads
|
96
|
+
The TLV default value is used across *all* threads, when there is no value
|
97
|
+
specifically assigned for a given thread.
|
90
98
|
|
91
99
|
tlv_init(:timeout, default)
|
92
100
|
tlv_init(:timeout) { default }
|
93
101
|
|
94
|
-
The `tlv_init` method is essentially the same as `tlv_set_default
|
95
|
-
default value (or block) of a given TLVar, without
|
96
|
-
thread-local variables already assigned.
|
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.
|
97
105
|
|
98
106
|
Alternative ways to initialize:
|
99
107
|
|
100
|
-
tlv_set(:timeout, 0)
|
108
|
+
tlv_set(:timeout, 0) # => 0
|
101
109
|
|
102
|
-
tlv_set(:timeout)
|
110
|
+
tlv_set(:timeout) # @timeout = Concurrent::ThreadLocalVar.new
|
103
111
|
@timeout.value = 0
|
104
112
|
|
105
113
|
### More Details
|
@@ -121,8 +129,8 @@ a TLV value only if has not currently be set already.
|
|
121
129
|
|
122
130
|
tlv_set_once(name) { |old_val| new_value }
|
123
131
|
|
124
|
-
For `tlv_accessor` instance variables, it's possible to use the assign operators,
|
125
|
-
For example:
|
132
|
+
For `tlv_accessor` instance variables, it's possible to use the assign operators,
|
133
|
+
eg: `+=`, or `||=`. For example:
|
126
134
|
|
127
135
|
tlv_accessor :timeout, :count
|
128
136
|
|
@@ -152,14 +160,16 @@ Then:
|
|
152
160
|
|
153
161
|
## Usage
|
154
162
|
|
155
|
-
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:
|
156
165
|
|
157
166
|
class MyNewClass
|
158
167
|
include ThreadLocalVarAccessors
|
159
168
|
...
|
160
169
|
end
|
161
170
|
|
162
|
-
With the include above, you can use the class methods to declare instance getter
|
171
|
+
With the include above, you can use the class methods to declare instance getter
|
172
|
+
and setter methods:
|
163
173
|
|
164
174
|
class MyNewClass
|
165
175
|
include ThreadLocalVarAccessors
|
@@ -175,11 +185,15 @@ The above invocations:
|
|
175
185
|
- create reader methods for `name1`, `name3`, and `name4`.
|
176
186
|
- create writer methods for `name2`, `name3`, and `name4`.
|
177
187
|
|
178
|
-
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.
|
179
190
|
|
180
|
-
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`.
|
181
194
|
|
182
|
-
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:
|
183
197
|
|
184
198
|
tlv_get(name)
|
185
199
|
tlv_set(name, value)
|
@@ -192,7 +206,8 @@ Alternative block forms:
|
|
192
206
|
|
193
207
|
In all cases, the `name` can be a string or symbol, with or without a leading `@`.
|
194
208
|
|
195
|
-
Ultimately, these methods are all
|
209
|
+
Ultimately, these methods are all performing basic accesses of the corresponding
|
210
|
+
instance variables:
|
196
211
|
|
197
212
|
@name1 ||= ThreadLocalVar.new
|
198
213
|
@name1.value = per_thread_value
|
@@ -222,7 +237,10 @@ class MyClass
|
|
222
237
|
self.sleep_time = args[:sleep_time]
|
223
238
|
end
|
224
239
|
|
225
|
-
# if the ivars might possibly be inherited in new threads after
|
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
|
+
|
226
244
|
def alt_initialize(**args)
|
227
245
|
# for each ivar, set the default value, which is inherited across all threads
|
228
246
|
tlv_init :limit, args[:limit]
|
@@ -246,9 +264,12 @@ end
|
|
246
264
|
|
247
265
|
## Development
|
248
266
|
|
249
|
-
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.
|
250
270
|
|
251
|
-
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.
|
252
273
|
|
253
274
|
## Testing
|
254
275
|
|
@@ -256,11 +277,15 @@ For both development and testing, the environment variables described above must
|
|
256
277
|
|
257
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.
|
258
279
|
|
259
|
-
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.
|
260
282
|
|
261
|
-
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)_.
|
262
285
|
|
263
|
-
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.
|
264
289
|
|
265
290
|
## Original Author:
|
266
291
|
|
data/Rakefile
CHANGED
@@ -1,19 +1,37 @@
|
|
1
1
|
# Rakefile for thread_local_var_accessors
|
2
|
-
require
|
3
|
-
require
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'rspec/core/rake_task'
|
4
4
|
require 'yard'
|
5
5
|
|
6
|
+
namespace :spec do
|
7
|
+
desc 'run tests with code coverage'
|
8
|
+
task :coverage do
|
9
|
+
sh 'COVERAGE=1 bundle exec rake spec'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
task build: %i[bundle:add_linux]
|
14
|
+
task install: %i[build spec]
|
15
|
+
task release: %i[build spec]
|
16
|
+
|
6
17
|
# Local CI testing
|
7
18
|
|
8
19
|
namespace :ci do
|
9
|
-
desc
|
20
|
+
desc 'Check CIRCLECI config'
|
10
21
|
task :check do
|
11
|
-
sh
|
22
|
+
sh 'circleci config validate', verbose: true
|
12
23
|
end
|
13
24
|
|
14
|
-
desc
|
25
|
+
desc 'Run CIRCLECI config locally'
|
15
26
|
task :local do
|
16
|
-
sh
|
27
|
+
sh 'circleci local execute', verbose: true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
namespace :bundle do
|
32
|
+
desc 'add linux platform to Gemfile.lock'
|
33
|
+
task :add_linux do
|
34
|
+
sh "grep -s 'x86_64-linux' Gemfile.lock >/dev/null || bundle lock --add-platform x86_64-linux"
|
17
35
|
end
|
18
36
|
end
|
19
37
|
|
@@ -22,7 +40,7 @@ end
|
|
22
40
|
RSpec::Core::RakeTask.new(:spec)
|
23
41
|
|
24
42
|
namespace :spec do
|
25
|
-
desc
|
43
|
+
desc 'run Simplecov'
|
26
44
|
task :coverage do
|
27
45
|
sh 'CODE_COVERAGE=1 bundle exec rake spec'
|
28
46
|
end
|
@@ -160,7 +160,7 @@ require 'concurrent-ruby'
|
|
160
160
|
#
|
161
161
|
# def timeout=(value)
|
162
162
|
# var = instance_variable_get(:@timeout) ||
|
163
|
-
#
|
163
|
+
# instance_variable_set(:@timeout, Concurrent::ThreadLocalVar.new)
|
164
164
|
# var.value = block_given? ? yield(var.value) : value
|
165
165
|
# end
|
166
166
|
#
|
@@ -230,16 +230,34 @@ module ThreadLocalVarAccessors
|
|
230
230
|
# instance methods
|
231
231
|
# Returns the value of the TLV instance variable, if any.
|
232
232
|
def tlv_get(name)
|
233
|
-
|
233
|
+
tlv_get_var(name)&.value
|
234
234
|
end
|
235
235
|
|
236
|
-
#
|
237
|
-
|
238
|
-
|
239
|
-
|
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
|
240
255
|
def tlv_set(name, value = nil, &block)
|
241
|
-
var =
|
242
|
-
|
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
|
243
261
|
end
|
244
262
|
|
245
263
|
# Sets the thread-local value of the TLV instance variable, but only
|
@@ -247,56 +265,66 @@ module ThreadLocalVarAccessors
|
|
247
265
|
# @name ||= ThreadLocalVar.new
|
248
266
|
# @name.value ||= block_given? yield : value
|
249
267
|
def tlv_set_once(name, value = nil, &block)
|
250
|
-
if (var =
|
268
|
+
if (var = tlv_get_var(name))&.value
|
251
269
|
var.value
|
252
270
|
elsif var # var is set, but its value is nil
|
253
271
|
tlv_set_var(var, value, &block)
|
254
|
-
else # var is not set
|
255
|
-
|
272
|
+
else # var is not set, initialize it
|
273
|
+
tlv_new(name, value, &block).value
|
256
274
|
end
|
257
275
|
end
|
258
276
|
|
259
277
|
# Creates a new TLVar with the given default value or block.
|
260
278
|
# Equivalent to:
|
261
279
|
# @name = ThreadLocalVar.new(block_given? ? yield : default)
|
262
|
-
def tlv_new(name, default=nil, &block)
|
263
|
-
instance_variable_set(
|
280
|
+
def tlv_new(name, default = nil, &block)
|
281
|
+
instance_variable_set(
|
282
|
+
name.to_ivar,
|
283
|
+
Concurrent::ThreadLocalVar.new(default, &block)
|
284
|
+
)
|
264
285
|
end
|
265
286
|
|
266
287
|
# Creates a new TLVar with a default, or assigns the default value to an
|
267
288
|
# existing TLVar, without affecting any existing thread-local values.
|
289
|
+
# Returns the value of the new TLVar.
|
268
290
|
# Equivalent to:
|
269
291
|
# @name ||= ThreadLocalVar.new
|
270
292
|
# @name.instance_variable_set(:@default, block_given? ? yield : default)
|
271
|
-
|
293
|
+
# @name.value
|
294
|
+
def tlv_init(name, default = nil, &block)
|
272
295
|
tlv_set_default(name, default, &block)
|
296
|
+
tlv_get(name)
|
273
297
|
end
|
274
298
|
|
275
|
-
# Fetches the default value for the TLVar
|
276
|
-
# Equivalent to:
|
277
|
-
# @name&.send(:default)
|
299
|
+
# Fetches the default value for the TLVar at the ivar
|
278
300
|
def tlv_default(name)
|
279
|
-
|
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)
|
280
308
|
end
|
281
309
|
|
282
310
|
# Sets the default value or block for the TLV _(which is applied across all threads)_.
|
283
311
|
# Creates a new TLV if the instance variable is not initialized.
|
284
|
-
|
312
|
+
# @return [Object] the effective default value of the TLV instance variable
|
313
|
+
def tlv_set_default(name, default = nil, &block)
|
314
|
+
1
|
285
315
|
tlv = instance_variable_get(name.to_ivar)
|
286
316
|
if tlv
|
287
|
-
raise ArgumentError,
|
317
|
+
raise ArgumentError, 'tlv_set_default: can only use a default or a block, not both' if default && block
|
288
318
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
tlv.instance_variable_set(:@default_block, nil)
|
294
|
-
tlv.instance_variable_set(:@default, default)
|
295
|
-
end
|
319
|
+
# in both cases, the default is set to the given value, only one of which
|
320
|
+
# can be given at a time.
|
321
|
+
tlv.instance_variable_set(:@default_block, block)
|
322
|
+
tlv.instance_variable_set(:@default, default)
|
296
323
|
tlv
|
297
324
|
else
|
298
|
-
tlv_new(name, default, &block)
|
325
|
+
tlv = tlv_new(name, default, &block)
|
299
326
|
end
|
327
|
+
tlv_get_default(tlv)
|
300
328
|
end
|
301
329
|
|
302
330
|
# @!visibility private
|
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.1
|
4
|
+
version: 1.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alan Stebbens
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-05-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -242,7 +242,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
242
242
|
- !ruby/object:Gem::Version
|
243
243
|
version: '0'
|
244
244
|
requirements: []
|
245
|
-
rubygems_version: 3.
|
245
|
+
rubygems_version: 3.3.26
|
246
246
|
signing_key:
|
247
247
|
specification_version: 4
|
248
248
|
summary: Ruby gem to make ThreadLocalVars easy to use
|