diff --git a/lib/capistrano/configuration.rb b/lib/capistrano/configuration.rb index cef9fa86..68819d71 100644 --- a/lib/capistrano/configuration.rb +++ b/lib/capistrano/configuration.rb @@ -1,5 +1,6 @@ require_relative "configuration/filter" require_relative "configuration/question" +require_relative "configuration/plugin_installer" require_relative "configuration/server" require_relative "configuration/servers" @@ -151,6 +152,10 @@ module Capistrano fetch(:sshkit_backend) == SSHKit::Backend::Printer end + def install_plugin(plugin, hooks:true) + installer.install(plugin, hooks: hooks) + end + private def cmdline_filters @@ -169,6 +174,10 @@ module Capistrano @validators ||= {} end + def installer + @installer ||= PluginInstaller.new + end + def fetch_for(key, default, &block) if block_given? config.fetch(key, &block) diff --git a/lib/capistrano/configuration/plugin_installer.rb b/lib/capistrano/configuration/plugin_installer.rb new file mode 100644 index 00000000..71ce1f45 --- /dev/null +++ b/lib/capistrano/configuration/plugin_installer.rb @@ -0,0 +1,33 @@ +# Encapsulates the logic for installing plugins into Capistrano. Plugins must +# simply conform to a basic API; the PluginInstaller takes care of invoking the +# API at appropriate times. +# +# This class is not used directly; instead it is typically accessed via the +# `install_plugin` method of the Capistrano DSL. +# +module Capistrano + class Configuration + class PluginInstaller + # "Installs" a Plugin into Capistrano by loading its tasks, hooks, and + # defaults at the appropriate time. The hooks in particular can be + # skipped, if you want full control over when and how the plugin's tasks + # are executed. Simply pass `hooks:false` to opt out. + # + # The plugin class or instance may be provided. These are equivalent: + # + # install(Capistrano::SCM::Git) + # install(Capistrano::SCM::Git.new) + # + def install(plugin, hooks:true) + plugin = plugin.is_a?(Class) ? plugin.new : plugin + + plugin.define_tasks + plugin.register_hooks if hooks + + Rake::Task.define_task("load:defaults") do + plugin.set_defaults + end + end + end + end +end diff --git a/lib/capistrano/dsl/env.rb b/lib/capistrano/dsl/env.rb index cc22dd50..d609fd45 100644 --- a/lib/capistrano/dsl/env.rb +++ b/lib/capistrano/dsl/env.rb @@ -6,7 +6,8 @@ module Capistrano extend Forwardable def_delegators :env, :configure_backend, :fetch, :set, :set_if_empty, :delete, - :ask, :role, :server, :primary, :validate, :append, :remove, :dry_run? + :ask, :role, :server, :primary, :validate, :append, + :remove, :dry_run?, :install_plugin def is_question?(key) env.is_question?(key) diff --git a/lib/capistrano/plugin.rb b/lib/capistrano/plugin.rb index 0901174a..63cee207 100644 --- a/lib/capistrano/plugin.rb +++ b/lib/capistrano/plugin.rb @@ -20,37 +20,21 @@ require "rake/tasklib" # # Package up and distribute your plugin class as a gem and you're good to go! # -# To use a plugin, all a user has to do is instantiate it in the Capfile, like -# this: +# To use a plugin, all a user has to do is install it in the Capfile, like this: # # # Capfile # require "capistrano/superfancy" -# Capistrano::Superfancy.new +# install_plugin Capistrano::Superfancy # # Or, to install the plugin without its hooks: # # # Capfile # require "capistrano/superfancy" -# Capistrano::Superfancy.new(hooks: false) +# install_plugin Capistrano::Superfancy, hooks: false # class Capistrano::Plugin < Rake::TaskLib include Capistrano::DSL - # Constructing a plugin "installs" it into Capistrano by loading its tasks, - # hooks, and defaults at the appropriate time. The hooks in particular can be - # skipped, if you want full control over when and how the plugin's tasks are - # executed. Simply pass `hooks:false` to opt out. - # - def initialize(hooks: true) - define_tasks - register_hooks if hooks - task "load:defaults" do - set_defaults - end - end - - private - # Implemented by subclasses to provide default values for settings needed by # this plugin. Typically done using the `set_if_empty` Capistrano DSL method. # @@ -94,6 +78,8 @@ class Capistrano::Plugin < Rake::TaskLib # def define_tasks; end + private + # Read and eval a .rake file in such a way that `self` within the .rake file # refers to this plugin instance. This gives the tasks in the file access to # helper methods defined by the plugin. diff --git a/spec/lib/capistrano/plugin_spec.rb b/spec/lib/capistrano/plugin_spec.rb index 7767c04b..4865b2e6 100644 --- a/spec/lib/capistrano/plugin_spec.rb +++ b/spec/lib/capistrano/plugin_spec.rb @@ -42,33 +42,35 @@ module Capistrano end it "defines tasks when constructed" do - DummyPlugin.new + install_plugin(DummyPlugin) expect(Rake::Task["hello"]).not_to be_nil end it "registers hooks when constructed" do - DummyPlugin.new + install_plugin(DummyPlugin) expect(Rake::Task["deploy:published"].prerequisites).to include("hello") end it "skips registering hooks if :hooks => false" do - DummyPlugin.new(hooks: false) + install_plugin(DummyPlugin, hooks: false) expect(Rake::Task["deploy:published"].prerequisites).to be_empty end it "doesn't call set_defaults immediately" do dummy = DummyPlugin.new + install_plugin(dummy) dummy.expects(:set_defaults).never end it "calls set_defaults during load:defaults" do dummy = DummyPlugin.new dummy.expects(:set_defaults).once + install_plugin(dummy) Rake::Task["load:defaults"].invoke end it "is able to load tasks from a .rake file" do - ExternalTasksPlugin.new + install_plugin(ExternalTasksPlugin) Rake::Task["plugin_test"].invoke expect(fetch(:plugin_result)).to eq("hello") end