module RSpec
  module Core
    # Provides `before`, `after` and `around` hooks as a means of
    # supporting common setup and teardown. This module is extended
    # onto {ExampleGroup}, making the methods available from any `describe`
    # or `context` block and included in {Configuration}, making them
    # available off of the configuration object to define global setup
    # or teardown logic.
    module Hooks
      # @api public
      #
      # @overload before(&block)
      # @overload before(scope, &block)
      #   @param scope [Symbol] `:example`, `:context`, or `:suite`
      #     (defaults to `:example`)
      # @overload before(scope, *conditions, &block)
      #   @param scope [Symbol] `:example`, `:context`, or `:suite`
      #     (defaults to `:example`)
      #   @param conditions [Array<Symbol>, Hash] constrains this hook to
      #     examples matching these conditions e.g.
      #     `before(:example, :ui => true) { ... }` will only run with examples
      #     or groups declared with `:ui => true`. Symbols will be transformed
      #     into hash entries with `true` values.
      # @overload before(conditions, &block)
      #   @param conditions [Hash]
      #     constrains this hook to examples matching these conditions e.g.
      #     `before(:example, :ui => true) { ... }` will only run with examples
      #     or groups declared with `:ui => true`.
      #
      # @see #after
      # @see #around
      # @see ExampleGroup
      # @see SharedContext
      # @see SharedExampleGroup
      # @see Configuration
      #
      # Declare a block of code to be run before each example (using `:example`)
      # or once before any example (using `:context`). These are usually
      # declared directly in the {ExampleGroup} to which they apply, but they
      # can also be shared across multiple groups.
      #
      # You can also use `before(:suite)` to run a block of code before any
      # example groups are run. This should be declared in {RSpec.configure}.
      #
      # Instance variables declared in `before(:example)` or `before(:context)`
      # are accessible within each example.
      #
      # ### Order
      #
      # `before` hooks are stored in three scopes, which are run in order:
      # `:suite`, `:context`, and `:example`. They can also be declared in
      # several different places: `RSpec.configure`, a parent group, the current
      # group. They are run in the following order:
      #
      #     before(:suite)    # Declared in RSpec.configure.
      #     before(:context)  # Declared in RSpec.configure.
      #     before(:context)  # Declared in a parent group.
      #     before(:context)  # Declared in the current group.
      #     before(:example)  # Declared in RSpec.configure.
      #     before(:example)  # Declared in a parent group.
      #     before(:example)  # Declared in the current group.
      #
      # If more than one `before` is declared within any one example group, they
      # are run in the order in which they are declared. Any `around` hooks will
      # execute after `before` context hooks but before any `before` example
      # hook regardless of where they are declared.
      #
      # ### Conditions
      #
      # When you add a conditions hash to `before(:example)` or
      # `before(:context)`, RSpec will only apply that hook to groups or
      # examples that match the conditions. e.g.
      #
      #     RSpec.configure do |config|
      #       config.before(:example, :authorized => true) do
      #         log_in_as :authorized_user
      #       end
      #     end
      #
      #     RSpec.describe Something, :authorized => true do
      #       # The before hook will run in before each example in this group.
      #     end
      #
      #     RSpec.describe SomethingElse do
      #       it "does something", :authorized => true do
      #         # The before hook will run before this example.
      #       end
      #
      #       it "does something else" do
      #         # The hook will not run before this example.
      #       end
      #     end
      #
      # Note that filtered config `:context` hooks can still be applied
      # to individual examples that have matching metadata. Just like
      # Ruby's object model is that every object has a singleton class
      # which has only a single instance, RSpec's model is that every
      # example has a singleton example group containing just the one
      # example.
      #
      # ### Warning: `before(:suite, :with => :conditions)`
      #
      # The conditions hash is used to match against specific examples. Since
      # `before(:suite)` is not run in relation to any specific example or
      # group, conditions passed along with `:suite` are effectively ignored.
      #
      # ### Exceptions
      #
      # When an exception is raised in a `before` block, RSpec skips any
      # subsequent `before` blocks and the example, but runs all of the
      # `after(:example)` and `after(:context)` hooks.
      #
      # ### Warning: implicit before blocks
      #
      # `before` hooks can also be declared in shared contexts which get
      # included implicitly either by you or by extension libraries. Since
      # RSpec runs these in the order in which they are declared within each
      # scope, load order matters, and can lead to confusing results when one
      # before block depends on state that is prepared in another before block
      # that gets run later.
      #
      # ### Warning: `before(:context)`
      #
      # It is very tempting to use `before(:context)` to speed things up, but we
      # recommend that you avoid this as there are a number of gotchas, as well
      # as things that simply don't work.
      #
      # #### Context
      #
      # `before(:context)` is run in an example that is generated to provide
      # group context for the block.
      #
      # #### Instance variables
      #
      # Instance variables declared in `before(:context)` are shared across all
      # the examples in the group. This means that each example can change the
      # state of a shared object, resulting in an ordering dependency that can
      # make it difficult to reason about failures.
      #
      # #### Unsupported RSpec constructs
      #
      # RSpec has several constructs that reset state between each example
      # automatically. These are not intended for use from within
      # `before(:context)`:
      #
      #   * `let` declarations
      #   * `subject` declarations
      #   * Any mocking, stubbing or test double declaration
      #
      # ### other frameworks
      #
      # Mock object frameworks and database transaction managers (like
      # ActiveRecord) are typically designed around the idea of setting up
      # before an example, running that one example, and then tearing down. This
      # means that mocks and stubs can (sometimes) be declared in
      # `before(:context)`, but get torn down before the first real example is
      # ever run.
      #
      # You _can_ create database-backed model objects in a `before(:context)`
      # in rspec-rails, but it will not be wrapped in a transaction for you, so
      # you are on your own to clean up in an `after(:context)` block.
      #
      # @example before(:example) declared in an {ExampleGroup}
      #
      #     RSpec.describe Thing do
      #       before(:example) do
      #         @thing = Thing.new
      #       end
      #
      #       it "does something" do
      #         # Here you can access @thing.
      #       end
      #     end
      #
      # @example before(:context) declared in an {ExampleGroup}
      #
      #     RSpec.describe Parser do
      #       before(:context) do
      #         File.open(file_to_parse, 'w') do |f|
      #           f.write <<-CONTENT
      #             stuff in the file
      #           CONTENT
      #         end
      #       end
      #
      #       it "parses the file" do
      #         Parser.parse(file_to_parse)
      #       end
      #
      #       after(:context) do
      #         File.delete(file_to_parse)
      #       end
      #     end
      #
      # @note The `:example` and `:context` scopes are also available as
      #       `:each` and `:all`, respectively. Use whichever you prefer.
      # @note The `:suite` scope is only supported for hooks registered on
      #       `RSpec.configuration` since they exist independently of any
      #       example or example group.
      def before(*args, &block)
        hooks.register :append, :before, *args, &block
      end

      alias_method :append_before, :before

      # Adds `block` to the front of the list of `before` blocks in the same
      # scope (`:example`, `:context`, or `:suite`).
      #
      # See {#before} for scoping semantics.
      def prepend_before(*args, &block)
        hooks.register :prepend, :before, *args, &block
      end

      # @api public
      # @overload after(&block)
      # @overload after(scope, &block)
      #   @param scope [Symbol] `:example`, `:context`, or `:suite` (defaults to
      #     `:example`)
      # @overload after(scope, *conditions, &block)
      #   @param scope [Symbol] `:example`, `:context`, or `:suite` (defaults to
      #     `:example`)
      #   @param conditions [Array<Symbol>, Hash] constrains this hook to
      #     examples matching these conditions e.g.
      #     `after(:example, :ui => true) { ... }` will only run with examples
      #     or groups declared with `:ui => true`. Symbols will be transformed
      #     into hash entries with `true` values.
      # @overload after(conditions, &block)
      #   @param conditions [Hash]
      #     constrains this hook to examples matching these conditions e.g.
      #     `after(:example, :ui => true) { ... }` will only run with examples
      #     or groups declared with `:ui => true`.
      #
      # @see #before
      # @see #around
      # @see ExampleGroup
      # @see SharedContext
      # @see SharedExampleGroup
      # @see Configuration
      #
      # Declare a block of code to be run after each example (using `:example`)
      # or once after all examples n the context (using `:context`). See
      # {#before} for more information about ordering.
      #
      # ### Exceptions
      #
      # `after` hooks are guaranteed to run even when there are exceptions in
      # `before` hooks or examples. When an exception is raised in an after
      # block, the exception is captured for later reporting, and subsequent
      # `after` blocks are run.
      #
      # ### Order
      #
      # `after` hooks are stored in three scopes, which are run in order:
      # `:example`, `:context`, and `:suite`. They can also be declared in
      # several different places: `RSpec.configure`, a parent group, the current
      # group. They are run in the following order:
      #
      #     after(:example) # Declared in the current group.
      #     after(:example) # Declared in a parent group.
      #     after(:example) # Declared in RSpec.configure.
      #     after(:context) # Declared in the current group.
      #     after(:context) # Declared in a parent group.
      #     after(:context) # Declared in RSpec.configure.
      #     after(:suite)   # Declared in RSpec.configure.
      #
      # This is the reverse of the order in which `before` hooks are run.
      # Similarly, if more than one `after` is declared within any example
      # group, they are run in reverse order of that in which they are declared.
      # Also `around` hooks will run after any `after` example hooks are
      # invoked but before any `after` context hooks.
      #
      # @note The `:example` and `:context` scopes are also available as
      #       `:each` and `:all`, respectively. Use whichever you prefer.
      # @note The `:suite` scope is only supported for hooks registered on
      #       `RSpec.configuration` since they exist independently of any
      #       example or example group.
      def after(*args, &block)
        hooks.register :prepend, :after, *args, &block
      end

      alias_method :prepend_after, :after

      # Adds `block` to the back of the list of `after` blocks in the same
      # scope (`:example`, `:context`, or `:suite`).
      #
      # See {#after} for scoping semantics.
      def append_after(*args, &block)
        hooks.register :append, :after, *args, &block
      end

      # @api public
      # @overload around(&block)
      # @overload around(scope, &block)
      #   @param scope [Symbol] `:example` (defaults to `:example`)
      #     present for syntax parity with `before` and `after`, but
      #     `:example`/`:each` is the only supported value.
      # @overload around(scope, *conditions, &block)
      #   @param scope [Symbol] `:example` (defaults to `:example`)
      #     present for syntax parity with `before` and `after`, but
      #     `:example`/`:each` is the only supported value.
      #   @param conditions [Array<Symbol>, Hash] constrains this hook to
      #     examples matching these conditions e.g.
      #     `around(:example, :ui => true) { ... }` will only run with examples
      #     or groups declared with `:ui => true`. Symbols will be transformed
      #     into hash entries with `true` values.
      # @overload around(conditions, &block)
      #   @param conditions [Hash] constrains this hook to examples matching
      #     these conditions e.g. `around(:example, :ui => true) { ... }` will
      #     only run with examples or groups declared with `:ui => true`.
      #
      # @yield [Example] the example to run
      #
      # @note the syntax of `around` is similar to that of `before` and `after`
      #   but the semantics are quite different. `before` and `after` hooks are
      #   run in the context of the examples with which they are associated,
      #   whereas `around` hooks are actually responsible for running the
      #   examples. Consequently, `around` hooks do not have direct access to
      #   resources that are made available within the examples and their
      #   associated `before` and `after` hooks.
      #
      # @note `:example`/`:each` is the only supported scope.
      #
      # Declare a block of code, parts of which will be run before and parts
      # after the example. It is your responsibility to run the example:
      #
      #     around(:example) do |ex|
      #       # Do some stuff before.
      #       ex.run
      #       # Do some stuff after.
      #     end
      #
      # The yielded example aliases `run` with `call`, which lets you treat it
      # like a `Proc`. This is especially handy when working with libraries
      # that manage their own setup and teardown using a block or proc syntax,
      # e.g.
      #
      #     around(:example) {|ex| Database.transaction(&ex)}
      #     around(:example) {|ex| FakeFS(&ex)}
      #
      # ### Order
      #
      # The `around` hooks execute surrounding an example and its hooks.
      #
      # This means after any `before` context hooks, but before any `before`
      # example hooks, and similarly after any `after` example hooks but before
      # any `after` context hooks.
      #
      # They are not a synonym for `before`/`after`.
      def around(*args, &block)
        hooks.register :prepend, :around, *args, &block
      end

      # @private
      # Holds the various registered hooks.
      def hooks
        @hooks ||= HookCollections.new(self, FilterableItemRepository::UpdateOptimized)
      end

      # @private
      Hook = Struct.new(:block, :options)

      # @private
      class BeforeHook < Hook
        def run(example)
          example.instance_exec(example, &block)
        end
      end

      # @private
      class AfterHook < Hook
        def run(example)
          example.instance_exec(example, &block)
        rescue Support::AllExceptionsExceptOnesWeMustNotRescue => ex
          example.set_exception(ex)
        end
      end

      # @private
      class AfterContextHook < Hook
        def run(example)
          example.instance_exec(example, &block)
        rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e
          RSpec.configuration.reporter.notify_non_example_exception(e, "An error occurred in an `after(:context)` hook.")
        end
      end

      # @private
      class AroundHook < Hook
        def execute_with(example, procsy)
          example.instance_exec(procsy, &block)
          return if procsy.executed?
          Pending.mark_skipped!(example,
                                "#{hook_description} did not execute the example")
        end

        if Proc.method_defined?(:source_location)
          def hook_description
            "around hook at #{Metadata.relative_path(block.source_location.join(':'))}"
          end
        else # for 1.8.7
          # :nocov:
          def hook_description
            "around hook"
          end
          # :nocov:
        end
      end

      # @private
      #
      # This provides the primary API used by other parts of rspec-core. By hiding all
      # implementation details behind this facade, it's allowed us to heavily optimize
      # this, so that, for example, hook collection objects are only instantiated when
      # a hook is added. This allows us to avoid many object allocations for the common
      # case of a group having no hooks.
      #
      # This is only possible because this interface provides a "tell, don't ask"-style
      # API, so that callers _tell_ this class what to do with the hooks, rather than
      # asking this class for a list of hooks, and then doing something with them.
      class HookCollections
        def initialize(owner, filterable_item_repo_class)
          @owner                      = owner
          @filterable_item_repo_class = filterable_item_repo_class
          @before_example_hooks       = nil
          @after_example_hooks        = nil
          @before_context_hooks       = nil
          @after_context_hooks        = nil
          @around_example_hooks       = nil
        end

        def register_globals(host, globals)
          parent_groups = host.parent_groups

          process(host, parent_groups, globals, :before, :example, &:options)
          process(host, parent_groups, globals, :after,  :example, &:options)
          process(host, parent_groups, globals, :around, :example, &:options)

          process(host, parent_groups, globals, :before, :context, &:options)
          process(host, parent_groups, globals, :after,  :context, &:options)
        end

        def register_global_singleton_context_hooks(example, globals)
          parent_groups = example.example_group.parent_groups

          process(example, parent_groups, globals, :before, :context) { {} }
          process(example, parent_groups, globals, :after,  :context) { {} }
        end

        def register(prepend_or_append, position, *args, &block)
          scope, options = scope_and_options_from(*args)

          if scope == :suite
            # TODO: consider making this an error in RSpec 4. For SemVer reasons,
            # we are only warning in RSpec 3.
            RSpec.warn_with "WARNING: `#{position}(:suite)` hooks are only supported on " \
                            "the RSpec configuration object. This " \
                            "`#{position}(:suite)` hook, registered on an example " \
                            "group, will be ignored."
            return
          elsif scope == :context && position == :around
            # TODO: consider making this an error in RSpec 4. For SemVer reasons,
            # we are only warning in RSpec 3.
            RSpec.warn_with "WARNING: `around(:context)` hooks are not supported and " \
                            "behave like `around(:example)`."
          end

          hook = HOOK_TYPES[position][scope].new(block, options)
          ensure_hooks_initialized_for(position, scope).__send__(prepend_or_append, hook, options)
        end

        # @private
        #
        # Runs all of the blocks stored with the hook in the context of the
        # example. If no example is provided, just calls the hook directly.
        def run(position, scope, example_or_group)
          return if RSpec.configuration.dry_run?

          if scope == :context
            unless example_or_group.class.metadata[:skip]
              run_owned_hooks_for(position, :context, example_or_group)
            end
          else
            case position
            when :before then run_example_hooks_for(example_or_group, :before, :reverse_each)
            when :after  then run_example_hooks_for(example_or_group, :after,  :each)
            when :around then run_around_example_hooks_for(example_or_group) { yield }
            end
          end
        end

        SCOPES = [:example, :context]

        SCOPE_ALIASES = { :each => :example, :all => :context }

        HOOK_TYPES = {
          :before => Hash.new { BeforeHook },
          :after  => Hash.new { AfterHook  },
          :around => Hash.new { AroundHook }
        }

        HOOK_TYPES[:after][:context] = AfterContextHook

      protected

        EMPTY_HOOK_ARRAY = [].freeze

        def matching_hooks_for(position, scope, example_or_group)
          repository = hooks_for(position, scope) { return EMPTY_HOOK_ARRAY }

          # It would be nice to not have to switch on type here, but
          # we don't want to define `ExampleGroup#metadata` because then
          # `metadata` from within an individual example would return the
          # group's metadata but the user would probably expect it to be
          # the example's metadata.
          metadata = case example_or_group
                     when ExampleGroup then example_or_group.class.metadata
                     else example_or_group.metadata
                     end

          repository.items_for(metadata)
        end

        def all_hooks_for(position, scope)
          hooks_for(position, scope) { return EMPTY_HOOK_ARRAY }.items_and_filters.map(&:first)
        end

        def run_owned_hooks_for(position, scope, example_or_group)
          matching_hooks_for(position, scope, example_or_group).each do |hook|
            hook.run(example_or_group)
          end
        end

        def processable_hooks_for(position, scope, host)
          if scope == :example
            all_hooks_for(position, scope)
          else
            matching_hooks_for(position, scope, host)
          end
        end

      private

        def hooks_for(position, scope)
          if position == :before
            scope == :example ? @before_example_hooks : @before_context_hooks
          elsif position == :after
            scope == :example ? @after_example_hooks : @after_context_hooks
          else # around
            @around_example_hooks
          end || yield
        end

        def ensure_hooks_initialized_for(position, scope)
          if position == :before
            if scope == :example
              @before_example_hooks ||= @filterable_item_repo_class.new(:all?)
            else
              @before_context_hooks ||= @filterable_item_repo_class.new(:all?)
            end
          elsif position == :after
            if scope == :example
              @after_example_hooks ||= @filterable_item_repo_class.new(:all?)
            else
              @after_context_hooks ||= @filterable_item_repo_class.new(:all?)
            end
          else # around
            @around_example_hooks ||= @filterable_item_repo_class.new(:all?)
          end
        end

        def process(host, parent_groups, globals, position, scope)
          hooks_to_process = globals.processable_hooks_for(position, scope, host)
          return if hooks_to_process.empty?

          hooks_to_process -= FlatMap.flat_map(parent_groups) do |group|
            group.hooks.all_hooks_for(position, scope)
          end
          return if hooks_to_process.empty?

          repository = ensure_hooks_initialized_for(position, scope)
          hooks_to_process.each { |hook| repository.append hook, (yield hook) }
        end

        def scope_and_options_from(*args)
          return :suite if args.first == :suite
          scope = extract_scope_from(args)
          meta  = Metadata.build_hash_from(args, :warn_about_example_group_filtering)
          return scope, meta
        end

        def extract_scope_from(args)
          if known_scope?(args.first)
            normalized_scope_for(args.shift)
          elsif args.any? { |a| a.is_a?(Symbol) }
            error_message = "You must explicitly give a scope " \
              "(#{SCOPES.join(", ")}) or scope alias " \
              "(#{SCOPE_ALIASES.keys.join(", ")}) when using symbols as " \
              "metadata for a hook."
            raise ArgumentError.new error_message
          else
            :example
          end
        end

        def known_scope?(scope)
          SCOPES.include?(scope) || SCOPE_ALIASES.keys.include?(scope)
        end

        def normalized_scope_for(scope)
          SCOPE_ALIASES[scope] || scope
        end

        def run_example_hooks_for(example, position, each_method)
          owner_parent_groups.__send__(each_method) do |group|
            group.hooks.run_owned_hooks_for(position, :example, example)
          end
        end

        def run_around_example_hooks_for(example)
          hooks = FlatMap.flat_map(owner_parent_groups) do |group|
            group.hooks.matching_hooks_for(:around, :example, example)
          end

          return yield if hooks.empty? # exit early to avoid the extra allocation cost of `Example::Procsy`

          initial_procsy = Example::Procsy.new(example) { yield }
          hooks.inject(initial_procsy) do |procsy, around_hook|
            procsy.wrap { around_hook.execute_with(example, procsy) }
          end.call
        end

        if respond_to?(:singleton_class) && singleton_class.ancestors.include?(singleton_class)
          def owner_parent_groups
            @owner.parent_groups
          end
        else # Ruby < 2.1 (see https://bugs.ruby-lang.org/issues/8035)
          # :nocov:
          def owner_parent_groups
            @owner_parent_groups ||= [@owner] + @owner.parent_groups
          end
          # :nocov:
        end
      end
    end
  end
