diff --git a/.travis.yml b/.travis.yml index 5fc2b04..5718ab1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,3 +5,5 @@ rvm: - 2.0.0 - 2.1.2 script: bundle exec rspec +before_install: + - gem install bundler diff --git a/lib/monads/eventually.rb b/lib/monads/eventually.rb index 8bb203e..32047fc 100644 --- a/lib/monads/eventually.rb +++ b/lib/monads/eventually.rb @@ -1,11 +1,12 @@ require 'monads/monad' module Monads - Eventually = Struct.new(:block) do + class Eventually include Monad + attr_reader :block def initialize(&block) - super(block) + @block = block end def run(&success) diff --git a/lib/monads/many.rb b/lib/monads/many.rb index 1fbf06b..8823ee9 100644 --- a/lib/monads/many.rb +++ b/lib/monads/many.rb @@ -1,8 +1,13 @@ require 'monads/monad' module Monads - Many = Struct.new(:values) do + class Many include Monad + attr_reader :values + + def initialize(values) + @values = values + end def and_then(&block) block = ensure_monadic_result(&block) diff --git a/lib/monads/monad.rb b/lib/monads/monad.rb index 26e0bbd..cc19c90 100644 --- a/lib/monads/monad.rb +++ b/lib/monads/monad.rb @@ -12,6 +12,12 @@ def method_missing(*args, &block) end end + def respond_to_missing?(method_name, include_private=false) + within do |value| + value.respond_to?(method_name, include_private) + end || super + end + private def ensure_monadic_result(&block) diff --git a/lib/monads/optional.rb b/lib/monads/optional.rb index 9ce77b3..c44da9a 100644 --- a/lib/monads/optional.rb +++ b/lib/monads/optional.rb @@ -1,8 +1,13 @@ require 'monads/monad' module Monads - Optional = Struct.new(:value) do + class Optional include Monad + attr_reader :value + + def initialize(value) + @value = value + end def and_then(&block) block = ensure_monadic_result(&block) diff --git a/spec/monads/eventually_spec.rb b/spec/monads/eventually_spec.rb index 2f6405a..7353aee 100644 --- a/spec/monads/eventually_spec.rb +++ b/spec/monads/eventually_spec.rb @@ -102,6 +102,8 @@ module Monads it 'forwards any unrecognised message to the block’s value' do expect(value).to receive(:challenge) Eventually.new { |success| success.call(value) }.challenge.run {} + expect { Eventually.new { |success| success.call(value) }.method(:challenge) }.not_to raise_error + expect(Eventually.new { |success| success.call(value) }).to respond_to(:challenge) end it 'returns the message’s result wrapped in an Eventually' do @@ -109,6 +111,17 @@ module Monads Eventually.new { |success| success.call(value) }.challenge.run { |result| @result = result } expect(@result).to eq response end + + context 'when value is Enumerable' do + let(:value) { [1, 2, 3] } + + it 'forwards any unrecognised message to the value' do + expect(Eventually.new { |success| success.call(value) }.first).to be_a(Eventually) + expect(Eventually.new { |success| success.call(value) }.first.run { |value| value }).to eq 1 + expect(Eventually.new { |success| success.call(value) }.last).to be_a(Eventually) + expect(Eventually.new { |success| success.call(value) }.last.run { |value| value }).to eq 3 + end + end end end end diff --git a/spec/monads/many_spec.rb b/spec/monads/many_spec.rb index e5bda34..58e3489 100644 --- a/spec/monads/many_spec.rb +++ b/spec/monads/many_spec.rb @@ -87,11 +87,24 @@ module Monads expect(value).to receive(:challenge) end many.challenge + expect { many.method(:challenge) }.not_to raise_error + expect(many).to respond_to(:challenge) end it 'returns the messages’ results wrapped in a Many' do expect(many.challenge.values).to eq responses end + + context 'when values are Enumerable' do + let(:values) { [[1, 2], [3, 5]] } + + it 'forwards any unrecognised message to the value' do + expect(many.first).to be_a(Many) + expect(many.first.values).to eq [1, 3] + expect(many.last).to be_a(Many) + expect(many.last.values).to eq [2, 5] + end + end end end end diff --git a/spec/monads/optional_spec.rb b/spec/monads/optional_spec.rb index 2ae6e46..120483f 100644 --- a/spec/monads/optional_spec.rb +++ b/spec/monads/optional_spec.rb @@ -89,11 +89,24 @@ module Monads it 'forwards any unrecognised message to the value' do expect(value).to receive(:challenge) optional.challenge + expect { optional.method(:challenge) }.not_to raise_error + expect(optional).to respond_to(:challenge) end it 'returns the message’s result wrapped in an Optional' do expect(optional.challenge.value).to eq response end + + context 'when value is Enumerable' do + let(:value) { [1, 2, 3] } + + it 'forwards any unrecognised message to the value' do + expect(optional.first).to be_a(Optional) + expect(optional.first.value).to eq 1 + expect(optional.last).to be_a(Optional) + expect(optional.last.value).to eq 3 + end + end end end end