class RSpec::Core::Hooks::HookCollections

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

Constants

EMPTY_HOOK_ARRAY
HOOK_TYPES
SCOPES
SCOPE_ALIASES

Public Class Methods

# File rspec-core/lib/rspec/core/hooks.rb, line 421
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

Public Instance Methods

# File rspec-core/lib/rspec/core/hooks.rb, line 449
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
# File rspec-core/lib/rspec/core/hooks.rb, line 442
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
# File rspec-core/lib/rspec/core/hooks.rb, line 431
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

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

# File rspec-core/lib/rspec/core/hooks.rb, line 475
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

Protected Instance Methods

# File rspec-core/lib/rspec/core/hooks.rb, line 523
def all_hooks_for(position, scope)
  hooks_for(position, scope) { return EMPTY_HOOK_ARRAY }.items_and_filters.map(&:first)
end
# File rspec-core/lib/rspec/core/hooks.rb, line 507
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
# File rspec-core/lib/rspec/core/hooks.rb, line 533
def processable_hooks_for(position, scope, host)
  if scope == :example
    all_hooks_for(position, scope)
  else
    matching_hooks_for(position, scope, host)
  end
end
# File rspec-core/lib/rspec/core/hooks.rb, line 527
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