diff --git a/Dockerfile b/Dockerfile index eea0343..eee00c6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,11 @@ -FROM ruby:2.3.1 +FROM ruby:2.6.5 ENV TEST_HOME=/pipes ADD *.gemspec $TEST_HOME/ ADD Gemfile $TEST_HOME/ WORKDIR $TEST_HOME +RUN gem install bundler RUN bundle install --jobs 8 --retry 5 ADD . $TEST_HOME diff --git a/Gemfile b/Gemfile index cdfab82..fa75df1 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,3 @@ source 'https://rubygems.org' gemspec - -group :test do - gem 'rspec' -end diff --git a/codequest_pipes.gemspec b/codequest_pipes.gemspec index e312bcb..5859a35 100644 --- a/codequest_pipes.gemspec +++ b/codequest_pipes.gemspec @@ -14,6 +14,9 @@ Gem::Specification.new do |spec| spec.files = Dir['lib/**/*.rb'] + Dir['spec/**/*.rb'] spec.test_files = spec.files.grep(/^spec/) - spec.add_development_dependency 'bundler', '~> 1.6', '>= 1.6.9' - spec.add_development_dependency 'rake', '~> 10.3' + spec.add_development_dependency 'bundler', '~> 2.0' + spec.add_development_dependency 'rake', '~> 13.0' + spec.add_development_dependency 'rspec', '~> 3.9' + + spec.add_dependency 'ice_nine', '~> 0.11' end diff --git a/lib/codequest_pipes/context.rb b/lib/codequest_pipes/context.rb index 155c1e1..fc5968c 100644 --- a/lib/codequest_pipes/context.rb +++ b/lib/codequest_pipes/context.rb @@ -1,3 +1,5 @@ +require 'ice_nine' + require 'codequest_pipes/context/error_collector' module Pipes @@ -16,19 +18,23 @@ class ExecutionTerminated < ::StandardError; end # Context constructor. # # @param values [Hash] - def initialize(values = {}) - add(values) + def initialize(values = {}, mutable_values = {}) @error_collector = ErrorCollector.new + @mutable_values = Set.new + add(values, mutable_values) end # Method `add` allows adding new properties (as a Hash) to the Context. # # @param values [Hash] - def add(values) + def add(values, mutable_values = {}) values.each do |key, val| - k_sym = key.to_sym - fail Override, "Property :#{key} already present" if respond_to?(k_sym) - define_singleton_method(k_sym) { val } + add_value(key.to_sym, IceNine.deep_freeze(val)) + end + + mutable_values.each do |key, val| + add_value(key.to_sym, val) + @mutable_values << key end end @@ -101,6 +107,13 @@ def add_errors(collectable_errors) private - attr_reader :error_collector + attr_reader :error_collector, :mutable_values + + def add_value(key, value) + if respond_to?(key) && !mutable_values.include?(key) + fail Override, "Property :#{key} already exists and is non-mutable" + end + define_singleton_method(key) { value } + end end # class Context end # module Pipes diff --git a/spec/context_spec.rb b/spec/context_spec.rb index 0982f67..1d5edfa 100644 --- a/spec/context_spec.rb +++ b/spec/context_spec.rb @@ -12,6 +12,23 @@ expect { subject.add(key: 'other_val') } .to raise_error(Pipes::Context::Override) end + + it 'does not allow modifying existing fields' do + subject.add(key: 'val') + expect { subject.key << 'ue' }.to raise_error(FrozenError) + end + + it 'allows rewriting existing mutable fields' do + subject.add({}, key: 'val') + expect { subject.add(key: 'other_val') } + .to change { subject.key }.from('val').to 'other_val' + end + + it 'allows modifying existing mutable fields' do + subject.add({}, key: 'val') + expect { subject.key << 'ue' } + .to change { subject.key }.from('val').to 'value' + end end # describe '#add' describe '#inspect' do diff --git a/spec/pipe_spec.rb b/spec/pipe_spec.rb index 2dc8bb3..d644c7c 100644 --- a/spec/pipe_spec.rb +++ b/spec/pipe_spec.rb @@ -63,7 +63,7 @@ class RequiringNumericChild < Parent class NoMethodPipe < Pipes::Pipe; end describe Pipes::Pipe do - let(:ctx) { Pipes::Context.new(flow: []) } + let(:ctx) { Pipes::Context.new({}, flow: []) } subject { pipe.call(ctx) }