tlattr_accessors 0.0.3

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.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT license:
2
+ Copyright (c) 2009 Maximilian Schöfmann
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
@@ -0,0 +1,81 @@
1
+ = Thread-local accessors for your classes
2
+
3
+ Yet another tiny library to tackle this problem.
4
+
5
+ Install with: <tt>gem install schoefmax-tlattr_accessors --source=http://gems.github.com</tt>
6
+
7
+ === Example
8
+
9
+ require 'rubygems'
10
+ require 'tlattr_accessors'
11
+
12
+ class ThreadExample
13
+ extend ThreadLocalAccessors
14
+ tlattr_accessor :foo
15
+
16
+ def test
17
+ self.foo = "bla"
18
+ Thread.new {
19
+ puts foo # prints "nil"
20
+ self.foo = "blubb"
21
+ puts foo # prints "blubb"
22
+ }.join
23
+ puts foo # prints "bla"
24
+ end
25
+ end
26
+
27
+ ThreadExample.new.test
28
+
29
+ If you want to enable them globally, add this somewhere (e.g. an initializer in Rails)
30
+
31
+ Object.send :extend, ThreadLocalAccessors
32
+
33
+ === Default values
34
+
35
+ Adding +true+ as last parameter will cause the first value set on the
36
+ attribute to act as default value for all other threads:
37
+
38
+ tlattr_accessor :yeah, :baby, true
39
+
40
+ def test_default
41
+ self.yeah = "bla"
42
+ Thread.new {
43
+ puts yeah # prints "bla"
44
+ puts baby # prints "nil"
45
+ self.baby = "blubb"
46
+ self.yeah = "blabla"
47
+ }.join
48
+ puts yeah # prints "bla"
49
+ puts baby # prints "blubb"
50
+ end
51
+
52
+ === Getters and Setters
53
+
54
+ This gem doesn't support <tt>tlattr</tt> or <tt>tlattr_reader|writer</tt> for
55
+ the simple reason that they don't make any sense here (you don't have an "instance
56
+ variable", so you need both methods).
57
+ If you want to hide one of them from your API, you can always make them private:
58
+
59
+ tlattr_accessor :foo
60
+ private :foo= # hide the setter
61
+
62
+ === Performance
63
+
64
+ The <tt>Thread.current</tt>-Hash is a global namespace. Using it to store
65
+ thread-local variables safely requires carefully crafted keys, which tend to
66
+ be rather expensive to compute. This hurts performance if the attribute is
67
+ accessed frequently.
68
+ Therefore, this library uses a different approach, which is a lot faster:
69
+ The values are stored in a separate hash which is keyed by the object_id of
70
+ the thread. Finalizers make sure no memory is leaked when threads finish
71
+ (see the spec).
72
+
73
+ === Running specs
74
+
75
+ If you haven't already, install the rspec gem, then run:
76
+
77
+ spec spec
78
+
79
+
80
+ (c) 2009, Max Schoefmann <max (a) pragmatic-it de>
81
+ Released under the MIT license
@@ -0,0 +1,45 @@
1
+ module ThreadLocalAccessors
2
+ # Creates thread-local accessors for the given attribute name.
3
+ #
4
+ # === Example:
5
+ #
6
+ # tlattr_accessor :my_attr, :another_attr
7
+ #
8
+ # === Default values
9
+ #
10
+ # You can make the attribute inherit the first value that was set on it in
11
+ # any thread:
12
+ #
13
+ # tlattr_accessor :my_attr, true
14
+ #
15
+ # def initialize
16
+ # self.my_attr = "foo"
17
+ # Thread.new do
18
+ # puts self.my_attr # => "foo" (instead of nil)
19
+ # end.join
20
+ # end
21
+ def tlattr_accessor(*names)
22
+ first_is_default = names.pop if [true, false].include?(names.last)
23
+ names.each do |name|
24
+ ivar = "@_tlattr_#{name}"
25
+ class_eval %Q{
26
+ def #{name}
27
+ if #{ivar}
28
+ #{ivar}[Thread.current.object_id]
29
+ else
30
+ nil
31
+ end
32
+ end
33
+
34
+ def #{name}=(val)
35
+ #{ivar} = Hash.new #{'{|h, k| h[k] = val}' if first_is_default} unless #{ivar}
36
+ thread_id = Thread.current.object_id
37
+ unless #{ivar}.has_key?(thread_id)
38
+ ObjectSpace.define_finalizer(Thread.current, lambda { #{ivar}.delete(thread_id) })
39
+ end
40
+ #{ivar}[thread_id] = val
41
+ end
42
+ }, __FILE__, __LINE__
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,87 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'tlattr_accessors')
4
+
5
+ describe ThreadLocalAccessors do
6
+
7
+ class Foo
8
+ extend ThreadLocalAccessors
9
+ tlattr_accessor :bar, true
10
+ tlattr_accessor :foo, :baz
11
+ end
12
+
13
+ class Bar
14
+ def initialize(value)
15
+ @value = value
16
+ end
17
+ end
18
+
19
+ it 'should allow defining multiple attributes at once' do
20
+ x = Foo.new
21
+ [:foo, :foo=, :baz=, :baz].each do |method|
22
+ x.should respond_to(method)
23
+ end
24
+ end
25
+
26
+ it "should store values local to the thread" do
27
+ x = Foo.new
28
+ x.baz = 2
29
+ Thread.new do
30
+ x.baz = 3
31
+ Thread.new do
32
+ x.baz = 5
33
+ end.join
34
+ x.baz.should == 3
35
+ end.join
36
+ x.baz.should == 2
37
+ end
38
+
39
+ it 'should, by default, not return a default value' do
40
+ x = Foo.new
41
+ x.baz = 2
42
+ Thread.new do
43
+ x.baz.should be_nil
44
+ end.join
45
+ end
46
+
47
+ it 'should, if told to, use the first value as default for subsequent threads' do
48
+ # Foo#bar is defined with +true+ as last param
49
+ x = Foo.new
50
+ x.bar = 2
51
+ Thread.new do
52
+ x.bar.should == 2
53
+ x.bar = 3
54
+ Thread.new do
55
+ x.bar.should == 2
56
+ x.bar = 5
57
+ end.join
58
+ x.bar.should == 3
59
+ end.join
60
+ x.bar.should == 2
61
+ end
62
+
63
+ it 'should not leak memory' do
64
+ x = Foo.new
65
+ n = 6000
66
+ # create many thread-local values to make sure GC is invoked
67
+ n.times do
68
+ Thread.new do
69
+ x.bar = Bar.new(rand)
70
+ end.join
71
+ end
72
+ hash = x.send :instance_variable_get, '@_tlattr_bar'
73
+ hash.size.should < (n / 2) # it should be a lot lower than n!
74
+ end
75
+
76
+ it 'should define only one finalizer per thread' do
77
+ ObjectSpace.should_receive(:define_finalizer).exactly(:twice)
78
+ x = Foo.new
79
+ 2.times do
80
+ Thread.new do
81
+ 10.times { x.foo = "bar" }
82
+ end.join
83
+ end
84
+ end
85
+
86
+ end
87
+
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{tlattr_accessors}
5
+ s.version = "0.0.3"
6
+ s.authors = ["Maximilian Sch\303\266fmann"]
7
+ s.date = %q{2009-03-10}
8
+ s.description = "thread-local accessors for your classes"
9
+ s.email = "max@pragmatic-it.de"
10
+ s.extra_rdoc_files = ["LICENSE", "README.rdoc"]
11
+ s.files = ["lib/tlattr_accessors.rb", "LICENSE", "README.rdoc", "spec/thread_local_accessors_spec.rb", "tlattr_accessors.gemspec"]
12
+ s.has_rdoc = true
13
+ s.homepage = "http://github.com/schoefmax/tlattr_accessors"
14
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "tlattr_accessors", "--main", "README.rdoc"]
15
+ s.require_paths = ["lib"]
16
+ s.rubyforge_project = "tlattr_accessors"
17
+ s.rubygems_version = %q{1.3.1}
18
+ s.summary = "thread-local accessors for your classes"
19
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tlattr_accessors
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - "Maximilian Sch\xC3\xB6fmann"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-10 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: thread-local accessors for your classes
17
+ email: max@pragmatic-it.de
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.rdoc
25
+ files:
26
+ - lib/tlattr_accessors.rb
27
+ - LICENSE
28
+ - README.rdoc
29
+ - spec/thread_local_accessors_spec.rb
30
+ - tlattr_accessors.gemspec
31
+ has_rdoc: true
32
+ homepage: http://github.com/schoefmax/tlattr_accessors
33
+ licenses: []
34
+
35
+ post_install_message:
36
+ rdoc_options:
37
+ - --line-numbers
38
+ - --inline-source
39
+ - --title
40
+ - tlattr_accessors
41
+ - --main
42
+ - README.rdoc
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project: tlattr_accessors
60
+ rubygems_version: 1.3.5
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: thread-local accessors for your classes
64
+ test_files: []
65
+