class MacWifi::BaseModel

Public Class Methods

new(verbose = false) click to toggle source
# File lib/mac-wifi/base_model.rb, line 20
def initialize(verbose = false)
  @verbose_mode = verbose
end

Public Instance Methods

connect(network_name, password = nil) click to toggle source

Connects to the passed network name, optionally with password. Turns wifi on first, in case it was turned off. Relies on subclass implementation of os_level_connect().

# File lib/mac-wifi/base_model.rb, line 127
def connect(network_name, password = nil)
  # Allow symbols and anything responding to to_s for user convenience
  network_name = network_name.to_s if network_name
  password     = password.to_s     if password

  if network_name.nil? || network_name.empty?
    raise "A network name is required but was not provided."
  end
  wifi_on
  os_level_connect(network_name, password)

  # Verify that the network is now connected:
  actual_network_name = connected_network_name
  unless actual_network_name == network_name
    message = %Q{Expected to connect to "#{network_name}" but }
    if actual_network_name
      message << %Q{connected to "#{connected_network_name}" instead.}
    else
      message << "unable to connect to any network. Did you "
    end
    message << (password ? "provide the correct password?" : "need to provide a password?")
    raise message
  end
  nil
end
connected_to?(network_name) click to toggle source
# File lib/mac-wifi/base_model.rb, line 119
def connected_to?(network_name)
  network_name == connected_network_name
end
connected_to_internet?() click to toggle source

TODO Investigate using Curl options: –connect-timeout 1 –max-time 2 –retry 0 to greatly simplify this method.

# File lib/mac-wifi/base_model.rb, line 55
def connected_to_internet?

  tempfile = Tempfile.open('mac-wifi-')

  begin
    start_status_script = -> do
      script = "curl --silent --head http://www.google.com/ > /dev/null ; echo $? > #{tempfile.path} &"
      pid = Process.spawn(script)
      Process.detach(pid)
      pid
    end

    process_is_running = ->(pid) do
      script = %Q{ps -p #{pid} > /dev/null; echo $?}
      output = `#{script}`.chomp
      output == "0"
    end

    get_connected_state_from_curl = -> do
      tempfile.close
      File.read(tempfile.path).chomp == '0'
    end

    # Do one run, iterating during the timeout period to see if the command has completed
    do_one_run = -> do
      end_time = Time.now + 3
      pid = start_status_script.()
      while Time.now < end_time
        if process_is_running.(pid)
          sleep 0.5
        else
          return get_connected_state_from_curl.()
        end
      end
      Process.kill('KILL', pid) if process_is_running.(pid)
      :hung
    end

    3.times do
      connected = do_one_run.()
      return connected if connected != :hung
    end

    raise "Could not determine Internet status."

  ensure
    tempfile.unlink
  end

end
cycle_network() click to toggle source

Turns wifi off and then on, reconnecting to the originally connecting network.

# File lib/mac-wifi/base_model.rb, line 108
def cycle_network
  # TODO: Make this network name saving and restoring conditional on it not having a password.
  # If the disabled code below is enabled, an error will be raised if a password is required,
  # even though it is stored.
  # network_name = current_network
  wifi_off
  wifi_on
  # connect(network_name) if network_name
end
nameservers() click to toggle source

@return array of nameserver IP addresses from /etc/resolv.conf, or nil if not found Though this is strictly not OS-agnostic, it will be used by most OS's, and can be overridden by subclasses (e.g. Windows).

# File lib/mac-wifi/base_model.rb, line 233
def nameservers
  begin
    File.readlines('/etc/resolv.conf').grep(/^nameserver /).map { |line| line.split.last }
  rescue Errno::ENOENT
    nil
  end
end
preferred_network_password(preferred_network_name) click to toggle source
# File lib/mac-wifi/base_model.rb, line 163
def preferred_network_password(preferred_network_name)
  preferred_network_name = preferred_network_name.to_s
  if preferred_networks.include?(preferred_network_name)
    os_level_preferred_network_password(preferred_network_name)
  else
    raise "Network #{preferred_network_name} not in preferred networks list."
  end
end
public_ip_address_info() click to toggle source

Reaches out to ipinfo.io to get public IP address information in the form of a hash. You may need to enclose this call in a begin/rescue.

# File lib/mac-wifi/base_model.rb, line 225
def public_ip_address_info
  JSON.parse(`curl -s ipinfo.io`)
end
remove_preferred_networks(*network_names) click to toggle source

Removes the specified network(s) from the preferred network list. @param network_names names of networks to remove; may be empty or contain nonexistent networks @return names of the networks that were removed (excludes non-preexisting networks)

# File lib/mac-wifi/base_model.rb, line 157
def remove_preferred_networks(*network_names)
  networks_to_remove = network_names & preferred_networks # exclude any nonexistent networks
  networks_to_remove.each { |name| remove_preferred_network(name) }
end
run_os_command(command) click to toggle source
# File lib/mac-wifi/base_model.rb, line 25
def run_os_command(command)
  output = `#{command} 2>&1` # join stderr with stdout
  if $?.exitstatus != 0
    raise OsCommandError.new($?.exitstatus, command, output)
  end
  if @verbose_mode
    puts "\n\n#{'-' * 79}\nCommand: #{command}\n\nOutput:\n#{output}#{'-' * 79}\n\n"
  end
  output
end
till(target_status, wait_interval_in_secs = nil) click to toggle source

Waits for the Internet connection to be in the desired state. @param target_status must be in [:conn, :disc, :off, :on]; waits for that state @param wait_interval_in_secs sleeps this interval between retries; if nil or absent,

a default will be provided
# File lib/mac-wifi/base_model.rb, line 178
def till(target_status, wait_interval_in_secs = nil)

  # One might ask, why not just put the 0.5 up there as the default argument.
  # We could do that, but we'd still need the line below in case nil
  # was explicitly specified. The default argument of nil above emphasizes that
  # the absence of an argument and a specification of nil will behave identically.
  wait_interval_in_secs ||= 0.5

  finished_predicates = {
      conn: -> { connected_to_internet? },
      disc: -> { ! connected_to_internet? },
      on:   -> { wifi_on? },
      off:  -> { ! wifi_on? }
  }

  finished_predicate = finished_predicates[target_status]

  if finished_predicate.nil?
    raise ArgumentError.new(
        "Option must be one of #{finished_predicates.keys.inspect}. Was: #{target_status.inspect}")
  end

  loop do
    return if finished_predicate.()
    sleep(wait_interval_in_secs)
  end
end
try_os_command_until(command, stop_condition, max_tries = 100) click to toggle source

Tries an OS command until the stop condition is true. @command the command to run in the OS @stop_condition a lambda taking the commands stdout as its sole parameter @return the stdout produced by the command

# File lib/mac-wifi/base_model.rb, line 211
def try_os_command_until(command, stop_condition, max_tries = 100)
  max_tries.times do
    stdout = run_os_command(command)
    if stop_condition.(stdout)
      return stdout
    end
  end
  nil
end