From 8c3a1bb9e198a816136c3dd891eebcbc600bc825 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Mon, 6 Apr 2020 08:56:35 +0100 Subject: [PATCH] Add helper for running class_exec with keywords when needed --- .../support/with_keywords_when_needed.rb | 33 +++++++++++++ .../support/with_keywords_when_needed_spec.rb | 48 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 lib/rspec/support/with_keywords_when_needed.rb create mode 100644 spec/rspec/support/with_keywords_when_needed_spec.rb diff --git a/lib/rspec/support/with_keywords_when_needed.rb b/lib/rspec/support/with_keywords_when_needed.rb new file mode 100644 index 000000000..56b67e70b --- /dev/null +++ b/lib/rspec/support/with_keywords_when_needed.rb @@ -0,0 +1,33 @@ +RSpec::Support.require_rspec_support("method_signature_verifier") + +module RSpec + module Support + module WithKeywordsWhenNeeded + # This module adds keyword sensitive support for core ruby methods + # where we cannot use `ruby2_keywords` directly. + + module_function + + if RSpec::Support::RubyFeatures.kw_args_supported? + # Remove this in RSpec 4 in favour of explictly passed in kwargs where + # this is used. Works around a warning in Ruby 2.7 + + def class_exec(klass, *args, &block) + if MethodSignature.new(block).has_kw_args_in?(args) + binding.eval(<<-CODE, __FILE__, __LINE__) + kwargs = args.pop + klass.class_exec(*args, **kwargs, &block) + CODE + else + klass.class_exec(*args, &block) + end + end + ruby2_keywords :class_exec if respond_to?(:ruby2_keywords, true) + else + def class_exec(klass, *args, &block) + klass.class_exec(*args, &block) + end + end + end + end +end diff --git a/spec/rspec/support/with_keywords_when_needed_spec.rb b/spec/rspec/support/with_keywords_when_needed_spec.rb new file mode 100644 index 000000000..50a069fd4 --- /dev/null +++ b/spec/rspec/support/with_keywords_when_needed_spec.rb @@ -0,0 +1,48 @@ +require 'rspec/support/with_keywords_when_needed' + +module RSpec::Support + RSpec.describe "WithKeywordsWhenNeeded" do + + describe ".class_exec" do + extend RubyFeatures + + let(:klass) do + Class.new do + def self.check_argument(argument) + raise ArgumentError unless argument == 42 + end + end + end + + def run(klass, *args, &block) + WithKeywordsWhenNeeded.class_exec(klass, *args, &block) + end + + it "will run a block without keyword arguments" do + run(klass, 42) { |arg| check_argument(arg) } + end + + it "will run a block with a hash with out keyword arguments" do + run(klass, "value" => 42) { |arg| check_argument(arg["value"]) } + end + + it "will run a block with optional keyword arguments when none are provided", :if => kw_args_supported? do + binding.eval(<<-CODE, __FILE__, __LINE__) + run(klass, 42) { |arg, val: nil| check_argument(arg) } + CODE + end + + it "will run a block with optional keyword arguments when they are provided", :if => required_kw_args_supported? do + binding.eval(<<-CODE, __FILE__, __LINE__) + run(klass, val: 42) { |val: nil| check_argument(val) } + CODE + end + + it "will run a block with required keyword arguments", :if => required_kw_args_supported? do + binding.eval(<<-CODE, __FILE__, __LINE__) + run(klass, val: 42) { |val:| check_argument(val) } + CODE + end + end + end +end