mtik 3.0.5 → 3.1.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.
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