From 3b9c90327bcf15a0b0411eabaabe4547c21d62c0 Mon Sep 17 00:00:00 2001 From: Matt Brictson Date: Sun, 17 Jan 2016 16:01:21 -0800 Subject: [PATCH] Monkey patch SSHKit to expose current backend Eventually this will be part of SSHKit itself, but for flexibility in using Capistrano with older SSHKit versions, I'm including this here as a monkey patch. The point of SSHKit::Backend.current is to give code access to the current Backend without having to pass around a reference to it. For example, consider a `git` helper that executes a git command: def git(*args) SSHKit::Backend.current.execute(:git, *args) end Thanks to `current`, we can use this git method wherever we are in an `on` block, without having to pass a reference to `self`: on release_roles(:all) do git "status" end Without the thread local, the same task would be much more awkward due to the necessary passing of `self`: def git(backend, *args) backend.execute(:git, *args) end on release_roles(:all) do git self, "status" end To someone not intimately familiar with SSHKit, what `self` in this context is confusing and not obvious at all. It is because SSHKit uses `instance_exec` within the `on` block that `self` magically transforms. Better to avoid using `self` altogether and prefer the thread local. --- .../ext/sshkit/backend/thread_local.rb | 25 ++++++++++++++++ .../ext/sshkit/backend/thread_local_spec.rb | 30 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 lib/capistrano/ext/sshkit/backend/thread_local.rb create mode 100644 spec/lib/capistrano/ext/sshkit/backend/thread_local_spec.rb diff --git a/lib/capistrano/ext/sshkit/backend/thread_local.rb b/lib/capistrano/ext/sshkit/backend/thread_local.rb new file mode 100644 index 00000000..c8ffe16e --- /dev/null +++ b/lib/capistrano/ext/sshkit/backend/thread_local.rb @@ -0,0 +1,25 @@ +require "sshkit/backends/abstract" + +# Monkey patch older versions of SSHKit to make the currently-executing Backend +# available via a thread local value. +# +# TODO: Remove this code once capistrano.gemspec requires newer SSHKit version. + +unless SSHKit::Backend.respond_to?(:current) + module SSHKit + module Backend + def self.current + Thread.current["sshkit_backend"] + end + + class Abstract + def run + Thread.current["sshkit_backend"] = self + instance_exec(@host, &@block) + ensure + Thread.current["sshkit_backend"] = nil + end + end + end + end +end diff --git a/spec/lib/capistrano/ext/sshkit/backend/thread_local_spec.rb b/spec/lib/capistrano/ext/sshkit/backend/thread_local_spec.rb new file mode 100644 index 00000000..90d679db --- /dev/null +++ b/spec/lib/capistrano/ext/sshkit/backend/thread_local_spec.rb @@ -0,0 +1,30 @@ +require "spec_helper" +require "capistrano/ext/sshkit/backend/thread_local" + +module SSHKit + module Backend + describe "#current" do + require "sshkit/dsl" + + it "refers to the currently executing backend" do + backend = nil + current = nil + + on(:local) do + backend = self + current = SSHKit::Backend.current + end + + expect(current).to eq(backend) + end + + it "is nil outside of an on block" do + on(:local) do + # nothing + end + + expect(SSHKit::Backend.current).to be_nil + end + end + end +end