kontext 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color --order random
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ gem "rake"
6
+ gem "awesome_print"
data/README.md ADDED
@@ -0,0 +1,48 @@
1
+ Thread and Fiber specific global state
2
+ ======================================
3
+
4
+ Know DataMapper's repository contexts? Need request-specific global state in an
5
+ evented webserver? Wanna do complex AOP but care about concurrency? Kontext
6
+ comes to your rescue. It provides you with nestable per-thread, per-fiber
7
+ context stacks.
8
+
9
+ ```ruby
10
+ require "kontext"
11
+
12
+ context = Kontext.new
13
+
14
+ context.with "foo" do
15
+ p context.last
16
+ # => "foo"
17
+
18
+ context.with "bar" do
19
+ p context.stack
20
+ # => ["foo", "bar"]
21
+ end
22
+
23
+ Fiber.new do
24
+ p context.last
25
+ # => nil
26
+
27
+ context.with "different context!" do
28
+ p context.last
29
+ # => "different context!"
30
+
31
+ Thread.start do
32
+ context.with "again different" do
33
+ p context.stack
34
+ # => ["again different"]
35
+ end
36
+ end
37
+
38
+ sleep 0.1
39
+ end
40
+ end.resume
41
+ end
42
+ ```
43
+
44
+ To do
45
+ -----
46
+
47
+ * Fix memory leak in `#store`
48
+ * Make new threads and fibers inherit a copy of the previous context stack
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ task :default => :spec
2
+
3
+ require "rspec/core/rake_task"
4
+ RSpec::Core::RakeTask.new :spec
data/kontext.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "kontext/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "kontext"
7
+ s.version = Kontext::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Lars Gierth"]
10
+ s.email = ["lars.gierth@gmail.com"]
11
+ s.homepage = "https://rubygems.org/gems/kontext"
12
+ s.summary = %q{Thread and Fiber specific global state}
13
+ s.description = s.summary
14
+
15
+ s.add_development_dependency "rspec"
16
+
17
+ s.files = `git ls-files`.split("\n") - [".gitignore", ".travis.yml"]
18
+ s.test_files = `git ls-files -- spec/*`.split("\n")
19
+ s.require_paths = ["lib"]
20
+ end
@@ -0,0 +1,3 @@
1
+ class Kontext
2
+ VERSION = "0.1.0"
3
+ end
data/lib/kontext.rb ADDED
@@ -0,0 +1,67 @@
1
+ require "kontext/version"
2
+
3
+ begin
4
+ require "fiber"
5
+ rescue LoadError => ex
6
+ raise(ex) if ex.message =~ "fiber"
7
+ end
8
+
9
+ class Kontext
10
+ RootFiber = defined?(::Fiber) ? Fiber.current : nil
11
+
12
+ def thread?
13
+ Thread.current != Thread.main
14
+ end
15
+
16
+ def fiber?
17
+ defined?(::Fiber) && Fiber.current != RootFiber
18
+ end
19
+
20
+ def store
21
+ if fiber?
22
+ # TODO over time this will leak massively
23
+ Thread.current[Fiber.current.object_id.to_s] ||= {}
24
+ elsif thread?
25
+ Thread.current
26
+ else
27
+ @store ||= {}
28
+ end
29
+ end
30
+
31
+ def stack
32
+ store[self] ||= []
33
+ end
34
+
35
+ def with(obj)
36
+ old_size = size
37
+ push(obj)
38
+
39
+ begin
40
+ result = yield(obj)
41
+ ensure
42
+ truncate(old_size)
43
+ end
44
+
45
+ result
46
+ end
47
+
48
+ def push(obj)
49
+ stack.push(obj)
50
+ end
51
+
52
+ def pop
53
+ stack.pop
54
+ end
55
+
56
+ def last
57
+ stack.last
58
+ end
59
+
60
+ def size
61
+ stack.length
62
+ end
63
+
64
+ def truncate(new_size)
65
+ pop until size == new_size
66
+ end
67
+ end
@@ -0,0 +1,169 @@
1
+ require "awesome_print"
2
+ require "kontext"
3
+
4
+ describe Kontext do
5
+ describe Kontext::RootFiber do
6
+ it "is a reference to the root fiber" do
7
+ Kontext::RootFiber.should equal(Fiber.current)
8
+ end
9
+ end
10
+
11
+ let :kontext do
12
+ Kontext.new
13
+ end
14
+
15
+ describe "#thread?" do
16
+ it "returns true if the current thread is not the main thread" do
17
+ result = nil
18
+ Thread.new { result = kontext.thread? }
19
+ sleep 0.01
20
+
21
+ result.should be_true
22
+ end
23
+
24
+ it "returns false if we're in the main thread" do
25
+ kontext.thread?.should be_false
26
+ end
27
+ end
28
+
29
+ describe "#fiber?" do
30
+ it "returns true if the current fiber is not the root fiber" do
31
+ result = nil
32
+ Fiber.new { result = kontext.fiber? }.resume
33
+
34
+ result.should be_true
35
+ end
36
+
37
+ it "returns false if we're in the root fiber" do
38
+ kontext.fiber?.should be_false
39
+ end
40
+ end
41
+
42
+ describe "#store" do
43
+ describe "when in a fiber" do
44
+ before do
45
+ kontext.stub :fiber? => true, :thread? => false
46
+ end
47
+
48
+ it "returns a fiber specific store" do
49
+ key = Fiber.current.object_id.to_s
50
+ kontext.store.should equal(Thread.current[key])
51
+ end
52
+ end
53
+
54
+ describe "when in a thread" do
55
+ before do
56
+ kontext.stub :fiber? => false, :thread? => true
57
+ end
58
+
59
+ it "returns a thread specific hash store" do
60
+ kontext.store.should equal(Thread.current)
61
+ end
62
+ end
63
+
64
+ describe "when neither in thread nor in fiber" do
65
+ before do
66
+ kontext.stub :fiber? => false, :thread? => false
67
+ end
68
+
69
+ it "returns a hash" do
70
+ kontext.store.should be_a(Hash)
71
+ kontext.store.should be_empty
72
+ end
73
+ end
74
+ end
75
+
76
+ describe "#stack" do
77
+ it "returns the instance's stack" do
78
+ kontext.stack.should be_an(Array)
79
+ kontext.stack.should be_empty
80
+ kontext.stack.should equal(kontext.store[kontext])
81
+ end
82
+ end
83
+
84
+ describe "#with(obj)" do
85
+ let(:obj) { stub "obj" }
86
+
87
+ it "pushes the object to the stack" do
88
+ kontext.with obj do
89
+ kontext.last.should equal(obj)
90
+
91
+ kontext.with obj do
92
+ kontext.stack.should == [obj, obj]
93
+ end
94
+ end
95
+ end
96
+
97
+ it "yields the object" do
98
+ kontext.with obj do |o|
99
+ o.should equal(obj)
100
+ end
101
+ end
102
+
103
+ it "correctly updates the stack when the block raises" do
104
+ begin
105
+ kontext.with obj do
106
+ begin
107
+ kontext.with(obj) { raise }
108
+ ensure
109
+ kontext.stack.should == [obj]
110
+ end
111
+ end
112
+ rescue
113
+ kontext.stack.should be_empty
114
+ end
115
+ end
116
+
117
+ it "expects a block" do
118
+ proc { kontext.with obj }.should raise_error(LocalJumpError)
119
+ end
120
+ end
121
+
122
+ describe "#push(obj)" do
123
+ it "adds an object to the stack" do
124
+ kontext.push :foo
125
+ kontext.push :bar
126
+ kontext.last.should equal(:bar)
127
+ end
128
+ end
129
+
130
+ describe "#pop" do
131
+ it "removes an object from the top of the stack" do
132
+ kontext.pop.should be_nil
133
+
134
+ kontext.push :foo
135
+ kontext.pop.should equal(:foo)
136
+ kontext.size.should be_zero
137
+ end
138
+ end
139
+
140
+ describe "#last" do
141
+ it "returns the object at the top of the stack" do
142
+ kontext.last.should be_nil
143
+
144
+ kontext.push :foo
145
+ kontext.last.should equal(:foo)
146
+ end
147
+ end
148
+
149
+ describe "#size" do
150
+ it "is zero initially" do
151
+ kontext.size.should be_zero
152
+ end
153
+
154
+ it "increases by 1 for each nesting level" do
155
+ kontext.with :foo do
156
+ kontext.size.should equal(1)
157
+ end
158
+ end
159
+ end
160
+
161
+ describe "#truncate(new_size)" do
162
+ it "pops objects until there are $new_size object left" do
163
+ [:foo, :bar, :baz].each &kontext.method(:push)
164
+
165
+ kontext.truncate 1
166
+ kontext.last.should equal(:foo)
167
+ end
168
+ end
169
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kontext
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Lars Gierth
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-20 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &85309510 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *85309510
25
+ description: Thread and Fiber specific global state
26
+ email:
27
+ - lars.gierth@gmail.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - .rspec
33
+ - Gemfile
34
+ - README.md
35
+ - Rakefile
36
+ - kontext.gemspec
37
+ - lib/kontext.rb
38
+ - lib/kontext/version.rb
39
+ - spec/kontext_spec.rb
40
+ homepage: https://rubygems.org/gems/kontext
41
+ licenses: []
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 1.8.16
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: Thread and Fiber specific global state
64
+ test_files:
65
+ - spec/kontext_spec.rb
66
+ has_rdoc: