wifi-wand 2.10.1 → 2.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 301b4e83a32c377d251c8cdef985299c26220e4c7322ba72fe43488aa050b94e
4
- data.tar.gz: 1880892a4a794728a932ca5e42830ca546c46ce55403d0cf3b2059d92381c707
3
+ metadata.gz: 5f21e7e9af16e0eea5c7d254ef75ce061fbae1ada6c707201a189ec78f9238be
4
+ data.tar.gz: ec4b2ef369ca6e3186fe02b59e57c059f89a17b03eba2cdf760035b93fdaa9b9
5
5
  SHA512:
6
- metadata.gz: a8aede3fee3958093e2c5cca0dbf8c01118d79cb2cca7cac815d83aa69f1c45a51f6fb94846cf0ec9bbbf6e5a88e29dd3644af1f5a51d3ad7d191a3c07238134
7
- data.tar.gz: 6a7ed65e40957546032570d54e36db58157885de5bc4e11723e4564e7b4d0040ec433876c8a5b5c67943130be5293ae6ad2c159cabf82147464e7abb9338fcb3
6
+ metadata.gz: a86d2fd563801ca4a3259d200de197d8811c6f46360a805b85779fc488107045c8df20f6013c69866b61bdddcc40f11ff77a338bb2e0984eef8690d86a2016fd
7
+ data.tar.gz: cd16ad95aed459d769e51539a911797f004be30e7ad9dfab8c6352d2aaa226cbadcbe46d5d0c4075c58b6f478bb1ccb3f6a66408aa932b1111cf0205d5ea2e3c
data/RELEASE_NOTES.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## v2.11.0
2
+
3
+ * Various fixes and clarifications.
4
+ * Change implementation of available_network_names to use REXML; first implemented w/position number, then XPath.
5
+ * Add attempt count to try_os_command_until in verbose mode.
6
+
1
7
  ## v2.10.1
2
8
 
3
9
  * Fix egregious bug; the 'a' command did not work if `airport` was not in the path; I should have been using the AIRPORT_CMD constant but hard coded `airport` instead.
@@ -1,5 +1,6 @@
1
1
  require_relative 'operating_systems'
2
2
  require 'ostruct'
3
+ require_relative 'error'
3
4
  require_relative 'version'
4
5
 
5
6
  module WifiWand
@@ -79,11 +80,11 @@ ro[pen] - open resource (#{OPEN_RESOURCES.help_string})
79
80
  t[ill] - returns when the desired Internet connection state is true. Options:
80
81
  1) 'on'/:on, 'off'/:off, 'conn'/:conn, or 'disc'/:disc
81
82
  2) wait interval, in seconds (optional, defaults to 0.5 seconds)
82
- w[ifion] - is the wifi on?
83
+ w[ifi_on] - is the wifi on?
83
84
  x[it] - exits this program (interactive shell mode only) (see also 'q')
84
85
 
85
86
  When in interactive shell mode:
86
- * use quotes for string parameters such as method names.
87
+ * remember to quote string literals.
87
88
  * for pry commands, use prefix `%`.
88
89
 
89
90
  "
@@ -91,7 +92,7 @@ When in interactive shell mode:
91
92
  def initialize(options)
92
93
  @options = options
93
94
  current_os = OperatingSystems.new.current_os
