Class: Unobtainium::Driver

Inherits:
Object
  • Object
show all
Defined in:
lib/unobtainium/driver.rb

Overview

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.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args, &block) ⇒ Object

Map any missing method to the driver implementation



221
222
223
224
225
226
227
228
# 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

Instance Attribute Details

#implObject (readonly)

Returns the driver implementation itself; do not use this unless you have to.

Returns:

  • (Object)

    the driver implementation itself; do not use this unless you have to.



208
209
210
# File 'lib/unobtainium/driver.rb', line 208

def impl
  @impl
end

#labelSymbol (readonly)

Returns the normalized label for the driver implementation.

Returns:

  • (Symbol)

    the normalized label for the driver implementation



201
202
203
# File 'lib/unobtainium/driver.rb', line 201

def label
  @label
end

#optionsHash (readonly)

Returns the options hash the driver implementation is using.

Returns:

  • (Hash)

    the options hash the driver implementation is using.



204
205
206
# File 'lib/unobtainium/driver.rb', line 204

def options
  @options
end

Class Method Details

.create(label, opts = nil) ⇒ Object

Create a driver instance with the given arguments.

Parameters:

  • label (String, Symbol)

    Label for the driver to create. Driver implementations may accept normalized and alias labels, e.g. ‘:firefox, `:ff`, `:remote`, etc.

  • opts (Hash) (defaults to: nil)

    Options passed to the driver implementation.



29
30
31
# File 'lib/unobtainium/driver.rb', line 29

def create(label, opts = nil)
  new(label, opts)
end

.get_driver(label) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Out of the loaded drivers, returns the one matching the label (if any).

Parameters:

  • label (Symbol)

    The label matching a driver.



185
186
187
188
189
190
191
192
193
194
# 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

.load_driversObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Load drivers; this loads all driver implementations included in this gem. You can register external implementations with the :register_implementation method.



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
# 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

.register_implementation(klass, path) ⇒ Object

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`.

Parameters:

  • klass (Class)

    Driver implementation class to register.

  • path (String)

    Implementation path of the driver class.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# 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

.register_module(klass, path) ⇒ Object

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`.

Parameters:

  • klass (Class)

    Driver implementation class to register.

  • path (String)

    Implementation path of the driver class.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# 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

.resolve_options(label, opts = nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Resolves everything to do with driver options:

  • Normalizes the label

  • Loads the driver class

  • Normalizes and extends options from the driver implementation

Parameters:

  • label (Symbol, String)

    the driver label

  • opts (Hash) (defaults to: nil)

    driver options



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# 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

Instance Method Details

#respond_to_missing?(meth, include_private = false) ⇒ Boolean

Map any missing method to the driver implementation

Returns:

  • (Boolean)


212
213
214
215
216
217
# 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