class Unobtainium::Driver
Creating a Driver
instance creates either an Appium or Selenium driver depending on the arguments, and delegates all else to the underlying implementation.
It's possible to add more drivers, but Appium and Selenium are the main targets.
Constants
- DRIVER_METHODS
Methods that drivers must implement
- MODULE_METHODS
Methods that driver modules must implement
Attributes
@return [Object] the driver implementation itself; do not use this unless
you have to.
@return [Symbol] the normalized label for the driver implementation
@return [Hash] the options hash the driver implementation is using.
Public Class Methods
Create a driver instance with the given arguments.
@param label [String, Symbol] Label for the driver to create. Driver
implementations may accept normalized and alias labels, e.g. `:firefox, `:ff`, `:remote`, etc.
@param opts [Hash] Options passed to the driver implementation.
# File lib/unobtainium/driver.rb, line 29 def create(label, opts = nil) new(label, opts) end
@api private Out of the loaded drivers, returns the one matching the label (if any). @param label [Symbol] The label matching a driver.
# File lib/unobtainium/driver.rb, line 185 def get_driver(label) # Of all the loaded classes, choose the first (unsorted) to match the # requested driver label @@drivers.keys.each do |klass| if klass.matches?(label) return klass end end return nil end
@api private Load drivers; this loads all driver implementations included in this gem. You can register external implementations with the :register_implementation method.
# File lib/unobtainium/driver.rb, line 155 def load_drivers pattern = File.join(File.dirname(__FILE__), 'drivers', '*.rb') Dir.glob(pattern).each do |fpath| # Determine class name from file name fname = File.basename(fpath, '.rb') fname = fname.split('_').map(&:capitalize).join begin require fpath klassname = 'Unobtainium::Drivers::' + fname klass = Object.const_get(klassname) Driver.register_implementation(klass, fpath) rescue LoadError => err # :nocov: raise LoadError, "#{err.message}: unknown problem loading driver, "\ "aborting!" # :nocov: rescue NameError => err # :nocov: raise LoadError, "#{err.message}: unknown problem loading driver, "\ "aborting!" # :nocov: end end end
Initializer
# File lib/unobtainium/driver.rb, line 234 def initialize(label, opts = nil) # Sanitize options @label, @options, driver_klass = ::Unobtainium::Driver.resolve_options( label, opts ) # Perform precondition checks of the driver class driver_klass.ensure_preconditions(@label, @options) # Great, instanciate! opts = nil if not @options.nil? opts = @options.dup opts.delete('unobtainium_instance_id') end @impl = driver_klass.create(@label, opts) # Now also extend this implementation with all the modues that match @@modules.each do |klass, _| if klass.matches?(@impl) @impl.extend(klass) end end end
Add a new driver implementation. The first parameter is the class itself, the second should be a file path pointing to the file where the class is defined. You would typically pass `__FILE__` for the second parameter.
Using file names lets us figure out whether the class is a duplicate, or merely a second registration of the same class.
Driver
classes must implement the class methods listed in `DRIVER_METHODS`.
@param klass (Class) Driver
implementation class to register. @param path (String) Implementation path of the driver class.
# File lib/unobtainium/driver.rb, line 46 def register_implementation(klass, path) # We need to deal with absolute paths only fpath = File.absolute_path(path) # Figure out if the class implements all the methods we need; we're not # checking for anything else. klass_methods = klass.methods - klass.instance_methods - Object.methods if DRIVER_METHODS - klass_methods != [] raise LoadError, "Driver #{klass.name} is not implementing all of "\ "the class methods #{DRIVER_METHODS}, aborting!" end # The second question is whether the same class is already known, or # whether a class with the same name but under a different location is # known. if @@drivers.include?(klass) and @@drivers[klass] != fpath raise LoadError, "Driver #{klass.name} is duplicated in file "\ "'#{fpath}'; previous definition is here: "\ "'#{@@drivers[klass]}'" end # If all of that was ok, we can register the implementation. @@drivers[klass] = fpath end
Add a new driver module. The first parameter is the class itself, the second should be a file path pointing to the file where the class is defined. You would typically pass `__FILE__` for the second parameter.
Driver
modules must implement the class methods listed in `MODULE_METHODS`.
@param klass (Class) Driver
implementation class to register. @param path (String) Implementation path of the driver class.
# File lib/unobtainium/driver.rb, line 81 def register_module(klass, path) # We need to deal with absolute paths only fpath = File.absolute_path(path) # Figure out if the class implements all the methods we need; we're not # checking for anything else. klass_methods = klass.methods - klass.instance_methods - Object.methods if MODULE_METHODS - klass_methods != [] raise LoadError, "Driver module #{klass.name} is not implementing all "\ "of the class methods #{MODULE_METHODS}, aborting!" end # The second question is whether the same class is already known, or # whether a class with the same name but under a different location is # known. if @@modules.include?(klass) and @@modules[klass] != fpath raise LoadError, "Driver module #{klass.name} is duplicated in file "\ "'#{fpath}'; previous definition is here: "\ "'#{@@modules[klass]}'" end # If all of that was ok, we can register the implementation. @@modules[klass] = fpath end
@api private Resolves everything to do with driver options:
-
Normalizes the label
-
Loads the driver class
-
Normalizes and extends options from the driver implementation
@param label [Symbol, String] the driver label @param opts [Hash] driver options
# File lib/unobtainium/driver.rb, line 119 def resolve_options(label, opts = nil) if label.nil? or label.empty? raise ArgumentError, "Need at least one argument specifying the driver!" end label = label.to_sym if not opts.nil? if not opts.is_a? Hash raise ArgumentError, "The second argument is expected to be an "\ "options Hash!" end end # Get the driver class. load_drivers driver_klass = get_driver(label) if not driver_klass raise LoadError, "No driver implementation matching #{label} found, "\ "aborting!" end # Sanitize options according to the driver's idea options = opts if driver_klass.respond_to?(:resolve_options) label, options = driver_klass.resolve_options(label, opts) end return label, options, driver_klass end
Public Instance Methods
Map any missing method to the driver implementation
# File lib/unobtainium/driver.rb, line 221 def method_missing(meth, *args, &block) if not @impl.nil? and @impl.respond_to?(meth) return @impl.send(meth.to_s, *args, &block) end # :nocov: return super # :nocov: end
Map any missing method to the driver implementation
# File lib/unobtainium/driver.rb, line 212 def respond_to_missing?(meth, include_private = false) if not @impl.nil? and @impl.respond_to?(meth, include_private) return true end return super end