thread-local 1.0.0 → 1.1.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: 96d9a2bba22cf0213da7a37e9834689cda7727e256bee89402006ddde7c34aa6
4
- data.tar.gz: 64a871588d675a66599a36aa04a2353a844063bf88ad9af500064d42aad37a86
3
+ metadata.gz: 6f4032930cf7762d586307e1bb9ced4d69b2d3f72b5f1165c616df4df6a96d85
4
+ data.tar.gz: da327e71a8e4b8c7ad46aac6bbb3a8036142a6fee070db816dbec9dcf73861f7
5
5
  SHA512:
6
- metadata.gz: ab2d8d85eaa85e6d30d30b1b28613393f447b1ecee6e278f50135641f1ab1d9507bb036d8c57d025f98605300cb69d20cc2af29edc2f2611f6153bf5a09625b9
7
- data.tar.gz: b52551c7a93f1de9ccae111a2f8ad3acafcac461c88bb40e5de953fa3a27f199f68931ed545c23eefc8ed318478f76d592fe44808fdb9d1cc711454f9075bec8
6
+ metadata.gz: 56bca34b696ab47635edbd9bfd9dd4099f59a57cccdfd795aae7b5eec739f7fb166282e383b9f546964be0251b3a4cdf08320a399f9e260c85e35cb6e762487a
7
+ data.tar.gz: 101f513ddd1a8b5900dc7ae68e7398dacd5e80418b8d9e755a130fb719c2658eb1c84dd7c25b3f8d728061bece1fb57b3baee430b7d29100308e87de08099f6a
@@ -0,0 +1,81 @@
1
+ # Thread::Local
2
+
3
+ Implements a standard interface for per-class/module thread local instances.
4
+
5
+ ## Per-Class Instance
6
+
7
+ By default, classes are instantiated using `self.new`:
8
+
9
+ ~~~ ruby
10
+ require 'thread/local'
11
+
12
+ class MyClass
13
+ extend Thread::Local
14
+ end
15
+
16
+ p MyClass.instance
17
+ ~~~
18
+
19
+ ### Extensions
20
+
21
+ If you need to change the behaviour of an existing thread local, you can prepend a module to wrap the call to `self.new`:
22
+
23
+ ~~~ ruby
24
+ require 'thread/local'
25
+
26
+ class MyClass
27
+ extend Thread::Local
28
+
29
+ attr_accessor :data
30
+ end
31
+
32
+ module MyExtension
33
+ def local
34
+ instance = super
35
+
36
+ # Do something with the thread local instance, e.g.:
37
+ instance.data = [1, 2, 3]
38
+
39
+ return instance
40
+ end
41
+ end
42
+
43
+ MyClass.extend(MyExtension)
44
+
45
+ p MyClass.instance
46
+ ~~~
47
+
48
+ ### Assignment
49
+
50
+ Generally, you should avoid assigning to the thread-local instance. You should prefer extensions as outlined above. However, in some situations (e.g. testing) you may prefer to directly assign to the thread local:
51
+
52
+ ~~~ruby
53
+ require 'thread/local'
54
+
55
+ class MyClass
56
+ extend Thread::Local
57
+ end
58
+
59
+ # Change the instance for the current thread:
60
+ MyClass.instance = Object.new
61
+
62
+ p MyClass.instance
63
+ ~~~
64
+
65
+ ## Per-Module Instance
66
+
67
+ By default, you cannot instantiate a module. So, you must implement your own `self.new` method:
68
+
69
+ ~~~ ruby
70
+ require 'thread/local'
71
+
72
+ module MyModule
73
+ extend Thread::Local
74
+
75
+ def self.new
76
+ Object.new
77
+ end
78
+ end
79
+
80
+ p MyModule.instance
81
+ ~~~
@@ -20,25 +20,37 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
21
  # THE SOFTWARE.
22
22
 
23
- require "thread/local/version"
23
+ require_relative "local/version"
24
24
 
25
25
  class Thread
26
26
  module Local
27
27
  # Instantiate a new thread-local object.
28
+ # By default, invokes {new} to generate the instance.
29
+ # @returns [Object]
28
30
  def local
29
31
  self.new
30
32
  end
31
33
 
32
34
  # Get the current thread-local instance. Create it if required.
33
- def instance(thread = Thread.current)
35
+ # @returns [Object] The thread-local instance.
36
+ def instance
37
+ thread = Thread.current
34
38
  name = self.name
35
39
 
36
40
  unless instance = thread.thread_variable_get(name)
