Commit 5870462b authored by Fabio Pelosin's avatar Fabio Pelosin

Merge pull request #1755 from CocoaPods/fabio/hooks

[Plugins] Added support for hooks registratrion
parents 46ced0b6 06b73f2c
...@@ -6,7 +6,20 @@ To install or update CocoaPods see this [guide](http://docs.cocoapods.org/guides ...@@ -6,7 +6,20 @@ To install or update CocoaPods see this [guide](http://docs.cocoapods.org/guides
##### Enhancements ##### Enhancements
* Display indication for deprecated pods when searching for Pods. * Added hooks for plugins. Currently only the installer hook is supported.
A plugin can register itself to be activated after the installation with the
following syntax:
Pod::HooksManager.register(:post_install) do |installer_context|
# implementation
end
The `installer_context` is an instance of the `Pod::Installer:HooksContext`
class which provides the information about the installation.
[Fabio Pelosin][irrationalfab]
[Core#132](https://github.com/CocoaPods/Core/pull/1755)
* Display indication for deprecated pods when searching for Pods.
[Hugo Tunius][k0nserv] [Hugo Tunius][k0nserv]
[#2180](https://github.com/CocoaPods/CocoaPods/issues/2180) [#2180](https://github.com/CocoaPods/CocoaPods/issues/2180)
......
...@@ -40,6 +40,7 @@ module Pod ...@@ -40,6 +40,7 @@ module Pod
autoload :Executable, 'cocoapods/executable' autoload :Executable, 'cocoapods/executable'
autoload :ExternalSources, 'cocoapods/external_sources' autoload :ExternalSources, 'cocoapods/external_sources'
autoload :Installer, 'cocoapods/installer' autoload :Installer, 'cocoapods/installer'
autoload :HooksManager, 'cocoapods/hooks_manager'
autoload :PodTarget, 'cocoapods/target/pod_target' autoload :PodTarget, 'cocoapods/target/pod_target'
autoload :Project, 'cocoapods/project' autoload :Project, 'cocoapods/project'
autoload :Resolver, 'cocoapods/resolver' autoload :Resolver, 'cocoapods/resolver'
......
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
class << self
# @return [Hash{Symbol => Proc}] The list of the blocks that are
# registered for each notification name.
#
attr_reader :registrations
# Registers a block for the hook with the given name.
#
# @param [Symbol] name
# The name of the notification.
#
# @param [Proc] block
# The block.
#
def register(name, &block)
raise ArgumentError, 'Missing name' unless name
raise ArgumentError, 'Missing block' unless block
@registrations ||= Hash.new
@registrations[name] ||= Array.new
@registrations[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.
#
def run(name, context)
raise ArgumentError, 'Missing name' unless name
raise ArgumentError, 'Missing options' unless context
if @registrations
blocks = @registrations[name]
if blocks
blocks.each do |block|
block.call(context)
end
end
end
end
end
end
end
...@@ -37,6 +37,7 @@ module Pod ...@@ -37,6 +37,7 @@ module Pod
autoload :AggregateTargetInstaller, 'cocoapods/installer/target_installer/aggregate_target_installer' autoload :AggregateTargetInstaller, 'cocoapods/installer/target_installer/aggregate_target_installer'
autoload :PodTargetInstaller, 'cocoapods/installer/target_installer/pod_target_installer' autoload :PodTargetInstaller, 'cocoapods/installer/target_installer/pod_target_installer'
autoload :UserProjectIntegrator, 'cocoapods/installer/user_project_integrator' autoload :UserProjectIntegrator, 'cocoapods/installer/user_project_integrator'
autoload :HooksContext, 'cocoapods/installer/hooks_context'
include Config::Mixin include Config::Mixin
...@@ -117,7 +118,7 @@ module Pod ...@@ -117,7 +118,7 @@ module Pod
install_libraries install_libraries
set_target_dependencies set_target_dependencies
link_aggregate_target link_aggregate_target
run_post_install_hooks run_podfile_post_install_hooks
write_pod_project write_pod_project
write_lockfiles write_lockfiles
end end
...@@ -293,9 +294,17 @@ module Pod ...@@ -293,9 +294,17 @@ module Pod
# @return [void] # @return [void]
# #
def perform_post_install_actions def perform_post_install_actions
run_plugins_post_install_hooks
warn_for_deprecations warn_for_deprecations
end end
# Runs the registered callbacks for the plugins post install hooks.
#
def run_plugins_post_install_hooks
context = HooksContext.generate(sandbox, aggregate_targets)
HooksManager.run(:post_install, context)
end
# Prints a warning for any pods that are deprecated # Prints a warning for any pods that are deprecated
# #
# @return [void] # @return [void]
...@@ -521,7 +530,7 @@ module Pod ...@@ -521,7 +530,7 @@ module Pod
# #
# @return [void] # @return [void]
# #
def run_post_install_hooks def run_podfile_post_install_hooks
UI.message "- Running post install hooks" do UI.message "- Running post install hooks" do
executed = run_podfile_post_install_hook executed = run_podfile_post_install_hook
UI.message "- Podfile" if executed UI.message "- Podfile" if executed
......
module Pod
class Installer
# Context object designed to be used with the HooksManager which describes
# the context of the installer.
#
class HooksContext
# @return [String] The path to the sandbox root (`Pods` directory).
#
attr_accessor :sandbox_root
# @return [Array<UmbrellaTargetDescription>] The list of
# the CocoaPods umbrella targets generated by the installer.
#
attr_accessor :umbrella_targets
# @return [HooksContext] Convenience class method to generate the
# static context.
#
def self.generate(sandbox, aggregate_targets)
umbrella_targets_descriptions = []
aggregate_targets.each do |umbrella|
desc = UmbrellaTargetDescription.new
desc.user_project_path = umbrella.user_project_path
desc.user_target_uuids = umbrella.user_target_uuids
desc.specs = umbrella.specs
desc.platform_name = umbrella.platform.name
desc.platform_deployment_target = umbrella.platform.deployment_target.to_s
desc.cocoapods_target_label = umbrella.label
umbrella_targets_descriptions << desc
end
result = new
result.sandbox_root = sandbox.root.to_s
result.umbrella_targets = umbrella_targets_descriptions
result
end
# Pure data class which describes and umbrella target.
#
class UmbrellaTargetDescription
# @return [String] The path of the user project
# integrated by this target.
#
attr_accessor :user_project_path
# @return [Array<String>] The list of the UUIDs of the
# user targets integrated by this umbrella
# target. They can be used to find the
# targets opening the project They can be used
# to find the targets opening the project with
# Xcodeproj.
#
attr_accessor :user_target_uuids
# @return [Array<Specification>] The list of the
# specifications of the target.
#
attr_accessor :specs
# @return [Symbol] The platform (either `:ios` or `:osx`).
#
attr_accessor :platform_name
# @return [String] The deployment target.
#
attr_accessor :platform_deployment_target
# @return [String] The label for the target.
#
attr_accessor :cocoapods_target_label
end
end
end
end
require File.expand_path('../../spec_helper', __FILE__)
module Pod
describe HooksManager do
before do
@hooks_manager = Pod::HooksManager
end
describe 'register' do
it "allows to register a block for a notification with a given name" do
@hooks_manager.register(:post_install) {}
@hooks_manager.registrations[:post_install].count.should == 1
@hooks_manager.registrations[:post_install].first.class.should == Proc
end
it "raises if no name is given" do
should.raise ArgumentError do
@hooks_manager.register(nil) {}
end
end
it "raises if no block is given" do
should.raise ArgumentError do
@hooks_manager.register(:post_install)
end
end
end
describe 'run' do
it "invokes the hooks" do
@hooks_manager.register(:post_install) do |options|
true.should.be.true
end
@hooks_manager.run(:post_install, Object.new)
end
it "handles the case that no listeners have registered" do
should.not.raise do
@hooks_manager.run(:post_install, Object.new)
end
end
it "handles the case that no listeners have registered for a name" do
@hooks_manager.register(:post_install) do |options|
true.should.be.true
end
should.not.raise do
@hooks_manager.run(:pre_install, Object.new)
end
end
it "raises if no name is given" do
should.raise ArgumentError do
@hooks_manager.run(nil, Object.new) {}
end
end
it "raises if no context object is given" do
should.raise ArgumentError do
@hooks_manager.run(:post_install, nil)
end
end
end
end
end
require File.expand_path('../../../spec_helper', __FILE__)
module Pod
describe Installer::HooksContext do
it "offers a convenience method to be generated" do
sandbox = stub(:root => '/path')
spec = fixture_spec('banana-lib/BananaLib.podspec')
target_definition = Podfile::TargetDefinition.new('Pods', nil)
pod_target = PodTarget.new([spec], target_definition, config.sandbox)
umbrella = AggregateTarget.new(target_definition, config.sandbox)
umbrella.user_project_path = '/path/project.xcodeproj'
umbrella.user_target_uuids = ['UUID']
umbrella.stubs(:platform).returns(Platform.new(:ios, '8.0'))
umbrella.pod_targets = [pod_target]
result = Installer::HooksContext.generate(sandbox, [umbrella])
result.class.should == Installer::HooksContext
result.sandbox_root.should == '/path'
result.umbrella_targets.count.should == 1
umbrella_target = result.umbrella_targets.first
umbrella_target.user_target_uuids.should == ['UUID']
umbrella_target.user_project_path.should == '/path/project.xcodeproj'
umbrella_target.specs.should == [spec]
umbrella_target.platform_name.should == :ios
umbrella_target.platform_deployment_target.should == '8.0'
umbrella_target.cocoapods_target_label.should == 'Pods'
end
end
end
...@@ -52,6 +52,7 @@ module Pod ...@@ -52,6 +52,7 @@ module Pod
@installer.stubs(:download_dependencies) @installer.stubs(:download_dependencies)
@installer.stubs(:generate_pods_project) @installer.stubs(:generate_pods_project)
@installer.stubs(:integrate_user_project) @installer.stubs(:integrate_user_project)
@installer.stubs(:run_plugins_post_install_hooks)
@installer.stubs(:perform_post_install_actions) @installer.stubs(:perform_post_install_actions)
end end
...@@ -77,7 +78,7 @@ module Pod ...@@ -77,7 +78,7 @@ module Pod
@installer.stubs(:write_lockfiles) @installer.stubs(:write_lockfiles)
@installer.stubs(:aggregate_targets).returns([]) @installer.stubs(:aggregate_targets).returns([])
@installer.unstub(:generate_pods_project) @installer.unstub(:generate_pods_project)
def @installer.run_post_install_hooks def @installer.run_podfile_post_install_hooks
@hook_called = true @hook_called = true
end end
def @installer.write_pod_project def @installer.write_pod_project
...@@ -475,6 +476,22 @@ module Pod ...@@ -475,6 +476,22 @@ module Pod
end end
describe "Plugins Hooks" do
before do
@installer.send(:analyze)
@specs = @installer.pod_targets.map(&:specs).flatten
@spec = @specs.find { |spec| spec && spec.name == 'JSONKit' }
@installer.stubs(:installed_specs).returns(@specs)
end
it "runs plugins post install hook" do
context = stub()
Installer::HooksContext.expects(:generate).returns(context)
HooksManager.expects(:run).with(:post_install, context)
@installer.send(:run_plugins_post_install_hooks)
end
end
#-------------------------------------------------------------------------# #-------------------------------------------------------------------------#
describe "Hooks" do describe "Hooks" do
...@@ -497,13 +514,13 @@ module Pod ...@@ -497,13 +514,13 @@ module Pod
@installer.send(:run_pre_install_hooks) @installer.send(:run_pre_install_hooks)
end end
it "run_post_install_hooks" do it "run_podfile_post_install_hooks" do
installer_rep = stub() installer_rep = stub()
target_installer_data = stub() target_installer_data = stub()
@installer.expects(:installer_rep).returns(installer_rep) @installer.expects(:installer_rep).returns(installer_rep)
@installer.podfile.expects(:post_install!).with(installer_rep) @installer.podfile.expects(:post_install!).with(installer_rep)
@installer.send(:run_post_install_hooks) @installer.send(:run_podfile_post_install_hooks)
end end
it "calls the hooks in the specs for each target" do it "calls the hooks in the specs for each target" do
...@@ -519,7 +536,7 @@ module Pod ...@@ -519,7 +536,7 @@ module Pod
@installer.stubs(:installer_rep).returns(stub()) @installer.stubs(:installer_rep).returns(stub())
@installer.podfile.expects(:pre_install!) @installer.podfile.expects(:pre_install!)
@installer.send(:run_pre_install_hooks) @installer.send(:run_pre_install_hooks)
@installer.send(:run_post_install_hooks) @installer.send(:run_podfile_post_install_hooks)
end end
it "returns the hook representation of the installer" do it "returns the hook representation of the installer" do
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment