From 5181d157789878d90f365233df2fd5fa1b52dce1 Mon Sep 17 00:00:00 2001 From: Radan Skoric Date: Fri, 23 Feb 2018 14:40:24 +0100 Subject: [PATCH 1/2] Add failing spec for parent class initializer problem Currently, if a class includes `Dry::Transaction` it can't have any functionality relying on parent classes initializers being called because dry transaction doesn't call in its initializer. --- lib/dry/transaction/instance_methods.rb | 4 ++- spec/integration/user_class_hierarchy_spec.rb | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 spec/integration/user_class_hierarchy_spec.rb diff --git a/lib/dry/transaction/instance_methods.rb b/lib/dry/transaction/instance_methods.rb index e271f32..ba5a182 100644 --- a/lib/dry/transaction/instance_methods.rb +++ b/lib/dry/transaction/instance_methods.rb @@ -12,7 +12,7 @@ module InstanceMethods attr_reader :listeners attr_reader :stack - def initialize(steps: (self.class.steps), listeners: nil, **operations) + def initialize(*args, steps: (self.class.steps), listeners: nil, **operations) @steps = steps.map { |step| operation = resolve_operation(step, operations) step.with(operation: operation) @@ -20,6 +20,8 @@ def initialize(steps: (self.class.steps), listeners: nil, **operations) @operations = operations @stack = Stack.new(@steps) subscribe(listeners) unless listeners.nil? + # This fixes the failing test but breaks 42 other tests. + # super(*args, **operations) end def call(input = nil, &block) diff --git a/spec/integration/user_class_hierarchy_spec.rb b/spec/integration/user_class_hierarchy_spec.rb new file mode 100644 index 0000000..33877a5 --- /dev/null +++ b/spec/integration/user_class_hierarchy_spec.rb @@ -0,0 +1,25 @@ +RSpec.describe "dry transaction used with inheriting user classes" do + let(:base_class) do + Class.new do + attr_reader :base_called + + def initialize(*_args) + @base_called = true + end + end + end + + let(:child_class) do + Class.new(base_class) do + include Dry::Transaction + end + end + + context "when user class inherits from another class" do + subject { child_class.new } + + it "calls base class initializer" do + expect(subject.base_called).to be true + end + end +end From 207eccc18ddfcdbdc9e04c73b19565d2f5c0706a Mon Sep 17 00:00:00 2001 From: Tim Riley Date: Tue, 20 Mar 2018 10:11:34 +1100 Subject: [PATCH 2/2] Call super initialize and pass through args (if it accepts them) This is not a complete solution yet, it currently ignores keyrest-type parameters in the super method. It could also do with some tidying. --- lib/dry/transaction/instance_methods.rb | 28 +++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/dry/transaction/instance_methods.rb b/lib/dry/transaction/instance_methods.rb index ba5a182..23aeb88 100644 --- a/lib/dry/transaction/instance_methods.rb +++ b/lib/dry/transaction/instance_methods.rb @@ -20,8 +20,32 @@ def initialize(*args, steps: (self.class.steps), listeners: nil, **operations) @operations = operations @stack = Stack.new(@steps) subscribe(listeners) unless listeners.nil? - # This fixes the failing test but breaks 42 other tests. - # super(*args, **operations) + + next_super_method = method(__method__).super_method + super_initialize = nil + + loop do + if next_super_method.owner.name =~ /Dry::Transaction/ + next_super_method = next_super_method.super_method + else + super_initialize = next_super_method + break + end + end + + if super_initialize && super_initialize.parameters.empty? + super() + elsif super_initialize + super_kwarg_names = super_initialize.parameters.each_with_object([]) { |(type, name), names| + names << name if [:key, :keyreq].include?(type) + } + + super_kwargs = operations.each_with_object({}) { |(key, val), kwargs| + kwargs[key] = val if super_kwarg_names.include?(key) + } + + super(*args, **super_kwargs) + end end def call(input = nil, &block)