37
- instance = self.local
38
- thread.thread_variable_set(name, instance)
41
+ if instance = self.local
42
+ thread.thread_variable_set(name, instance)
43
+ end
39
44
  end
40
45
 
41
46
  return instance
42
47
  end
48
+
49
+ # Assigns the current thread-local instance.
50
+ # @parameter instance [Object] The object that will become the thread-local instance.
51
+ def instance= instance
52
+ thread = Thread.current
53
+ thread.thread_variable_set(self.name, instance)
54
+ end
43
55
  end
44
56
  end
@@ -20,10 +20,8 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
21
  # THE SOFTWARE.
22
22
 
23
- require 'thread'
24
-
25
23
  class Thread
26
24
  module Local
27
- VERSION = "1.0.0"
25
+ VERSION = "1.1.0"
28
26
  end
29
27
  end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thread-local
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-22 00:00:00.000000000 Z
11
+ date: 2020-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: covered
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: bundler
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -39,7 +25,7 @@ dependencies:
39
25
  - !ruby/object:Gem::Version
40
26
  version: '0'
41
27
  - !ruby/object:Gem::Dependency
42
- name: rspec
28
+ name: covered
43
29
  requirement: !ruby/object:Gem::Requirement
44
30
  requirements:
45
31
  - - ">="
@@ -53,7 +39,7 @@ dependencies:
53
39
  - !ruby/object:Gem::Version
54
40
  version: '0'
55
41
  - !ruby/object:Gem::Dependency
56
- name: rake
42
+ name: rspec
57
43
  requirement: !ruby/object:Gem::Requirement
58
44
  requirements:
59
45
  - - ">="
@@ -66,27 +52,20 @@ dependencies:
66
52
  - - ">="
67
53
  - !ruby/object:Gem::Version
68
54
  version: '0'
69
- description:
55
+ description:
70
56
  email:
71
- - samuel.williams@oriontransfer.co.nz
72
57
  executables: []
73
58
  extensions: []
74
59
  extra_rdoc_files: []
75
60
  files:
76
- - ".github/workflows/development.yml"
77
- - ".gitignore"
78
- - ".rspec"
79
- - Gemfile
80
- - README.md
81
- - Rakefile
61
+ - lib/thread/local.md
82
62
  - lib/thread/local.rb
83
63
  - lib/thread/local/version.rb
84
- - thread-local.gemspec
85
- homepage:
64
+ homepage: https://github.com/socketry/thread-local
86
65
  licenses:
87
66
  - MIT
88
67
  metadata: {}
89
- post_install_message:
68
+ post_install_message:
90
69
  rdoc_options: []
91
70
  require_paths:
92
71
  - lib
@@ -94,7 +73,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
94
73
  requirements:
95
74
  - - ">="
96
75
  - !ruby/object:Gem::Version
97
- version: 2.3.0
76
+ version: 2.5.0
98
77
  required_rubygems_version: !ruby/object:Gem::Requirement
99
78
  requirements:
100
79
  - - ">="
@@ -102,7 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
81
  version: '0'
103
82
  requirements: []
104
83
  rubygems_version: 3.1.2
105
- signing_key:
84
+ signing_key:
106
85
  specification_version: 4
107
86
  summary: Provides a class-level mixin to make thread local state easy.
108
87
  test_files: []
