From 0a7b8c2e1ddcc1ba61832353ae56afedd0ca0c62 Mon Sep 17 00:00:00 2001 From: pjanek Date: Fri, 8 Mar 2019 14:16:09 +0100 Subject: [PATCH 1/6] strong context elements v1-simple --- lib/codequest_pipes.rb | 1 + lib/codequest_pipes/pipe.rb | 37 ++++++++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/lib/codequest_pipes.rb b/lib/codequest_pipes.rb index 1072217..ef3a4a7 100755 --- a/lib/codequest_pipes.rb +++ b/lib/codequest_pipes.rb @@ -5,4 +5,5 @@ module Pipes class MissingCallMethod < ::Exception; end class MissingContext < ::Exception; end + class InvalidType < ::Exception; end end # module Pipes diff --git a/lib/codequest_pipes/pipe.rb b/lib/codequest_pipes/pipe.rb index d2eff11..56061cd 100644 --- a/lib/codequest_pipes/pipe.rb +++ b/lib/codequest_pipes/pipe.rb @@ -26,12 +26,18 @@ def self.call(ctx) _validate_ctx(_provided_context_elements, ctx) end - def self.require_context(*args) - _required_context_elements.push(*args) + def self.require_context(*args, **kwargs) + _required_context_elements.merge!( + **args.map { |a| [a, nil] }.to_h, + **kwargs + ) end - def self.provide_context(*args) - _provided_context_elements.push(*args) + def self.provide_context(*args, **kwargs) + _provided_context_elements.merge!( + **args.map { |a| [a, nil] }.to_h, + **kwargs + ) end def self._combine(first, second) @@ -50,23 +56,36 @@ def self._check_interface(klass) private_class_method :_check_interface def self._required_context_elements - @required_context_elements ||= [] + @required_context_elements ||= {} end private_class_method :_required_context_elements def self._provided_context_elements - @provided_context_elements ||= [] + @provided_context_elements ||= {} end private_class_method :_provided_context_elements def self._validate_ctx(collection, ctx) - collection.each do |element| - next if ctx.respond_to?(element) - fail MissingContext, "context does not respond to '#{element}'" + collection.each do |element, cls| + _raise_missing_context(element) unless ctx.respond_to?(element) + next unless cls + obj = ctx.public_send(element) + _raise_invalid_type(element, obj, cls) unless obj.is_a?(cls) end end private_class_method :_validate_ctx + def self._raise_missing_context(element) + raise MissingContext, "context does not respond to '#{element}'" + end + private_class_method :_raise_missing_context + + def self._raise_invalid_type(element, obj, cls) + raise InvalidType, + "'#{element}' has invalid type #{obj.class} (expected: #{cls})" + end + private_class_method :_raise_invalid_type + private def method_missing(name, *args, &block) From 36c6519467579ad4140e593d5f5e19956531eb5c Mon Sep 17 00:00:00 2001 From: pjanek Date: Fri, 8 Mar 2019 14:50:09 +0100 Subject: [PATCH 2/6] tests for strong context elements --- spec/pipe_spec.rb | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/spec/pipe_spec.rb b/spec/pipe_spec.rb index 426536b..ece3d62 100644 --- a/spec/pipe_spec.rb +++ b/spec/pipe_spec.rb @@ -82,6 +82,40 @@ class NotProvidingChild < Parent expect { subject }.to raise_error Pipes::MissingContext end end # context 'when context element not provided' + + context 'when context element with invalid type provided' do + class ProvidingInvalidChild < Parent + provide_context :bacon, eggs: Numeric + + def call + super + add(bacon: true, eggs: "yes, please") + end + end + + let(:pipe) { Parent | ProvidingInvalidChild } + + it 'raises InvalidType' do + expect { subject }.to raise_error Pipes::InvalidType + end + end # context 'when context element with invalid type provided' + + context 'when context element with valid type provided' do + class ProvidingValidChild < Parent + provide_context :bacon, eggs: Numeric + + def call + super + add(bacon: true, eggs: 4) + end + end # class ProvideChild + + let(:pipe) { Parent | ProvidingValidChild } + + it 'does not raise' do + expect { subject }.to_not raise_error + end + end # context 'when context element with valid type provided' end # describe '.provide_context' describe 'pipes declared using Pipe::Closure' do From d423c09bbe4206e57e18c4b62165e0cb15aa4b3e Mon Sep 17 00:00:00 2001 From: pjanek Date: Fri, 15 Mar 2019 09:42:03 +0100 Subject: [PATCH 3/6] extract _merge_context_elements method --- lib/codequest_pipes/pipe.rb | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/codequest_pipes/pipe.rb b/lib/codequest_pipes/pipe.rb index 56061cd..4e03378 100644 --- a/lib/codequest_pipes/pipe.rb +++ b/lib/codequest_pipes/pipe.rb @@ -27,17 +27,11 @@ def self.call(ctx) end def self.require_context(*args, **kwargs) - _required_context_elements.merge!( - **args.map { |a| [a, nil] }.to_h, - **kwargs - ) + _merge_context_elements(_required_context_elements, args, kwargs) end def self.provide_context(*args, **kwargs) - _provided_context_elements.merge!( - **args.map { |a| [a, nil] }.to_h, - **kwargs - ) + _merge_context_elements(_provided_context_elements, args, kwargs) end def self._combine(first, second) @@ -86,6 +80,14 @@ def self._raise_invalid_type(element, obj, cls) end private_class_method :_raise_invalid_type + def self._merge_context_elements(elements, args, kwargs) + elements.merge!( + **args.map { |a| [a, nil] }.to_h, + **kwargs + ) + end + private_class_method :_merge_context_elements + private def method_missing(name, *args, &block) From 0e0d4056a1de0db2d44732895df3bbc89323390d Mon Sep 17 00:00:00 2001 From: pjanek Date: Fri, 15 Mar 2019 09:45:29 +0100 Subject: [PATCH 4/6] rename 'cls' to 'klass' --- lib/codequest_pipes/pipe.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/codequest_pipes/pipe.rb b/lib/codequest_pipes/pipe.rb index 4e03378..41b811f 100644 --- a/lib/codequest_pipes/pipe.rb +++ b/lib/codequest_pipes/pipe.rb @@ -60,11 +60,11 @@ def self._provided_context_elements private_class_method :_provided_context_elements def self._validate_ctx(collection, ctx) - collection.each do |element, cls| + collection.each do |element, klass| _raise_missing_context(element) unless ctx.respond_to?(element) - next unless cls + next unless klass obj = ctx.public_send(element) - _raise_invalid_type(element, obj, cls) unless obj.is_a?(cls) + _raise_invalid_type(element, obj, klass) unless obj.is_a?(klass) end end private_class_method :_validate_ctx @@ -74,9 +74,9 @@ def self._raise_missing_context(element) end private_class_method :_raise_missing_context - def self._raise_invalid_type(element, obj, cls) + def self._raise_invalid_type(element, obj, klass) raise InvalidType, - "'#{element}' has invalid type #{obj.class} (expected: #{cls})" + "'#{element}' has invalid type #{obj.class} (expected: #{klass})" end private_class_method :_raise_invalid_type From 37d58f63b51a69dafbe01e8ee709d06d88b6a878 Mon Sep 17 00:00:00 2001 From: pjanek Date: Fri, 15 Mar 2019 10:45:56 +0100 Subject: [PATCH 5/6] reformat _validate_ctx --- lib/codequest_pipes/pipe.rb | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/codequest_pipes/pipe.rb b/lib/codequest_pipes/pipe.rb index 41b811f..851816d 100644 --- a/lib/codequest_pipes/pipe.rb +++ b/lib/codequest_pipes/pipe.rb @@ -61,24 +61,25 @@ def self._provided_context_elements def self._validate_ctx(collection, ctx) collection.each do |element, klass| - _raise_missing_context(element) unless ctx.respond_to?(element) - next unless klass - obj = ctx.public_send(element) - _raise_invalid_type(element, obj, klass) unless obj.is_a?(klass) + _validate_value_presence(ctx, element) + _validate_value_type(ctx, element, klass) if klass end end private_class_method :_validate_ctx - def self._raise_missing_context(element) + def self._validate_value_presence(ctx, element) + return if ctx.respond_to?(element) raise MissingContext, "context does not respond to '#{element}'" end - private_class_method :_raise_missing_context + private_class_method :_validate_value_presence - def self._raise_invalid_type(element, obj, klass) + def self._validate_value_type(ctx, element, klass) + obj = ctx.public_send(element) + return if obj.is_a?(klass) raise InvalidType, "'#{element}' has invalid type #{obj.class} (expected: #{klass})" end - private_class_method :_raise_invalid_type + private_class_method :_validate_value_type def self._merge_context_elements(elements, args, kwargs) elements.merge!( From 0f1b69efb2945d0298e2d80fe51f377fb019d388 Mon Sep 17 00:00:00 2001 From: pjanek Date: Fri, 15 Mar 2019 10:46:30 +0100 Subject: [PATCH 6/6] reformat tests and add tests for require_context --- spec/pipe_spec.rb | 104 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 73 insertions(+), 31 deletions(-) diff --git a/spec/pipe_spec.rb b/spec/pipe_spec.rb index ece3d62..2dc8bb3 100644 --- a/spec/pipe_spec.rb +++ b/spec/pipe_spec.rb @@ -20,6 +20,45 @@ def call end end +class ProvidingChild < Parent + provide_context :bacon + + def call + super + add(bacon: true) + end +end # class ProvidingChild + +class ProvidingNumericChild < Parent + provide_context bacon: Numeric + + def call + super + add(bacon: 4) + end +end # class ProvidingNumericChild + +class NotProvidingChild < Parent + provide_context :bacon +end # class NotProvidingChild + +class ProvidingInvalidChild < Parent + provide_context bacon: Numeric + + def call + super + add(bacon: "yes, please") + end +end # class ProvidingInvalidChild + +class RequiringChild < Parent + require_context :bacon +end # class RequiringChild + +class RequiringNumericChild < Parent + require_context bacon: Numeric +end # class RequiringNumericChild + # NoMethodPipe will break with NoMethodError. class NoMethodPipe < Pipes::Pipe; end @@ -55,15 +94,6 @@ class NoMethodPipe < Pipes::Pipe; end describe '.provide_context' do context 'when context element provided' do - class ProvidingChild < Parent - provide_context :bacon - - def call - super - add(bacon: true) - end - end # class ProvideChild - let(:pipe) { Parent | ProvidingChild } it 'does not raise' do @@ -72,10 +102,6 @@ def call end # context 'when context element provided' context 'when context element not provided' do - class NotProvidingChild < Parent - provide_context :bacon - end - let(:pipe) { Parent | NotProvidingChild } it 'raises MissingContext' do @@ -84,15 +110,6 @@ class NotProvidingChild < Parent end # context 'when context element not provided' context 'when context element with invalid type provided' do - class ProvidingInvalidChild < Parent - provide_context :bacon, eggs: Numeric - - def call - super - add(bacon: true, eggs: "yes, please") - end - end - let(:pipe) { Parent | ProvidingInvalidChild } it 'raises InvalidType' do @@ -101,21 +118,46 @@ def call end # context 'when context element with invalid type provided' context 'when context element with valid type provided' do - class ProvidingValidChild < Parent - provide_context :bacon, eggs: Numeric + let(:pipe) { Parent | ProvidingNumericChild } - def call - super - add(bacon: true, eggs: 4) - end - end # class ProvideChild + it 'does not raise' do + expect { subject }.to_not raise_error + end + end # context 'when context element with valid type provided' + end # describe '.provide_context' - let(:pipe) { Parent | ProvidingValidChild } + describe '.require_context' do + context 'when required context element present' do + let(:pipe) { ProvidingChild | RequiringChild } it 'does not raise' do expect { subject }.to_not raise_error end - end # context 'when context element with valid type provided' + end # context 'when required context element present' + + context 'when required context element missing' do + let(:pipe) { Parent | RequiringChild } + + it 'raises MissingContext' do + expect { subject }.to raise_error Pipes::MissingContext + end + end # context 'when context element missing' + + context 'when required context element present with invalid type' do + let(:pipe) { ProvidingInvalidChild | RequiringNumericChild } + + it 'raises InvalidType' do + expect { subject }.to raise_error Pipes::InvalidType + end + end # context 'when context element present with invalid type' + + context 'when required context element present with valid type' do + let(:pipe) { ProvidingNumericChild | RequiringNumericChild } + + it 'raises InvalidType' do + expect { subject }.to_not raise_error + end + end # context 'when context element present with valid type' end # describe '.provide_context' describe 'pipes declared using Pipe::Closure' do