end


def ensure_hooks_initialized_for(position, scope)
          if position == :before
            if scope == :example
              @before_example_hooks ||= @filterable_item_repo_class.new(:all?)
            else
              @before_context_hooks ||= @filterable_item_repo_class.new(:all?)
            end
          elsif position == :after
            if scope == :example
              @after_example_hooks ||= @filterable_item_repo_class.new(:all?)
            else
              @after_context_hooks ||= @filterable_item_repo_class.new(:all?)
            end
          else # around
            @around_example_hooks ||= @filterable_item_repo_class.new(:all?)
          end
        end


def process(host, parent_groups, globals, position, scope)
          hooks_to_process = globals.processable_hooks_for(position, scope, host)
          return if hooks_to_process.empty?

          hooks_to_process -= FlatMap.flat_map(parent_groups) do |group|
            group.hooks.all_hooks_for(position, scope)
          end
          return if hooks_to_process.empty?

          repository = ensure_hooks_initialized_for(position, scope)
          hooks_to_process.each { |hook| repository.append hook, (yield hook) }
        end


def extract_scope_from(args)
          if known_scope?(args.first)
            normalized_scope_for(args.shift)
          elsif args.any? { |a| a.is_a?(Symbol) }
            error_message = "You must explicitly give a scope " \
              "(#{SCOPES.join(", ")}) or scope alias " \
              "(#{SCOPE_ALIASES.keys.join(", ")}) when using symbols as " \
              "metadata for a hook."
            raise ArgumentError.new error_message
          else
            :example
          end
        end


