promising 0.3.1
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 +7 -0
- data/.gemspec +36 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.yardopts +11 -0
- data/AUTHORS +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +50 -0
- data/Rakefile +2 -0
- data/UNLICENSE +24 -0
- data/VERSION +1 -0
- data/lib/promising.rb +35 -0
- data/lib/promising/future.rb +51 -0
- data/lib/promising/promise.rb +110 -0
- data/lib/promising/version.rb +3 -0
- data/promising.gemspec +23 -0
- data/spec/future_spec.rb +42 -0
- data/spec/promise_spec.rb +72 -0
- data/spec/shared.rb +140 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 493adbf314de9f4ead683c75a8f636473f87e1bb
|
4
|
+
data.tar.gz: 70b84824ed3a74f75c540ff345aad10714ae1794
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dcdf423f31163f04d83de0bbbe2dd78a196a987dfc12e006a7e12c648f597bc6c87ae80dbd72158579cc7c16e3e206843d3974292c14c0367d3bf9119f8d9426
|
7
|
+
data.tar.gz: 0ca8863810f0ad45573a7aecff01326457a079b15037ad5a46ef74c2b5e72298b7d433709e998a1ef4f8d7aec23faf925dfd1c88707532a3b8e2e61ab65cca87
|
data/.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby -rubygems
|
2
|
+
# -*- encoding: utf-8 -*-
|
3
|
+
|
4
|
+
GEMSPEC = Gem::Specification.new do |gem|
|
5
|
+
gem.version = File.read('VERSION').chomp
|
6
|
+
gem.date = File.mtime('VERSION').strftime('%Y-%m-%d')
|
7
|
+
|
8
|
+
gem.name = 'promise'
|
9
|
+
gem.homepage = 'http://promise.rubyforge.org/'
|
10
|
+
gem.license = 'Public Domain' if gem.respond_to?(:license=)
|
11
|
+
gem.summary = 'Promises and futures for Ruby'
|
12
|
+
gem.description = <<-EOF
|
13
|
+
A glimpse of a promising future in which Ruby supports delayed execution.
|
14
|
+
Provides global 'promise' and 'future' methods.
|
15
|
+
EOF
|
16
|
+
gem.rubyforge_project = 'promising-future'
|
17
|
+
|
18
|
+
gem.authors = ['Ben Lavender']
|
19
|
+
gem.email = 'blavender@gmail.com'
|
20
|
+
|
21
|
+
gem.platform = Gem::Platform::RUBY
|
22
|
+
gem.files = %w(AUTHORS README UNLICENSE VERSION) + Dir.glob('lib/**/*.rb')
|
23
|
+
gem.bindir = %q(bin)
|
24
|
+
gem.executables = %w()
|
25
|
+
gem.default_executable = gem.executables.first
|
26
|
+
gem.require_paths = %w(lib)
|
27
|
+
gem.extensions = %w()
|
28
|
+
gem.test_files = %w()
|
29
|
+
gem.has_rdoc = false
|
30
|
+
|
31
|
+
gem.required_ruby_version = '>= 1.8.2'
|
32
|
+
gem.requirements = []
|
33
|
+
gem.add_development_dependency 'rspec', '>= 1.3.0'
|
34
|
+
gem.add_development_dependency 'yard' , '>= 0.5.8'
|
35
|
+
gem.post_install_message = nil
|
36
|
+
end
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.yardopts
ADDED
data/AUTHORS
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 lifu
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Notice
|
2
|
+
|
3
|
+
This gem is come from [Promise](https://rubygems.org/gems/promise)
|
4
|
+
|
5
|
+
# Promising Future
|
6
|
+
A glimpse of a promising future in which Ruby supports lazy evaluation.
|
7
|
+
|
8
|
+
## Overview
|
9
|
+
[Promises and futures][] both transparently defer the execution of a block.
|
10
|
+
Promises evaluate the given block if and when its result is first needed.
|
11
|
+
Futures evaluate the given block optimistically in another thread.
|
12
|
+
|
13
|
+
require 'promise'
|
14
|
+
require 'future' # you can just require 'future' if using both
|
15
|
+
|
16
|
+
x = promise { 1 + 2 }
|
17
|
+
y = future { sleep 10 && 6 * 7 }
|
18
|
+
|
19
|
+
puts x #=> 3
|
20
|
+
sleep 5 # ... do work for 5 seconds ...
|
21
|
+
puts y #=> 42, after blocking 5 seconds
|
22
|
+
|
23
|
+
Note that this is pretty useless in Ruby's interactive shell `irb`, as it
|
24
|
+
will eagerly evaluate everything as part of its read-eval-print loop,
|
25
|
+
forcing promises and futures to yield their results.
|
26
|
+
|
27
|
+
The library has been tested with Ruby 1.8.7, 1.9.1 and JRuby 1.5.0.
|
28
|
+
YARD documentation is available at <http://promise.rubyforge.org/>
|
29
|
+
|
30
|
+
## Classes
|
31
|
+
|
32
|
+
* {Promise}
|
33
|
+
* {Future}
|
34
|
+
|
35
|
+
## Installation
|
36
|
+
The library is distributed via [RubyGems](http://rubygems.org/):
|
37
|
+
|
38
|
+
$ gem install promise
|
39
|
+
|
40
|
+
## Source
|
41
|
+
The source is available at <http://github.com/fsword/future>
|
42
|
+
|
43
|
+
## Author
|
44
|
+
[Fsword](http://github.com/fsword)
|
45
|
+
|
46
|
+
## Unlicense
|
47
|
+
Promising Future is free and unencumbered public domain software. For more
|
48
|
+
information, see <http://unlicense.org/> or the accompanying UNLICENSE file.
|
49
|
+
|
50
|
+
[Promises and futures]: http://en.wikipedia.org/wiki/Futures_and_promises
|
data/Rakefile
ADDED
data/UNLICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
4
|
+
distribute this software, either in source code form or as a compiled
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
6
|
+
means.
|
7
|
+
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
9
|
+
of this software dedicate any and all copyright interest in the
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
11
|
+
of the public at large and to the detriment of our heirs and
|
12
|
+
successors. We intend this dedication to be an overt act of
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
14
|
+
software under copyright law.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
24
|
+
For more information, please refer to <http://unlicense.org/>
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.1
|
data/lib/promising.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require "promising/version"
|
2
|
+
require "promising/promise"
|
3
|
+
require "promising/future"
|
4
|
+
|
5
|
+
module Kernel
|
6
|
+
##
|
7
|
+
# Creates a new promise.
|
8
|
+
#
|
9
|
+
# @example Lazily evaluate an arithmetic operation
|
10
|
+
# x = promise { 3 + 3 }
|
11
|
+
#
|
12
|
+
# @yield []
|
13
|
+
# A block to be lazily evaluated.
|
14
|
+
# @yieldreturn [Object]
|
15
|
+
# The return value of the block will be the lazily evaluated value of the promise.
|
16
|
+
# @return [Promise]
|
17
|
+
def promise(timeout:nil,&block)
|
18
|
+
::Promising::Promise.new(timeout:timeout,&block)
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Creates a new future.
|
23
|
+
#
|
24
|
+
# @example Evaluate an operation in another thread
|
25
|
+
# x = future { 3 + 3 }
|
26
|
+
#
|
27
|
+
# @yield []
|
28
|
+
# A block to be optimistically evaluated in another thread.
|
29
|
+
# @yieldreturn [Object]
|
30
|
+
# The return value of the block will be the evaluated value of the future.
|
31
|
+
# @return [Future]
|
32
|
+
def future(timeout:nil, &block)
|
33
|
+
::Promising::Future.new(timeout:timeout, &block)
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'promising/promise'
|
2
|
+
|
3
|
+
##
|
4
|
+
# A delayed-execution result, optimistically evaluated in a new thread.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# x = future { sleep 5; 1 + 2 }
|
8
|
+
# # do stuff...
|
9
|
+
# y = x * 2 # => 6. blocks unless 5 seconds has passed.
|
10
|
+
#
|
11
|
+
module Promising
|
12
|
+
class Future < defined?(BasicObject) ? BasicObject : Object
|
13
|
+
instance_methods.each { |m| undef_method m unless m =~ /^(__.*|object_id)$/ }
|
14
|
+
|
15
|
+
##
|
16
|
+
# Creates a new future.
|
17
|
+
#
|
18
|
+
# @yield [] The block to evaluate optimistically.
|
19
|
+
# @see Kernel#future
|
20
|
+
def initialize(timeout:nil,&block)
|
21
|
+
@promise = Promise.new(timeout:timeout,&block)
|
22
|
+
@thread = ::Thread.new{@promise.__force__}
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# The value of the future's evaluation. Blocks until result available.
|
27
|
+
#
|
28
|
+
# @return [Object]
|
29
|
+
def __force__
|
30
|
+
@thread.join if @thread
|
31
|
+
@promise
|
32
|
+
end
|
33
|
+
alias_method :force, :__force__
|
34
|
+
|
35
|
+
##
|
36
|
+
# Does this future support the given method?
|
37
|
+
#
|
38
|
+
# @param [Symbol]
|
39
|
+
# @return [Boolean]
|
40
|
+
def respond_to?(method, include_all=false)
|
41
|
+
:force.equal?(method) || :__force__.equal?(method) || __force__.respond_to?(method, include_all)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def method_missing(method, *args, &block)
|
47
|
+
__force__.__send__(method, *args, &block)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,110 @@
|
|
1
|
+
##
|
2
|
+
# A delayed-execution promise. Promises are only executed once.
|
3
|
+
#
|
4
|
+
# @example
|
5
|
+
# x = promise { factorial 20 }
|
6
|
+
# y = promise { fibonacci 10**6 }
|
7
|
+
# a = x + 1 # => factorial 20 + 1 after factorial calculates
|
8
|
+
# result = promise { a += y }
|
9
|
+
# abort "" # whew, we never needed to calculate y
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# y = 5
|
13
|
+
# x = promise { y = y + 5 }
|
14
|
+
# x + 5 # => 15
|
15
|
+
# x + 5 # => 15
|
16
|
+
#
|
17
|
+
require 'timeout'
|
18
|
+
|
19
|
+
module Promising
|
20
|
+
class Promise < defined?(BasicObject) ? BasicObject : ::Object
|
21
|
+
NOT_SET = ::Object.new.freeze
|
22
|
+
|
23
|
+
instance_methods.each { |m| undef_method m unless m =~ /^(__.*|object_id)$/ }
|
24
|
+
|
25
|
+
##
|
26
|
+
# Creates a new promise.
|
27
|
+
#
|
28
|
+
# @example Lazily evaluate a database call
|
29
|
+
# result = promise { @db.query("SELECT * FROM TABLE") }
|
30
|
+
#
|
31
|
+
# @yield [] The block to evaluate lazily.
|
32
|
+
# @see Kernel#promise
|
33
|
+
def initialize(timeout:nil,&block)
|
34
|
+
if block.arity > 0
|
35
|
+
::Kernel.raise ::ArgumentError, "Cannot store a promise that requires an argument"
|
36
|
+
end
|
37
|
+
@timeout = timeout
|
38
|
+
@block = block
|
39
|
+
@mutex = ::Mutex.new
|
40
|
+
@result = NOT_SET
|
41
|
+
@error = NOT_SET
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Force the evaluation of this promise immediately
|
46
|
+
#
|
47
|
+
# @return [Object]
|
48
|
+
def __force__
|
49
|
+
@mutex.synchronize do
|
50
|
+
if @result.equal?(NOT_SET) && @error.equal?(NOT_SET)
|
51
|
+
begin
|
52
|
+
if @timeout
|
53
|
+
::Timeout.timeout(@timeout) do
|
54
|
+
@result = @block.call
|
55
|
+
end
|
56
|
+
else
|
57
|
+
@result = @block.call
|
58
|
+
end
|
59
|
+
rescue ::Exception => e
|
60
|
+
@error = e
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end if @result.equal?(NOT_SET) && @error.equal?(NOT_SET)
|
64
|
+
# BasicObject won't send raise to Kernel
|
65
|
+
@error.equal?(NOT_SET) ? @result : ::Kernel.raise(@error)
|
66
|
+
end
|
67
|
+
alias_method :force, :__force__
|
68
|
+
|
69
|
+
##
|
70
|
+
# Does this promise support the given method?
|
71
|
+
#
|
72
|
+
# @param [Symbol, Boolean]
|
73
|
+
# @return [Boolean]
|
74
|
+
def respond_to?(method, include_all=false)
|
75
|
+
# If the promised object implements marshal_dump, Marshal will use it in
|
76
|
+
# preference to our _dump, so make sure that doesn't happen.
|
77
|
+
return false if :marshal_dump.equal?(method)
|
78
|
+
|
79
|
+
:_dump.equal?(method) || # for Marshal
|
80
|
+
:force.equal?(method) ||
|
81
|
+
:__force__.equal?(method) ||
|
82
|
+
__force__.respond_to?(method, include_all)
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# Method used by Marshal to serialize the object. Forces evaluation.
|
87
|
+
#
|
88
|
+
# @param [Integer] limit -- refer to Marshal doc
|
89
|
+
# @return [Object]
|
90
|
+
def _dump(limit)
|
91
|
+
::Marshal.dump(__force__, limit)
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Method used by Marshal to deserialize the object.
|
96
|
+
#
|
97
|
+
# @param [Object]
|
98
|
+
# @return [Promise]
|
99
|
+
def self._load(obj)
|
100
|
+
::Marshal.load(obj)
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def method_missing(method, *args, &block)
|
106
|
+
__force__.__send__(method, *args, &block)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
data/promising.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'promising/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "promising"
|
8
|
+
spec.version = Promising::VERSION
|
9
|
+
spec.authors = ["fsword"]
|
10
|
+
spec.email = ["li.jianye@gmail.com"]
|
11
|
+
spec.summary = %q{A simple promise/future lib for ruby}
|
12
|
+
spec.description = %q{Forking from bhuga/promising-future on github, so Version is started from 0.3.1}
|
13
|
+
spec.homepage = "https://github.com/fsword/promising"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
end
|
data/spec/future_spec.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
|
2
|
+
$:.unshift File.dirname(__FILE__)
|
3
|
+
|
4
|
+
require 'time'
|
5
|
+
require 'shared'
|
6
|
+
module Promising
|
7
|
+
describe Future do
|
8
|
+
|
9
|
+
before :each do
|
10
|
+
@method = Kernel.method(:future)
|
11
|
+
end
|
12
|
+
|
13
|
+
if defined?(BasicObject)
|
14
|
+
it "should inherit from BasicObject if available, and not otherwise" do
|
15
|
+
expect(Future.ancestors).to include BasicObject
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it_should_behave_like "A Promise"
|
20
|
+
|
21
|
+
it "should work in the background" do
|
22
|
+
start = Time.now
|
23
|
+
x = future { sleep 3; 5 }
|
24
|
+
middle = Time.now
|
25
|
+
y = x + 5
|
26
|
+
expect(y).to eq 10
|
27
|
+
finish = Time.now
|
28
|
+
expect(middle - start).to be_within(10**-2).of(0)
|
29
|
+
expect(finish - start).to be_within(10**-2).of(3)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should finished when timeout" do
|
33
|
+
# timeout
|
34
|
+
x = future(timeout:1){ sleep 2; 5 }
|
35
|
+
expect{x + 5}.to raise_error(::Timeout::Error)
|
36
|
+
|
37
|
+
# not timeout
|
38
|
+
x = future(timeout:2){ sleep 1; 5 }
|
39
|
+
expect(x + 5).to eq 10
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
|
2
|
+
$:.unshift File.dirname(__FILE__)
|
3
|
+
|
4
|
+
require 'shared'
|
5
|
+
|
6
|
+
module Promising
|
7
|
+
describe Promise do
|
8
|
+
|
9
|
+
before :each do
|
10
|
+
@method = Kernel.method(:promise)
|
11
|
+
end
|
12
|
+
|
13
|
+
if defined?(BasicObject)
|
14
|
+
it "should inherit from BasicObject if available, and not otherwise" do
|
15
|
+
expect(Promise.ancestors).to include BasicObject
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it_should_behave_like "A Promise"
|
20
|
+
|
21
|
+
it "should delay execution" do
|
22
|
+
value = 5
|
23
|
+
x = @method.call { value = 10 ; value }
|
24
|
+
expect(value).to eq 5
|
25
|
+
y = x + 5
|
26
|
+
expect(y).to eq 15
|
27
|
+
expect(value).to eq 10
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should delay execution of invalid code" do
|
31
|
+
expect {x = [ 1, x / 0 ]}.to raise_error
|
32
|
+
expect {x = [ 1, @method.call { x / 0 }]}.to_not raise_error
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'an object referencing a promise' do
|
36
|
+
class ClassResulting
|
37
|
+
attr_reader :value
|
38
|
+
def initialize(value)
|
39
|
+
@value = value
|
40
|
+
end
|
41
|
+
def marshal_dump
|
42
|
+
[@value]
|
43
|
+
end
|
44
|
+
def marshal_load(custom_struct)
|
45
|
+
@value = custom_struct[0]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class ClassReferencingAPromise
|
50
|
+
attr_reader :long_computation
|
51
|
+
def initialize
|
52
|
+
@long_computation = promise { ClassResulting.new(8) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'can be marshaled and unmarshalled' do
|
57
|
+
clazz_ = Marshal.load(Marshal.dump(ClassReferencingAPromise.new))
|
58
|
+
expect(clazz_.long_computation.value).to eq 8
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should finished when timeout" do
|
62
|
+
# timeout
|
63
|
+
x = promise(timeout:1){ sleep 2; 5 }
|
64
|
+
expect{x + 5}.to raise_error(::Timeout::Error)
|
65
|
+
|
66
|
+
# not timeout
|
67
|
+
x = promise(timeout:2){ sleep 1; 5 }
|
68
|
+
expect(x + 5).to eq 10
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/spec/shared.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'promising'
|
3
|
+
|
4
|
+
shared_examples_for "A Promise" do
|
5
|
+
|
6
|
+
it "should be createable" do
|
7
|
+
expect {x = @method.call { 3 + 5 }}.to_not raise_error
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should not accept a block requiring arguments" do
|
11
|
+
expect {x = @method.call { | x | 3 + 5 }}.to raise_error
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should be forceable" do
|
15
|
+
x = @method.call { 3 + 5 }
|
16
|
+
expect(x.__force__).to eq 8
|
17
|
+
expect(x).to eq 8
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should respond_to? force" do
|
21
|
+
x = @method.call { 3 + 5 }
|
22
|
+
expect(x).to respond_to(:force)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should respond_to? __force__" do
|
26
|
+
x = @method.call { 3 + 5 }
|
27
|
+
expect(x).to respond_to(:__force__)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should respond_to? a method on the result" do
|
31
|
+
x = @method.call { 3 + 5 }
|
32
|
+
expect(x).to respond_to(:+)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should not respond_to? a method not on the result" do
|
36
|
+
x = @method.call { 3 + 5 }
|
37
|
+
expect(x).to_not respond_to(:asdf)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should evaluate to a value" do
|
41
|
+
expect(5 + @method.call { 1 + 2 }).to eq 8
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should hold its value" do
|
45
|
+
y = 5
|
46
|
+
x = @method.call { y = y + 5 }
|
47
|
+
expect(x).to eq 10
|
48
|
+
expect(x).to eq 10
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should only execute once" do
|
52
|
+
y = 1
|
53
|
+
x = @method.call { (y += 1) && false }
|
54
|
+
expect(x).to eq false
|
55
|
+
expect(x).to eq false
|
56
|
+
expect(y).to eq 2
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should raise exceptions raised during execution when accessed" do
|
60
|
+
y = Object.new
|
61
|
+
y = @method.call { 1 / 0 }
|
62
|
+
expect { y.inspect }.to raise_error ZeroDivisionError
|
63
|
+
expect { y.inspect }.to raise_error ZeroDivisionError
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should only execute once when execptions are raised" do
|
67
|
+
y = 1
|
68
|
+
x = @method.call { (y += 1) && (1 / 0) }
|
69
|
+
expect { x.inspect }.to raise_error ZeroDivisionError
|
70
|
+
expect { x.inspect }.to raise_error ZeroDivisionError
|
71
|
+
expect(y).to eq 2
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should remain the same for an object reference" do
|
75
|
+
h = {}
|
76
|
+
x = Object.new
|
77
|
+
h[:test] = @method.call { x }
|
78
|
+
expect(h[:test]).to eq x
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should be eql? for results" do
|
82
|
+
x = Object.new
|
83
|
+
y = @method.call { x }
|
84
|
+
expect(y).to eq x
|
85
|
+
# this would be ideal, but it can't be done in Ruby. result
|
86
|
+
# objects that have a redefined #eql? should do fine.
|
87
|
+
#x.should eql y
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should be equal? for results" do
|
91
|
+
x = Object.new
|
92
|
+
y = @method.call { x }
|
93
|
+
expect(y).to eq x
|
94
|
+
# this would be ideal, but it can't be done in Ruby.
|
95
|
+
#x.should equal y
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should be thread safe" do
|
99
|
+
x = @method.call { res = 1; 3.times { res = res * 5 ; sleep 1 } ; res}
|
100
|
+
threads = []
|
101
|
+
results = []
|
102
|
+
changeds = []
|
103
|
+
10.times do
|
104
|
+
threads << Thread.new do
|
105
|
+
changed = false
|
106
|
+
res = old_res = 125
|
107
|
+
10.times do |i|
|
108
|
+
old_res = res
|
109
|
+
res = x + 5
|
110
|
+
changed ||= res != old_res && i != 0
|
111
|
+
sleep 0.3
|
112
|
+
end
|
113
|
+
results << res
|
114
|
+
changeds << changed
|
115
|
+
end
|
116
|
+
end
|
117
|
+
threads.each do |t|
|
118
|
+
t.join
|
119
|
+
end
|
120
|
+
results.each do |result|
|
121
|
+
expect(result).to eq 130
|
122
|
+
end
|
123
|
+
changeds.each do |changed|
|
124
|
+
expect(changed).to eq false
|
125
|
+
end
|
126
|
+
expect(changeds.size).to eq 10
|
127
|
+
end
|
128
|
+
|
129
|
+
describe 'compatibility with Marshal' do
|
130
|
+
it 'should not respond_to? marshal_dump' do
|
131
|
+
x = @method.call { 3 + 5 }
|
132
|
+
expect(x).to_not respond_to(:marshal_dump)
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'should respond_to? _dump' do
|
136
|
+
x = @method.call { 3 + 5 }
|
137
|
+
expect(x).to respond_to(:_dump)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: promising
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- fsword
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-09-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description: Forking from bhuga/promising-future on github, so Version is started
|
42
|
+
from 0.3.1
|
43
|
+
email:
|
44
|
+
- li.jianye@gmail.com
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".gemspec"
|
50
|
+
- ".gitignore"
|
51
|
+
- ".rspec"
|
52
|
+
- ".yardopts"
|
53
|
+
- AUTHORS
|
54
|
+
- Gemfile
|
55
|
+
- LICENSE.txt
|
56
|
+
- README.md
|
57
|
+
- Rakefile
|
58
|
+
- UNLICENSE
|
59
|
+
- VERSION
|
60
|
+
- lib/promising.rb
|
61
|
+
- lib/promising/future.rb
|
62
|
+
- lib/promising/promise.rb
|
63
|
+
- lib/promising/version.rb
|
64
|
+
- promising.gemspec
|
65
|
+
- spec/future_spec.rb
|
66
|
+
- spec/promise_spec.rb
|
67
|
+
- spec/shared.rb
|
68
|
+
homepage: https://github.com/fsword/promising
|
69
|
+
licenses:
|
70
|
+
- MIT
|
71
|
+
metadata: {}
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
requirements: []
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 2.2.2
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: A simple promise/future lib for ruby
|
92
|
+
test_files:
|
93
|
+
- spec/future_spec.rb
|
94
|
+
- spec/promise_spec.rb
|
95
|
+
- spec/shared.rb
|
96
|
+
has_rdoc:
|