class RSpec::Core::Hooks::HookCollections

asking this class for a list of hooks, and then doing something with them.
API, so that callers tell this class what to do with the hooks, rather than
This is only possible because this interface provides a “tell, don’t ask”-style
case of a group having no hooks.
a hook is added. This allows us to avoid many object allocations for the common
this, so that, for example, hook collection objects are only instantiated when
implementation details behind this facade, it’s allowed us to heavily optimize
This provides the primary API used by other parts of rspec-core. By hiding all
@private

def all_hooks_for(position, scope)

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

def ensure_hooks_initialized_for(position, scope)

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 extract_scope_from(args)

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 hooks_for(position, scope)

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 initialize(owner, filterable_item_repo_class)

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 known_scope?(scope)

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

def matching_hooks_for(position, scope, example_or_group)

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 normalized_scope_for(scope)

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

def owner_parent_groups

def owner_parent_groups
  @owner.parent_groups
end

def owner_parent_groups

:nocov:
Ruby < 2.1 (see https://bugs.ruby-lang.org/issues/8035)
def owner_parent_groups
  @owner_parent_groups ||= [@owner] + @owner.parent_groups
end

def process(host, parent_groups, globals, position, scope)

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 processable_hooks_for(position, scope, host)

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

def register(prepend_or_append, position, *args, &block)

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

def register_global_singleton_context_hooks(example, globals)

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_globals(host, globals)

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 run(position, scope, example_or_group)

Other tags:
    Private: -
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

def run_around_example_hooks_for(example)

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

def run_example_hooks_for(example, position, each_method)

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_owned_hooks_for(position, scope, example_or_group)

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 scope_and_options_from(*args)

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