wifi-wand 2.10.1 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/RELEASE_NOTES.md +6 -0
- data/lib/wifi-wand/command_line_interface.rb +30 -23
- data/lib/wifi-wand/error.rb +4 -0
- data/lib/wifi-wand/main.rb +4 -2
- data/lib/wifi-wand/models/base_model.rb +19 -21
- data/lib/wifi-wand/models/helpers/command_output_formatter.rb +18 -0
- data/lib/wifi-wand/models/mac_os_model.rb +73 -22
- data/lib/wifi-wand/models/model_validator.rb +60 -0
- data/lib/wifi-wand/operating_systems.rb +4 -3
- data/lib/wifi-wand/version.rb +1 -1
- data/wifi-wand.gemspec +2 -2
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f21e7e9af16e0eea5c7d254ef75ce061fbae1ada6c707201a189ec78f9238be
|
4
|
+
data.tar.gz: ec4b2ef369ca6e3186fe02b59e57c059f89a17b03eba2cdf760035b93fdaa9b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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[
|
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
|
-
*
|
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
|
-
|
181
|
-
|
182
|
-
|
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
|
-
#
|
201
|
-
def
|
202
|
-
|
203
|
-
|
204
|
-
|
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
|
-
|
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
|
218
|
-
|
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 "#{
|
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
|
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
|
data/lib/wifi-wand/main.rb
CHANGED
@@ -37,8 +37,10 @@ class Main
|
|
37
37
|
choice = v[0].downcase
|
38
38
|
|
39
39
|
unless formatters.keys.include?(choice)
|
40
|
-
|
41
|
-
|
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
|
-
|
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 = "#{
|
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
|
-
#
|
96
|
-
#
|
97
|
-
#
|
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
|
-
|
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
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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("#{
|
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("#{
|
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 #{
|
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(
|
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
|
43
|
-
def current_display_name; current_os
|
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
|
data/lib/wifi-wand/version.rb
CHANGED
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
|
11
|
-
spec.summary = %q{Mac
|
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.
|
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-
|
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
|
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
|
111
|
+
summary: Mac WiFi utility
|
109
112
|
test_files: []
|