From 684b58411467aa551f00f0963de4276b8105eabf Mon Sep 17 00:00:00 2001 From: Phil Pirozhkov Date: Wed, 30 Dec 2020 17:02:25 +0300 Subject: [PATCH] wip --- lib/rspec/core/example_group.rb | 136 ++++++++++++++----------- lib/rspec/core/metadata.rb | 21 ++-- lib/rspec/core/shared_example_group.rb | 4 +- 3 files changed, 89 insertions(+), 72 deletions(-) diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index 766395797f..4725d97048 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -41,6 +41,7 @@ def self.idempotently_define_singleton_method(name, &definition) (class << self; self; end).module_exec do remove_method(name) if method_defined?(name) && instance_method(name).owner == self define_method(name, &definition) + ruby2_keywords name if respond_to?(:ruby2_keywords, true) end end @@ -268,7 +269,7 @@ def self.define_example_group_method(name, metadata={}) combined_metadata.merge!(args.pop) if args.last.is_a? Hash args << combined_metadata - subclass(self, description, args, registration_collection, &example_group_block) + subclass(self, description, registration_collection, *args, &example_group_block) ensure thread_data.delete(:in_example_group) if top_level end @@ -340,8 +341,11 @@ def self.define_nested_shared_group_method(new_name, report_label="it should beh # context. # # @see SharedExampleGroup - def self.include_context(name, *args, &block) - find_and_eval_shared("context", name, caller.first, *args, &block) + class << self + def include_context(name, *args, &block) + find_and_eval_shared("context", name, caller.first, *args, &block) + end + ruby2_keywords :include_context if respond_to?(:ruby2_keywords, true) end # Includes shared content mapped to `name` directly in the group in which @@ -350,8 +354,11 @@ def self.include_context(name, *args, &block) # context. # # @see SharedExampleGroup - def self.include_examples(name, *args, &block) - find_and_eval_shared("examples", name, caller.first, *args, &block) + class << self + def include_examples(name, *args, &block) + find_and_eval_shared("examples", name, caller.first, *args, &block) + end + ruby2_keywords :include_examples if respond_to?(:ruby2_keywords, true) end # Clear memoized values when adding/removing examples @@ -376,74 +383,83 @@ def self.remove_example(example) end # @private - def self.find_and_eval_shared(label, name, inclusion_location, *args, &customization_block) - shared_module = RSpec.world.shared_example_group_registry.find(parent_groups, name) + class << self + def find_and_eval_shared(label, name, inclusion_location, *args, &customization_block) + shared_module = RSpec.world.shared_example_group_registry.find(parent_groups, name) - unless shared_module - raise ArgumentError, "Could not find shared #{label} #{name.inspect}" - end + unless shared_module + raise ArgumentError, "Could not find shared #{label} #{name.inspect}" + end - shared_module.include_in( - self, Metadata.relative_path(inclusion_location), - args, customization_block - ) + shared_module.include_in( + self, Metadata.relative_path(inclusion_location), + args, customization_block + ) + end + ruby2_keywords :find_and_eval_shared if respond_to?(:ruby2_keywords, true) end # @!endgroup # @private - def self.subclass(parent, description, args, registration_collection, &example_group_block) - subclass = Class.new(parent) - subclass.set_it_up(description, args, registration_collection, &example_group_block) - subclass.module_exec(&example_group_block) if example_group_block - - # The LetDefinitions module must be included _after_ other modules - # to ensure that it takes precedence when there are name collisions. - # Thus, we delay including it until after the example group block - # has been eval'd. - MemoizedHelpers.define_helpers_on(subclass) - - subclass - end - - # @private - def self.set_it_up(description, args, registration_collection, &example_group_block) - # Ruby 1.9 has a bug that can lead to infinite recursion and a - # SystemStackError if you include a module in a superclass after - # including it in a subclass: https://gist.github.com/845896 - # To prevent this, we must include any modules in - # RSpec::Core::ExampleGroup before users create example groups and have - # a chance to include the same module in a subclass of - # RSpec::Core::ExampleGroup. So we need to configure example groups - # here. - ensure_example_groups_are_configured - - # Register the example with the group before creating the metadata hash. - # This is necessary since creating the metadata hash triggers - # `when_first_matching_example_defined` callbacks, in which users can - # load RSpec support code which defines hooks. For that to work, the - # examples and example groups must be registered at the time the - # support code is called or be defined afterwards. - # Begin defined beforehand but registered afterwards causes hooks to - # not be applied where they should. - registration_collection << self + class << self + def subclass(parent, description, registration_collection, *args, &example_group_block) + subclass = Class.new(parent) + subclass.set_it_up(description, args, registration_collection, &example_group_block) + subclass.module_exec(&example_group_block) if example_group_block - @user_metadata = Metadata.build_hash_from(args) + # The LetDefinitions module must be included _after_ other modules + # to ensure that it takes precedence when there are name collisions. + # Thus, we delay including it until after the example group block + # has been eval'd. + MemoizedHelpers.define_helpers_on(subclass) - @metadata = Metadata::ExampleGroupHash.create( - superclass_metadata, @user_metadata, - superclass.method(:next_runnable_index_for), - description, *args, &example_group_block - ) + subclass + end + ruby2_keywords :subclass if respond_to?(:ruby2_keywords, true) + end + + # @private + class << self + def set_it_up(description, args, registration_collection, &example_group_block) + # Ruby 1.9 has a bug that can lead to infinite recursion and a + # SystemStackError if you include a module in a superclass after + # including it in a subclass: https://gist.github.com/845896 + # To prevent this, we must include any modules in + # RSpec::Core::ExampleGroup before users create example groups and have + # a chance to include the same module in a subclass of + # RSpec::Core::ExampleGroup. So we need to configure example groups + # here. + ensure_example_groups_are_configured + + # Register the example with the group before creating the metadata hash. + # This is necessary since creating the metadata hash triggers + # `when_first_matching_example_defined` callbacks, in which users can + # load RSpec support code which defines hooks. For that to work, the + # examples and example groups must be registered at the time the + # support code is called or be defined afterwards. + # Begin defined beforehand but registered afterwards causes hooks to + # not be applied where they should. + registration_collection << self + + @user_metadata = Metadata.build_hash_from(args) + + @metadata = Metadata::ExampleGroupHash.create( + superclass_metadata, @user_metadata, + superclass.method(:next_runnable_index_for), + description, *args, &example_group_block + ) - config = RSpec.configuration - config.apply_derived_metadata_to(@metadata) + config = RSpec.configuration + config.apply_derived_metadata_to(@metadata) - ExampleGroups.assign_const(self) + ExampleGroups.assign_const(self) - @currently_executing_a_context_hook = false + @currently_executing_a_context_hook = false - config.configure_group(self) + config.configure_group(self) + end + ruby2_keywords :set_it_up if respond_to?(:ruby2_keywords, true) end # @private diff --git a/lib/rspec/core/metadata.rb b/lib/rspec/core/metadata.rb index 5e0d7e2bdb..5892f9ccc9 100644 --- a/lib/rspec/core/metadata.rb +++ b/lib/rspec/core/metadata.rb @@ -245,17 +245,20 @@ def full_description # @private class ExampleGroupHash < HashPopulator - def self.create(parent_group_metadata, user_metadata, example_group_index, *args, &block) - group_metadata = hash_with_backwards_compatibility_default_proc + class << self + def create(parent_group_metadata, user_metadata, example_group_index, *args, &block) + group_metadata = hash_with_backwards_compatibility_default_proc - if parent_group_metadata - group_metadata.update(parent_group_metadata) - group_metadata[:parent_example_group] = parent_group_metadata - end + if parent_group_metadata + group_metadata.update(parent_group_metadata) + group_metadata[:parent_example_group] = parent_group_metadata + end - hash = new(group_metadata, user_metadata, example_group_index, args, block) - hash.populate - hash.metadata + hash = new(group_metadata, user_metadata, example_group_index, args, block) + hash.populate + hash.metadata + end + ruby2_keywords :create if respond_to?(:ruby2_keywords, true) end def self.hash_with_backwards_compatibility_default_proc diff --git a/lib/rspec/core/shared_example_group.rb b/lib/rspec/core/shared_example_group.rb index 1bccff73be..938b7f07d0 100644 --- a/lib/rspec/core/shared_example_group.rb +++ b/lib/rspec/core/shared_example_group.rb @@ -1,5 +1,3 @@ -RSpec::Support.require_rspec_support "with_keywords_when_needed" - module RSpec module Core # Represents some functionality that is shared with multiple example groups. @@ -35,7 +33,7 @@ def include_in(klass, inclusion_line, args, customization_block) klass.update_inherited_metadata(@metadata) unless @metadata.empty? SharedExampleGroupInclusionStackFrame.with_frame(@description, inclusion_line) do - RSpec::Support::WithKeywordsWhenNeeded.class_exec(klass, *args, &@definition) + klass.class_exec(*args, &@definition) klass.class_exec(&customization_block) if customization_block end end