module Unobtainium::World

The World module combines other modules, defining simpler entry points into the gem's functionality.

Public Instance Methods

driver(label = nil, options = nil) click to toggle source

(see Driver#create)

Returns a driver instance with the given options. If no options are provided, options from the global configuration are used.

# File lib/unobtainium/world.rb, line 85
def driver(label = nil, options = nil)
  # Resolve unique options
  label, options = resolve_options(label, options)

  # Create a key for the label and options. This should always
  # return the same key for the same label and options.
  key = options['unobtainium_instance_id']
  if key.nil?
    key = identifier('driver', label, options)
  end

  # Only create a driver with this exact configuration once. Unfortunately
  # We'll have to bind the destructor to whatever configuration exists at
  # this point in time, so we have to create a proc here - whether the Driver
  # gets created or not.
  at_end = config.fetch("at_end", "quit")
  dtor = proc do |the_driver|
    # :nocov:
    if the_driver.nil?
      return
    end

    # We'll rescue Exception here because we really want all destructors
    # to run.
    # rubocop:disable Lint/RescueException
    begin
      meth = at_end.to_sym
      the_driver.send(meth)
    rescue Exception => err
      puts "Exception in destructor: [#{err.class}] #{err}"
    end
    # rubocop:enable Lint/RescueException
    # :nocov:
  end
  return ::Unobtainium::Runtime.instance.store_with_if(key, dtor) do
    ::Unobtainium::Driver.create(label, options)
  end
end

Private Instance Methods

resolve_options(label, options) click to toggle source

World's own option resolution ensures that the same options always get resolved the same, by storing anything resolved from Driver in the Runtime instance (i.e. asking the Driver only once per unique set of label and options).

# File lib/unobtainium/world.rb, line 131
def resolve_options(label, options)
  # Make sure we have a label for the driver
  if label.nil?
    label = config["driver"]
  end

  # Make sure we have options matching the driver
  if options.nil?
    options = config["drivers.#{label}"]
  end

  # The merged/extended options might define a "base"; that's the label
  # we need to use.
  if not options.nil? and not options["base"].nil?
    bases = options["base"]

    # Collapsium config returns an Array of bases, but we really only want
    # one. We'll have to do the sensible thing and only use one of the bases
    # which also is a driver for the label. Since there's no better choice,
    # let's default to the first of those.
    bases.each do |base|
      if not base.start_with?(".drivers.")
        next
      end
      label = base.gsub(/^\.drivers\./, '')
      break
    end

    # Unfortunately, the "base" key may not be recognized by the drivers,
    # which could lead to errors down the road. Let's remove it; it's reserved
    # by the Config class, so drivers can't use it anyhow.
    options = options.dup
    options.delete("base")
  end

  # Do we have options already resolved?
  option_key = identifier('options', label, options)
  begin
    stored_opts = ::Unobtainium::Runtime.instance.fetch(option_key)
    options = ::Collapsium::UberHash.new(options)
    options.recursive_merge!(stored_opts)
  rescue KeyError
    label, options, _ = ::Unobtainium::Driver.resolve_options(label, options)
  end

  # The driver may modify the options; if so, we should let it do that
  # here. That way our key (below) is based on the expanded options.
  ::Unobtainium::Runtime.instance.store(option_key, options)

  return label, options
end