blob: 0e36abbc7cd18aa72559337b3b444a8dc3175d02 [file] [log] [blame]
require 'active_support/core_ext/hash/indifferent_access'
module Pod
# Provides support for the hook system of CocoaPods. The system is designed
# especially for plugins. Interested clients can register to notifications by
# name.
#
# The blocks, to prevent compatibility issues, will receive
# one and only one argument: a context object. This object should be simple
# storage of information (a typed hash). Notifications senders are
# responsible to indicate the class of the object associated with their
# notification name.
#
# Context object should not remove attribute accessors to not break
# compatibility with the plugins (this promise will be honoured strictly
# from CocoaPods 1.0).
#
module HooksManager
# Represents a single registered hook.
#
class Hook
# @return [String]
# The name of the plugin that registered the hook.
#
attr_reader :plugin_name
# @return [String]
# The name of the hook.
#
attr_reader :name
# @return [Proc]
# The block.
#
attr_reader :block
# Initialize a new instance
#
# @param [String] name @see {#name}.
#
# @param [String] plugin_name @see {#plugin_name}.
#
# @param [Proc] block @see {#block}.
#
def initialize(name, plugin_name, block)
raise ArgumentError, 'Missing name' unless name
raise ArgumentError, 'Missing plugin_name' unless plugin_name
raise ArgumentError, 'Missing block' unless block
@name = name
@plugin_name = plugin_name
@block = block
end
end
class << self
# @return [Hash{Symbol => Array<Hook>}] The list of the hooks that are
# registered for each hook name.
#
attr_reader :registrations
# Registers a block for the hook with the given name.
#
# @param [String] plugin_name
# The name of the plugin the hook comes from.
#
# @param [Symbol] hook_name
# The name of the notification.
#
# @param [Proc] block
# The block.
#
def register(plugin_name, hook_name, &block)
@registrations ||= {}
@registrations[hook_name] ||= []
@registrations[hook_name] << Hook.new(hook_name, plugin_name, block)
end
# Runs all the registered blocks for the hook with the given name.
#
# @param [Symbol] name
# The name of the hook.
#
# @param [Object] context
# The context object which should be passed to the blocks.
#
# @param [Hash<String, Hash>] whitelisted_plugins
# The plugins that should be run, in the form of a hash keyed by
# plugin name, where the values are the custom options that should
# be passed to the hook's block if it supports taking a second
# argument.
#
def run(name, context, whitelisted_plugins = nil)
raise ArgumentError, 'Missing name' unless name
raise ArgumentError, 'Missing options' unless context
if registrations
hooks = registrations[name]
if hooks
UI.message "- Running #{name.to_s.tr('_', ' ')} hooks" do
hooks.each do |hook|
next if whitelisted_plugins && !whitelisted_plugins.key?(hook.plugin_name)
UI.message "- #{hook.plugin_name} from " \
"`#{hook.block.source_location.first}`" do
block = hook.block
if block.arity > 1
user_options = whitelisted_plugins[hook.plugin_name]
user_options = user_options.with_indifferent_access if user_options
block.call(context, user_options)
else
block.call(context)
end
end
end
end
end
end
end
end
end
end