tlattr_accessors 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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
+