mtik 3.0.5 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.txt CHANGED
@@ -1,3 +1,17 @@
1
+ 2010-04-24 (24 APR 2010) VERSION 3.1.0 Aaron D. Gifford (http://www.aarongifford.com)
2
+ * Added find_sentences() method to MTik::Reply -- just sugar to Array.select()
3
+ * Changed MTik::Connection.fetch() method to add an optional timeout parameter
4
+ which should not affect the API and should be backward compatible. By default,
5
+ there is no inactivity timeout for downloads. But if you set the timeout parameter
6
+ to a positive number, when a reply arrives and no progress/activity has been
7
+ made for timeout seconds, the command will be cancelled. This should help with
8
+ stalled downloads (i.e. the remote side has stopped sending but the TCP connection
9
+ remains open/active).
10
+ * Also add the MTik::Request object as a parameter to the MTik::Connection.fetch()
11
+ method's callback so that a script could use the request object to cancel the
12
+ command if needed. Due to this change, I decided to bump the version to 3.1.0.
13
+ * Fixed RDoc formatting in several files, and added an RDocTask to the Rakefile
14
+
1
15
  2010-04-23 (23 APR 2010) VERSION 3.0.5 Aaron D. Gifford (http://www.aarongifford.com)
2
16
  * Double bug-fix (typo fix and logic fix) to request.rb thanks to Allan Eising and
3
17
  Søren Daugaard. Thank you both for the patch!
data/README.txt CHANGED
@@ -43,7 +43,7 @@ For documentation on the MikroTik RouterOS APi, see
43
43
 
44
44
  To install MTik is through its GEM file:
45
45
 
46
- % [sudo] gem install mtik-3.3.0.gem
46
+ % [sudo] gem install mtik-3.1.0.gem
47
47
 
48
48
  == License
49
49
 
data/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'rubygems'
2
2
  require 'rake/gempackagetask'
3
+ require 'rake/rdoctask'
3
4
 
4
5
  gemspec = Gem::Specification.new do |spec|
5
6
  spec.name = 'mtik'
@@ -39,5 +40,15 @@ Rake::GemPackageTask.new(gemspec) do |pkg|
39
40
  pkg.need_tar = true
40
41
  end
41
42
 
42
- task :default => [ 'pkg/mtik-' + File.open('VERSION.txt','r').to_a.join.strip + '.gem' ]
43
+ Rake::RDocTask.new do |rdoc|
44
+ rdoc.name = 'rdoc'
45
+ rdoc.main = 'README.txt'
46
+ rdoc.rdoc_dir = 'doc'
47
+ rdoc.rdoc_files.include('README.txt', 'LICENSE.txt', 'CHANGELOG.txt', 'lib/**/*.rb')
48
+ end
49
+
50
+ task :default => [
51
+ 'pkg/mtik-' + File.open('VERSION.txt','r').to_a.join.strip + '.gem',
52
+ :rdoc
53
+ ]
43
54
 
data/VERSION.txt CHANGED
@@ -1 +1 @@
1
- 3.0.5
1
+ 3.1.0
data/examples/tikfetch.rb CHANGED
@@ -73,7 +73,7 @@ while ARGV.length > 0
73
73
  print ">>> ERROR: There is a file named '#{filename}' already on the device.\n"
74
74
  else
75
75
  print ">>> OK: Fetching file '#{filename}' from URL '#{url}'...\n"
76
- mt.fetch(url, filename) do |status, total, bytes|
76
+ mt.fetch(url, filename, 120) do |status, total, bytes, req|
77
77
  case status
78
78
  when 'failed'
79
79
  print ">>> ERROR: File '#{filename}' download failed!\n"
data/lib/mtik.rb CHANGED
@@ -188,10 +188,12 @@ module MTik
188
188
  ## more commands, retrieve the response(s), close the connection,
189
189
  ## and return the response(s).
190
190
  ##
191
- ## *WARNING* :: If you use this call with an API command like
192
- ## +/tool/fetch+ it will forever keep reading replies, never
193
- ## returning. So do _NOT_ use this with any API command that does
194
- ## not complete with a "!done" with no additional interaction.
191
+ ## *WARNING*:
192
+ ## If you use this call with an API command like <i>"/tool/fetch"</i>
193
+ ## or a similar command that generates replies until canceled,
194
+ ## it will forever keep reading replies, never returning. So do
195
+ ## _NOT_ use this with any API command that does not complete with
196
+ ## a <i>"!done"</i> response without any additional interaction.
195
197
  def self.command(args)
196
198
  tk = MTik::Connection.new(
197
199
  :host => args[:host],
@@ -43,16 +43,16 @@ class MTik::Connection
43
43
  ## key/value pair style arguments must be specified. The one
44
44
  ## required argument is the host or IP of the device to connect
45
45
  ## to.
46
- ## _host_ :: This is the only _required_ argument. Example:
47
- ## + :host => "rb411.example.org" +
48
- ## _port_ :: Override the default API port (8728)
49
- ## _user_ :: Override the default API username ('admin')
50
- ## _pass_ :: Override the default API password (blank)
51
- ## _conn_timeout_ :: Override the default connection
52
- ## timeout (60 seconds) -- *NOT USED*
53
- ## _cmd_timeout_ :: Override the default command timeout
54
- ## (60 seconds) -- the number of seconds
55
- ## to wait for additional API input.
46
+ ## +host+:: This is the only _required_ argument. Example:
47
+ ## <i> :host => "rb411.example.org" </i>
48
+ ## +port+:: Override the default API port (8728)
49
+ ## +user+:: Override the default API username ('admin')
50
+ ## +pass+:: Override the default API password (blank)
51
+ ## +conn_timeout+:: Override the default connection
52
+ ## timeout (60 seconds) -- *NOT USED*
53
+ ## +cmd_timeout+:: Override the default command timeout
54
+ ## (60 seconds) -- the number of seconds
55
+ ## to wait for additional API input.
56
56
  def initialize(args)
57
57
  @sock = nil
58
58
  @requests = Hash.new
@@ -321,18 +321,18 @@ class MTik::Connection
321
321
  end
322
322
 
323
323
  ## Send a request to the device.
324
- ## +await_completion+ :: Boolean indicating whether to execute callbacks
325
- ## only once upon request completion (if set to _true_)
326
- ## or to execute for every received complete sentence
327
- ## (if set to _false_). ALTERNATIVELY, this parameter
328
- ## may be an object (MTik::Request) to be sent, in which
329
- ## case any command and/or arguments will be treated as
330
- ## additional arguments to the request contained in the
331
- ## object.
332
- ## +command+ :: The command to be executed.
333
- ## +args+ :: Zero or more arguments to the command
334
- ## +callback+ :: Proc/lambda code (or code block if not provided as
335
- ## an argument) to be called. (See the +await_completion+
324
+ ## +await_completion+:: Boolean indicating whether to execute callbacks
325
+ ## only once upon request completion (if set to _true_)
326
+ ## or to execute for every received complete sentence
327
+ ## (if set to _false_). ALTERNATIVELY, this parameter
328
+ ## may be an object (MTik::Request) to be sent, in which
329
+ ## case any command and/or arguments will be treated as
330
+ ## additional arguments to the request contained in the
331
+ ## object.
332
+ ## +command+:: The command to be executed.
333
+ ## +args+:: Zero or more arguments to the command
334
+ ## +callback+:: Proc/lambda code (or code block if not provided as
335
+ ## an argument) to be called. (See the +await_completion+
336
336
  ##
337
337
  def send_request(await_completion, command, *args, &callback)
338
338
  if await_completion.is_a?(MTik::Request)
@@ -367,18 +367,19 @@ class MTik::Connection
367
367
 
368
368
  ## Send a command, then wait for the command to complete, then return
369
369
  ## the completed reply.
370
- ## _NOTE_ :: This call has its own event loop that will cycle
371
- ## until the command in question completes. You
372
- ## should:
373
- ## * NOT call get_reply with a command that may not
374
- ## complete with a "!done" response on its own
375
- ## (with no additional intervention); and
376
- ## * BE CAREFUL to understand how things interact if
377
- ## you mix this call with requests that generate
378
- ## continuous output.
379
- ## +command+ :: The command to execute
380
- ## +args+ :: Arguments (if any)
381
- ## +callback+ :: Proc/lambda or code block to act as callback
370
+ ##
371
+ ## +command+:: The command to execute
372
+ ## +args+:: Arguments (if any)
373
+ ## +callback+:: Proc/lambda or code block to act as callback
374
+ ##
375
+ ## *NOTE*: This call has its own event loop that will cycle until
376
+ ## the command in question completes. You should:
377
+ ## * NOT call get_reply with a command that may not
378
+ ## complete with a "!done" response on its own
379
+ ## (with no additional intervention); and
380
+ ## * BE CAREFUL to understand how things interact if
381
+ ## you mix this call with requests that generate
382
+ ## continuous output.
382
383
  def get_reply(command, *args, &callback)
383
384
  req = send_request(true, command, *args, &callback)
384
385
  wait_for_request(req)
@@ -483,20 +484,29 @@ class MTik::Connection
483
484
  ## Utility to execute the "/tool/fetch" command, instructing
484
485
  ## the device to download a file from the specified URL.
485
486
  ## Status updates are provided via the provided callback.
486
- ## _url_ :: The URL to fetch the file from
487
- ## _filename_ :: The filename to use on the device
488
- ## _callback_ :: Callback called for status updates. The three
489
- ## arguments passed to the callback are:
490
- ## _status_ :: Either 'downloading', 'connecting',
491
- ## 'failed', 'requesting', or 'finished',
492
- ## otherwise a '!trap' error occured,
493
- ## and the value is the trap message.
494
- ## _total_ :: Final expected file size in bytes
495
- ## _bytes_ :: Number of bytes transferred so far
496
- def fetch(url, filename, &callback)
497
- total = bytes = 0
487
+ ## +url+:: The URL to fetch the file from
488
+ ## +filename+:: The filename to use on the device
489
+ ## +timeout+:: Cancel command if a reply indicates the
490
+ ## download has stalled for +timeout+ seconds.
491
+ ## This is disabled by default. Disable by
492
+ ## setting +timeout+ to nil or zero, enable by
493
+ ## supplying a positive number of seconds.
494
+ ## (OPTIONAL argument)
495
+ ## +callback+:: Callback called for status updates.
496
+ ##
497
+ ## The arguments passed to the callback are:
498
+ ## +status+:: Either 'downloading', 'connecting',
499
+ ## 'failed', 'requesting', or 'finished',
500
+ ## otherwise a '!trap' error occured,
501
+ ## and the value is the trap message.
502
+ ## +total+:: Final expected file size in bytes
503
+ ## +bytes+:: Number of bytes transferred so far
504
+ ## +request+:: The MTik::Request object
505
+ def fetch(url, filename, timeout=nil, &callback)
506
+ total = bytes = oldbytes = 0
498
507
  status = ''
499
508
  done = false
509
+ lastactivity = Time.now
500
510
  req = get_reply_each(
501
511
  '/tool/fetch',
502
512
  '=url=' + url,
@@ -511,12 +521,18 @@ class MTik::Connection
511
521
  when 'downloading'
512
522
  total = s['total'].to_i
513
523
  bytes = s['downloaded'].to_i
514
- callback.call(status, total, bytes)
524
+ if bytes != oldbytes
525
+ lastactivity = Time.now
526
+ elsif timeout != 0 && !timeout.nil? && Time.now - lastactivity > timeout
527
+ ## Cancel the request (idle too long):
528
+ get_reply('/cancel', '=tag=' + req.tag) {}
529
+ end
530
+ callback.call(status, total, bytes, req)
515
531
  when 'connecting', 'requesting'
516
- callback.call(status, 0, 0)
532
+ callback.call(status, 0, 0, req)
517
533
  when 'failed', 'finished'
518
534
  bytes = total if status == 'finished'
519
- callback.call(status, total, bytes)
535
+ callback.call(status, total, bytes, req)
520
536
  done = true
521
537
  ## Now terminate the download request (since it's done):
522
538
  get_reply('/cancel', '=tag=' + req.tag) {}
@@ -526,7 +542,7 @@ class MTik::Connection
526
542
  elsif s.key?('!trap')
527
543
  ## Pass trap message back (unless finished--in which case we
528
544
  ## ignore the 'interrrupted' trap message):
529
- callback.call(s['message'], total, bytes) if !done
545
+ callback.call(s['message'], total, bytes, req) if !done
530
546
  end
531
547
  end
532
548
  end
@@ -32,6 +32,6 @@
32
32
  ## THE POSSIBILITY OF SUCH DAMAGE.
33
33
  ############################################################################
34
34
 
35
- ## Execption raised when a '!fatal' response is received from a device
35
+ ## Execption raised when a <i>"!fatal"</i> response is received from a device
36
36
  class MTik::FatalError < MTik::Error ; end
37
37
 
data/lib/mtik/reply.rb CHANGED
@@ -34,17 +34,23 @@
34
34
 
35
35
  ## A MikroTik API reply is stored as an array of response sentences. Each
36
36
  ## sentence is a key/value Hash object. The MTik::Reply class is simply
37
- ## a basic Ruby Array with a find_sentence method added.
37
+ ## a basic Ruby Array with find_sentence and find_sentences methods added.
38
38
  class MTik::Reply < Array
39
+ ## This method is nearly identical to Array.select{|i| i.key?(key)}[0]
40
+ ## except that this method short-circuits and returns when the first
41
+ ## match is found.
39
42
  def find_sentence(key)
40
- i = 0
41
- while i < self.length
42
- if self[i].key?(key)
43
- return self[i]
44
- end
45
- i += 1
43
+ self.each do |sentence|
44
+ return sentence if sentence.key?(key)
46
45
  end
47
46
  return nil
48
47
  end
48
+
49
+ ## This method is simply an alias for Array.select{|i| i.key?(key)}
50
+ def find_sentences(key)
51
+ return self.select do |sentence|
52
+ sentence.key?(key)
53
+ end
54
+ end
49
55
  end
50
56
 
data/lib/mtik/request.rb CHANGED
@@ -35,33 +35,34 @@
35
35
  ## A MikroTik API request object is stored as an array of MikroTik
36
36
  ## API-style words, the first word being the command, subsequent words
37
37
  ## (if any) are command arguments. Each request will automatically
38
- ## have a unique tag generated (so any +.tag=value+ arguments will be
39
- ## ignored). A request is incomplete until the final +!done+ response
40
- ## sentence has been received.
38
+ ## have a unique tag generated (so any <i>".tag=value"</i> arguments
39
+ ## will be ignored). A request is incomplete until the final
40
+ ## <i>"!done"</i> response sentence has been received.
41
41
  class MTik::Request < Array
42
42
  @@tagspace = 0 ## Used to keep all tags unique.
43
43
 
44
44
  ## Create a new MTik::Request.
45
- ## +await_completion+ :: A boolean parameter indicating when callback(s)
46
- ## should be called. A value of _true_ will result
47
- ## in callback(s) only being called once, when the
48
- ## final +!done+ response is received. A value of
49
- ## _false_ means callback(s) will be called _each_
50
- ## time a response sentence is received.
51
- ## +command+ :: The MikroTik API command to execute (a String).
52
- ## Examples:
53
- ## * +"/interface/getall"+
54
- ## * +"/ip/route/add"+
55
- ## +args+ :: Zero or more String arguments for the command,
56
- ## already encoded in +"=key=value"+, +".id=value"+
57
- ## or +"?query"+ API format.
58
- ## +callback+:: A Proc or code block may be passed which will
59
- ## be called with two arguments:
60
- ## 1. this MTik::Request object; and
61
- ## 2. the most recently received response sentence.
62
- ## When or how often callbacks are called depends on
63
- ## whether the +await_completion+ parameter is _true_
64
- ## or _false_ (see above).
45
+ ## +await_completion+:: A boolean parameter indicating when callback(s)
46
+ ## should be called. A value of _true_ will result
47
+ ## in callback(s) only being called once, when the
48
+ ## final +!done+ response is received. A value of
49
+ ## _false_ means callback(s) will be called _each_
50
+ ## time a response sentence is received.
51
+ ## +command+:: The MikroTik API command to execute (a String).
52
+ ## Examples:
53
+ ## "/interface/getall"
54
+ ## "/ip/route/add"
55
+ ## +args+:: Zero or more String arguments for the command,
56
+ ## already encoded in <i>"=key=value"</i>,
57
+ ## <i>".id=value"</i>, or <i>"?query"</i> API
58
+ ## format.
59
+ ## +callback+:: A Proc or code block may be passed which will
60
+ ## be called with two arguments:
61
+ ## 1. this MTik::Request object; and
62
+ ## 2. the most recently received response sentence.
63
+ ## When or how often callbacks are called depends on
64
+ ## whether the +await_completion+ parameter is _true_
65
+ ## or _false_ (see above).
65
66
  def initialize(await_completion, command, *args, &callback)
66
67
  @reply = MTik::Reply.new
67
68
  @command = command
@@ -175,7 +176,7 @@ class MTik::Request < Array
175
176
  end
176
177
 
177
178
  ## Another utility method to encode a byte string as a
178
- ## valid API _"word"_
179
+ ## valid API <i>"word"</i>
179
180
  def self.to_tikword(str)
180
181
  str = str.dup
181
182
  if RUBY_VERSION >= '1.9.0'
@@ -260,7 +261,7 @@ class MTik::Request < Array
260
261
  end
261
262
 
262
263
  ## Method the internal parser calls to flag this reply as completed
263
- ## upon receipt of a +!done+ reply sentence.
264
+ ## upon receipt of a <i>"!done"</i> reply sentence.
264
265
  def done!
265
266
  @state = 'complete'
266
267
  return true
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 3
7
+ - 1
7
8
  - 0
8
- - 5
9
- version: 3.0.5
9
+ version: 3.1.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Aaron D. Gifford