Class: AutomateIt::Plugin::Manager

Inherits:
Base show all
Defined in:
lib/automateit/plugin/manager.rb

Overview

Plugin::Manager

A manager provides high-level wrappers for features, e.g. installing software packages. It does not actually implement these features, but instead manages a set of drivers. When one of the manager’s wrapper methods is called, it queries the drivers to find the most suitable one and dispatches the user’s request to that driver.

For example, the PlatformManager is a Manager class that manages a set of Driver instances, including PlatformManager::Uname and PlatformManager::LSB. When you invoke the high-level PlatformManager#query wrapper method, it interrogates the drivers to find which one is best-suited for this method and then dispatches the request to that driver’s low-level implementation of this method.

The manager subclasses typically have no functionality of their own and just contain wrapper methods and documentation.

Constant Summary

Constants included from Constants

Constants::HELPERS_DIR, Constants::INSTALL_DIR, Constants::PERROR, Constants::PEXEC, Constants::PNOTE, Constants::WARNING_BOILERPLATE

Instance Attribute Summary collapse

Attributes inherited from Common

#interpreter

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#token, token

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

#driversObject

Drivers for this manager as a hash of driver tokens to driver instances.



47
48
49
# File 'lib/automateit/plugin/manager.rb', line 47

def drivers
  @drivers
end

Class Method Details

.abstract_managerObject

Declare that this manager class is abstract. It can be subclassed but will not be instantiated by the Interpreter.



32
33
34
# File 'lib/automateit/plugin/manager.rb', line 32

def self.abstract_manager
  classes.delete(self)
end

.alias_methods(*methods) ⇒ Object

Methods to alias into the Interpreter, specified as an array of symbols.



40
41
42
43
# File 'lib/automateit/plugin/manager.rb', line 40

def self.alias_methods(*methods)
  self.aliased_methods ||= Set.new
  self.aliased_methods.merge(methods.flatten)
end

.driver_classesObject

Driver classes used by this Manager.



50
51
52
# File 'lib/automateit/plugin/manager.rb', line 50

def self.driver_classes
  Driver.classes[token] || []
end

.inherited(subclass) ⇒ Object

Register managers.



26
27
28
# File 'lib/automateit/plugin/manager.rb', line 26

def self.inherited(subclass) # :nodoc:
  classes << subclass unless classes.include?(subclass)
end

Instance Method Details

#[](token) ⇒ Object

Returns the Driver with the specified token. E.g., :apt will return the APT driver.



90
91
92
# File 'lib/automateit/plugin/manager.rb', line 90

def [](token)
  return @drivers[token]
end

#available?(method, *args, &block) ⇒ Boolean

Is a driver available for this method and args? Uses automatic-detection routines and returns a boolean to indicate if a suitable driver is available. Unlike #driver_for, this will not raise an exception.

Returns:

  • (Boolean)


214
215
216
217
218
219
220
221
# File 'lib/automateit/plugin/manager.rb', line 214

def available?(method, *args, &block)
  begin
    driver_for(method, *args, &block)
    true
  rescue NotImplementedError
    false
  end
end

#default(token = nil) ⇒ Object

Manipulate the default driver. Without arguments, gets the driver token as a symbol. With argument, sets the default driver to the token, e.g., the argument :apt will make the APT driver the default.



97
98
99
100
101
102
103
# File 'lib/automateit/plugin/manager.rb', line 97

def default(token=nil)
  if token.nil?
    @default
  else
    @default = token
  end
end

#default=(token) ⇒ Object

Set the default driver to the token. See also #default.



106
107
108
# File 'lib/automateit/plugin/manager.rb', line 106

def default=(token)
  default(token)
end

#dispatch(*args, &block) ⇒ Object

Dispatch a method by guessing its name. This is the recommended way to write wrappers for a Manager methods.

Example:

class MyManager < Plugin::Manager
  # Your RDoc here
  def my_method(*args)
    # Will guess that you want to +dispatch_to+ the +my_method+ by
    # introspecting the name of the wrapper method.
    dispatch(*args)
  end
  ...
end


123
124
125
126
127
# File 'lib/automateit/plugin/manager.rb', line 123

