thread_local_var_accessors 1.0.0 → 1.3.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: 44a2d47618384051b762c595994639af0699cde590462760853d3be02a611ffa
4
- data.tar.gz: 4d4e3629f6bdfa482e5f080c6576d4a74d465a6b67c567b3c169db87e3b782fa
3
+ metadata.gz: 4537e0db3960367d75a6ab4d731026c6a425e315816bf0523d078b563b90a09a
4
+ data.tar.gz: d270efc557c3b04f41ae9adf7850157d98c793a7d57cd025e8c88a40d2aaa154
5
5
  SHA512:
6
- metadata.gz: e840c90fd9f096762ee4fd12ba5c4cffc9fb6952af177375327e04395cd9734a572542f1301ada916704daa507c21aaf42d4543d4dd01538e85bdf652a631c34
7
- data.tar.gz: 826a2db9381d3f7ab70482b0c866427554e58e4c7d70e238964e34593437add0d276067a21aa2e2775f8b9dc050cf05e99776f56374f330dd2a9e952738ecda7
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.0.0)
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 single
32
- argument that is the new value. This method checks for an existing value
33
- on the instance variable named `@name`, which should be a
34
- `Concurrent::ThreadLocalVar` instance. If `@name` value is nil, then a new
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
- The TLV default value is used across *all* threads.
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
- tlv_init(:timeout, _default_)
61
- tlv_init(:timeout) { _default_ }
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) # ensure that @timeout is initialized to an TLV
110
+ tlv_set(:timeout) # @timeout = Concurrent::ThreadLocalVar.new
68
111
  @timeout.value = 0
69
112
 
70
- The following methods are used within the above reader, writer, accessor
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, eg: `+=`, or `||=`.
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: :@name),
101
- and setter-method name syntax (eg :name=).
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 class, with:
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 and setter methods:
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 an optional, associated block.
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 object must have both a reader and writer method. In other words, it needs to have been created as an `tlv_accessor`.
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 the underlying instance methods:
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 doing these basic accesses of the corresponding instance variables:
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, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
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 be defined.
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 the `develop` branch by a repo admin.
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 be tested with all of the `rspec` tests _(except the integration tests)_.
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 is invoked which causes the newest build of the gem to be pushed to rubygems.org.
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,3 +1,3 @@
1
1
  module ThreadLocalVarAccessors
2
- VERSION = '1.0.0'
2
+ VERSION = '1.3.0'
3
3
  end
@@ -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
- # tlv_init :timeout, default_timeout
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
- # tlv_init :timeout { default_timeout }
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
- # Assign the current thread value for the TLV variable:
64
+ # which is the same as:
47
65
  #
48
- # self.timeout = 0.5 # stores the TLV value just for this thread
66
+ # @timeout ||= ThreadLocalVar.new
67
+ # @timeout.value
49
68
  #
50
69
  # Alternative ways to initialize the thread-local value:
51
70
  #
52
- # ltv_set(:timeout, 0)
53
- #
54
- # ltv_set(:timeout) # ensure that @timeout is initialized to an LTV
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
- # tlv_init(:timeout, 0.15)
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
- # tlv_init(:sleep_time) { computed_sleep_time }
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 each thread associates a new, thread-local value with the TLV.
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
- instance_variable_get(name.to_ivar)&.value
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 = instance_variable_get(name.to_ivar) || tlv_new(name)
209
- tlv_set_var(var, value, &block)
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 = instance_variable_get(name.to_ivar)) && !var.value.nil?
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
- tlv_set_var(tlv_new(name), value, &block)
272
+ else # var is not set, initialize it
273
+ tlv_new(name, value, &block).value
219
274
  end
220
275
  end
221
276
 
222
- # @param [String|Symbol] name the TLV name
223
- # @param [Object|nil] default the optional default value
224
- # @param [Proc] block the optional associated block
225
- # @return [ThreadLocalVar] a new TLV set in the instance variable
226
- # @example Default argument
227
- # tlv_init(:ivar, default_value)
228
- # @example Default block
229
- # tlv_init(:ivar) { default_value }
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
- instance_variable_set(name.to_ivar, Concurrent::ThreadLocalVar.new(default, &block))
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
- instance_variable_get(name.to_ivar)&.send(:default)
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
- def tlv_set_default(name, default=nil, &block)
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
- tlv_init(name, default, &block)
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.0.0
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-05-26 00:00:00.000000000 Z
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