Class: AutomateIt::Plugin::Driver
- Defined in:
- lib/automateit/plugin/driver.rb
Overview
Plugin::Driver
A driver provides the low-level functionality for features, e.g., the PackageManager::APT driver is responsible for installing a software package using the Debian apt-get
command. Multiple drivers providing common functionality are managed by a single Manager class, e.g., drivers that install software packages are managed by the PackageManager.
A driver may only be available on certain platforms and provides its manager with an idea of when it’s suitable. For example, if a platform doesn’t have the apt-get
command, the PackageManager::APT driver must tell the PackageManager class that it’s not suitable.
Writing your own drivers
To write a driver, find the most similar driver available for a specific plugin, copy it, and rework its code. Save the code for the new driver in a file ending with .rb into the project’s lib
directory, it will be automatically loaded whenever the Interpreter for that project is run. Please test and contribute drivers so that others can benefit.
IMPORTANT GOTCHA: You must prefix the AutomateIt module name with a “::”!
Here’s a minimalistic PackageManager that can be dropped into lib
:
class MyDriver < ::AutomateIt::PackageManager::BaseDriver
depends_on :nothing
def suitability(method, *args) # :nodoc:
# Never select as default driver
return 0
end
end
Direct Known Subclasses
AccountManager::BaseDriver, AddressManager::BaseDriver, DownloadManager::BaseDriver, EditManager::BaseDriver, FieldManager::BaseDriver, AutomateIt::PlatformManager::BaseDriver, ServiceManager::BaseDriver, ShellManager::BaseDriver, TagManager::BaseDriver, TemplateManager::BaseDriver
Constant Summary collapse
- BASE_DRIVER_NAME =
"BaseDriver"
Constants included from Constants
Constants::PERROR, Constants::PEXEC, Constants::PNOTE, Constants::WARNING_BOILERPLATE
Instance Attribute Summary collapse
-
#manager ⇒ Object
Returns the Plugin::Manager instance for this Driver.
Attributes inherited from Common
Class Method Summary collapse
-
.abstract_driver ⇒ Object
Declare that this driver class is abstract.
-
.base_driver ⇒ Object
Retrieve the base driver class for this driver.
-
.base_driver? ⇒ Boolean
Is this a base driver?.
-
.depends_on(opts) ⇒ Object
Defines resources this driver depends on.
-
.inherited(subclass) ⇒ Object
Register drivers.
-
.manager_token ⇒ Object
Retrieve the manager token for this driver.
Instance Method Summary collapse
-
#available? ⇒ Boolean
Is this driver available on this platform? Queries the dependencies set by #depends_on to make sure that they’re all present, otherwise raises a NotImplementedError.
-
#setup(opts = {}) ⇒ Object
Setup a Driver.
-
#suitability(method, *args, &block) ⇒ Object
What is this driver’s suitability for automatic detection? The Manager queries its drivers when there isn’t a driver specified with a
:with
or #default so it can choose a suitable driver for themethod
andargs
.
Methods inherited from Base
Methods inherited from Common
#initialize, #log, #nitpick, #noop, #noop=, #noop?, #preview, #preview=, #preview?, #preview_for, #superuser?, #writing, #writing=, #writing?
Constructor Details
This class inherits a constructor from AutomateIt::Common
Instance Attribute Details
#manager ⇒ Object
Returns the Plugin::Manager instance for this Driver.
49 50 51 |
# File 'lib/automateit/plugin/driver.rb', line 49 def manager @manager end |
Class Method Details
.abstract_driver ⇒ Object
Declare that this driver class is abstract. It can be subclassed but will not be instantiated by the Interpreter’s Managers. BaseDriver classes are automatically declared abstract.
102 103 104 105 106 107 108 109 110 |
# File 'lib/automateit/plugin/driver.rb', line 102 def self.abstract_driver if base_driver? # Ignore, base drivers should never have been registered elsif manager = manager_token classes[manager].delete(self) else raise TypeError.new("Can't find manager for abstract plugin: #{self}") end end |
.base_driver ⇒ Object
Retrieve the base driver class for this driver.
74 75 76 |
# File 'lib/automateit/plugin/driver.rb', line 74 def self.base_driver ancestors.select{|t| t.to_s =~ /::#{BASE_DRIVER_NAME}/}.last end |
.base_driver? ⇒ Boolean
Is this a base driver?
69 70 71 |
# File 'lib/automateit/plugin/driver.rb', line 69 def self.base_driver? to_s =~ /::#{BASE_DRIVER_NAME}/ end |
.depends_on(opts) ⇒ Object
Defines resources this driver depends on.
Options:
-
:files – Array of filenames that must exist.
-
:directories – Array of directories that must exist.
-
:programs – Array of programs, checked with
which
, that must exist. -
:callbacks – Array of lambdas that must return true.
-
:libraries – Array of libraries to
require
.
Example:
class APT < Plugin::Driver
depends_on :programs => %w(apt-get dpkg)
# ...
end
126 127 128 129 130 131 |
# File 'lib/automateit/plugin/driver.rb', line 126 def self.depends_on(opts) do attr_accessor :_depends_on_opts, :_is_available, :_missing_dependencies end self._depends_on_opts = opts end |
.inherited(subclass) ⇒ Object
Register drivers. Concrete drivers are added to a class-wide data structure which maps them to the manager they belong to.
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/automateit/plugin/driver.rb', line 80 def self.inherited(subclass) # :nodoc: base_driver = subclass.base_driver if subclass.base_driver? # Ignore, base drivers should never be registered elsif base_driver manager_token = subclass.manager_token classes[manager_token] ||= [] unless classes[manager_token].include?(subclass) classes[manager_token] << subclass end ### puts "manager_token %s / driver %s" % [manager_token, subclass] else # XXX Should this really raise an exception? raise TypeError.new("Can't determine manager for driver: #{subclass}") end end |
.manager_token ⇒ Object
Retrieve the manager token for this driver
61 62 63 64 |
# File 'lib/automateit/plugin/driver.rb', line 61 def self.manager_token fragments = base_driver.to_s.split(/::/) return fragments[fragments.size-2].underscore.to_sym end |
Instance Method Details
#available? ⇒ Boolean
Is this driver available on this platform? Queries the dependencies set by #depends_on to make sure that they’re all present, otherwise raises a NotImplementedError. If a driver author needs to do some other kind of check, it’s reasonable to override this method.
For example, this method is used by the PackageManager driver for APT to determine if the “apt-get” program is installed.
What’s the difference between #available? and #suitability? The #available? method is used to determine if the driver can do the work, while the #suitability method determines if the driver should be automatically selected.
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/automateit/plugin/driver.rb', line 145 def available? # Some drivers don't run +depends_on+, so assume they're available. return true unless self.class.respond_to?(:_depends_on_opts) opts = self.class._depends_on_opts # Driver said that it +depends_on :nothing+, so it's available. return true if opts == :nothing is_available = self.class._is_available if is_available.nil? and opts.nil? #log.debug(PNOTE+"don't know if driver #{self.class} is available, maybe it doesn't state what it +depends_on+") return false elsif is_available.nil? all_present = true missing = {} for kind in opts.keys #IK# for kind in [:files, :directories, :programs, :callbacks, :requires, :libraries] next unless opts[kind] for item in [opts[kind]].flatten present = \ case kind when :files File.exists?(item) when :directories File.directory?(item) when :programs # XXX Find less awkward way to check if a program exists. Can't use +shell_manager.which+ because that will use +dispatch+ and go into an infinite loop checking +available?+. The +which+ command isn't available on all platforms, so that failure must be handled as well. begin interpreter.shell_manager[:which].which!(item) true rescue ArgumentError, NotImplementedError, NoMethodError false end when :requires, :libraries begin require item true rescue LoadError false end when :callbacks item.call() ? true : false else raise "unknown kind: #{kind}" end unless present all_present = false missing[kind] ||= [] missing[kind] << item end end end self.class._missing_dependencies = missing self.class._is_available = all_present log.debug(PNOTE+"Driver #{self.class} #{all_present ? "is" : "isn't"} available") end return self.class._is_available end |
#setup(opts = {}) ⇒ Object
Setup a Driver.
Options:
-
:manager – The Plugin::Manager instance controlling this driver.
55 56 57 58 |
# File 'lib/automateit/plugin/driver.rb', line 55 def setup(opts={}) self.manager = opts[:manager] if opts[:manager] super(opts) end |
#suitability(method, *args, &block) ⇒ Object
What is this driver’s suitability for automatic detection? The Manager queries its drivers when there isn’t a driver specified with a :with
or #default so it can choose a suitable driver for the method
and args
. Any driver that returns an integer 1 or greater claims to be suitable. The Manager will then select the driver with the highest suitability level. Drivers that return an integer less than 1 are excluded from automatic detection.
The available?
method is used to determine if the driver can do the work, while the suitability
method determines if the driver should be automatically selected.
229 230 231 232 |
# File 'lib/automateit/plugin/driver.rb', line 229 def suitability(method, *args, &block) log.debug("driver #{self.class} doesn't implement the +suitability+ method") return -1 end |