mac-wifi 1.3.0 → 2.0.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/README.md +40 -39
- data/RELEASE_NOTES.md +18 -0
- data/exe/mac-wifi +284 -108
- data/mac-wifi.gemspec +1 -1
- data/sample-avail-network-data.xml +4042 -0
- data/sample-available-networks.json +27 -0
- data/sample-available-networks.yaml +26 -0
- metadata +5 -2
data/exe/mac-wifi
CHANGED
@@ -19,13 +19,16 @@
|
|
19
19
|
# License: MIT License
|
20
20
|
|
21
21
|
|
22
|
+
require 'json'
|
23
|
+
require 'yaml'
|
24
|
+
|
22
25
|
require 'shellwords'
|
23
26
|
require 'tempfile'
|
24
27
|
|
25
28
|
module MacWifi
|
26
29
|
|
27
30
|
# This version must be kept in sync with the version in the gemspec file.
|
28
|
-
VERSION = '
|
31
|
+
VERSION = '2.0.0'
|
29
32
|
|
30
33
|
|
31
34
|
class BaseModel
|
@@ -46,6 +49,19 @@ class BaseModel
|
|
46
49
|
end
|
47
50
|
|
48
51
|
|
52
|
+
def run_os_command(command)
|
53
|
+
output = `#{command} 2>&1` # join stderr with stdout
|
54
|
+
if $?.exitstatus != 0
|
55
|
+
raise OsCommandError.new($?.exitstatus, command, output)
|
56
|
+
end
|
57
|
+
if @verbose_mode
|
58
|
+
puts "\n\n#{'-' * 79}\nCommand: #{command}\n\nOutput:\n#{output}#{'-' * 79}\n\n"
|
59
|
+
end
|
60
|
+
output
|
61
|
+
end
|
62
|
+
private :run_os_command
|
63
|
+
|
64
|
+
|
49
65
|
# This method returns whether or not there is a working Internet connection.
|
50
66
|
# Because of a Mac issue which causes a request to hang if the network is turned
|
51
67
|
# off during its lifetime, we give it only 5 seconds per try,
|
@@ -135,7 +151,7 @@ class BaseModel
|
|
135
151
|
|
136
152
|
# Connects to the passed network name, optionally with password.
|
137
153
|
# Turns wifi on first, in case it was turned off.
|
138
|
-
# Relies on subclass implementation of
|
154
|
+
# Relies on subclass implementation of os_level_connect().
|
139
155
|
def connect(network_name, password = nil)
|
140
156
|
# Allow symbols and anything responding to to_s for user convenience
|
141
157
|
network_name = network_name.to_s if network_name
|
@@ -145,7 +161,7 @@ class BaseModel
|
|
145
161
|
raise "A network name is required but was not provided."
|
146
162
|
end
|
147
163
|
wifi_on
|
148
|
-
|
164
|
+
os_level_connect(network_name, password)
|
149
165
|
|
150
166
|
# Verify that the network is now connected:
|
151
167
|
actual_network_name = connected_network_name
|
@@ -175,7 +191,7 @@ class BaseModel
|
|
175
191
|
def preferred_network_password(preferred_network_name)
|
176
192
|
preferred_network_name = preferred_network_name.to_s
|
177
193
|
if preferred_networks.include?(preferred_network_name)
|
178
|
-
|
194
|
+
os_level_preferred_network_password(preferred_network_name)
|
179
195
|
else
|
180
196
|
raise "Network #{preferred_network_name} not in preferred networks list."
|
181
197
|
end
|
@@ -214,6 +230,21 @@ class BaseModel
|
|
214
230
|
sleep(wait_interval_in_secs)
|
215
231
|
end
|
216
232
|
end
|
233
|
+
|
234
|
+
|
235
|
+
# Tries an OS command until the stop condition is true.
|
236
|
+
# @command the command to run in the OS
|
237
|
+
# @stop_condition a lambda taking the commands stdout as its sole parameter
|
238
|
+
# @return the stdout produced by the command
|
239
|
+
def try_os_command_until(command, stop_condition, max_tries = 100)
|
240
|
+
max_tries.times do
|
241
|
+
stdout = run_os_command(command)
|
242
|
+
if stop_condition.(stdout)
|
243
|
+
return stdout
|
244
|
+
end
|
245
|
+
end
|
246
|
+
nil
|
247
|
+
end
|
217
248
|
end
|
218
249
|
|
219
250
|
|
@@ -266,37 +297,99 @@ class MacOsModel < BaseModel
|
|
266
297
|
command = "#{AIRPORT_CMD} -s"
|
267
298
|
max_attempts = 50
|
268
299
|
|
300
|
+
|
269
301
|
reformat_line = ->(line) do
|
270
302
|
ssid = line[0..31].strip
|
271
303
|
"%-32.32s%s" % [ssid, line[32..-1]]
|
272
304
|
end
|
273
305
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
reformat_line.(line)
|
283
|
-
end
|
284
|
-
data_lines.sort!
|
285
|
-
return [reformat_line.(header_line)] + data_lines
|
306
|
+
|
307
|
+
process_tabular_data = ->(output) do
|
308
|
+
lines = output.split("\n")
|
309
|
+
header_line = lines[0]
|
310
|
+
data_lines = lines[1..-1]
|
311
|
+
data_lines.map! do |line|
|
312
|
+
# Reformat the line so that the name is left instead of right justified
|
313
|
+
reformat_line.(line)
|
286
314
|
end
|
315
|
+
data_lines.sort!
|
316
|
+
[reformat_line.(header_line)] + data_lines
|
287
317
|
end
|
288
318
|
|
289
319
|
|
290
|
-
|
320
|
+
output = try_os_command_until(command, ->(output) do
|
321
|
+
! ([nil, ''].include?(output))
|
322
|
+
end)
|
323
|
+
|
324
|
+
if output
|
325
|
+
process_tabular_data.(output)
|
326
|
+
else
|
327
|
+
raise "Unable to get available network information after #{max_attempts} attempts."
|
328
|
+
end
|
291
329
|
end
|
292
330
|
|
293
331
|
|
294
|
-
|
295
|
-
|
296
|
-
|
332
|
+
def parse_network_names(info)
|
333
|
+
if info.nil?
|
334
|
+
nil
|
335
|
+
else
|
336
|
+
info[1..-1] \
|
297
337
|
.map { |line| line[0..32].rstrip } \
|
298
338
|
.uniq \
|
299
339
|
.sort { |s1, s2| s1.casecmp(s2) }
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
# @return an array of unique available network names only, sorted alphabetically
|
344
|
+
# Kludge alert: the tabular data does not differentiate between strings with and without leading whitespace
|
345
|
+
# Therefore, we get the data once in tabular format, and another time in XML format.
|
346
|
+
# The XML element will include any leading whitespace. However, it includes all <string> elements,
|
347
|
+
# many of which are not network names.
|
348
|
+
# As an improved approximation of the correct result, for each network name found in tabular mode,
|
349
|
+
# we look to see if there is a corresponding string element with leading whitespace, and, if so,
|
350
|
+
# replace it.
|
351
|
+
#
|
352
|
+
# This will not behave correctly if a given name has occurrences with different amounts of whitespace,
|
353
|
+
# e.g. ' x' and ' x'.
|
354
|
+
#
|
355
|
+
# The reason we don't use an XML parser to get the exactly correct result is that we don't want
|
356
|
+
# users to need to install any external dependencies in order to run this script.
|
357
|
+
def available_network_names
|
358
|
+
|
359
|
+
# Parses the XML text (using grep, not XML parsing) to find
|
360
|
+
# <string> elements, and extracts the network name candidates
|
361
|
+
# containing leading spaces from it.
|
362
|
+
get_leading_space_names = ->(text) do
|
363
|
+
text.split("\n") \
|
364
|
+
.grep(%r{<string>}) \
|
365
|
+
.sort \
|
366
|
+
.uniq \
|
367
|
+
.map { |line| line.gsub("<string>", '').gsub('</string>', '').gsub("\t", '') } \
|
368
|
+
.select { |s| s[0] == ' ' }
|
369
|
+
end
|
370
|
+
|
371
|
+
|
372
|
+
output_is_valid = ->(output) { ! ([nil, ''].include?(output)) }
|
373
|
+
tabular_data = try_os_command_until("#{AIRPORT_CMD} -s", output_is_valid)
|
374
|
+
xml_data = try_os_command_until("#{AIRPORT_CMD} -s -x", output_is_valid)
|
375
|
+
|
376
|
+
if tabular_data.nil? || xml_data.nil?
|
377
|
+
raise "Unable to get available network information; please try again."
|
378
|
+
end
|
379
|
+
|
380
|
+
tabular_data_lines = tabular_data[1..-1] # omit header line
|
381
|
+
names_no_spaces = parse_network_names(tabular_data_lines.split("\n")).map(&:strip)
|
382
|
+
names_maybe_spaces = get_leading_space_names.(xml_data)
|
383
|
+
|
384
|
+
names = names_no_spaces.map do |name_no_spaces|
|
385
|
+
match = names_maybe_spaces.detect do |name_maybe_spaces|
|
386
|
+
%r{[ \t]?#{name_no_spaces}$}.match(name_maybe_spaces)
|
387
|
+
end
|
388
|
+
|
389
|
+
match ? match : name_no_spaces
|
390
|
+
end
|
391
|
+
|
392
|
+
names.sort { |s1, s2| s1.casecmp(s2) } # sort alphabetically, case insensitively
|
300
393
|
end
|
301
394
|
|
302
395
|
|
@@ -344,7 +437,7 @@ class MacOsModel < BaseModel
|
|
344
437
|
|
345
438
|
|
346
439
|
# This method is called by BaseModel#connect to do the OS-specific connection logic.
|
347
|
-
def
|
440
|
+
def os_level_connect(network_name, password = nil)
|
348
441
|
command = "networksetup -setairportnetwork #{wifi_hardware_port} " + "#{Shellwords.shellescape(network_name)}"
|
349
442
|
if password
|
350
443
|
command << ' ' << Shellwords.shellescape(password)
|
@@ -359,7 +452,7 @@ class MacOsModel < BaseModel
|
|
359
452
|
# If not, return nil
|
360
453
|
# else
|
361
454
|
# raise an error
|
362
|
-
def
|
455
|
+
def os_level_preferred_network_password(preferred_network_name)
|
363
456
|
command = %Q{security find-generic-password -D "AirPort network password" -a "#{preferred_network_name}" -w 2>&1}
|
364
457
|
begin
|
365
458
|
return run_os_command(command).chomp
|
@@ -429,19 +522,6 @@ class MacOsModel < BaseModel
|
|
429
522
|
end
|
430
523
|
|
431
524
|
|
432
|
-
def run_os_command(command)
|
433
|
-
output = `#{command} 2>&1` # join stderr with stdout
|
434
|
-
if $?.exitstatus != 0
|
435
|
-
raise OsCommandError.new($?.exitstatus, command, output)
|
436
|
-
end
|
437
|
-
if @verbose_mode
|
438
|
-
puts "\n\n#{'-' * 79}\nCommand: #{command}\n\nOutput:\n#{output}#{'-' * 79}\n\n"
|
439
|
-
end
|
440
|
-
output
|
441
|
-
end
|
442
|
-
private :run_os_command
|
443
|
-
|
444
|
-
|
445
525
|
# Parses output like the text below into a hash:
|
446
526
|
# SSID: Pattara211
|
447
527
|
# MCS: 5
|
@@ -461,7 +541,7 @@ end
|
|
461
541
|
|
462
542
|
class CommandLineInterface
|
463
543
|
|
464
|
-
attr_reader :model, :interactive_mode
|
544
|
+
attr_reader :model, :interactive_mode, :options
|
465
545
|
|
466
546
|
class Command < Struct.new(:min_string, :max_string, :action); end
|
467
547
|
|
@@ -475,23 +555,29 @@ class CommandLineInterface
|
|
475
555
|
|
476
556
|
# Help text to be used when requested by 'h' command, in case of unrecognized or nonexistent command, etc.
|
477
557
|
HELP_TEXT = "
|
478
|
-
mac-wifi version #{VERSION}
|
558
|
+
Command Line Switches: [mac-wifi version #{VERSION}]
|
559
|
+
|
560
|
+
-o[i,j,p,y] - outputs data in inspect, JSON, puts, or YAML format when not in shell mode
|
561
|
+
-s - run in shell mode
|
562
|
+
-v - verbose mode (prints OS commands and their outputs)
|
563
|
+
|
564
|
+
Commands:
|
479
565
|
|
480
|
-
a[
|
566
|
+
a[vail_nets] - array of names of the available networks
|
481
567
|
ci - connected to Internet (not just wifi on)?
|
482
568
|
co[nnect] network-name - turns wifi on, connects to network-name
|
483
569
|
cy[cle] - turns wifi off, then on, preserving network selection
|
484
570
|
d[isconnect] - disconnects from current network, does not turn off wifi
|
485
571
|
h[elp] - prints this help
|
486
572
|
i[nfo] - a hash of wifi-related information
|
487
|
-
l[
|
573
|
+
l[s_avail_nets] - details about available networks
|
488
574
|
n[etwork_name] - name (SSID) of currently connected network
|
489
575
|
on - turns wifi on
|
490
576
|
of[f] - turns wifi off
|
491
577
|
pa[ssword] network-name - password for preferred network-name
|
492
|
-
pr[
|
578
|
+
pr[ef_nets] - preferred (not necessarily available) networks
|
493
579
|
q[uit] - exits this program (interactive shell mode only) (see also 'x')
|
494
|
-
r[
|
580
|
+
r[m_pref_nets] network-name - removes network-name from the preferred networks list
|
495
581
|
(can provide multiple names separated by spaces)
|
496
582
|
s[hell] - opens an interactive pry shell (command line only)
|
497
583
|
t[ill] - returns when the desired Internet connection state is true. Options:
|
@@ -507,16 +593,18 @@ When in interactive shell mode:
|
|
507
593
|
"
|
508
594
|
|
509
595
|
|
510
|
-
def initialize
|
596
|
+
def initialize(options)
|
597
|
+
@options = options
|
511
598
|
@model = MacOsModel.new(verbose_mode)
|
512
|
-
@interactive_mode =
|
599
|
+
@interactive_mode = !!(options.interactive_mode)
|
600
|
+
run_shell if @interactive_mode
|
513
601
|
end
|
514
602
|
|
515
603
|
|
516
604
|
# Until command line option parsing is added, the only way to specify
|
517
605
|
# verbose mode is in the environment variable MAC_WIFI_OPTS.
|
518
606
|
def verbose_mode
|
519
|
-
|
607
|
+
options.verbose
|
520
608
|
end
|
521
609
|
|
522
610
|
|
@@ -563,7 +651,7 @@ When in interactive shell mode:
|
|
563
651
|
# Asserts that a command has been passed on the command line.
|
564
652
|
def validate_command_line
|
565
653
|
if ARGV.empty?
|
566
|
-
puts "Syntax is: #{__FILE__} command [
|
654
|
+
puts "Syntax is: #{__FILE__} [options] command [command_options]"
|
567
655
|
print_help
|
568
656
|
exit(-1)
|
569
657
|
end
|
@@ -575,23 +663,16 @@ When in interactive shell mode:
|
|
575
663
|
# that is not needed here.
|
576
664
|
def run_pry
|
577
665
|
binding.pry
|
666
|
+
|
667
|
+
# the seemingly useless line below is needed to avoid pry's exiting
|
668
|
+
# (see https://github.com/deivid-rodriguez/pry-byebug/issues/45)
|
669
|
+
_a = nil
|
578
670
|
end
|
579
671
|
|
580
672
|
|
581
673
|
# Runs a pry session in the context of this object.
|
582
674
|
# Commands and options specified on the command line can also be specified in the shell.
|
583
675
|
def run_shell
|
584
|
-
if interactive_mode
|
585
|
-
puts "Already in shell."
|
586
|
-
return
|
587
|
-
end
|
588
|
-
|
589
|
-
@interactive_mode = true
|
590
|
-
|
591
|
-
# For conveniently calling to_json or to_yaml on an object:
|
592
|
-
require 'json'
|
593
|
-
require 'yaml'
|
594
|
-
|
595
676
|
begin
|
596
677
|
require 'pry'
|
597
678
|
rescue LoadError
|
@@ -656,15 +737,30 @@ When in interactive shell mode:
|
|
656
737
|
|
657
738
|
def cmd_a
|
658
739
|
info = model.available_network_names
|
659
|
-
|
660
|
-
|
740
|
+
if interactive_mode
|
741
|
+
info
|
742
|
+
else
|
743
|
+
if post_processor
|
744
|
+
puts post_processor.(info)
|
745
|
+
else
|
746
|
+
message = if model.wifi_on?
|
747
|
+
"Available networks are:\n\n#{fancy_string(info)}"
|
748
|
+
else
|
749
|
+
"Wifi is off, cannot see available networks."
|
750
|
+
end
|
751
|
+
puts(message)
|
752
|
+
end
|
753
|
+
end
|
661
754
|
end
|
662
755
|
|
663
756
|
|
664
757
|
def cmd_ci
|
665
758
|
connected = model.connected_to_internet?
|
666
|
-
|
667
|
-
|
759
|
+
if interactive_mode
|
760
|
+
connected
|
761
|
+
else
|
762
|
+
puts (post_processor ? post_processor.(connected) : "Connected to Internet: #{connected}")
|
763
|
+
end
|
668
764
|
end
|
669
765
|
|
670
766
|
|
@@ -690,22 +786,41 @@ When in interactive shell mode:
|
|
690
786
|
|
691
787
|
def cmd_i
|
692
788
|
info = model.wifi_info
|
693
|
-
|
694
|
-
|
789
|
+
if interactive_mode
|
790
|
+
info
|
791
|
+
else
|
792
|
+
if post_processor
|
793
|
+
puts post_processor.(info)
|
794
|
+
else
|
795
|
+
puts fancy_string(info)
|
796
|
+
end
|
797
|
+
end
|
695
798
|
end
|
696
799
|
|
697
800
|
|
698
801
|
def cmd_lsa
|
699
802
|
info = model.available_network_info
|
700
|
-
|
701
|
-
|
803
|
+
if interactive_mode
|
804
|
+
info
|
805
|
+
else
|
806
|
+
if post_processor
|
807
|
+
puts post_processor.(info)
|
808
|
+
else
|
809
|
+
message = model.wifi_on? ? fancy_string(info) : "Wifi is off, cannot see available networks."
|
810
|
+
puts(message)
|
811
|
+
end
|
812
|
+
end
|
702
813
|
end
|
703
814
|
|
704
815
|
|
705
816
|
def cmd_n
|
706
817
|
name = model.connected_network_name
|
707
|
-
|
708
|
-
|
818
|
+
if interactive_mode
|
819
|
+
name
|
820
|
+
else
|
821
|
+
display_name = name ? name : '[none]'
|
822
|
+
puts (post_processor ? post_processor.(name) : %Q{Network (SSID) name: "#{display_name}"})
|
823
|
+
end
|
709
824
|
end
|
710
825
|
|
711
826
|
|
@@ -722,20 +837,27 @@ When in interactive shell mode:
|
|
722
837
|
def cmd_pa(network)
|
723
838
|
password = model.preferred_network_password(network)
|
724
839
|
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
840
|
+
if interactive_mode
|
841
|
+
password
|
842
|
+
else
|
843
|
+
if post_processor
|
844
|
+
puts post_processor.(password)
|
845
|
+
else
|
846
|
+
output = %Q{Preferred network "#{model.connected_network_name}" }
|
847
|
+
output << (password ? %Q{stored password is "#{password}".} : "has no stored password.")
|
848
|
+
puts output
|
849
|
+
end
|
729
850
|
end
|
730
|
-
|
731
|
-
password
|
732
851
|
end
|
733
852
|
|
734
853
|
|
735
854
|
def cmd_pr
|
736
855
|
networks = model.preferred_networks
|
737
|
-
|
738
|
-
|
856
|
+
if interactive_mode
|
857
|
+
networks
|
858
|
+
else
|
859
|
+
puts (post_processor ? post_processor.(networks) : fancy_string(networks))
|
860
|
+
end
|
739
861
|
end
|
740
862
|
|
741
863
|
|
@@ -745,12 +867,12 @@ When in interactive shell mode:
|
|
745
867
|
|
746
868
|
|
747
869
|
def cmd_r(*options)
|
748
|
-
model.remove_preferred_networks(*options)
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
870
|
+
removed_networks = model.remove_preferred_networks(*options)
|
871
|
+
if interactive_mode
|
872
|
+
removed_networks
|
873
|
+
else
|
874
|
+
puts (post_processor ? post_processor.(removed_networks) : "Removed networks: #{removed_networks.inspect}")
|
875
|
+
end
|
754
876
|
end
|
755
877
|
|
756
878
|
|
@@ -763,8 +885,11 @@ When in interactive shell mode:
|
|
763
885
|
|
764
886
|
def cmd_w
|
765
887
|
on = model.wifi_on?
|
766
|
-
|
767
|
-
|
888
|
+
if interactive_mode
|
889
|
+
on
|
890
|
+
else
|
891
|
+
puts (post_processor ? post_processor.(on) : "Wifi on: #{on}")
|
892
|
+
end
|
768
893
|
end
|
769
894
|
|
770
895
|
|
@@ -775,25 +900,24 @@ When in interactive shell mode:
|
|
775
900
|
|
776
901
|
def commands
|
777
902
|
@commands_ ||= [
|
778
|
-
Command.new('a', '
|
779
|
-
Command.new('ci', 'ci',
|
780
|
-
Command.new('co', 'connect',
|
781
|
-
Command.new('cy', 'cycle',
|
782
|
-
Command.new('d', 'disconnect',
|
783
|
-
Command.new('h', 'help',
|
784
|
-
Command.new('i', 'info',
|
785
|
-
Command.new('l', '
|
786
|
-
Command.new('n', 'network_name',
|
787
|
-
Command.new('of', 'off',
|
788
|
-
Command.new('on', 'on',
|
789
|
-
Command.new('pa', 'password',
|
790
|
-
Command.new('pr', '
|
791
|
-
Command.new('q', 'quit',
|
792
|
-
Command.new('r', '
|
793
|
-
Command.new('
|
794
|
-
Command.new('
|
795
|
-
Command.new('
|
796
|
-
Command.new('x', 'xit', -> (*_options) { cmd_x })
|
903
|
+
Command.new('a', 'avail_nets', -> (*_options) { cmd_a }),
|
904
|
+
Command.new('ci', 'ci', -> (*_options) { cmd_ci }),
|
905
|
+
Command.new('co', 'connect', -> (*options) { cmd_co(*options) }),
|
906
|
+
Command.new('cy', 'cycle', -> (*_options) { cmd_cy }),
|
907
|
+
Command.new('d', 'disconnect', -> (*_options) { cmd_d }),
|
908
|
+
Command.new('h', 'help', -> (*_options) { cmd_h }),
|
909
|
+
Command.new('i', 'info', -> (*_options) { cmd_i }),
|
910
|
+
Command.new('l', 'ls_avail_nets', -> (*_options) { cmd_lsa }),
|
911
|
+
Command.new('n', 'network_name', -> (*_options) { cmd_n }),
|
912
|
+
Command.new('of', 'off', -> (*_options) { cmd_of }),
|
913
|
+
Command.new('on', 'on', -> (*_options) { cmd_on }),
|
914
|
+
Command.new('pa', 'password', -> (*options) { cmd_pa(*options) }),
|
915
|
+
Command.new('pr', 'pref_nets', -> (*_options) { cmd_pr }),
|
916
|
+
Command.new('q', 'quit', -> (*_options) { cmd_q }),
|
917
|
+
Command.new('r', 'rm_pref_nets', -> (*options) { cmd_r(*options) }),
|
918
|
+
Command.new('t', 'till', -> (*options) { cmd_t(*options) }),
|
919
|
+
Command.new('w', 'wifion', -> (*_options) { cmd_w }),
|
920
|
+
Command.new('x', 'xit', -> (*_options) { cmd_x })
|
797
921
|
]
|
798
922
|
end
|
799
923
|
|
@@ -809,6 +933,18 @@ When in interactive shell mode:
|
|
809
933
|
end
|
810
934
|
|
811
935
|
|
936
|
+
# If a post-processor has been configured (e.g. YAML or JSON), use it.
|
937
|
+
def post_process(object)
|
938
|
+
post_processor ? post_processor.(object) : object
|
939
|
+
end
|
940
|
+
|
941
|
+
|
942
|
+
|
943
|
+
def post_processor
|
944
|
+
options.post_processor
|
945
|
+
end
|
946
|
+
|
947
|
+
|
812
948
|
def call
|
813
949
|
validate_command_line
|
814
950
|
begin
|
@@ -822,6 +958,9 @@ When in interactive shell mode:
|
|
822
958
|
end
|
823
959
|
|
824
960
|
|
961
|
+
require 'optparse'
|
962
|
+
require 'ostruct'
|
963
|
+
|
825
964
|
class Main
|
826
965
|
|
827
966
|
# @return true if this file is being run as a script, else false
|
@@ -873,20 +1012,57 @@ class Main
|
|
873
1012
|
end
|
874
1013
|
|
875
1014
|
|
1015
|
+
# Parses the command line with Ruby's internal 'optparse'.
|
1016
|
+
# Looks for "-v" flag to set verbosity to true.
|
1017
|
+
# optparse removes what it processes from ARGV, which simplifies our command parsing.
|
1018
|
+
def parse_command_line
|
1019
|
+
options = OpenStruct.new
|
1020
|
+
OptionParser.new do |parser|
|
1021
|
+
parser.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
1022
|
+
options.verbose = v
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
parser.on("-s", "--shell", "Start interactive shell") do |v|
|
1026
|
+
options.interactive_mode = true
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
parser.on("-o", "--output_format FORMAT", "Format output data") do |v|
|
1030
|
+
|
1031
|
+
transformers = {
|
1032
|
+
'i' => ->(object) { object.inspect },
|
1033
|
+
'j' => ->(object) { JSON.pretty_generate(object) },
|
1034
|
+
'p' => ->(object) { sio = StringIO.new; sio.puts(object); sio.string },
|
1035
|
+
'y' => ->(object) { object.to_yaml }
|
1036
|
+
}
|
1037
|
+
|
1038
|
+
choice = v[0].downcase
|
1039
|
+
|
1040
|
+
unless transformers.keys.include?(choice)
|
1041
|
+
raise %Q{Output format "#{choice}" not in list of available formats} +
|
1042
|
+
" (#{transformers.keys.inspect})."
|
1043
|
+
end
|
1044
|
+
|
1045
|
+
options.post_processor = transformers[choice]
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
parser.on("-h", "--help", "Show help") do |_help_requested|
|
1049
|
+
ARGV << 'h' # pass on the request to the command processor
|
1050
|
+
end
|
1051
|
+
end.parse!
|
1052
|
+
options
|
1053
|
+
end
|
1054
|
+
|
1055
|
+
|
876
1056
|
def call
|
877
1057
|
assert_os_is_mac_os
|
878
1058
|
|
1059
|
+
options = parse_command_line
|
1060
|
+
|
879
1061
|
# If this file is being called as a script, run it.
|
880
1062
|
# Else, it may be loaded to use the model in a different way.
|
881
1063
|
if running_as_script?
|
882
1064
|
begin
|
883
|
-
MacWifi::CommandLineInterface.new.call
|
884
|
-
rescue => error
|
885
|
-
puts error
|
886
|
-
stack_trace_filename = './mac-wifi-error-stack-trace.txt'
|
887
|
-
File.write(stack_trace_filename, caller.join("\n"))
|
888
|
-
puts "Exiting. Stack trace written to '#{File.expand_path(stack_trace_filename)}'."
|
889
|
-
exit(-1)
|
1065
|
+
MacWifi::CommandLineInterface.new(options).call
|
890
1066
|
end
|
891
1067
|
end
|
892
1068
|
end
|