def dispatch(*args, &block)
  # Extract caller's method as symbol to save user from having to specify it
  method = caller[0].match(/:in `(.+?)'/)[1].to_sym
  dispatch_to(method, *args, &block)
end

#dispatch_safely(*args, &block) ⇒ Object

Same as #dispatch but returns nil if couldn’t dispatch, rather than raising an exception.



171
172
173
174
# File 'lib/automateit/plugin/manager.rb', line 171

def dispatch_safely(*args, &block)
  method = caller[0].match(/:in `(.+?)'/)[1].to_sym
  dispatch_safely_to(method, *args, &block)
end

#dispatch_safely_to(method, *args, &block) ⇒ Object

Same as #dispatch_to but returns nil if couldn’t dispatch, rather than raising an exception.



161
162
163
164
165
166
167
# File 'lib/automateit/plugin/manager.rb', line 161

def dispatch_safely_to(method, *args, &block)
  begin
    dispatch_to(method, *args, &block)
  rescue NotImplementedError
    nil
  end
end

#dispatch_to(method, *args, &block) ⇒ Object

Dispatch the method with args and block to the appropriate driver. If the arguments include an option of the form :with => :mytoken argument, then the method will be dispatched to the driver represented by :mytoken. If no :with argument is specified, the most-suitable driver will be automatically selected. If no suitable driver is available, a NotImplementedError will be raised.

Examples:

# Run 'hostnames' method on most appropriate AddressManager driver:
address_manager.dispatch_to(:hostnames)

# Run AddressManager::Portable#hostnames
address_manager.dispatch_to(:hostnames, :with => :portable)

You will typically not want to use this method directly and instead write wrappers that call #dispatch because it can guess the name of the method argument for you.



146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/automateit/plugin/manager.rb', line 146

def dispatch_to(method, *args, &block)
  list, options = args_and_opts(*args)
  driver = \
    if options and options[:with]
      @drivers[options[:with].to_sym]
    elsif default
      @drivers[default.to_sym]
    else
      driver_for(method, *args, &block)
    end
  driver.send(method, *args, &block)
end

#driver_for(method, *args, &block) ⇒ Object

Get the most suitable driver for this method and args. Uses automatic-detection routines and returns the most suitable driver instance found, else raises a NotImplementedError if no suitable driver is found.



201
202
203
204
205
206
207
208
# File 'lib/automateit/plugin/manager.rb', line 201

def driver_for(method, *args, &block)
  driver, level = driver_suitability_levels_for(method, *args, &block).sort_by{|k,v| v}.last
  if driver and level > 0
    return @drivers[driver]
  else
    raise NotImplementedError.new("can't find driver for method '#{method}' with arguments: #{args.inspect}")
  end
end

#driver_suitability_levels_for(method, *args, &block) ⇒ Object

Returns structure which helps choose a suitable driver for the method and args. Result is a hash of driver tokens and their suitability levels.

For example, if we ask the AddressManager for suitability levels for the AddressManager#hostnames method, we might find that there are two drivers (:portable is the token for AddressManager::Portable) and that the :linux driver is most appropriate because it has the highest suitability level:

address_manager.driver_suitability_levels_for(:hostnames)
# => {:portable=>1, :linux=>2}


188
189
190
191
192
193
194
195
# File 'lib/automateit/plugin/manager.rb', line 188

def driver_suitability_levels_for(method, *args, &block)
  results = {}
  @drivers.each_pair do |name, driver|
    next unless driver.respond_to?(method)
    results[name] = driver.suitability(method, *args, &block)
  end
  return results
end

#instantiate_driversObject

Instantiate drivers for this manager. This method is smart enough that it can be called multiple times and will only instantiate drivers it hasn’t instantiated yet. All drivers will share an instance of the Interpreter, thus providing common state storage.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/automateit/plugin/manager.rb', line 73

def instantiate_drivers
  @drivers ||= {}
  self.class.driver_classes.each do |driver_class|
    driver_token = driver_class.token
    unless @drivers[driver_token]
      @drivers[driver_token] = driver_class.allocate
    end
  end
  self.class.driver_classes.each do |driver_class|
    driver_token = driver_class.token
    @drivers[driver_token].setup(
      :interpreter => @interpreter, :manager => self)
  end
end

#setup(opts = {}) ⇒ Object

Options:

  • :default – The token of the driver to set as the default.



56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/automateit/plugin/manager.rb', line 56

def setup(opts={})
  super(opts)

  @default ||= nil

  instantiate_drivers

  if driver = opts[:default] || opts[:driver]
    default(opts[:default]) if opts[:default]
    @drivers[driver].setup(opts)
  end
end