promise.rb 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # promise.rb changelog
2
+
3
+ ## 0.4.0 (December 13, 2013)
4
+
5
+ * Disclaiming my copyright, promise.rb is now in the Public Domain
6
+ * on_fulfill argument to #then now takes precedence over block
7
+ * The rejection reason now defaults to RuntimeError, so that it can be re-raised by #sync
8
+ * `progress` and `on_progress` are now first class citizens
data/Gemfile CHANGED
@@ -13,7 +13,6 @@ platform :rbx do
13
13
  gem 'rubysl', '~> 2.0'
14
14
  gem 'rubysl-json', '~> 2.0'
15
15
  gem 'rubinius', '~> 2.0'
16
- gem 'racc'
17
16
  end
18
17
 
19
18
  # Added by devtools
data/Gemfile.devtools CHANGED
@@ -4,6 +4,10 @@ group :development do
4
4
  gem 'rake', '~> 10.1.0'
5
5
  gem 'rspec', '~> 2.14.1'
6
6
  gem 'yard', '~> 0.8.7'
7
+
8
+ platform :rbx do
9
+ gem 'rubysl-singleton', '~> 2.0.0'
10
+ end
7
11
  end
8
12
 
9
13
  group :yard do
@@ -11,14 +15,13 @@ group :yard do
11
15
  end
12
16
 
13
17
  group :guard do
14
- gem 'guard', '~> 1.8.1'
15
- gem 'guard-bundler', '~> 1.0.0'
16
- gem 'guard-rspec', '~> 3.0.2'
17
- gem 'guard-rubocop', '~> 0.2.0'
18
- gem 'guard-mutant', '~> 0.0.1'
18
+ gem 'guard', '~> 2.2.4'
19
+ gem 'guard-bundler', '~> 2.0.0'
20
+ gem 'guard-rspec', '~> 4.0.4'
21
+ gem 'guard-rubocop', '~> 1.0.0'
19
22
 
20
23
  # file system change event handling
21
- gem 'listen', '~> 1.3.0'
24
+ gem 'listen', '~> 2.2.0'
22
25
  gem 'rb-fchange', '~> 0.0.6', require: false
23
26
  gem 'rb-fsevent', '~> 0.9.3', require: false
24
27
  gem 'rb-inotify', '~> 0.9.0', require: false
@@ -30,18 +33,27 @@ group :guard do
30
33
  end
31
34
 
32
35
  group :metrics do
33
- gem 'coveralls', '~> 0.6.7'
36
+ gem 'coveralls', '~> 0.7.0'
34
37
  gem 'flay', '~> 2.4.0'
35
- gem 'flog', '~> 4.1.1'
38
+ gem 'flog', '~> 4.2.0'
36
39
  gem 'reek', '~> 1.3.2'
37
- gem 'rubocop', '~> 0.12.0'
38
- gem 'simplecov', '~> 0.7.1'
40
+ gem 'rubocop', '~> 0.15.0'
41
+ gem 'simplecov', '~> 0.8.2'
39
42
  gem 'yardstick', '~> 0.9.7', git: 'https://github.com/dkubb/yardstick.git'
40
43
 
41
44
  platforms :ruby_19, :ruby_20 do
42
- gem 'mutant', git: 'https://github.com/mbj/mutant.git'
45
+ gem 'mutant', '~> 0.3.0.rc3', git: 'https://github.com/mbj/mutant.git'
46
+ gem 'unparser', '~> 0.1.5', git: 'https://github.com/mbj/unparser.git'
43
47
  gem 'yard-spellcheck', '~> 0.1.5'
44
48
  end
49
+
50
+ platform :rbx do
51
+ gem 'json', '~> 1.8.1'
52
+ gem 'racc', '~> 1.4.10'
53
+ gem 'rubysl-logger', '~> 2.0.0'
54
+ gem 'rubysl-open-uri', '~> 2.0.0'
55
+ gem 'rubysl-prettyprint', '~> 2.0.2'
56
+ end
45
57
  end
46
58
 
47
59
  group :benchmarks do