94
- raise "Could not determine operating system" if current_os.nil?
95
+ raise Error.new("Could not determine operating system") if current_os.nil?
95
96
  model_options = OpenStruct.new({
96
97
  verbose: options.verbose,
97
98
  wifi_port: options.wifi_port
@@ -107,8 +108,6 @@ When in interactive shell mode:
107
108
  end
108
109
 
109
110
 
110
- # Until command line option parsing is added, the only way to specify
111
- # verbose mode is in the environment variable MAC_WIFI_OPTS.
112
111
  def verbose_mode
113
112
  options.verbose
114
113
  end
@@ -177,9 +176,9 @@ When in interactive shell mode:
177
176
  begin
178
177
  require 'pry'
179
178
  rescue LoadError
180
- puts "The 'pry' gem and/or one of its prerequisites, required for running the shell, was not found." +
181
- " Please `gem install pry` or, if necessary, `sudo gem install pry`."
182
- exit(-1)
179
+ message = "The 'pry' gem and/or one of its prerequisites, required for running the shell, was not found." +
180
+ " Please `gem install pry` or, if necessary, `sudo gem install pry`."
181
+ raise Error.new(message)
183
182
  end
184
183
 
185
184
  print_help
@@ -197,13 +196,25 @@ When in interactive shell mode:
197
196
  end
198
197
 
199
198
 
200
- # For use by the shell; when typing a command and options, it is passed to process_command_line
201
- def method_missing(method_name, *method_args)
202
- method_name = method_name.to_s
203
- if find_command_action(method_name)
204
- process_command_line(method_name, method_args)
199
+ # Look up the command name and, if found, run it. If not, execute the passed block.
200
+ def attempt_command_action(command, *args, &error_handler_block)
201
+ action = find_command_action(command)
202
+
203
+ if action
204
+ action.(*args)
205
205
  else
206
- puts(%Q{"#{method_name}" is not a valid command or option. If you intend for this to be a string literal, use quotes or %q/Q{}.})
206
+ error_handler_block.call
207
+ nil
208
+ end
209
+ end
210
+
211
+
212
+ # For use by the shell when the user types the DSL commands
213
+ def method_missing(method_name, *method_args)
214
+ attempt_command_action(method_name.to_s, *method_args) do
215
+ puts(%Q{"#{method_name}" is not a valid command or option. } \
216
+ << 'If you intend for this to be a string literal, ' \
217
+ << 'use quotes or %q{}/%Q{}.')
207
218
  end
208
219
  end
209
220
 
@@ -214,18 +225,16 @@ When in interactive shell mode:
214
225
  # be in a form that the Ruby interpreter will recognize as a string,
215
226
  # i.e. single or double quotes, %q, %Q, etc.
216
227
  # Otherwise it will assume it's a method name and pass it to method_missing!
217
- def process_command_line(command, args)
218
- action = find_command_action(command)
219
- if action
220
- action.(*args)
221
- else
228
+ def process_command_line
229
+ attempt_command_action(ARGV[0], *ARGV[1..-1]) do
222
230
  print_help
223
231
  raise BadCommandError.new(
224
- %Q{Unrecognized command. Command was "#{command}" and options were #{args.inspect}.})
232
+ %Q{! Unrecognized command. Command was "#{ARGV.first.inspect}" and options were #{ARGV[1..-1].inspect}.})
225
233
  end
226
234
  end
227
235
 
228
236
 
237
+
229
238
  def quit
230
239
  if interactive_mode
231
240
  exit(0)
@@ -313,7 +322,6 @@ When in interactive shell mode:
313
322
  # Performs nameserver functionality.
314
323
  # @param subcommand 'get' or no arg to get, 'clear' to clear, and an array of IP addresses to set
315
324
  def cmd_na(*args)
316
-
317
325
  subcommand = if [[], ['get']].include?(args)
318
326
  :get
319
327
  elsif args == ['clear']
@@ -473,7 +481,6 @@ When in interactive shell mode:
473
481
  && \
474
482
  command_string.length >= cmd.min_string.length # e.g. 'c' by itself should not work
475
483
  end
476
-
477
484
  result ? result.action : nil
478
485
  end
479
486
 
@@ -494,7 +501,7 @@ When in interactive shell mode:
494
501
  begin
495
502
  # By this time, the Main class has removed the command line options, and all that is left
496
503
  # in ARGV is the commands and their options.
497
- process_command_line(ARGV[0], ARGV[1..-1])
504
+ process_command_line
498
505
  rescue BadCommandError => error
499
506
  separator_line = "! #{'-' * 75} !\n"
500
507
  puts '' << separator_line << error.to_s << "\n" << separator_line
@@ -0,0 +1,4 @@
1
+ module WifiWand
2
+ class Error < RuntimeError
3
+ end
4
+ end
@@ -37,8 +37,10 @@ class Main
37
37
  choice = v[0].downcase
38
38
 
39
39
  unless formatters.keys.include?(choice)
40
- raise %Q{Output format "#{choice}" not in list of available formats} +
41
- " (#{formatters.keys.inspect})."
40
+ message = %Q{Output format "#{choice}" not in list of available formats} <<
41
+ " (#{formatters.keys})."
42
+ puts; puts message; puts
43
+ raise Error.new(message)
42
44
  end
43
45
 
44
46
  options.post_processor = formatters[choice]
@@ -2,6 +2,8 @@ require 'json'
2
2
  require 'net/http'
3
3
  require 'tempfile'
4
4
  require 'uri'
5
+ require_relative 'helpers/command_output_formatter'
6
+ require_relative '../error'
5
7
  require_relative '../../wifi-wand'
6
8
 
7
9
  module WifiWand
@@ -33,30 +35,18 @@ class BaseModel
33
35
  @verbose_mode = options.verbose
34
36
 
35
37
  if options.wifi_port && (! is_wifi_port?(options.wifi_port))
36
- raise "#{options.wifi_port} is not a Wi-Fi interface."
38
+ raise Error.new("#{options.wifi_port} is not a Wi-Fi interface.")
37
39
  end
38
40
  @wifi_port = options.wifi_port
39
41
  end
40
42
 
41
43
 
42
- def banner_line
43
- @banner_line ||= '-' * 79
44
- end
45
-
46
- def command_attempt_as_string(command)
47
- "\n\n#{banner_line}\nCommand: #{command}\n"
48
- end
49
-
50
- def command_result_as_string(output)
51
- "#{output}#{banner_line}\n\n"
52
- end
53
-
54
44
 
55
45
 
56
46
  def run_os_command(command, raise_on_error = true)
57
47
 
58
48
  if @verbose_mode
59
- puts command_attempt_as_string(command)
49
+ puts CommandOutputFormatter.command_attempt_as_string(command)
60
50
  end
61
51
 
62
52
  start_time = Time.now
@@ -64,7 +54,7 @@ class BaseModel
64
54
 
65
55
  if @verbose_mode
66
56
  puts "Duration: #{'%.4f' % [Time.now - start_time]} seconds"
67
- puts command_result_as_string(output)
57
+ puts CommandOutputFormatter.command_result_as_string(output)
68
58
  end
69
59
 
70
60
  if $?.exitstatus != 0 && raise_on_error
@@ -94,7 +84,7 @@ class BaseModel
94
84
  success = true
95
85
 
96
86
  if @verbose_mode
97
- puts command_attempt_as_string("[Calling Net:HTTP.start(#{url.host})]")
87
+ puts CommandOutputFormatter.command_attempt_as_string("[Calling Net:HTTP.start(#{url.host})]")
98
88
  end
99
89
 
100
90
  start_time = Time.now
@@ -110,7 +100,7 @@ class BaseModel
110
100
 
111
101
  if @verbose_mode
112
102
  puts "Duration: #{'%.4f' % [Time.now - start_time]} seconds"
113
- puts command_result_as_string("#{success}\n")
103
+ puts CommandOutputFormatter.command_result_as_string("#{success}\n")
114
104
  end
115
105
 
116
106
  success
@@ -146,7 +136,7 @@ class BaseModel
146
136
  password = password.to_s if password
147
137
 
148
138
  if network_name.nil? || network_name.empty?
149
- raise "A network name is required but was not provided."
139
+ raise Error.new("A network name is required but was not provided.")
150
140
  end
151
141
  wifi_on
152
142
  os_level_connect(network_name, password)
@@ -161,7 +151,7 @@ class BaseModel
161
151
  message << %Q{connected to "#{connected_network_name}" instead.}
162
152
  end
163
153
  message << ' Did you ' << (password ? "provide the correct password?" : "need to provide a password?")
164
- raise message
154
+ raise Error.new(message)
165
155
  end
166
156
  nil
167
157
  end
@@ -181,7 +171,7 @@ class BaseModel
181
171
  if preferred_networks.include?(preferred_network_name)
182
172
  os_level_preferred_network_password(preferred_network_name)
183
173
  else
184
- raise "Network #{preferred_network_name} not in preferred networks list."
174
+ raise Error.new("Network #{preferred_network_name} not in preferred networks list.")
185
175
  end
186
176
  end
187
177
 
@@ -225,12 +215,20 @@ class BaseModel
225
215
  # @stop_condition a lambda taking the command's stdout as its sole parameter
226
216
  # @return the stdout produced by the command
227
217
  def try_os_command_until(command, stop_condition, max_tries = 100)
228
- max_tries.times do
218
+
219
+ report_attempt_count = ->(attempt_count) do
220
+ puts "Command was executed #{attempt_count} time(s)." if @verbose_mode
221
+ end
222
+
223
+ max_tries.times do |n|
229
224
  stdout = run_os_command(command)
230
225
  if stop_condition.(stdout)
226
+ report_attempt_count.(n + 1)
231
227
  return stdout
232
228
  end
233
229
  end
230
+
231
+ report_attempt_count.(max_tries)
234
232
  nil
235
233
  end
236
234
 
@@ -0,0 +1,18 @@
1
+ module WifiWand
2
+ module CommandOutputFormatter
3
+
4
+ module_function
5
+
6
+ def banner_line
7
+ @banner_line ||= '-' * 79
8
+ end
9
+
10
+ def command_attempt_as_string(command)
11
+ "\n\n#{banner_line}\nCommand: #{command}\n"
12
+ end
13
+
14
+ def command_result_as_string(output)
15
+ "#{output}#{banner_line}\n\n"
16
+ end
17
+ end
18
+ end
@@ -1,8 +1,10 @@
1
1
  require 'ipaddr'
2
2
  require 'ostruct'
3
+ require 'rexml/document'
3
4
  require 'shellwords'
4
5
 
5
6
  require_relative 'base_model'
7
+ require_relative '../error'
6
8
 
7
9
  module WifiWand
8
10
 
@@ -16,6 +18,21 @@ class MacOsModel < BaseModel
16
18
  end
17
19
 
18
20
 
21
+ # Although at this time the airport command utility is predictable,
22
+ # allow putting it elsewhere in the path for overriding and easier fix
23
+ # if that location should change.
24
+ def airport_command
25
+ airport_in_path = `which airport`.chomp
26
+ if ! airport_in_path.empty?
27
+ airport_in_path
28
+ elsif File.exist?(AIRPORT_CMD)
29
+ AIRPORT_CMD
30
+ else
31
+ raise Error.new("Airport command not found.")
32
+ end
33
+ end
34
+
35
+
19
36
  # Identifies the (first) wireless network hardware port in the system, e.g. en0 or en1
20
37
  # This may not detect wifi ports with nonstandard names, such as USB wifi devices.
21
38
  def detect_wifi_port
@@ -35,7 +52,7 @@ class MacOsModel < BaseModel
35
52
  end
36
53
 
37
54
  if wifi_port_line_num.nil?
38
- raise %Q{Wifi port (e.g. "en0") not found in output of: networksetup -listallhardwareports}
55
+ raise Error.new(%Q{Wifi port (e.g. "en0") not found in output of: networksetup -listallhardwareports})
39
56
  else
40
57
  lines[wifi_port_line_num + 1].split(': ').last
41
58
  end
@@ -56,7 +73,7 @@ class MacOsModel < BaseModel
56
73
  # "DIRECT-sq-BRAVIA 02:71:cc:87:4a:8c -76 6 Y -- WPA2(PSK/AES/AES) ", #
57
74
  def available_network_info
58
75
  return nil unless wifi_on? # no need to try
59
- command = "#{AIRPORT_CMD} -s"
76
+ command = "#{airport_command} -s"
60
77
  max_attempts = 50
61
78
 
62
79
 
@@ -87,27 +104,59 @@ class MacOsModel < BaseModel
87
104
  if output
88
105
  process_tabular_data.(output)
89
106
  else
90
- raise "Unable to get available network information after #{max_attempts} attempts."
107
+ raise Error.new("Unable to get available network information after #{max_attempts} attempts.")
91
108
  end
92
109
  end
93
110
 
94
111
 
95
- # @return an array of unique available network names only, sorted alphabetically
96
- # The reason we don't use an XML parser to get the exactly correct result is that we don't want
97
- # users to need to install any external dependencies in order to run this script.
112
+ # The Mac OS airport utility (at
113
+ # /System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport)
114
+ # outputs the network names right padded with spaces so there is no way to differentiate a
115
+ # network name *with* leading space(s) from one without:
116
+ #
117
+ # SSID BSSID RSSI CHANNEL HT CC SECURITY (auth/unicast/group)
118
+ # ngHub_319442NL0293C 04:a1:51:58:5b:05 -65 11 Y US WPA2(PSK/AES/AES)
119
+ # NETGEAR89_2GEXT 9c:3d:cf:11:69:b4 -67 8 Y US NONE
120
+ #
121
+ # To remedy this, they offer a "-x" option that outputs the information in (pseudo) XML.
122
+ # This XML has 'dict' elements that contain many elements. The SSID can be found in the
123
+ # XML element <string> which immediately follows an XML element whose text is "SSID_STR".
124
+ # Unfortunately, since there is no way to connect the two other than their physical location,
125
+ # the key is rather useless for XML parsing.
126
+ #
127
+ # I tried extracting the arrays of keys and strings, and finding the string element
128
+ # at the same position in the string array as the 'SSID_STR' was in the keys array.
129
+ # However, not all keys had string elements, so the index in the key array was the wrong index.
130
+ # Here is an excerpt from the XML output:
131
+ #
132
+ # <key>RSSI</key>
133
+ # <integer>-91</integer>
134
+ # <key>SSID</key>
135
+ # <data>
136
+ # TkVUR0VBUjY1
137
+ # </data>
138
+ # <key>SSID_STR</key>
139
+ # <string>NETGEAR65</string>
140
+ #
141
+ # The kludge I came up with was that the ssid was always the 2nd value in the <string> element
142
+ # array, so that's what is used here.
143
+ #
144
+ # But now even that approach has been superseded by the XPath approach now used.
145
+ #
146
+ # REXML is used here to avoid the need for the user to install Nokogiri.
98
147
  def available_network_names
148
+ # For some reason, the airport command very often returns nothing, so we need to try until
149
+ # we get data in the response:
99
150
 
100
- # awk command below kindly provided by @nohillside at https://apple.stackexchange.com/questions/320323/how-to-programmatically-get-available-wifi-networks-without-airport-utility.
101
- command = %Q{#{AIRPORT_CMD} -s -x | awk '{ if (catch == 1) { print; catch=0 } } /SSID_STR/ { catch=1 }'}
151
+ command = "#{airport_command} -s -x"
102
152
  stop_condition = ->(response) { ! [nil, ''].include?(response) }
103
-
104
153
  output = try_os_command_until(command, stop_condition)
105
- output = output.split("\n")
106
- output.map!(&:strip)
107
- output.map! { |s| s.gsub(/<\/?string>/, '') }
108
- output.sort! { |s1, s2| s1.casecmp(s2) } # sort alphabetically, case insensitively
109
- output.uniq!
110
- output
154
+ doc = REXML::Document.new(output)
155
+ xpath = '//key[text() = "SSID_STR"][1]/following-sibling::*[1]' # provided by @ScreenStaring on Twitter
156
+ REXML::XPath.match(doc, xpath) \
157
+ .map(&:text) \
158
+ .sort { |x,y| x.casecmp(y) } \
159
+ .uniq
111
160
  end
112
161
 
113
162
 
@@ -136,7 +185,7 @@ class MacOsModel < BaseModel
136
185
 
137
186
  # Returns true if wifi is on, else false.
138
187
  def wifi_on?
139
- lines = run_os_command("#{AIRPORT_CMD} -I").split("\n")
188
+ lines = run_os_command("#{airport_command} -I").split("\n")
140
189
  lines.grep("AirPort: Off").none?
141
190
  end
142
191
 
@@ -145,7 +194,7 @@ class MacOsModel < BaseModel
145
194
  def wifi_on
146
195
  return if wifi_on?
147
196
  run_os_command("networksetup -setairportpower #{wifi_port} on")
148
- wifi_on? ? nil : raise("Wifi could not be enabled.")
197
+ wifi_on? ? nil : Error.new(raise("Wifi could not be enabled."))
149
198
  end
150
199
 
151
200
 
@@ -153,7 +202,7 @@ class MacOsModel < BaseModel
153
202
  def wifi_off
154
203
  return unless wifi_on?
155
204
  run_os_command("networksetup -setairportpower #{wifi_port} off")
156
- wifi_on? ? raise("Wifi could not be disabled.") : nil
205
+ wifi_on? ? Error.new(raise("Wifi could not be disabled.")) : nil
157
206
  end
158
207
 
159
208
 
@@ -210,7 +259,7 @@ class MacOsModel < BaseModel
210
259
 
211
260
  # Returns the network currently connected to, or nil if none.
212
261
  def connected_network_name
213
- lines = run_os_command("#{AIRPORT_CMD} -I").split("\n")
262
+ lines = run_os_command("#{airport_command} -I").split("\n")
214
263
  ssid_lines = lines.grep(/ SSID:/)
215
264
  ssid_lines.empty? ? nil : ssid_lines.first.split('SSID: ').last.lstrip
216
265
  end
@@ -218,7 +267,7 @@ class MacOsModel < BaseModel
218
267
 
219
268
  # Disconnects from the currently connected network. Does not turn off wifi.
220
269
  def disconnect
221
- run_os_command("sudo #{AIRPORT_CMD} -z")
270
+ run_os_command("sudo #{airport_command} -z")
222
271
  nil
223
272
  end
224
273
 
@@ -253,7 +302,7 @@ class MacOsModel < BaseModel
253
302
  'nameservers' => nameservers_using_scutil,
254
303
  'timestamp' => Time.now,
255
304
  }
256
- more_output = run_os_command(AIRPORT_CMD + " -I")
305
+ more_output = run_os_command(airport_command + " -I")
257
306
  more_info = colon_output_to_hash(more_output)
258
307
  info.merge!(more_info)
259
308
  info.delete('AirPort') # will be here if off, but info is already in wifi_on key
@@ -285,11 +334,12 @@ class MacOsModel < BaseModel
285
334
  end
286
335
 
287
336
  unless bad_addresses.empty?
288
- raise "Bad IP addresses provided: #{bad_addresses.join(', ')}"
337
+ raise Error.new("Bad IP addresses provided: #{bad_addresses.join(', ')}")
289
338
  end
290
339
  nameservers.join(' ')
291
340
  end
292
341
  run_os_command("networksetup -setdnsservers Wi-Fi #{arg}")
342
+ nameservers
293
343
  end
294
344
 
295
345
 
@@ -313,6 +363,7 @@ class MacOsModel < BaseModel
313
363
  end
314
364
  private :colon_output_to_hash
315
365
 
366
+
316
367
  # @return array of nameserver IP addresses from /etc/resolv.conf, or nil if not found
317
368
  # Though this is strictly *not* OS-agnostic, it will be used by most OS's,
318
369
  # and can be overridden by subclasses (e.g. Windows).
@@ -0,0 +1,60 @@
1
+ module WifiWand
2
+
3
+ class ModelValidator
4
+
5
+ BASE_MODEL_ESSENTIAL_METHODS = [
6
+ :connect,
7
+ :connected_to?,
8
+ :connected_to_internet?,
9
+ :cycle_network,
10
+ :preferred_network_password,
11
+ :public_ip_address_info,
12
+ :random_mac_address,
13
+ :remove_preferred_networks,
14
+ :run_os_command,
15
+ :till,
16
+ :try_os_command_until,
17
+ :verbose_mode,
18
+ :verbose_mode=,
19
+ :wifi_port,
20
+ :wifi_port=
21
+ ]
22
+
23
+
24
+ BASE_MODEL_NONESSENTIAL_METHODS = [
25
+ ]
26
+
27
+
28
+ MAC_OS_MODEL_ESSENTIAL_METHODS = [
29
+ :airport_command,
30
+ :available_network_info,
31
+ :available_network_names,
32
+ :connected_network_name,
33
+ :detect_wifi_port,
34
+ :disconnect,
35
+ :ip_address,
36
+ :is_wifi_port?,
37
+ :mac_address,
38
+ :nameservers_using_networksetup,
39
+ :nameservers_using_resolv_conf,
40
+ :nameservers_using_scutil,
41
+ :open_resource,
42
+ :os_level_connect,
43
+ :os_level_preferred_network_password,
44
+ :preferred_networks,
45
+ :remove_preferred_network,
46
+ :set_nameservers,
47
+ :wifi_info,
48
+ :wifi_off,
49
+ :wifi_on,
50
+ :wifi_on?
51
+ ]
52
+
53
+ MAC_OS_MODEL_ESSENTIAL_METHODS = [
54
+ ]
55
+
56
+ ALL_MODEL_METHODS = BASE_MODEL_ESSENTIAL_METHODS + MAC_OS_MODEL_ESSENTIAL_METHODS
57
+
58
+
59
+ end
60
+ end
@@ -1,3 +1,4 @@
1
+ require_relative 'error'
1
2
  require_relative 'os/base_os'
2
3
  require_relative 'os/imaginary_os'
3
4
  require_relative 'os/mac_os'
@@ -31,7 +32,7 @@ class OperatingSystems
31
32
  matches = supported_operating_systems.select { |os| os.current_os_is_this_os? }
32
33
  if matches.size > 1
33
34
  matching_names = matches.map(&:display_name)
34
- raise "There should only be 1 matching OS, but there were multiple: #{matching_names.inspect}"
35
+ raise Error.new("There should only be 1 matching OS, but there were multiple: #{matching_names.inspect}")
35
36
  end
36
37
  @current_os = matches.first
37
38
  end
@@ -39,8 +40,8 @@ class OperatingSystems
39
40
  end
40
41
 
41
42
 
42
- def current_id; current_os&.id; end
43
- def current_display_name; current_os&.display_name; end
43
+ def current_id; current_os ? current_os.id : nil; end
44
+ def current_display_name; current_os ? current_os.display_name : nil; end
44
45
 
45
46
  end
46
47
  end
@@ -1,5 +1,5 @@
1
1
  module WifiWand
2
2
 
3
- VERSION = '2.10.1'
3
+ VERSION = '2.11.0'
4
4
 
5
5
  end
data/wifi-wand.gemspec CHANGED
@@ -7,8 +7,8 @@ Gem::Specification.new do |spec|
7
7
  spec.version = WifiWand::VERSION
8
8
  spec.authors = ["Keith Bennett"]
9
9
  spec.email = ["keithrbennett@gmail.com"]
10
- spec.description = %q{A command line interface for managing wifi on a Mac.}
11
- spec.summary = %q{Mac wifi utility}
10
+ spec.description = %q{A command line interface for managing WiFi on a Mac.}
11
+ spec.summary = %q{Mac WiFi utility}
12
12
  spec.homepage = "https://github.com/keithrbennett/wifi-wand"
13
13
  spec.license = "MIT"
14
14
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wifi-wand
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.10.1
4
+ version: 2.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keith Bennett
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-04-28 00:00:00.000000000 Z
11
+ date: 2018-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,7 +52,7 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
- description: A command line interface for managing wifi on a Mac.
55
+ description: A command line interface for managing WiFi on a Mac.
56
56
  email:
57
57
  - keithrbennett@gmail.com
58
58
  executables:
@@ -68,9 +68,12 @@ files:
68
68
  - exe/wifi-wand
69
69
  - lib/wifi-wand.rb
70
70
  - lib/wifi-wand/command_line_interface.rb
71
+ - lib/wifi-wand/error.rb
71
72
  - lib/wifi-wand/main.rb
72
73
  - lib/wifi-wand/models/base_model.rb
74
+ - lib/wifi-wand/models/helpers/command_output_formatter.rb
73
75
  - lib/wifi-wand/models/mac_os_model.rb
76
+ - lib/wifi-wand/models/model_validator.rb
74
77
  - lib/wifi-wand/operating_systems.rb
75
78
  - lib/wifi-wand/os/base_os.rb
76
79
  - lib/wifi-wand/os/imaginary_os.rb
@@ -105,5 +108,5 @@ rubyforge_project:
105
108
  rubygems_version: 2.7.6
106
109
  signing_key:
107
110
  specification_version: 4
108
- summary: Mac wifi utility
111
+ summary: Mac WiFi utility
109
112
  test_files: []