@@ -1,34 +0,0 @@
1
- name: Development
2
-
3
- on: [push, pull_request]
4
-
5
- jobs:
6
- test:
7
- strategy:
8
- matrix:
9
- os:
10
- - ubuntu
11
- - macos
12
-
13
- ruby:
14
- - 2.4
15
- - 2.5
16
- - 2.6
17
- - 2.7
18
-
19
- include:
20
- - os: 'ubuntu'
21
- ruby: '2.6'
22
- env: COVERAGE=PartialSummary,Coveralls
23
-
24
- runs-on: ${{matrix.os}}-latest
25
-
26
- steps:
27
- - uses: actions/checkout@v1
28
- - uses: ruby/setup-ruby@v1
29
- with:
30
- ruby-version: ${{matrix.ruby}}
31
- - name: Install dependencies
32
- run: bundle install
33
- - name: Run tests
34
- run: ${{matrix.env}} bundle exec rspec
data/.gitignore DELETED
@@ -1,12 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
- /tmp/
9
-
10
- # rspec failure tracking
11
- .rspec_status
12
- Gemfile.lock
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- # Specify your gem's dependencies in thread-local.gemspec
4
- gemspec
data/README.md DELETED
@@ -1,85 +0,0 @@
1
- # Thread::Local ![Development](https://github.com/socketry/thread-local/workflows/Development/badge.svg?branch=master)
2
-
3
- A module to simplify thread-local state.
4
-
5
- ## Motivation
6
-
7
- In my own web framework, [utopia](https://github.com/socketry/utopia), I have been struggling with the best way to expose configuration details. I was setting both global variables and modifying `ENV` which made it impossible to have multiple isolated instances of the application in the same process. This in turn makes it hard to implement things like graceful restart in multi-threaded [falcon](https://github.com/socketry/falcon). Such issues also affect application code running in other multi-threaded contexts, which are becoming increasingly common (e.g. JRuby, TruffleRuby).
8
-
9
- Global variables are often not thread-safe and encourage poor programming style. In many cases it is desirable to have thread-local state, but implementing this directly in Ruby is unpleasant at best. This gem provides a best-practice wrapper which can extend existing classes to provide per-thread instances.
10
-
11
- Conceptually, a thread is a container for application state. This works well when servers consider applications to be isolated on a per-thread basis, but this isn't always the case:
12
-
13
- | Server | Application | Thread Safety |
14
- |----------------------|------------------|--------------------------|
15
- | Falcon Multi-Process | One per process. | Isolated. |
16
- | Falcon Multi-Thread | One per thread. | Isolated, Shared State. |
17
- | Puma Multi-Thread | One per process. | Reentrant, Shared State. |
18
- | Puma Cluster | One per worker. | Reentrant, Shared State. |
19
- | Unicorn | One per process. | Isolated. |
20
-
21
- Puma requires applications to be completely thread safe and reentrant, which isn't always easy. However, this gem attempts to provide a model which works in all the above servers, providing isolated, thread-safe, mutable per-thread state.
22
-
23
- ## Installation
24
-
25
- ```
26
- bundle add thread-local
27
- ```
28
-
29
- ## Usage
30
-
31
- In your own class:
32
-
33
- ```ruby
34
- class MyThing
35
- extend Thread::Local
36
- end
37
- ```
38
-
39
- Now, instead of instantiating your class `MyThing.new`, use `MyThing.instance`. It will return a thread-local instance.
40
-
41
- ```ruby
42
- Thread.new do
43
- MyThing.instance
44
- # => #<MyObject:0x000055a14ec6be80>
45
- end
46
-
47
- Thread.new do
48
- MyThing.instance
49
- # => #<MyObject:0x000055a14ec597d0>
50
- end
51
- ```
52
-
53
- Use this as a thread-safe alternative to globals.
54
-
55
- ## Contributing
56
-
57
- 1. Fork it
58
- 2. Create your feature branch (`git checkout -b my-new-feature`)
59
- 3. Commit your changes (`git commit -am 'Add some feature'`)
60
- 4. Push to the branch (`git push origin my-new-feature`)
61
- 5. Create new Pull Request
62
-
63
- ## License
64
-
65
- Released under the MIT license.
66
-
67
- Copyright, 2020, by [Samuel G. D. Williams](https://www.codeotaku.com).
68
-
69
- Permission is hereby granted, free of charge, to any person obtaining a copy
70
- of this software and associated documentation files (the "Software"), to deal
71
- in the Software without restriction, including without limitation the rights
72
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
73
- copies of the Software, and to permit persons to whom the Software is
74
- furnished to do so, subject to the following conditions:
75
-
76
- The above copyright notice and this permission notice shall be included in
77
- all copies or substantial portions of the Software.
78
-
79
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
80
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
81
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
82
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
83
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
84
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
85
- THE SOFTWARE.
data/Rakefile DELETED
@@ -1,6 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task :default => :spec
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
-
23
- require_relative 'lib/thread/local/version'
24
-
25
- Gem::Specification.new do |spec|
26
- spec.name = "thread-local"
27
- spec.version = Thread::Local::VERSION
28
- spec.authors = ["Samuel Williams"]
29
- spec.email = ["samuel.williams@oriontransfer.co.nz"]
30
-
31
- spec.summary = "Provides a class-level mixin to make thread local state easy."
32
- spec.license = "MIT"
33
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
34
-
35
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
36
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
37
- end
38
-
39
- spec.require_paths = ["lib"]
40
-
41
- spec.add_development_dependency "covered"
42
- spec.add_development_dependency "bundler"
43
- spec.add_development_dependency "rspec"
44
- spec.add_development_dependency "rake"
45
- end