zeevex_delayed 0.9.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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in zeevex_delayed.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ namespace :spec do
10
+ desc "Run on three Rubies"
11
+ task :platforms do
12
+ current = %x{rvm-prompt v}
13
+
14
+ fail = false
15
+ %w{1.8.7 1.9.3 ree}.each do |version|
16
+ puts "Switching to #{version}"
17
+ Bundler.with_clean_env do
18
+ system %{bash -c 'source ~/.rvm/scripts/rvm && rvm #{version} && bundle exec rake spec'}
19
+ end
20
+ if $?.exitstatus != 0
21
+ fail = true
22
+ break
23
+ end
24
+ end
25
+
26
+ system %{rvm #{current}}
27
+
28
+ exit (fail ? 1 : 0)
29
+ end
30
+ end
31
+
32
+ task :default => 'spec:platforms'
@@ -0,0 +1,34 @@
1
+ require 'zeevex_proxy'
2
+
3
+ module ZeevexDelayed
4
+ class Promise < ZeevexProxy::Base
5
+
6
+ def initialize(obj=nil, &block)
7
+ super
8
+ @__promise_block = block
9
+ raise ArgumentError, "Must supply a block!" unless block
10
+ end
11
+
12
+ def method_missing(name, *args, &block)
13
+ __force__.__send__(name, *args, &block)
14
+ end
15
+
16
+ def __force__
17
+ unless @__promise_forced
18
+ @obj = @__promise_block.call
19
+ @__promise_forced = true
20
+ end
21
+ @obj
22
+ end
23
+ end
24
+ end
25
+
26
+ module Kernel
27
+ def force_promise(promise)
28
+ promise.__force__
29
+ end
30
+
31
+ def promise(&block)
32
+ ZeevexDelayed::Promise.new &block
33
+ end
34
+ end
@@ -0,0 +1,45 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'promise'))
2
+ require 'zeevex_proxy'
3
+
4
+ module ZeevexDelayed
5
+ #
6
+ # Like a Promise, but evaluate and cache values on a per-thread basis
7
+ #
8
+ # e.g. will act just like a Promise if only called from one thread, but if
9
+ # Thread A and Thread B both access a Promise created like so:
10
+ #
11
+ # promise { Thread.current.object_id }
12
+ #
13
+ # Then they will see different values. This does cache, and it does not
14
+ # GC its cache when threads exit. Thus if you expect to have a very
15
+ # long-lived Promise accessed from many different threads that come and
16
+ # go, don't use this.
17
+ #
18
+
19
+ class ThreadPromise < ZeevexProxy::Base
20
+
21
+ def initialize(obj=nil, &block)
22
+ raise ArgumentError, "Must supply a block!" unless block_given?
23
+ super
24
+ @__promise_thread_map = {}
25
+ @__promise_block = block
26
+ end
27
+
28
+ def method_missing(name, *args, &block)
29
+ __force__.__send__(name, *args, &block)
30
+ end
31
+
32
+ def __force__
33
+ unless @__promise_thread_map.has_key?(Thread.current.object_id)
34
+ @__promise_thread_map[Thread.current.object_id] = @__promise_block.call
35
+ end
36
+ @__promise_thread_map[Thread.current.object_id]
37
+ end
38
+ end
39
+ end
40
+
41
+ module Kernel
42
+ def thread_promise(&block)
43
+ ZeevexDelayed::ThreadPromise.new &block
44
+ end
45
+ end
@@ -0,0 +1,3 @@
1
+ module ZeevexDelayed
2
+ VERSION = "0.9.0"
3
+ end
@@ -0,0 +1,6 @@
1
+ module ZeevexDelayed
2
+ VERSION = "0.9.0"
3
+ end
4
+
5
+ require 'zeevex_delayed/promise'
6
+ require 'zeevex_delayed/thread_promise'
@@ -0,0 +1,27 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper.rb')
4
+
5
+ describe ZeevexDelayed::Promise do
6
+
7
+ let :promise_class do
8
+ ZeevexDelayed::Promise
9
+ end
10
+
11
+ context "as a basic promise" do
12
+ it_should_behave_like "basic promise"
13
+ end
14
+
15
+ context "when called from different threads" do
16
+ it "should call the block once for all threads" do
17
+ canary = mock("canary")
18
+ canary.should_receive(:life).once.and_return(42)
19
+
20
+ @val = promise_class.new { canary.life }
21
+
22
+ Thread.new { @val.to_i }.join
23
+ Thread.new { @val.to_i }.join
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,95 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ $: << File.expand_path(File.dirname(__FILE__) + '../lib')
4
+
5
+ require 'rspec'
6
+ require 'zeevex_delayed'
7
+
8
+ puts "loading spec helper..."
9
+
10
+ # Be sure to include AuthenticatedTestHelper in spec/spec_helper.rb instead.
11
+ # Then, you can remove it from this and the functional test.
12
+
13
+ shared_examples_for "results caching" do
14
+ it "should only call the block once" do
15
+ @val = promise_class.new { canary.life }
16
+ @val.__force__
17
+ @val.__force__
18
+ end
19
+ end
20
+
21
+
22
+ shared_examples_for "basic promise" do
23
+
24
+ let :canary do
25
+ mock("canary").tap do |mock|
26
+ mock.stub(:doit).and_return("hello")
27
+ end
28
+ end
29
+
30
+ let :test_promise do
31
+ promise_class.new { canary.doit }
32
+ end
33
+
34
+ subject { test_promise }
35
+
36
+ context "#initialize" do
37
+ it "should not invoke the block during initialization" do
38
+ canary = mock("canary")
39
+ canary.should_receive(:oops).never
40
+
41
+ @val = promise_class.new { canary.oops }
42
+ end
43
+ end
44
+
45
+ it "should require a block" do
46
+ expect { promise_class.new }.
47
+ to raise_exception(ArgumentError)
48
+ end
49
+
50
+ it "should return the value of the block on to_s" do
51
+ promise_class.new { "a" + "b" }.should == "ab"
52
+ end
53
+
54
+ it "should call the block only when needed" do
55
+ foo = 1
56
+ @val = promise_class.new { foo }
57
+ foo = 2
58
+ @val.should == 2
59
+ end
60
+
61
+ context "when forcing computation" do
62
+ it "should respond to __force__" do
63
+ # check here because respond_to? invoked on a promise would delegate
64
+ promise_class.instance_methods.should include("__force__")
65
+ end
66
+
67
+ it "should be computed when Kernel#force_promise is called" do
68
+ canary.should_receive(:doit).and_return('abcde')
69
+ force_promise subject
70
+ end
71
+ end
72
+
73
+ it "should return the block's value from __force__" do
74
+ subject.__force__.should == "hello"
75
+ end
76
+
77
+
78
+ context "for blocks that return false" do
79
+ let :canary do
80
+ canary = mock("canary")
81
+ canary.should_receive(:life).once.and_return(false)
82
+ canary
83
+ end
84
+ it_should_behave_like "results caching"
85
+ end
86
+
87
+ context "for blocks that return nil" do
88
+ let :canary do
89
+ canary = mock("canary")
90
+ canary.should_receive(:life).once.and_return(nil)
91
+ canary
92
+ end
93
+ it_should_behave_like "results caching"
94
+ end
95
+ end
@@ -0,0 +1,35 @@
1
+
2
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper.rb')
3
+
4
+ describe ZeevexDelayed::ThreadPromise do
5
+
6
+ let :promise_class do
7
+ ZeevexDelayed::ThreadPromise
8
+ end
9
+
10
+ context "as a basic promise" do
11
+ it_should_behave_like "basic promise"
12
+ end
13
+
14
+ context "when called from different threads" do
15
+ it "should call the block again for each new thread" do
16
+ canary = mock("canary")
17
+ canary.should_receive(:life).twice.and_return(42)
18
+
19
+ @val = promise_class.new { canary.life }
20
+
21
+ Thread.new { @val.to_i }.join
22
+ Thread.new { @val.to_i }.join
23
+ end
24
+
25
+ it "should call the block only once for each thread" do
26
+ canary = mock("canary")
27
+ canary.should_receive(:life).twice.and_return(42)
28
+
29
+ @val = promise_class.new { canary.life }
30
+
31
+ Thread.new { @val.to_i; @val.to_i }.join
32
+ Thread.new { @val.to_i; @val.to_i }.join
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "zeevex_delayed/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "zeevex_delayed"
7
+ s.version = ZeevexDelayed::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Robert Sanders"]
10
+ s.email = ["robert@zeevex.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{Basic and per-thread promise classes.}
13
+ s.description = %q{This gem provides a couple of classes useful for deferred computation.}
14
+
15
+ s.rubyforge_project = "zeevex_delayed"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency 'zeevex_proxy'
23
+
24
+ s.add_development_dependency 'rspec', '~> 2.9.0'
25
+ s.add_development_dependency 'rake'
26
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zeevex_delayed
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 9
8
+ - 0
9
+ version: 0.9.0
10
+ platform: ruby
11
+ authors:
12
+ - Robert Sanders
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2012-05-02 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: zeevex_proxy
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: rspec
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ~>
38
+ - !ruby/object:Gem::Version
39
+ segments:
40
+ - 2
41
+ - 9
42
+ - 0
43
+ version: 2.9.0
44
+ type: :development
45
+ version_requirements: *id002
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ prerelease: false
49
+ requirement: &id003 !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ segments:
54
+ - 0
55
+ version: "0"
56
+ type: :development
57
+ version_requirements: *id003
58
+ description: This gem provides a couple of classes useful for deferred computation.
59
+ email:
60
+ - robert@zeevex.com
61
+ executables: []
62
+
63
+ extensions: []
64
+
65
+ extra_rdoc_files: []
66
+
67
+ files:
68
+ - .gitignore
69
+ - Gemfile
70
+ - Rakefile
71
+ - lib/zeevex_delayed.rb
72
+ - lib/zeevex_delayed/promise.rb
73
+ - lib/zeevex_delayed/thread_promise.rb
74
+ - lib/zeevex_delayed/version.rb
75
+ - spec/promise_spec.rb
76
+ - spec/spec_helper.rb
77
+ - spec/thread_promise_spec.rb
78
+ - zeevex_delayed.gemspec
79
+ has_rdoc: true
80
+ homepage: ""
81
+ licenses: []
82
+
83
+ post_install_message:
84
+ rdoc_options: []
85
+
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ segments:
93
+ - 0
94
+ version: "0"
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ requirements: []
103
+
104
+ rubyforge_project: zeevex_delayed
105
+ rubygems_version: 1.3.6
106
+ signing_key:
107
+ specification_version: 3
108
+ summary: Basic and per-thread promise classes.
109
+ test_files:
110
+ - spec/promise_spec.rb
111
+ - spec/spec_helper.rb
112
+ - spec/thread_promise_spec.rb