data/README.md CHANGED
@@ -1,7 +1,20 @@
1
1
  # promise.rb [![Build Status](https://travis-ci.org/lgierth/promise.rb.png?branch=master)](https://travis-ci.org/lgierth/promise.rb) [![Code Climate](https://codeclimate.com/github/lgierth/promise.rb.png)](https://codeclimate.com/github/lgierth/promise.rb) [![Coverage Status](https://coveralls.io/repos/lgierth/promise.rb/badge.png?branch=master)](https://coveralls.io/r/lgierth/promise.rb?branch=master)
2
2
 
3
3
  Ruby implementation of the [Promises/A+ spec](http://promisesaplus.com/).
4
- 100% mutation coverage, tested on 1.9, 2.0, Rubinius, and JRuby.
4
+ 100% [mutation coverage](https://github.com/mbj/mutant), tested on 1.9, 2.0, Rubinius, and JRuby.
5
+
6
+ Similar projects:
7
+
8
+ - [concurrent-ruby](https://github.com/jdantonio/concurrent-ruby/blob/master/md/promise.md), Promises/A(+) implementation, thread based
9
+ - [promise](https://github.com/bhuga/promising-future), a.k.a. promising-future, classic promises and futures, thread based
10
+ - [celluloid-promise](https://github.com/cotag/celluloid-promise), inspired by Q, backed by a Celluloid actor
11
+ - [em-promise](https://github.com/cotag/em-promise), inspired by Q, backed by an EventMachine reactor
12
+ - [futuristic](https://github.com/seanlilmateus/futuristic), MacRuby bindings for Grand Central Dispatch
13
+ - [methodmissing/promise](https://github.com/methodmissing/promise), thread based, abandoned
14
+
15
+ ## Todo
16
+
17
+ - test with https://gist.github.com/joeljackson/5722487
5
18
 
6
19
  ## Installation
7
20
 
@@ -19,19 +32,11 @@ Or install it yourself as:
19
32
 
20
33
  ## Usage
21
34
 
22
- promise.rb doesn't come with a way of scheduling callback dispatch.
35
+ This guide assumes that you are familiar with the [Promises/A+ spec](http://promisesaplus.com/). It's a quick read, though.
23
36
 
24
- ```ruby
25
- require 'promise'
26
-
27
- class MyPromise < Promise
28
- def defer(callback, arg)
29
- callback.dispatch(arg)
30
- end
31
- end
32
- ```
33
-
34
- The above scheduling mechanism violates the following section of the spec:
37
+ promise.rb comes with a very primitive way of scheduling callback dispatch. It
38
+ immediately executes the callback, instead of scheduling it for execution
39
+ *after* `Promise#fulfill` or `Promise#reject`, as demanded by the spec:
35
40
 
36
41
  > onFulfilled or onRejected must not be called until the execution context
37
42
  > stack contains only platform code.
@@ -44,8 +49,8 @@ require 'promise'
44
49
  require 'eventmachine'
45
50
 
46
51
  class MyPromise < Promise
47
- def defer(callback, arg)
48
- EM.next_tick { callback.dispatch(arg) }
52
+ def defer
53
+ EM.next_tick { yield }
49
54
  end
50
55
  end
51
56
  ```
@@ -76,10 +81,65 @@ end
76
81
  failing_stuff.then(proc { |value| }, proc { |reason| p reason })
77
82
  ```
78
83
 
79
- ## License
84
+ promise.rb also comes with the utility method `Promise#sync`, which waits for
85
+ the promise to be fulfilled and returns the value, or for it to be rejected and
86
+ re-raises the reason. Using `#sync` requires you to implement `#wait`. You could
87
+ for example cooperatively schedule fibers waiting for different promises:
88
+
89
+ ```ruby
90
+ require 'promise'
91
+ require 'eventmachine'
92
+
93
+ class MyPromise < Promise
94
+ def defer
95
+ EM.next_tick { yield }
96
+ end
97
+
98
+ def wait
99
+ fiber = Fiber.current
100
+ resume = proc do |arg|
101
+ defer { fiber.resume(arg) }
102
+ end
103
+
104
+ self.then(resume, resume)
105
+ Fiber.yield
106
+ end
107
+ end
108
+
109
+ promise = MyPromise.new
110
+ Fiber.new { p promise.sync }.resume
111
+ promise.fulfill
112
+ ```
113
+
114
+ Or have the rejection reason re-raised from `#sync`:
115
+
116
+ ```ruby
117
+ promise = MyPromise.new
118
+
119
+ Fiber.new do
120
+ begin
121
+ promise.sync
122
+ rescue MyError
123
+ p $!
124
+ end
125
+ end.resume
126
+
127
+ promise.reject(MyError.new)
128
+ ```
129
+
130
+ Very simple progress callbacks, as per Promises/A, are supported as well. They have been dropped in A+, but I found them to be a useful mechanism - if kept simple. Callback dispatch happens immediately in the call to `#progress`, in the order of definition via `#on_progress`. Also note that `#on_progress` does not return a new promise for chaining - the progress mechanism is meant to be very lightweight, and ignores many of the constraints and guarantees of `then`.
131
+
132
+ ```ruby
133
+ promise = MyPromise.new
134
+ promise.on_progress { |status| p status }
135
+ promise.progress(:anything)
136
+ ```
137
+
138
+ ## Unlicense
80
139
 
81
- Hatetepe is licensed under the [MIT License](http://opensource.org/licenses/MIT).
82
- See LICENSE.txt for details.
140
+ promise.rb is free and unencumbered public domain software. For more
141
+ information, see [unlicense.org](http://unlicense.org/) or the accompanying
142
+ UNLICENSE file.
83
143
 
84
144
  ## Contributing
85
145
 
File without changes
data/config/flay.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  ---
2
- threshold: 14
2
+ threshold: 10
3
3
  total_score: 49
data/config/rubocop.yml CHANGED
@@ -24,15 +24,6 @@ CollectionMethods:
24
24
  find: 'detect'
25
25
  find_all: 'select'
26
26
 
27
- # Do not force public/protected/private keyword to be indented at the same
28
- # level as the def keyword. My personal preference is to outdent these keywords
29
- # because I think when scanning code it makes it easier to identify the
30
- # sections of code and visually separate them. When the keyword is at the same
31
- # level I think it sort of blends in with the def keywords and makes it harder
32
- # to scan the code and see where the sections are.
33
- AccessControl:
34
- Enabled: false
35
-
36
27
  # Limit line length
37
28
  LineLength:
38
29
  Max: 79
data/lib/promise.rb CHANGED
@@ -6,6 +6,8 @@ require 'promise/callback'
6
6
  require 'promise/progress'
7
7
 
8
8
  class Promise
9
+ include Promise::Progress
10
+
9
11
  attr_reader :value, :reason
10
12
 
11
13
  def initialize
@@ -27,7 +29,7 @@ class Promise
27
29
  end
28
30
 
29
31
  def then(on_fulfill = nil, on_reject = nil, &block)
30
- on_fulfill = block if block
32
+ on_fulfill ||= block
31
33
  next_promise = add_callbacks(on_fulfill, on_reject)
32
34
 
33
35
  maybe_dispatch(@on_fulfill.last, @on_reject.last)
@@ -47,7 +49,7 @@ class Promise
47
49
  end
48
50
  end
49
51
 
50
- def reject(reason = nil)
52
+ def reject(reason = RuntimeError)
51
53
  dispatch(@on_reject, reason) do
52
54
  @state = :rejected
53
55
  @reason = reason
@@ -2,18 +2,15 @@
2
2
 
3
3
  class Promise
4
4
  module Progress
5
- def initialize
6
- super
7
- @on_progress = []
8
- end
9
-
10
5
  def on_progress(&block)
11
- @on_progress << block
6
+ @on_progress ||= []
7
+ @on_progress << block if block_given?
8
+ @on_progress
12
9
  end
13
10
 
14
11
  def progress(status)
15
12
  if pending?
16
- @on_progress.each { |block| block.call(status) }
13
+ on_progress.each { |block| block.call(status) }
17
14
  end
18
15
  end
19
16
  end
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  class Promise
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.0'
5
5
  end
data/promise.rb.gemspec CHANGED
@@ -10,9 +10,9 @@ Gem::Specification.new do |spec|
10
10
  spec.authors = ['Lars Gierth']
11
11
  spec.email = ['lars.gierth@gmail.com']
12
12
  spec.description = %q{Promises/A+ for Ruby}
13
- spec.summary = %q{Promises/A+ for Ruby}
13
+ spec.summary = %q{Ruby implementation of the Promises/A+ spec}
14
14
  spec.homepage = 'https://github.com/lgierth/promise'
15
- spec.license = 'MIT'
15
+ spec.license = 'Public Domain'
16
16
 
17
17
  spec.files = `git ls-files`.split($/)
18
18
  spec.test_files = spec.files.grep(%r{^spec/})
data/spec/promise_spec.rb CHANGED
@@ -117,6 +117,14 @@ describe Promise do
117
117
  subject.fulfill(value)
118
118
  expect(result).to eq(value)
119
119
  end
120
+
121
+ it 'takes precedence over block' do
122
+ result = nil
123
+ subject.then(proc { |_| result = :arg }) { |_| result = :block }
124
+
125
+ subject.fulfill(value)
126
+ expect(result).to be(:arg)
127
+ end
120
128
  end
121
129
 
122
130
  describe '3.2.3 on_reject' do
@@ -220,7 +228,7 @@ describe Promise do
220
228
  end
221
229
 
222
230
  it 'rejects promise2 with error raised by on_fulfill' do
223
- promise2 = subject.then(proc { |_| raise error })
231
+ promise2 = subject.then(proc { |_| fail(error) })
224
232
  expect { subject.fulfill(value) }.to raise_error(error)
225
233
 
226
234
  expect(promise2).to be_rejected
@@ -228,7 +236,7 @@ describe Promise do
228
236
  end
229
237
 
230
238
  it 'rejects promise2 with error raised by on_reject' do
231
- promise2 = subject.then(nil, proc { |_| raise error })
239
+ promise2 = subject.then(nil, proc { |_| fail(error) })
232
240
  expect { subject.reject(reason) }.to raise_error(error)
233
241
 
234
242
  expect(promise2).to be_rejected
@@ -305,11 +313,6 @@ describe Promise do
305
313
  end
306
314
 
307
315
  describe '#progress' do
308
- let(:klass) do
309
- Class.new(Promise) { include Promise::Progress }
310
- end
311
- let(:subject) { klass.new }
312
-
313
316
  let(:status) { double('status') }
314
317
 
315
318
  it 'calls the callbacks in the order of calls to #on_progress' do
@@ -355,7 +358,7 @@ describe Promise do
355
358
 
356
359
  it 'does not require a reason' do
357
360
  subject.reject
358
- expect(subject.reason).to be(nil)
361
+ expect(subject.reason).to be(RuntimeError)
359
362
  end
360
363
  end
361
364
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: promise.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-12-07 00:00:00.000000000 Z
12
+ date: 2013-12-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -37,11 +37,12 @@ files:
37
37
  - .gitignore
38
38
  - .rspec
39
39
  - .travis.yml
40
+ - CHANGELOG.md
40
41
  - Gemfile
41
42
  - Gemfile.devtools
42
- - LICENSE
43
43
  - README.md
44
44
  - Rakefile
45
+ - UNLICENSE
45
46
  - config/devtools.yml
46
47
  - config/flay.yml
47
48
  - config/flog.yml
@@ -58,7 +59,7 @@ files:
58
59
  - spec/spec_helper.rb
59
60
  homepage: https://github.com/lgierth/promise
60
61
  licenses:
61
- - MIT
62
+ - Public Domain
62
63
  post_install_message:
63
64
  rdoc_options: []
64
65
  require_paths:
@@ -80,7 +81,7 @@ rubyforge_project:
80
81
  rubygems_version: 1.8.23
81
82
  signing_key:
82
83
  specification_version: 3
83
- summary: Promises/A+ for Ruby
84
+ summary: Ruby implementation of the Promises/A+ spec
84
85
  test_files:
85
86
  - spec/promise_spec.rb
86
87
  - spec/spec_helper.rb