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



222
223
224
225
226
227
228
229
# File 'lib/unobtainium/driver.rb', line 222

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.



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

def impl
  @impl
end

#labelSymbol (readonly)

Returns the normalized label for the driver implementation.

Returns:

  • (Symbol)

    the normalized label for the driver implementation



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

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.



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

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.



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

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.



186
187
188
189
190
191
192
193
194
195
# File 'lib/unobtainium/driver.rb', line 186

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.



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
# File 'lib/unobtainium/driver.rb', line 156

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.



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/unobtainium/driver.rb', line 47

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.



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/unobtainium/driver.rb', line 82

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



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
149
# File 'lib/unobtainium/driver.rb', line 120

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)


213
214
215
216
217
218
# File 'lib/unobtainium/driver.rb', line 213

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