module RSpec
  module Core
    # @private
    module Invocations
      # @private
      class InitializeProject
        def call(*_args)
          RSpec::Support.require_rspec_core "project_initializer"
          ProjectInitializer.new.run
          0
        end
      end

      # @private
      class DRbWithFallback
        def call(options, err, out)
          require 'rspec/core/drb'
          begin
            return DRbRunner.new(options).run(err, out)
          rescue DRb::DRbConnError
            err.puts "No DRb server is running. Running in local process instead ..."
          end
          RSpec::Core::Runner.new(options).run(err, out)
        end
      end

      # @private
      class Bisect
        def call(options, err, out)
          RSpec::Support.require_rspec_core "bisect/coordinator"
          runner = Runner.new(options).tap { |r| r.configure(err, out) }
          formatter = bisect_formatter_klass_for(options.options[:bisect]).new(
            out, runner.configuration.bisect_runner
          )

          success = RSpec::Core::Bisect::Coordinator.bisect_with(
            runner, options.args, formatter
          )

          runner.exit_code(success)
        end

      private

        def bisect_formatter_klass_for(argument)
          return Formatters::BisectDebugFormatter if argument == "verbose"
          Formatters::BisectProgressFormatter
        end
      end

      # @private
      class PrintVersion
        def call(_options, _err, out)
          overall_version = RSpec::Core::Version::STRING
          unless overall_version =~ /[a-zA-Z]+/
            overall_version = overall_version.split('.').first(2).join('.')
          end

          out.puts "RSpec #{overall_version}"

          [:Core, :Expectations, :Mocks, :Rails, :Support].each do |const_name|
            lib_name = const_name.to_s.downcase
            begin
              require "rspec/#{lib_name}/version"
            rescue LoadError
              # Not worth mentioning libs that are not installed
              nil
            else
              out.puts "  - rspec-#{lib_name} #{RSpec.const_get(const_name)::Version::STRING}"
            end
          end

          0
        end
      end

      # @private
      PrintHelp = Struct.new(:parser, :hidden_options) do
        def call(_options, _err, out)
          # Removing the hidden options from the output.
          out.puts parser.to_s.gsub(/^\s+(#{hidden_options.join('|')})\b.*$\n/, '')
          0
        end
      end
    end
  end
end


def call(_options, _err, out)
          overall_version = RSpec::Core::Version::STRING
          unless overall_version =~ /[a-zA-Z]+/
            overall_version = overall_version.split('.').first(2).join('.')
          end

          out.puts "RSpec #{overall_version}"

          [:Core, :Expectations, :Mocks, :Rails, :Support].each do |const_name|
            lib_name = const_name.to_s.downcase
            begin
              require "rspec/#{lib_name}/version"
            rescue LoadError
              # Not worth mentioning libs that are not installed
              nil
            else
              out.puts "  - rspec-#{lib_name} #{RSpec.const_get(const_name)::Version::STRING}"
            end
          end

          0
        end


RSpec::Support.require_rspec_support 'reentrant_mutex'

module RSpec
  module Core
    # This module is included in {ExampleGroup}, making the methods
    # available to be called from within example blocks.
    #
    # @see ClassMethods
    module MemoizedHelpers
      # @note `subject` was contributed by Joe Ferris to support the one-liner
      #   syntax embraced by shoulda matchers:
      #
      #       RSpec.describe Widget do
      #         it { is_expected.to validate_presence_of(:name) }
      #         # or
      #         it { should validate_presence_of(:name) }
      #       end
      #
      #   While the examples below demonstrate how to use `subject`
      #   explicitly in examples, we recommend that you define a method with
      #   an intention revealing name instead.
      #
      # @example
      #
      #   # Explicit declaration of subject.
      #   RSpec.describe Person do
      #     subject { Person.new(:birthdate => 19.years.ago) }
      #     it "should be eligible to vote" do
      #       subject.should be_eligible_to_vote
      #       # ^ ^ explicit reference to subject not recommended
      #     end
      #   end
      #
      #   # Implicit subject => { Person.new }.
      #   RSpec.describe Person do
      #     it "should be eligible to vote" do
      #       subject.should be_eligible_to_vote
      #       # ^ ^ explicit reference to subject not recommended
      #     end
      #   end
      #
      #   # One-liner syntax - expectation is set on the subject.
      #   RSpec.describe Person do
      #     it { is_expected.to be_eligible_to_vote }
      #     # or
      #     it { should be_eligible_to_vote }
      #   end
      #
      # @note Because `subject` is designed to create state that is reset
      #   between each example, and `before(:context)` is designed to setup
      #   state that is shared across _all_ examples in an example group,
      #   `subject` is _not_ intended to be used in a `before(:context)` hook.
      #
      # @see #should
      # @see #should_not
      # @see #is_expected
      def subject
        __memoized.fetch_or_store(:subject) do
          described = described_class || self.class.metadata.fetch(:description_args).first
          Class === described ? described.new : described
        end
      end

      # When `should` is called with no explicit receiver, the call is
      # delegated to the object returned by `subject`. Combined with an
      # implicit subject this supports very concise expressions.
      #
      # @example
      #
      #   RSpec.describe Person do
      #     it { should be_eligible_to_vote }
      #   end
      #
      # @see #subject
      # @see #is_expected
      #
      # @note This only works if you are using rspec-expectations.
      # @note If you are using RSpec's newer expect-based syntax you may
      #       want to use `is_expected.to` instead of `should`.
      def should(matcher=nil, message=nil)
        enforce_value_expectation(matcher, 'should')
        RSpec::Expectations::PositiveExpectationHandler.handle_matcher(subject, matcher, message)
      end

      # Just like `should`, `should_not` delegates to the subject (implicit or
      # explicit) of the example group.
      #
      # @example
      #
      #   RSpec.describe Person do
      #     it { should_not be_eligible_to_vote }
      #   end
      #
      # @see #subject
      # @see #is_expected
      #
      # @note This only works if you are using rspec-expectations.
      # @note If you are using RSpec's newer expect-based syntax you may
      #       want to use `is_expected.to_not` instead of `should_not`.
      def should_not(matcher=nil, message=nil)
        enforce_value_expectation(matcher, 'should_not')
        RSpec::Expectations::NegativeExpectationHandler.handle_matcher(subject, matcher, message)
      end

      # Wraps the `subject` in `expect` to make it the target of an expectation.
      # Designed to read nicely for one-liners.
      #
      # @example
      #
      #   describe [1, 2, 3] do
      #     it { is_expected.to be_an Array }
      #     it { is_expected.not_to include 4 }
      #   end
      #
      # @see #subject
      # @see #should
      # @see #should_not
      #
      # @note This only works if you are using rspec-expectations.
      def is_expected
        expect(subject)
      end

      # @private
      # should just be placed in private section,
      # but Ruby issues warnings on private attributes.
      # and expanding it to the equivalent method upsets Rubocop,
      # b/c it should obviously be a reader
      attr_reader :__memoized
      private :__memoized

    private

      # @private
      def initialize(*)
        __init_memoized
        super
      end

      # @private
      def __init_memoized
        @__memoized = if RSpec.configuration.threadsafe?
                        ThreadsafeMemoized.new
                      else
                        NonThreadSafeMemoized.new
                      end
      end

      # @private
      def enforce_value_expectation(matcher, method_name)
        return if matcher_supports_value_expectations?(matcher)

        RSpec.deprecate(
          "#{method_name} #{RSpec::Support::ObjectFormatter.format(matcher)}",
          :message =>
            "The implicit block expectation syntax is deprecated, you should pass " \
            "a block to `expect` to use the provided block expectation matcher " \
            "(#{RSpec::Support::ObjectFormatter.format(matcher)}), " \
            "or the matcher must implement `supports_value_expectations?`."
        )
      end

      def matcher_supports_value_expectations?(matcher)
        matcher.supports_value_expectations?
      rescue
        true
      end

      # @private
      class ThreadsafeMemoized
        def initialize
          @memoized = {}
          @mutex = Support::ReentrantMutex.new
        end

        def fetch_or_store(key)
          @memoized.fetch(key) do # only first access pays for synchronization
            @mutex.synchronize do
              @memoized.fetch(key) { @memoized[key] = yield }
            end
          end
        end
      end

      # @private
      class NonThreadSafeMemoized
        def initialize
          @memoized = {}
        end

        def fetch_or_store(key)
          @memoized.fetch(key) { @memoized[key] = yield }
        end
      end

      # Used internally to customize the behavior of the
      # memoized hash when used in a `before(:context)` hook.
      #
      # @private
      class ContextHookMemoized
        def self.isolate_for_context_hook(example_group_instance)
          exploding_memoized = self

          example_group_instance.instance_exec do
            @__memoized = exploding_memoized

            begin
              yield
            ensure
              # This is doing a reset instead of just isolating for context hook.
              # Really, this should set the old @__memoized back into place.
              #
              # Caller is the before and after context hooks
              # which are both called from self.run
              # I didn't look at why it made tests fail, maybe an object was getting reused in RSpec tests,
              # if so, then that probably already works, and its the tests that are wrong.
              __init_memoized
            end
          end
        end

        def self.fetch_or_store(key, &_block)
          description = if key == :subject
                          "subject"
                        else
                          "let declaration `#{key}`"
                        end

          raise <<-EOS
#{description} accessed in #{article} #{hook_expression} hook at:
  #{CallerFilter.first_non_rspec_line}

`let` and `subject` declarations are not intended to be called
in #{article} #{hook_expression} hook, as they exist to define state that
is reset between each example, while #{hook_expression} exists to
#{hook_intention}.
EOS
        end

        # @private
        class Before < self
          def self.hook_expression
            "`before(:context)`"
          end

          def self.article
            "a"
          end

          def self.hook_intention
            "define state that is shared across examples in an example group"
          end
        end

        # @private
        class After < self
          def self.hook_expression
            "`after(:context)`"
          end

          def self.article
            "an"
          end

          def self.hook_intention
            "cleanup state that is shared across examples in an example group"
          end
        end
      end

      # This module is extended onto {ExampleGroup}, making the methods
      # available to be called from within example group blocks.
      # You can think of them as being analagous to class macros.
      module ClassMethods
        # Generates a method whose return value is memoized after the first
        # call. Useful for reducing duplication between examples that assign
        # values to the same local variable.
        #
        # @note `let` _can_ enhance readability when used sparingly (1,2, or
        #   maybe 3 declarations) in any given example group, but that can
        #   quickly degrade with overuse. YMMV.
        #
        # @note `let` can be configured to be threadsafe or not.
        #   If it is threadsafe, it will take longer to access the value.
        #   If it is not threadsafe, it may behave in surprising ways in examples
        #   that spawn separate threads. Specify this on `RSpec.configure`
        #
        # @note Because `let` is designed to create state that is reset between
        #   each example, and `before(:context)` is designed to setup state that
        #   is shared across _all_ examples in an example group, `let` is _not_
        #   intended to be used in a `before(:context)` hook.
        #
        # @example
        #
        #   RSpec.describe Thing do
        #     let(:thing) { Thing.new }
        #
        #     it "does something" do
        #       # First invocation, executes block, memoizes and returns result.
        #       thing.do_something
        #
        #       # Second invocation, returns the memoized value.
        #       thing.should be_something
        #     end
        #   end
        def let(name, &block)
          # We have to pass the block directly to `define_method` to
          # allow it to use method constructs like `super` and `return`.
          raise "#let or #subject called without a block" if block.nil?

          # A list of reserved words that can't be used as a name for a memoized helper
          # Matches for both symbols and passed strings
          if [:initialize, :to_s].include?(name.to_sym)
            raise ArgumentError, "#let or #subject called with reserved name `#{name}`"
          end

          our_module = MemoizedHelpers.module_for(self)

          # If we have a module clash in our helper module
          # then we need to remove it to prevent a warning.
          #
          # Note we do not check ancestor modules (see: `instance_methods(false)`)
          # as we can override them.
          if our_module.instance_methods(false).include?(name)
            our_module.__send__(:remove_method, name)
          end
          our_module.__send__(:define_method, name, &block)

          # If we have a module clash in the example module
          # then we need to remove it to prevent a warning.
          #
          # Note we do not check ancestor modules (see: `instance_methods(false)`)
          # as we can override them.
          if instance_methods(false).include?(name)
            remove_method(name)
          end

          # Apply the memoization. The method has been defined in an ancestor
          # module so we can use `super` here to get the value.
          if block.arity == 1
            define_method(name) { __memoized.fetch_or_store(name) { super(RSpec.current_example, &nil) } }
          else
            define_method(name) { __memoized.fetch_or_store(name) { super(&nil) } }
          end
        end

        # Just like `let`, except the block is invoked by an implicit `before`
        # hook. This serves a dual purpose of setting up state and providing a
        # memoized reference to that state.
        #
        # @example
        #
        #   class Thing
        #     def self.count
        #       @count ||= 0
        #     end
        #
        #     def self.count=(val)
        #       @count += val
        #     end
        #
        #     def self.reset_count
        #       @count = 0
        #     end
        #
        #     def initialize
        #       self.class.count += 1
        #     end
        #   end
        #
        #   RSpec.describe Thing do
        #     after(:example) { Thing.reset_count }
        #
        #     context "using let" do
        #       let(:thing) { Thing.new }
        #
        #       it "is not invoked implicitly" do
        #         Thing.count.should eq(0)
        #       end
        #
        #       it "can be invoked explicitly" do
        #         thing
        #         Thing.count.should eq(1)
        #       end
        #     end
        #
        #     context "using let!" do
        #       let!(:thing) { Thing.new }
        #
        #       it "is invoked implicitly" do
        #         Thing.count.should eq(1)
        #       end
        #
        #       it "returns memoized version on first invocation" do
        #         thing
        #         Thing.count.should eq(1)
        #       end
        #     end
        #   end
        def let!(name, &block)
          let(name, &block)
          before { __send__(name) }
        end

        # Declares a `subject` for an example group which can then be wrapped
        # with `expect` using `is_expected` to make it the target of an
        # expectation in a concise, one-line example.
        #
        # Given a `name`, defines a method with that name which returns the
        # `subject`. This lets you declare the subject once and access it
        # implicitly in one-liners and explicitly using an intention revealing
        # name.
        #
        # When given a `name`, calling `super` in the block is not supported.
        #
        # @note `subject` can be configured to be threadsafe or not.
        #   If it is threadsafe, it will take longer to access the value.
        #   If it is not threadsafe, it may behave in surprising ways in examples
        #   that spawn separate threads. Specify this on `RSpec.configure`
        #
        # @param name [String,Symbol] used to define an accessor with an
        #   intention revealing name
        # @param block defines the value to be returned by `subject` in examples
        #
        # @example
        #
        #   RSpec.describe CheckingAccount, "with $50" do
        #     subject { CheckingAccount.new(Money.new(50, :USD)) }
        #     it { is_expected.to have_a_balance_of(Money.new(50, :USD)) }
        #     it { is_expected.not_to be_overdrawn }
        #   end
        #
        #   RSpec.describe CheckingAccount, "with a non-zero starting balance" do
        #     subject(:account) { CheckingAccount.new(Money.new(50, :USD)) }
        #     it { is_expected.not_to be_overdrawn }
        #     it "has a balance equal to the starting balance" do
        #       account.balance.should eq(Money.new(50, :USD))
        #     end
        #   end
        #
        # @see MemoizedHelpers#should
        # @see MemoizedHelpers#should_not
        # @see MemoizedHelpers#is_expected
        def subject(name=nil, &block)
          if name
            let(name, &block)
            alias_method :subject, name

            self::NamedSubjectPreventSuper.__send__(:define_method, name) do
              raise NotImplementedError, "`super` in named subjects is not supported"
            end
          else
            let(:subject, &block)
          end
        end

        # Just like `subject`, except the block is invoked by an implicit
        # `before` hook. This serves a dual purpose of setting up state and
        # providing a memoized reference to that state.
        #
        # @example
        #
        #   class Thing
        #     def self.count
        #       @count ||= 0
        #     end
        #
        #     def self.count=(val)
        #       @count += val
        #     end
        #
        #     def self.reset_count
        #       @count = 0
        #     end
        #
        #     def initialize
        #       self.class.count += 1
        #     end
        #   end
        #
        #   RSpec.describe Thing do
        #     after(:example) { Thing.reset_count }
        #
        #     context "using subject" do
        #       subject { Thing.new }
        #
        #       it "is not invoked implicitly" do
        #         Thing.count.should eq(0)
        #       end
        #
        #       it "can be invoked explicitly" do
        #         subject
        #         Thing.count.should eq(1)
        #       end
        #     end
        #
        #     context "using subject!" do
        #       subject!(:thing) { Thing.new }
        #
        #       it "is invoked implicitly" do
        #         Thing.count.should eq(1)
        #       end
        #
        #       it "returns memoized version on first invocation" do
        #         subject
        #         Thing.count.should eq(1)
        #       end
        #     end
        #   end
        def subject!(name=nil, &block)
          subject(name, &block)
          before { subject }
        end
      end

      # @private
      #
      # Gets the LetDefinitions module. The module is mixed into
      # the example group and is used to hold all let definitions.
      # This is done so that the block passed to `let` can be
      # forwarded directly on to `define_method`, so that all method
      # constructs (including `super` and `return`) can be used in
      # a `let` block.
      #
      # The memoization is provided by a method definition on the
      # example group that supers to the LetDefinitions definition
      # in order to get the value to memoize.
      def self.module_for(example_group)
        get_constant_or_yield(example_group, :LetDefinitions) do
          mod = Module.new do
            include(Module.new {
              example_group.const_set(:NamedSubjectPreventSuper, self)
            })
          end

          example_group.const_set(:LetDefinitions, mod)
          mod
        end
      end

      # @private
      def self.define_helpers_on(example_group)
        example_group.__send__(:include, module_for(example_group))
      end

      if Module.method(:const_defined?).arity == 1 # for 1.8
        # @private
        #
        # Gets the named constant or yields.
        # On 1.8, const_defined? / const_get do not take into
        # account the inheritance hierarchy.
        # :nocov:
        def self.get_constant_or_yield(example_group, name)
          if example_group.const_defined?(name)
            example_group.const_get(name)
          else
            yield
          end
        end
        # :nocov:
      else
        # @private
        #
        # Gets the named constant or yields.
        # On 1.9, const_defined? / const_get take into account the
        # the inheritance by default, and accept an argument to
        # disable this behavior. It's important that we don't
        # consider inheritance here; each example group level that
        # uses a `let` should get its own `LetDefinitions` module.
        def self.get_constant_or_yield(example_group, name)
          if example_group.const_defined?(name, (check_ancestors = false))
            example_group.const_get(name, check_ancestors)
          else
            yield
          end
        end
      end
    end
  end
end


def self.isolate_for_context_hook(example_group_instance)
          exploding_memoized = self

          example_group_instance.instance_exec do
            @__memoized = exploding_memoized

            begin
              yield
            ensure
              # This is doing a reset instead of just isolating for context hook.
              # Really, this should set the old @__memoized back into place.
              #
              # Caller is the before and after context hooks
              # which are both called from self.run
              # I didn't look at why it made tests fail, maybe an object was getting reused in RSpec tests,
              # if so, then that probably already works, and its the tests that are wrong.
              __init_memoized
            end
          end
        end


def self.fetch_or_store(key, &_block)
          description = if key == :subject
                          "subject"
                        else
                          "let declaration `#{key}`"
                        end

          raise <<-EOS
#{description} accessed in #{article} #{hook_expression} hook at:
  #{CallerFilter.first_non_rspec_line}

`let` and `subject` declarations are not intended to be called
in #{article} #{hook_expression} hook, as they exist to define state that
is reset between each example, while #{hook_expression} exists to
#{hook_intention}.
EOS
        end


def let(name, &block)
          # We have to pass the block directly to `define_method` to
          # allow it to use method constructs like `super` and `return`.
          raise "#let or #subject called without a block" if block.nil?

          # A list of reserved words that can't be used as a name for a memoized helper
          # Matches for both symbols and passed strings
          if [:initialize, :to_s].include?(name.to_sym)
            raise ArgumentError, "#let or #subject called with reserved name `#{name}`"
          end

          our_module = MemoizedHelpers.module_for(self)

          # If we have a module clash in our helper module
          # then we need to remove it to prevent a warning.
          #
          # Note we do not check ancestor modules (see: `instance_methods(false)`)
          # as we can override them.
          if our_module.instance_methods(false).include?(name)
            our_module.__send__(:remove_method, name)
          end
          our_module.__send__(:define_method, name, &block)

          # If we have a module clash in the example module
          # then we need to remove it to prevent a warning.
          #
          # Note we do not check ancestor modules (see: `instance_methods(false)`)
          # as we can override them.
          if instance_methods(false).include?(name)
            remove_method(name)
          end

          # Apply the memoization. The method has been defined in an ancestor
          # module so we can use `super` here to get the value.
          if block.arity == 1
            define_method(name) { __memoized.fetch_or_store(name) { super(RSpec.current_example, &nil) } }
          else
            define_method(name) { __memoized.fetch_or_store(name) { super(&nil) } }
          end
        end


def subject(name=nil, &block)
          if name
            let(name, &block)
            alias_method :subject, name

            self::NamedSubjectPreventSuper.__send__(:define_method, name) do
              raise NotImplementedError, "`super` in named subjects is not supported"
            end
          else
            let(:subject, &block)
          end
        end


def self.module_for(example_group)
        get_constant_or_yield(example_group, :LetDefinitions) do
          mod = Module.new do
            include(Module.new {
              example_group.const_set(:NamedSubjectPreventSuper, self)
            })
          end

          example_group.const_set(:LetDefinitions, mod)
          mod
        end
      end


module RSpec
  module Core
    # Each ExampleGroup class and Example instance owns an instance of
    # Metadata, which is Hash extended to support lazy evaluation of values
    # associated with keys that may or may not be used by any example or group.
    #
    # In addition to metadata that is used internally, this also stores
    # user-supplied metadata, e.g.
    #
    #     RSpec.describe Something, :type => :ui do
    #       it "does something", :slow => true do
    #         # ...
    #       end
    #     end
    #
    # `:type => :ui` is stored in the Metadata owned by the example group, and
    # `:slow => true` is stored in the Metadata owned by the example. These can
    # then be used to select which examples are run using the `--tag` option on
    # the command line, or several methods on `Configuration` used to filter a
    # run (e.g. `filter_run_including`, `filter_run_excluding`, etc).
    #
    # @see Example#metadata
    # @see ExampleGroup.metadata
    # @see FilterManager
    # @see Configuration#filter_run_including
    # @see Configuration#filter_run_excluding
    module Metadata
      # Matches strings either at the beginning of the input or prefixed with a
      # whitespace, containing the current path, either postfixed with the
      # separator, or at the end of the string. Match groups are the character
      # before and the character after the string if any.
      #
      # http://rubular.com/r/fT0gmX6VJX
      # http://rubular.com/r/duOrD4i3wb
      # http://rubular.com/r/sbAMHFrOx1
      def self.relative_path_regex
        @relative_path_regex ||= /(\A|\s)#{File.expand_path('.')}(#{File::SEPARATOR}|\s|\Z)/
      end

      # @api private
      #
      # @param line [String] current code line
      # @return [String] relative path to line
      def self.relative_path(line)
        line = line.sub(relative_path_regex, "\\1.\\2".freeze)
        line = line.sub(/\A([^:]+:\d+)$/, '\\1'.freeze)
        return nil if line == '-e:1'.freeze
        line
      rescue SecurityError
        # :nocov: - SecurityError is no longer produced starting in ruby 2.7
        nil
        # :nocov:
      end

      # @private
      # Iteratively walks up from the given metadata through all
      # example group ancestors, yielding each metadata hash along the way.
      def self.ascending(metadata)
        yield metadata
        return unless (group_metadata = metadata.fetch(:example_group) { metadata[:parent_example_group] })

        loop do
          yield group_metadata
          break unless (group_metadata = group_metadata[:parent_example_group])
        end
      end

      # @private
      # Returns an enumerator that iteratively walks up the given metadata through all
      # example group ancestors, yielding each metadata hash along the way.
      def self.ascend(metadata)
        enum_for(:ascending, metadata)
      end

      # @private
      # Used internally to build a hash from an args array.
      # Symbols are converted into hash keys with a value of `true`.
      # This is done to support simple tagging using a symbol, rather
      # than needing to do `:symbol => true`.
      def self.build_hash_from(args, warn_about_example_group_filtering=false)
        hash = args.last.is_a?(Hash) ? args.pop : {}

        hash[args.pop] = true while args.last.is_a?(Symbol)

        if warn_about_example_group_filtering && hash.key?(:example_group)
          RSpec.deprecate("Filtering by an `:example_group` subhash",
                          :replacement => "the subhash to filter directly")
        end

        hash
      end

      # @private
      def self.deep_hash_dup(object)
        return object.dup if Array === object
        return object unless Hash  === object

        object.inject(object.dup) do |duplicate, (key, value)|
          duplicate[key] = deep_hash_dup(value)
          duplicate
        end
      end

      # @private
      def self.id_from(metadata)
        "#{metadata[:rerun_file_path]}[#{metadata[:scoped_id]}]"
      end

      # @private
      def self.location_tuple_from(metadata)
        [metadata[:absolute_file_path], metadata[:line_number]]
      end

      # @private
      # Used internally to populate metadata hashes with computed keys
      # managed by RSpec.
      class HashPopulator
        attr_reader :metadata, :user_metadata, :description_args, :block

        def initialize(metadata, user_metadata, index_provider, description_args, block)
          @metadata         = metadata
          @user_metadata    = user_metadata
          @index_provider   = index_provider
          @description_args = description_args
          @block            = block
        end

        def populate
          ensure_valid_user_keys

          metadata[:block]            = block
          metadata[:description_args] = description_args
          metadata[:description]      = build_description_from(*metadata[:description_args])
          metadata[:full_description] = full_description
          metadata[:described_class]  = described_class

          populate_location_attributes
          metadata.update(user_metadata)
        end

      private

        def populate_location_attributes
          backtrace = user_metadata.delete(:caller)

          file_path, line_number = if backtrace
                                     file_path_and_line_number_from(backtrace)
                                   elsif block.respond_to?(:source_location)
                                     block.source_location
                                   else
                                     file_path_and_line_number_from(caller)
                                   end

          relative_file_path            = Metadata.relative_path(file_path)
          absolute_file_path            = File.expand_path(relative_file_path)
          metadata[:file_path]          = relative_file_path
          metadata[:line_number]        = line_number.to_i
          metadata[:location]           = "#{relative_file_path}:#{line_number}"
          metadata[:absolute_file_path] = absolute_file_path
          metadata[:rerun_file_path]  ||= relative_file_path
          metadata[:scoped_id]          = build_scoped_id_for(absolute_file_path)
        end

        def file_path_and_line_number_from(backtrace)
          first_caller_from_outside_rspec = backtrace.find { |l| l !~ CallerFilter::LIB_REGEX }
          first_caller_from_outside_rspec ||= backtrace.first
          /(.+?):(\d+)(?:|:\d+)/.match(first_caller_from_outside_rspec).captures
        end

        def description_separator(parent_part, child_part)
          if parent_part.is_a?(Module) && /^(?:#|::|\.)/.match(child_part.to_s)
            ''.freeze
          else
            ' '.freeze
          end
        end

        def build_description_from(parent_description=nil, my_description=nil)
          return parent_description.to_s unless my_description
          return my_description.to_s if parent_description.to_s == ''
          separator = description_separator(parent_description, my_description)
          (parent_description.to_s + separator) << my_description.to_s
        end

        def build_scoped_id_for(file_path)
          index = @index_provider.call(file_path).to_s
          parent_scoped_id = metadata.fetch(:scoped_id) { return index }
          "#{parent_scoped_id}:#{index}"
        end

        def ensure_valid_user_keys
          RESERVED_KEYS.each do |key|
            next unless user_metadata.key?(key)
            raise <<-EOM.gsub(/^\s+\|/, '')
              |#{"*" * 50}
              |:#{key} is not allowed
              |
              |RSpec reserves some hash keys for its own internal use,
              |including :#{key}, which is used on:
              |
              |  #{CallerFilter.first_non_rspec_line}.
              |
              |Here are all of RSpec's reserved hash keys:
              |
              |  #{RESERVED_KEYS.join("\n  ")}
              |#{"*" * 50}
            EOM
          end
        end
      end

      # @private
      class ExampleHash < HashPopulator
        def self.create(group_metadata, user_metadata, index_provider, description, block)
          example_metadata = group_metadata.dup
          group_metadata = Hash.new(&ExampleGroupHash.backwards_compatibility_default_proc do |hash|
            hash[:parent_example_group]
          end)
          group_metadata.update(example_metadata)

          example_metadata[:execution_result] = Example::ExecutionResult.new
          example_metadata[:example_group] = group_metadata
          example_metadata[:shared_group_inclusion_backtrace] = SharedExampleGroupInclusionStackFrame.current_backtrace
          example_metadata.delete(:parent_example_group)

          description_args = description.nil? ? [] : [description]
          hash = new(example_metadata, user_metadata, index_provider, description_args, block)
          hash.populate
          hash.metadata
        end

      private

        def described_class
          metadata[:example_group][:described_class]
        end

        def full_description
          build_description_from(
            metadata[:example_group][:full_description],
            metadata[:description]
          )
        end
      end

      # @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

          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
        end

        def self.hash_with_backwards_compatibility_default_proc
          Hash.new(&backwards_compatibility_default_proc { |hash| hash })
        end

        def self.backwards_compatibility_default_proc(&example_group_selector)
          Proc.new do |hash, key|
            case key
            when :example_group
              # We commonly get here when rspec-core is applying a previously
              # configured filter rule, such as when a gem configures:
              #
              #   RSpec.configure do |c|
              #     c.include MyGemHelpers, :example_group => { :file_path => /spec\/my_gem_specs/ }
              #   end
              #
              # It's confusing for a user to get a deprecation at this point in
              # the code, so instead we issue a deprecation from the config APIs
              # that take a metadata hash, and MetadataFilter sets this thread
              # local to silence the warning here since it would be so
              # confusing.
              unless RSpec::Support.thread_local_data[:silence_metadata_example_group_deprecations]
                RSpec.deprecate("The `:example_group` key in an example group's metadata hash",
                                :replacement => "the example group's hash directly for the " \
                                "computed keys and `:parent_example_group` to access the parent " \
                                "example group metadata")
              end

              group_hash = example_group_selector.call(hash)
              LegacyExampleGroupHash.new(group_hash) if group_hash
            when :example_group_block
              RSpec.deprecate("`metadata[:example_group_block]`",
                              :replacement => "`metadata[:block]`")
              hash[:block]
            when :describes
              RSpec.deprecate("`metadata[:describes]`",
                              :replacement => "`metadata[:described_class]`")
              hash[:described_class]
            end
          end
        end

      private

        def described_class
          candidate = metadata[:description_args].first
          return candidate unless NilClass === candidate || String === candidate
          parent_group = metadata[:parent_example_group]
          parent_group && parent_group[:described_class]
        end

        def full_description
          description          = metadata[:description]
          parent_example_group = metadata[:parent_example_group]
          return description unless parent_example_group

          parent_description   = parent_example_group[:full_description]
          separator = description_separator(parent_example_group[:description_args].last,
                                            metadata[:description_args].first)

          parent_description + separator + description
        end
      end

      # @private
      RESERVED_KEYS = [
        :description,
        :description_args,
        :described_class,
        :example_group,
        :parent_example_group,
        :execution_result,
        :last_run_status,
        :file_path,
        :absolute_file_path,
        :rerun_file_path,
        :full_description,
        :line_number,
        :location,
        :scoped_id,
        :block,
        :shared_group_inclusion_backtrace
      ]
    end

    # Mixin that makes the including class imitate a hash for backwards
    # compatibility. The including class should use `attr_accessor` to
    # declare attributes.
    # @private
    module HashImitatable
      def self.included(klass)
        klass.extend ClassMethods
      end

      def to_h
        hash = extra_hash_attributes.dup

        self.class.hash_attribute_names.each do |name|
          hash[name] = __send__(name)
        end

        hash
      end

      (Hash.public_instance_methods - Object.public_instance_methods).each do |method_name|
        next if [:[], :[]=, :to_h].include?(method_name.to_sym)

        define_method(method_name) do |*args, &block|
          issue_deprecation(method_name, *args)

          hash = hash_for_delegation
          self.class.hash_attribute_names.each do |name|
            hash.delete(name) unless instance_variable_defined?(:"@#{name}")
          end

          hash.__send__(method_name, *args, &block).tap do
            # apply mutations back to the object
            hash.each do |name, value|
              if directly_supports_attribute?(name)
                set_value(name, value)
              else
                extra_hash_attributes[name] = value
              end
            end
          end
        end
      end

      def [](key)
        issue_deprecation(:[], key)

        if directly_supports_attribute?(key)
          get_value(key)
        else
          extra_hash_attributes[key]
        end
      end

      def []=(key, value)
        issue_deprecation(:[]=, key, value)

        if directly_supports_attribute?(key)
          set_value(key, value)
        else
          extra_hash_attributes[key] = value
        end
      end

    private

      def extra_hash_attributes
        @extra_hash_attributes ||= {}
      end

      def directly_supports_attribute?(name)
        self.class.hash_attribute_names.include?(name)
      end

      def get_value(name)
        __send__(name)
      end

      def set_value(name, value)
        __send__(:"#{name}=", value)
      end

      def hash_for_delegation
        to_h
      end

      def issue_deprecation(_method_name, *_args)
        # no-op by default: subclasses can override
      end

      # @private
      module ClassMethods
        def hash_attribute_names
          @hash_attribute_names ||= []
        end

        def attr_accessor(*names)
          hash_attribute_names.concat(names)
          super
        end
      end
    end

    # @private
    # Together with the example group metadata hash default block,
    # provides backwards compatibility for the old `:example_group`
    # key. In RSpec 2.x, the computed keys of a group's metadata
    # were exposed from a nested subhash keyed by `[:example_group]`, and
    # then the parent group's metadata was exposed by sub-subhash
    # keyed by `[:example_group][:example_group]`.
    #
    # In RSpec 3, we reorganized this to that the computed keys are
    # exposed directly of the group metadata hash (no nesting), and
    # `:parent_example_group` returns the parent group's metadata.
    #
    # Maintaining backwards compatibility was difficult: we wanted
    # `:example_group` to return an object that:
    #
    #   * Exposes the top-level metadata keys that used to be nested
    #     under `:example_group`.
    #   * Supports mutation (rspec-rails, for example, assigns
    #     `metadata[:example_group][:described_class]` when you use
    #     anonymous controller specs) such that changes are written
    #     back to the top-level metadata hash.
    #   * Exposes the parent group metadata as
    #     `[:example_group][:example_group]`.
    class LegacyExampleGroupHash
      include HashImitatable

      def initialize(metadata)
        @metadata = metadata
        parent_group_metadata = metadata.fetch(:parent_example_group) { {} }[:example_group]
        self[:example_group] = parent_group_metadata if parent_group_metadata
      end

      def to_h
        super.merge(@metadata)
      end

    private

      def directly_supports_attribute?(name)
        name != :example_group
      end

      def get_value(name)
        @metadata[name]
      end

      def set_value(name, value)
        @metadata[name] = value
      end
    end
  end
end


def populate_location_attributes
          backtrace = user_metadata.delete(:caller)

          file_path, line_number = if backtrace
                                     file_path_and_line_number_from(backtrace)
                                   elsif block.respond_to?(:source_location)
                                     block.source_location
                                   else
                                     file_path_and_line_number_from(caller)
                                   end

          relative_file_path            = Metadata.relative_path(file_path)
          absolute_file_path            = File.expand_path(relative_file_path)
          metadata[:file_path]          = relative_file_path
          metadata[:line_number]        = line_number.to_i
          metadata[:location]           = "#{relative_file_path}:#{line_number}"
          metadata[:absolute_file_path] = absolute_file_path
          metadata[:rerun_file_path]  ||= relative_file_path
          metadata[:scoped_id]          = build_scoped_id_for(absolute_file_path)
        end


def ensure_valid_user_keys
          RESERVED_KEYS.each do |key|
            next unless user_metadata.key?(key)
            raise <<-EOM.gsub(/^\s+\|/, '')
              |#{"*" * 50}
              |:#{key} is not allowed
              |
              |RSpec reserves some hash keys for its own internal use,
              |including :#{key}, which is used on:
              |
              |  #{CallerFilter.first_non_rspec_line}.
              |
              |Here are all of RSpec's reserved hash keys:
              |
              |  #{RESERVED_KEYS.join("\n  ")}
              |#{"*" * 50}
            EOM
          end
        end


def self.create(group_metadata, user_metadata, index_provider, description, block)
          example_metadata = group_metadata.dup
          group_metadata = Hash.new(&ExampleGroupHash.backwards_compatibility_default_proc do |hash|
            hash[:parent_example_group]
          end)
          group_metadata.update(example_metadata)

          example_metadata[:execution_result] = Example::ExecutionResult.new
          example_metadata[:example_group] = group_metadata
          example_metadata[:shared_group_inclusion_backtrace] = SharedExampleGroupInclusionStackFrame.current_backtrace
          example_metadata.delete(:parent_example_group)

          description_args = description.nil? ? [] : [description]
          hash = new(example_metadata, user_metadata, index_provider, description_args, block)
          hash.populate
          hash.metadata
        end


def self.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

          hash = new(group_metadata, user_metadata, example_group_index, args, block)
          hash.populate
          hash.metadata
        end

class ExampleGroupHash < < HashPopulator


def self.backwards_compatibility_default_proc(&example_group_selector)
          Proc.new do |hash, key|
            case key
            when :example_group
              # We commonly get here when rspec-core is applying a previously
              # configured filter rule, such as when a gem configures:
              #
              #   RSpec.configure do |c|
              #     c.include MyGemHelpers, :example_group => { :file_path => /spec\/my_gem_specs/ }
              #   end
              #
              # It's confusing for a user to get a deprecation at this point in
              # the code, so instead we issue a deprecation from the config APIs
              # that take a metadata hash, and MetadataFilter sets this thread
              # local to silence the warning here since it would be so
              # confusing.
              unless RSpec::Support.thread_local_data[:silence_metadata_example_group_deprecations]
                RSpec.deprecate("The `:example_group` key in an example group's metadata hash",
                                :replacement => "the example group's hash directly for the " \
                                "computed keys and `:parent_example_group` to access the parent " \
                                "example group metadata")
              end

              group_hash = example_group_selector.call(hash)
              LegacyExampleGroupHash.new(group_hash) if group_hash
            when :example_group_block
              RSpec.deprecate("`metadata[:example_group_block]`",
                              :replacement => "`metadata[:block]`")
              hash[:block]
            when :describes
              RSpec.deprecate("`metadata[:describes]`",
                              :replacement => "`metadata[:described_class]`")
              hash[:described_class]
            end
          end
        end


module RSpec
  module Core
    # Contains metadata filtering logic. This has been extracted from
    # the metadata classes because it operates ON a metadata hash but
    # does not manage any of the state in the hash. We're moving towards
    # having metadata be a raw hash (not a custom subclass), so externalizing
    # this filtering logic helps us move in that direction.
    module MetadataFilter
      class << self
        # @private
        def apply?(predicate, filters, metadata)
          filters.__send__(predicate) { |k, v| filter_applies?(k, v, metadata) }
        end

        # @private
        def filter_applies?(key, filter_value, metadata)
          silence_metadata_example_group_deprecations do
            return location_filter_applies?(filter_value, metadata) if key == :locations
            return id_filter_applies?(filter_value, metadata)       if key == :ids
            return filters_apply?(key, filter_value, metadata)      if Hash === filter_value

            meta_value = metadata.fetch(key) { return false }

            return true if TrueClass === filter_value && meta_value
            return proc_filter_applies?(key, filter_value, metadata) if Proc === filter_value
            return filter_applies_to_any_value?(key, filter_value, metadata) if Array === meta_value

            filter_value === meta_value || filter_value.to_s == meta_value.to_s
          end
        end

        # @private
        def silence_metadata_example_group_deprecations
          RSpec::Support.thread_local_data[:silence_metadata_example_group_deprecations] = true
          yield
        ensure
          RSpec::Support.thread_local_data.delete(:silence_metadata_example_group_deprecations)
        end

      private

        def filter_applies_to_any_value?(key, value, metadata)
          metadata[key].any? { |v| filter_applies?(key, v,  key => value) }
        end

        def id_filter_applies?(rerun_paths_to_scoped_ids, metadata)
          scoped_ids = rerun_paths_to_scoped_ids.fetch(metadata[:rerun_file_path]) { return false }

          Metadata.ascend(metadata).any? do |meta|
            scoped_ids.include?(meta[:scoped_id])
          end
        end

        def location_filter_applies?(locations, metadata)
          Metadata.ascend(metadata).any? do |meta|
            file_path = meta[:absolute_file_path]
            line_num  = meta[:line_number]

            locations[file_path].any? do |filter_line_num|
              line_num == RSpec.world.preceding_declaration_line(file_path, filter_line_num)
            end
          end
        end

        def proc_filter_applies?(key, proc, metadata)
          case proc.arity
          when 0 then proc.call
          when 2 then proc.call(metadata[key], metadata)
          else proc.call(metadata[key])
          end
        end

        def filters_apply?(key, value, metadata)
          subhash = metadata[key]
          return false unless Hash === subhash || HashImitatable === subhash
          value.all? { |k, v| filter_applies?(k, v, subhash) }
        end
      end
    end

    # Tracks a collection of filterable items (e.g. modules, hooks, etc)
    # and provides an optimized API to get the applicable items for the
    # metadata of an example or example group.
    #
    # There are two implementations, optimized for different uses.
    # @private
    module FilterableItemRepository
      # This implementation is simple, and is optimized for frequent
      # updates but rare queries. `append` and `prepend` do no extra
      # processing, and no internal memoization is done, since this
      # is not optimized for queries.
      #
      # This is ideal for use by a example or example group, which may
      # be updated multiple times with globally configured hooks, etc,
      # but will not be queried frequently by other examples or example
      # groups.
      # @private
      class UpdateOptimized
        attr_reader :items_and_filters

        def initialize(applies_predicate)
          @applies_predicate = applies_predicate
          @items_and_filters = []
        end

        def append(item, metadata)
          @items_and_filters << [item, metadata]
        end

        def prepend(item, metadata)
          @items_and_filters.unshift [item, metadata]
        end

        def delete(item, metadata)
          @items_and_filters.delete [item, metadata]
        end

        def items_for(request_meta)
          @items_and_filters.each_with_object([]) do |(item, item_meta), to_return|
            to_return << item if item_meta.empty? ||
                                 MetadataFilter.apply?(@applies_predicate, item_meta, request_meta)
          end
        end

        unless [].respond_to?(:each_with_object) # For 1.8.7
          # :nocov:
          undef items_for
          def items_for(request_meta)
            @items_and_filters.inject([]) do |to_return, (item, item_meta)|
              to_return << item if item_meta.empty? ||
                                   MetadataFilter.apply?(@applies_predicate, item_meta, request_meta)
              to_return
            end
          end
          # :nocov:
        end
      end

      # This implementation is much more complex, and is optimized for
      # rare (or hopefully no) updates once the queries start. Updates
      # incur a cost as it has to clear the memoization and keep track
      # of applicable keys. Queries will be O(N) the first time an item
      # is provided with a given set of applicable metadata; subsequent
      # queries with items with the same set of applicable metadata will
      # be O(1) due to internal memoization.
      #
      # This is ideal for use by config, where filterable items (e.g. hooks)
      # are typically added at the start of the process (e.g. in `spec_helper`)
      # and then repeatedly queried as example groups and examples are defined.
      # @private
      class QueryOptimized < UpdateOptimized
        alias find_items_for items_for
        private :find_items_for

        def initialize(applies_predicate)
          super
          @applicable_keys   = Set.new
          @proc_keys         = Set.new
          @memoized_lookups  = Hash.new do |hash, applicable_metadata|
            hash[applicable_metadata] = find_items_for(applicable_metadata)
          end
        end

        def append(item, metadata)
          super
          handle_mutation(metadata)
        end

        def prepend(item, metadata)
          super
          handle_mutation(metadata)
        end

        def delete(item, metadata)
          super
          reconstruct_caches
        end

        def items_for(metadata)
          # The filtering of `metadata` to `applicable_metadata` is the key thing
          # that makes the memoization actually useful in practice, since each
          # example and example group have different metadata (e.g. location and
          # description). By filtering to the metadata keys our items care about,
          # we can ignore extra metadata keys that differ for each example/group.
          # For example, given `config.include DBHelpers, :db`, example groups
          # can be split into these two sets: those that are tagged with `:db` and those
          # that are not. For each set, this method for the first group in the set is
          # still an `O(N)` calculation, but all subsequent groups in the set will be
          # constant time lookups when they call this method.
          applicable_metadata = applicable_metadata_from(metadata)

          if applicable_metadata.any? { |k, _| @proc_keys.include?(k) }
            # It's unsafe to memoize lookups involving procs (since they can
            # be non-deterministic), so we skip the memoization in this case.
            find_items_for(applicable_metadata)
          else
            @memoized_lookups[applicable_metadata]
          end
        end

      private

        def reconstruct_caches
          @applicable_keys.clear
          @proc_keys.clear
          @items_and_filters.each do |_item, metadata|
            handle_mutation(metadata)
          end
        end

        def handle_mutation(metadata)
          @applicable_keys.merge(metadata.keys)
          @proc_keys.merge(proc_keys_from metadata)
          @memoized_lookups.clear
        end

        def applicable_metadata_from(metadata)
          MetadataFilter.silence_metadata_example_group_deprecations do
            @applicable_keys.inject({}) do |hash, key|
              # :example_group is treated special here because...
              # - In RSpec 2, example groups had an `:example_group` key
              # - In RSpec 3, that key is deprecated (it was confusing!).
              # - The key is not technically present in an example group metadata hash
              #   (and thus would fail the `metadata.key?(key)` check) but a value
              #   is provided when accessed via the hash's `default_proc`
              # - Thus, for backwards compatibility, we have to explicitly check
              #   for `:example_group` here if it is one of the keys being used to
              #   filter.
              hash[key] = metadata[key] if metadata.key?(key) || key == :example_group
              hash
            end
          end
        end

        def proc_keys_from(metadata)
          metadata.each_with_object([]) do |(key, value), to_return|
            to_return << key if Proc === value
          end
        end

        unless [].respond_to?(:each_with_object) # For 1.8.7
          # :nocov:
          undef proc_keys_from
          def proc_keys_from(metadata)
            metadata.inject([]) do |to_return, (key, value)|
              to_return << key if Proc === value
              to_return
            end
          end
          # :nocov:
        end
      end
    end
  end
end