Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Promise.map and Promise.filter #37

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ if Gem.ruby_version < Gem::Version.new('2.0')
gem 'term-ansicolor', '~> 1.3.2'
gem 'tins', '~> 1.6.0'
end

if Gem.ruby_version >= Gem::Version.new('2.1')
gem 'devtools', '~> 0.1.16'
gem 'devtools', '0.1.18'
end

gem 'awesome_print'
Expand Down
2 changes: 1 addition & 1 deletion config/flog.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
threshold: 15.8
threshold: 18.1
1 change: 1 addition & 0 deletions config/reek.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ DuplicateMethodCall:
max_calls: 1
allow_calls:
- source.backtrace
- ~index
FeatureEnvy:
enabled: false
exclude: []
Expand Down
2 changes: 2 additions & 0 deletions config/rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ AccessModifierIndentation:
# Limit line length
LineLength:
Max: 79
Exclude:
- 'spec/**/*'

# Disable documentation checking until a class needs to be documented once
Documentation:
Expand Down
12 changes: 11 additions & 1 deletion lib/promise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
require 'promise/observer'
require 'promise/progress'
require 'promise/group'
require 'promise/mapping_group'
require 'promise/filter_group'

class Promise
Error = Class.new(RuntimeError)
Expand All @@ -22,7 +24,15 @@ def self.resolve(obj = nil)
end

def self.all(enumerable)
Group.new(new, enumerable).promise
Group.new(new, enumerable).perform
end

def self.map(enumerable, &block)
MappingGroup.new(new, enumerable, &block).perform
end

def self.filter(enumerable, &block)
FilterGroup.new(new, enumerable, &block).perform
end

def self.map_value(obj)
Expand Down
29 changes: 29 additions & 0 deletions lib/promise/filter_group.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
class Promise
class FilterGroup < MappingGroup
def initialize(result_promise, input, &block)
super(result_promise, input, &block)

@preserved_values = []
end

def promise_fulfilled(value, index)
@preserved_values[index] = value unless index < 0

super
end

protected

def fulfill
result = []

@preserved_values.zip(@values) do |value, check|
result.push(value) if check
end

@promise.fulfill(result)
end
end

private_constant :MappingGroup
end
87 changes: 44 additions & 43 deletions lib/promise/group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,69 +3,70 @@ class Group
include Promise::Observer

attr_accessor :source
attr_reader :promise

def initialize(result_promise, inputs)
@promise = result_promise
@inputs = inputs
@remaining = count_promises

if @remaining.zero?
promise.fulfill(inputs)
else
promise.source = self
chain_inputs
end

def initialize(promise, input)
@promise = promise
@promise.source = self

@input = input
@values = []

@resolved_count = 0
end

def wait
each_promise do |input_promise|
input_promise.wait if input_promise.pending?
@input.each do |obj|
obj.wait if obj.is_a?(Promise) && obj.pending?
end
end

def promise_fulfilled(_value = nil, _arg = nil)
@remaining -= 1
if @remaining.zero?
result = @inputs.map { |obj| promise?(obj) ? obj.value : obj }
promise.fulfill(result)
end
def promise_fulfilled(value, index)
@resolved_count += 1
@values[index] = value
fulfill if resolved?
end

def promise_rejected(reason, _arg = nil)
promise.reject(reason)
def promise_rejected(reason, _ = nil)
@promise.reject(reason)
end

private
def perform
index = 0

def chain_inputs
each_promise do |input_promise|
case input_promise.state
when :fulfilled
promise_fulfilled
when :rejected
promise_rejected(input_promise.reason)
@input.each do |maybe_promise|
if maybe_promise.is_a?(Promise)
case maybe_promise.state
when :fulfilled
promise_fulfilled(maybe_promise.value, index)
when :rejected
return promise_rejected(maybe_promise.reason)
else
maybe_promise.subscribe(self, index, nil)
end
else
input_promise.subscribe(self, nil, nil)
promise_fulfilled(maybe_promise, index)
end

index += 1
end
end

def promise?(obj)
obj.is_a?(Promise)
@total_count = index

fulfill if resolved?

@promise
end

def count_promises
count = 0
each_promise { count += 1 }
count
private

def fulfill
@promise.fulfill(@values)
end

def each_promise
@inputs.each do |obj|
yield obj if promise?(obj)
end
def resolved?
@total_count == @resolved_count
end
end

private_constant :Group
end
32 changes: 32 additions & 0 deletions lib/promise/mapping_group.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
class Promise
class MappingGroup < Group
def initialize(result_promise, input, &block)
super(result_promise, input)

@block = block
end

def promise_fulfilled(value, index)
return super(value, ~index) if index < 0

maybe_promise = begin
@block.call(value)
rescue => error
return promise_rejected(error)
end

return super(maybe_promise, index) unless maybe_promise.is_a?(Promise)

case maybe_promise.state
when :fulfilled
super(maybe_promise.value, index)
when :rejected
return promise_rejected(maybe_promise.reason)
else
maybe_promise.subscribe(self, ~index, nil)
end
end
end

private_constant :MappingGroup
end
Loading