cmdparse 2.0.6 → 3.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/COPYING +21 -674
- data/README.md +29 -26
- data/Rakefile +58 -94
- data/VERSION +1 -1
- data/doc/api.api +5 -0
- data/doc/api.template +24 -0
- data/doc/default.scss +1987 -0
- data/doc/default.template +47 -26
- data/doc/images/bg01.png +0 -0
- data/doc/images/bg02.png +0 -0
- data/doc/index.page +81 -74
- data/doc/{download.page → installation.page} +13 -14
- data/doc/metainfo +14 -0
- data/doc/news.page +113 -0
- data/doc/tutorial.page +211 -115
- data/doc/virtual +2 -5
- data/example/net.rb +85 -0
- data/lib/cmdparse.rb +675 -284
- data/webgen.config +22 -0
- metadata +39 -24
- data/COPYING.LESSER +0 -165
- data/doc/about.page +0 -24
- data/doc/default.css +0 -152
- data/doc/logo.png +0 -0
- data/lib/cmdparse/wrappers/optparse.rb +0 -63
- data/net.rb +0 -88
- data/test/tc_commandhash.rb +0 -96
data/doc/tutorial.page
CHANGED
@@ -3,189 +3,285 @@ title: Tutorial
|
|
3
3
|
in_menu: true
|
4
4
|
sort_info: 6
|
5
5
|
---
|
6
|
-
##
|
6
|
+
## Tutorial
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
The complete code for this example can be found in the file `example/net.rb` of the `cmdparse`
|
9
|
+
package (or online at [cmdparse's Github
|
10
|
+
repository][netrb]!
|
10
11
|
|
11
|
-
|
12
|
+
**Tl;dr:** In this tutorial we will create a small `net` program which can add, delete and list IP
|
13
|
+
addresses as well as show 'network statistics'. By doing this we show how easy it is to use the
|
14
|
+
`cmdparse` library for creating a command based program. The last part shows how our created program
|
15
|
+
can be invoked and the built-in help facility of `cmdparse`.
|
12
16
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
self.add_command(TestSubCmd.new)
|
18
|
-
end
|
19
|
-
end
|
17
|
+
Note that the shown code fragments do not comprise the whole program. So, depending on what you
|
18
|
+
like, just look at the code fragments and the explanations or open the example file alongside this
|
19
|
+
tutorial. Later use the `example/net.rb` file for running the program yourself and testing different
|
20
|
+
command line arguments.
|
20
21
|
|
21
|
-
class TestSubCmd < CmdParse::Command
|
22
|
-
def initialize
|
23
|
-
super('sub',false)
|
24
|
-
end
|
25
22
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
23
|
+
### Require statements
|
24
|
+
|
25
|
+
First we create a new new file and add the necessary `require` statements:
|
30
26
|
|
31
|
-
|
32
|
-
|
27
|
+
~~~ ruby
|
28
|
+
{extract: {lines: !ruby/range 4..4}}
|
33
29
|
~~~
|
34
30
|
|
35
|
-
|
31
|
+
When requiring `cmdparse`, the `optparse` library from Ruby's standard library is also automatically
|
32
|
+
required which is used for doing the option parsing part.
|
33
|
+
|
34
|
+
|
35
|
+
### The Basic Command Parser Object
|
36
|
+
|
37
|
+
Next we will define our command parser object through the [CmdParse::CommandParser] class. Objects
|
38
|
+
of this class are used for defining the top level commands of a program as well as the global
|
39
|
+
options and other information like the name of the program or its version.
|
36
40
|
|
37
41
|
~~~ ruby
|
38
|
-
|
39
|
-
testcmd = CmdParse::Command.new( 'test', true )
|
40
|
-
testcmd.short_desc = "Short desc"
|
41
|
-
cmd.add_command( testcmd )
|
42
|
-
|
43
|
-
sub = CmdParse::Command.new( 'sub', false )
|
44
|
-
sub.short_desc = "Add an IP address"
|
45
|
-
sub.set_execution_block do |args|
|
46
|
-
puts "Hallo #{args}"
|
47
|
-
end
|
48
|
-
testcmd.add_command( sub )
|
42
|
+
{extract: {lines: !ruby/range 26..29}}
|
49
43
|
~~~
|
50
44
|
|
45
|
+
We use the `handle_exceptions` argument of the constructor so that exceptions are automatically
|
46
|
+
handled gracefully, i.e. by showing an appropriate error message. If we used `true` as value, the
|
47
|
+
help screen would be shown in addition to the error message.
|
51
48
|
|
52
|
-
|
49
|
+
The next lines set the name of the program, its version and a banner that is shown on all help
|
50
|
+
messages. All this information is set on the [main options][CmdParse::CommandParser#main_options].
|
51
|
+
Setting this information on the global options or any other `OptionParser` instance has no effect!
|
53
52
|
|
54
|
-
The complete code for this example can be found in the file `net.rb` of the `cmdparse` package!
|
55
53
|
|
56
|
-
|
57
|
-
'network statistics'. The shown code fragments do not include the whole program. So, instead of
|
58
|
-
writing all the code yourself, just look at the code fragments first and then use the include
|
59
|
-
`net.rb` file for running the program.
|
54
|
+
### Specifying Options
|
60
55
|
|
61
|
-
|
56
|
+
An integral part of any CLI program are options that can be set when invoking the program. A command
|
57
|
+
based CLI program has several kinds of options:
|
62
58
|
|
63
|
-
|
59
|
+
* The [main options][CmdParse::CommandParser#main_options] which can only be used directly after the
|
60
|
+
program name itself. An example for such an option would be a `--version` switch that only makes
|
61
|
+
sense at the top level.
|
64
62
|
|
65
|
-
|
66
|
-
|
67
|
-
~~~
|
63
|
+
* The [command specific options][CmdParse::Command#options] which can only be used after the command
|
64
|
+
name and before any sub-command name.
|
68
65
|
|
69
|
-
|
66
|
+
* The [global options][CmdParse::CommandParser#global_options] which can be used directly after the
|
67
|
+
program name as well as after any command. Therefore global options are normally used for things
|
68
|
+
that affect all commands, like a global verbosity setting.
|
70
69
|
|
71
|
-
|
72
|
-
|
70
|
+
All these options are specified using the Ruby standard library `optparse` and its `OptionParser`
|
71
|
+
class. The `OptionParser` implementation is battle tested, easy to use and allows great flexibility.
|
73
72
|
|
74
|
-
|
75
|
-
exceptions should be handled gracefully, i.e. by showing an appropriate error message and the help
|
76
|
-
screen. The second boolean argument defines whether the top level commands should use partial
|
77
|
-
command matching instead of full command matching. If partial command matching is used, then the
|
78
|
-
shortest unambiguous part of a command name can be used instead of always specifing the full command
|
79
|
-
name.
|
73
|
+
We go back to our example now and define a global option:
|
80
74
|
|
81
75
|
~~~ ruby
|
82
|
-
{extract: {lines: !ruby/range 30..
|
76
|
+
{extract: {lines: !ruby/range 30..34}}
|
83
77
|
~~~
|
84
78
|
|
85
|
-
The
|
86
|
-
|
87
|
-
|
79
|
+
The [data attribute][CmdParse::CommandParser#data] on the command parse object (or any command
|
80
|
+
object) can be used to store arbitrary information. Here we use it to store the verbosity level so
|
81
|
+
that it can easily be used later by any command.
|
82
|
+
|
83
|
+
We could have used a global variable for this but storing such information with the command parser
|
84
|
+
object usually makes for a better design. Note that the data attribute is only really useful when
|
85
|
+
the [CmdParse::CommandParser] is not sub-classed.
|
86
|
+
|
87
|
+
|
88
|
+
### Parsing the Command Line Arguments
|
88
89
|
|
89
|
-
|
90
|
-
arguments
|
90
|
+
We have set up everything that is needed for a basic command based program. The last step is to tell
|
91
|
+
the program to use our newly defined command parser object to process the command line arguments:
|
91
92
|
|
92
93
|
~~~ ruby
|
93
|
-
{extract: {lines: !ruby/range
|
94
|
+
{extract: {lines: !ruby/range 84..84}}
|
94
95
|
~~~
|
95
96
|
|
96
|
-
The
|
97
|
-
no
|
97
|
+
The [parse method][CmdParse::CommandParser#parse] parses the given array of arguments (or `ARGV` if
|
98
|
+
no array is specified). All the command line arguments are parsed and the given command executed.
|
98
99
|
|
99
|
-
The program
|
100
|
+
The program could now be executed but it won't be useful as we did not specify any commands yet.
|
100
101
|
|
101
102
|
|
102
103
|
### Defining commands
|
103
104
|
|
104
|
-
|
105
|
-
|
105
|
+
After performing the basic setup, we need to add some commands so that our program actually does
|
106
|
+
something useful.
|
107
|
+
|
108
|
+
First, we will add two built-in commands, namely the `help` and the `version` command:
|
106
109
|
|
107
110
|
~~~ ruby
|
108
|
-
{extract: {lines: !ruby/range
|
111
|
+
{extract: {lines: !ruby/range 35..36}}
|
109
112
|
~~~
|
110
113
|
|
111
|
-
That was easy! Now you can execute the program and specify the commands `help` or `version`.
|
112
|
-
|
114
|
+
That was easy! Now you can execute the program and specify the commands `help` or `version`. You
|
115
|
+
will also find that the `help` command is automatically invoked when you don't specify any command.
|
116
|
+
This is because we used the `default: true` argument when adding the `help` command which sets the
|
117
|
+
added command as the default command that should be used if no explicit command name is given.
|
118
|
+
|
119
|
+
The next step is to create the needed commands for our program. There are several different ways of
|
120
|
+
doing this.
|
113
121
|
|
114
122
|
~~~ ruby
|
115
|
-
{extract: {lines: !ruby/range
|
123
|
+
{extract: {lines: !ruby/range 40..42}}
|
116
124
|
~~~
|
117
125
|
|
118
|
-
|
119
|
-
|
120
|
-
matching. Next we add a short description so that the `help` command can produce something useful.
|
121
|
-
And at last, we add this command to our `CommandParser` instance.
|
126
|
+
One way is to create an instance of [CmdParse::Command], update it with all needed properties and
|
127
|
+
then add it to the command parser object or another command.
|
122
128
|
|
123
|
-
|
129
|
+
Since the `ipaddr` command takes other commands and doesn't do anything by itself, no action is
|
130
|
+
defined for it. Also notice that we have redefined the default command to be the `ipaddr` command.
|
131
|
+
The last added command with the argument `default: true` will be the default command.
|
124
132
|
|
125
133
|
~~~ ruby
|
126
|
-
{extract: {lines: !ruby/range
|
134
|
+
{extract: {lines: !ruby/range 45..52}}
|
127
135
|
~~~
|
128
136
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
137
|
+
Another way would be to take advantage of the fact that the [add_command
|
138
|
+
method][CmdParse::Command#add_command] creates a [CmdParse::Command] object when a name is passed as
|
139
|
+
argument instead of a command object itself and that the added command is always yielded if a block
|
140
|
+
is given.
|
133
141
|
|
134
|
-
|
135
|
-
|
142
|
+
Using this block we can now easily customize the command. Since the `ipaddr add` command does not
|
143
|
+
take any commands, we need to define an [action block][CmdParse::Command#action] that gets called if
|
144
|
+
the command should be executed. By using `*ips` we advertise to `cmdparse` that this command takes
|
145
|
+
an arbitrary number of IP addresses as arguments. Note that this is also automatically reflected in
|
146
|
+
the usage line for the command!
|
136
147
|
|
137
|
-
|
138
|
-
|
139
|
-
is specified on the command line. Only one command can be specified as default command!
|
140
|
-
|
141
|
-
Till now we only used the basic `Command` class to specify commands. However, new commands can also
|
142
|
-
be created by subclassing the `Command` class, as shown with this last command:
|
148
|
+
We add the `ipaddr del` and `ipaddr list` commands in a similar manner and set the `list` command to
|
149
|
+
be the default sub-command for the `ipaddr` command.
|
143
150
|
|
144
151
|
~~~ ruby
|
145
|
-
{extract: {lines: !ruby/range
|
146
|
-
|
152
|
+
{extract: {lines: !ruby/range 6..24}}
|
153
|
+
|
154
|
+
{extract: {lines: !ruby/range 37..37}}
|
147
155
|
~~~
|
148
156
|
|
157
|
+
The last way for creating a command is to sub-class the [CmdParse::Command] class and do all
|
158
|
+
customization there. If this is done, it is recommended to override the `#execute` method instead of
|
159
|
+
setting an action block.
|
160
|
+
|
161
|
+
We can also see that the execute method takes one or two arguments and that these arguments are also
|
162
|
+
properly documented.
|
163
|
+
|
164
|
+
|
165
|
+
### Running the Program
|
166
|
+
|
167
|
+
<%= (context[:args] = nil; context.render_block('execution')) %>
|
168
|
+
|
169
|
+
Now that we have completed our program we can finally run it!
|
149
170
|
|
150
|
-
|
171
|
+
Below are some sample invocations with their respective output and some explanations.
|
151
172
|
|
152
|
-
|
153
|
-
which arguments you choose.
|
173
|
+
<%= (context[:args] = ""; context.render_block('execution')) %>
|
154
174
|
|
155
|
-
|
175
|
+
When called with no arguments, the default command is executed. The default top level command is
|
176
|
+
`ipaddr` and its default command is `list` which shows the added IP addresses. So far, no IP
|
177
|
+
addresses are stored - we should change that.
|
156
178
|
|
157
|
-
|
179
|
+
<%= (context[:args] = "ip add 192.168.0.1"; context.render_block('execution')) %>
|
158
180
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
181
|
+
Now we have added one IP address. You might have noticed that we used `ip` instead of `ipaddr`.
|
182
|
+
Since partial command matching is automatically done, the shortest unambiguous name for a command
|
183
|
+
can be used. As there is no other command starting with `ip` (or even with the letter `i`), it is
|
184
|
+
sufficient to write the above to select the `ipaddr` command.
|
163
185
|
|
164
|
-
|
186
|
+
Now lets add some more IPs but with some informational output.
|
165
187
|
|
166
|
-
|
188
|
+
<%= (context[:args] = "i a 192.168.0.2 192.168.0.4 192.168.0.3 -v"; context.render_block('execution')) %>
|
167
189
|
|
168
|
-
|
190
|
+
This time we added three IP addresses and by using the global option `-v` we got some informational
|
191
|
+
output, too.
|
169
192
|
|
170
|
-
|
193
|
+
Let's display which IP addresses are currently stored.
|
171
194
|
|
172
|
-
|
195
|
+
<%= (context[:args] = "ipaddr list"; context.render_block('execution')) %>
|
173
196
|
|
174
|
-
|
197
|
+
So we have four IPs stored. However, we really only need three so we delete one.
|
175
198
|
|
176
|
-
|
177
|
-
`list`) on the command line. However, as the `list` command is the default command for `ipaddr` you
|
178
|
-
do not need to type it.
|
199
|
+
<%= (context[:args] = "ip -v del 192.16.8.0.4 "; context.render_block('execution')) %>
|
179
200
|
|
180
|
-
|
201
|
+
That's much better! But we all are getting sick of this and don't want any IP addresses stored
|
202
|
+
anymore.
|
181
203
|
|
182
|
-
|
204
|
+
<%= (context[:args] = "ip -a del"; context.render_block('execution')) %>
|
183
205
|
|
184
|
-
|
185
|
-
|
186
|
-
`i`), it is sufficient to write the above to select the `ipaddr` command.
|
206
|
+
Alas, I mistyped that last command. The option `-a` is a command specific option of `ipaddr del` and
|
207
|
+
therefore not recognized by `ipaddr`.
|
187
208
|
|
188
|
-
|
189
|
-
they can be anywhere, like this
|
209
|
+
<%= (context[:args] = ["ip del -av", '']; context.render_block('execution')) %>
|
190
210
|
|
191
|
-
|
211
|
+
After deleting all IP addresses none are shown anymore - perfect!
|
212
|
+
|
213
|
+
Now we want to see the "network statistics" part of our program. What was the command name again?
|
214
|
+
Let's get some help!
|
215
|
+
|
216
|
+
<%= (context[:args] = ["help"]; context.render_block('execution')) %>
|
217
|
+
|
218
|
+
Ah, yes, the name was `stat`, now we remember!
|
219
|
+
|
220
|
+
And there are some interesting things in the help output worth pointing out:
|
221
|
+
|
222
|
+
* The usage line shows us that we can define top level option (in addition to the global options)
|
223
|
+
and also nicely lists the available commands.
|
224
|
+
|
225
|
+
* The asterisks in the section for the commands show us the default commands.
|
226
|
+
|
227
|
+
* We have a top level option `-v` for showing the version as well as a global option `-v` for
|
228
|
+
setting the verbosity level. As mentioned in the help output, the top level option (or a command
|
229
|
+
specific option) always takes precedence over global options.
|
230
|
+
|
231
|
+
To make the last point clear, we run the command with the `-v` option.
|
232
|
+
|
233
|
+
<%= (context[:args] = ["-v"]; context.render_block('execution')) %>
|
234
|
+
|
235
|
+
This shows us the version information as expected instead of invoking the default command.
|
236
|
+
|
237
|
+
Back to the network statistics. Now we now the command name but we have forgotten how to use the
|
238
|
+
command itself. The `help` command comes to our rescue again!
|
239
|
+
|
240
|
+
<%= (context[:args] = ["help stat"]; context.render_block('execution')) %>
|
241
|
+
|
242
|
+
There are again some things to point out:
|
243
|
+
|
244
|
+
* We get a short summary and a more detailed description of the command. The short description is
|
245
|
+
the one shown in the general help overview. The detailed description is normally longer than this
|
246
|
+
and fully explains the command.
|
247
|
+
|
248
|
+
* The arguments for the command are also described. When looking at the usage line, you can see that
|
249
|
+
the `M` argument is optional, but the `N` argument isn't (indicated by the brackets). This means
|
250
|
+
that we need at least one argument. If we provide only one argument, it is used for `N`. And if we
|
251
|
+
provide two arguments, they are used for `M` and `N` (in this order).
|
252
|
+
|
253
|
+
How does `cmdparse` know that `M` is optional? It has inferred this (as well as the names
|
254
|
+
themselves) by looking at the signature of the execute method of the command!
|
255
|
+
|
256
|
+
Now we know everything to invoke the command.
|
257
|
+
|
258
|
+
<%= (context[:args] = ["stat 5", "stat 3 5"]; context.render_block('execution')) %>
|
259
|
+
|
260
|
+
|
261
|
+
### Final Words
|
262
|
+
|
263
|
+
Our `net` program is certainly only useful for this tutorial, however, it nicely showcases many of
|
264
|
+
the features of `cmdparse` and how easy `cmdparse` is to use.
|
265
|
+
|
266
|
+
If you haven't done so by now, [install](installation.html) the `cmdparse` library,
|
267
|
+
[download][netrb] our sample `net` program and experiment a bit with it. The [API
|
268
|
+
documentation](/api/) is quite extensive and will answer all remaining questions. And if it doesn't,
|
269
|
+
you can contact [me](mailto:t_leitner@gmx.at).
|
270
|
+
|
271
|
+
|
272
|
+
|
273
|
+
[netrb]: https://github.com/gettalong/cmdparse/blob/master/example/net.rb
|
274
|
+
|
275
|
+
|
276
|
+
--- name:execution pipeline:ruby
|
277
|
+
if context[:args].nil?
|
278
|
+
require 'fileutils'
|
279
|
+
FileUtils.rm(context.website.directory + '/dumpnet', :force => true)
|
280
|
+
context.content = ''
|
281
|
+
else
|
282
|
+
args = [context[:args]].flatten
|
283
|
+
result = args.map do |arg|
|
284
|
+
["$ <strong>ruby example/net.rb #{arg}</strong>", h(`ruby -I#{context.website.directory}/lib #{context.website.directory}/example/net.rb #{arg}`).chomp]
|
285
|
+
end.flatten.push("$").delete_if {|l| l.empty? }.join("\n")
|
286
|
+
context.content = "<pre>#{result}\n</pre>"
|
287
|
+
end
|
data/doc/virtual
CHANGED
data/example/net.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# if something is changed here -> change line numbers in doc/tutorial.page
|
3
|
+
|
4
|
+
require 'cmdparse'
|
5
|
+
|
6
|
+
class NetStatCommand < CmdParse::Command
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
super('stat', takes_commands: false)
|
10
|
+
short_desc("Show network statistics")
|
11
|
+
long_desc("This command shows very useful 'network' statistics - eye catching!!!")
|
12
|
+
argument_desc(M: 'start row number', N: 'end row number')
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute(m = 1, n)
|
16
|
+
puts "Showing network statistics" if command_parser.data[:verbose]
|
17
|
+
puts
|
18
|
+
m.to_i.upto(n.to_i) do |row|
|
19
|
+
puts " "*(20 - row).abs + "#"*(row*2 - 1).abs
|
20
|
+
end
|
21
|
+
puts
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
parser = CmdParse::CommandParser.new(handle_exceptions: :no_help)
|
27
|
+
parser.main_options.program_name = "net"
|
28
|
+
parser.main_options.version = "0.1.1"
|
29
|
+
parser.main_options.banner = "This is net, a s[ai]mple network analytics program"
|
30
|
+
parser.global_options do |opt|
|
31
|
+
opt.on("-v", "--verbose", "Be verbose when outputting info") do
|
32
|
+
parser.data[:verbose] = true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
parser.add_command(CmdParse::HelpCommand.new, default: true)
|
36
|
+
parser.add_command(CmdParse::VersionCommand.new)
|
37
|
+
parser.add_command(NetStatCommand.new)
|
38
|
+
|
39
|
+
# ipaddr
|
40
|
+
ipaddr = CmdParse::Command.new('ipaddr')
|
41
|
+
ipaddr.short_desc = "Manage IP addresses"
|
42
|
+
parser.add_command(ipaddr, default: true)
|
43
|
+
|
44
|
+
# ipaddr add
|
45
|
+
ipaddr.add_command('add') do |cmd|
|
46
|
+
cmd.takes_commands(false)
|
47
|
+
cmd.short_desc("Add an IP address")
|
48
|
+
cmd.action do |*ips|
|
49
|
+
puts "Adding ip addresses: #{ips.join(', ')}" if parser.data[:verbose]
|
50
|
+
parser.data[:ipaddrs] += ips
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# ipaddr del
|
55
|
+
del = CmdParse::Command.new('del', takes_commands: false)
|
56
|
+
del.short_desc = "Delete an IP address"
|
57
|
+
del.options.on('-a', '--all', 'Delete all IPs') { del.data[:delete_all] = true }
|
58
|
+
del.action do |*ips|
|
59
|
+
if del.data[:delete_all]
|
60
|
+
puts "All IP adresses deleted!" if parser.data[:verbose]
|
61
|
+
parser.data[:ipaddrs] = []
|
62
|
+
else
|
63
|
+
puts "Deleting ip addresses: #{ips.join(', ')}" if parser.data[:verbose]
|
64
|
+
ips.each {|ip| parser.data[:ipaddrs].delete(ip) }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
ipaddr.add_command(del)
|
68
|
+
|
69
|
+
# ipaddr list
|
70
|
+
list = CmdParse::Command.new('list', takes_commands: false)
|
71
|
+
list.short_desc = "Lists all IP addresses"
|
72
|
+
list.action do
|
73
|
+
puts "Listing ip addresses:" if parser.data[:verbose]
|
74
|
+
puts parser.data[:ipaddrs].join("\n") unless parser.data[:ipaddrs].empty?
|
75
|
+
end
|
76
|
+
ipaddr.add_command(list, default: true)
|
77
|
+
|
78
|
+
|
79
|
+
parser.data[:ipaddrs] = if File.exists?('dumpnet')
|
80
|
+
Marshal.load(File.read('dumpnet', mode: 'rb'))
|
81
|
+
else
|
82
|
+
[]
|
83
|
+
end
|
84
|
+
parser.parse
|
85
|
+
File.write('dumpnet', Marshal.dump(parser.data[:ipaddrs]), mode: 'wb+')
|