class MTik::Request
A MikroTik API request object is stored as an array of MikroTik API-style words, the first word being the command, subsequent words (if any) are command arguments. Each request will automatically have a unique tag generated (so any “.tag=value” arguments will be ignored). A request is incomplete until the final “!done” response sentence has been received.
Attributes
Public Class Methods
Utility method for packing an unsigned integer as a binary byte string of variable length
# File lib/mtik/request.rb, line 162 def self.bytepack(num, size) s = String.new if RUBY_VERSION >= '1.9.0' s.force_encoding(Encoding::BINARY) end x = num < 0 ? -num : num ## Treat as unsigned while size > 0 size -= 1 s = (x & 0xff).chr + s x >>= 8 end raise RuntimeError.new( "Number #{num} is too large to fit in #{size} bytes." ) if x > 0 return s end
Create a new MTik::Request
.
await_completion
-
A boolean parameter indicating when callback(s) should be called. A value of true will result in callback(s) only being called once, when the final +!done+ response is received. A value of false means callback(s) will be called each time a response sentence is received.
command
-
The MikroTik API command to execute (a String). Examples:
"/interface/getall" "/ip/route/add"
args
-
Zero or more String arguments for the command, already encoded in “=key=value”, “.id=value”, or “?query” API format.
callback
-
A Proc or code block may be passed which will be called with two arguments:
-
this
MTik::Request
object; and -
the most recently received response sentence.
When or how often callbacks are called depends on whether the
await_completion
parameter is true or false (see above). -
# File lib/mtik/request.rb, line 67 def initialize(await_completion, command, *args, &callback) @reply = MTik::Reply.new @command = command @await_completion = await_completion @state = :new ## :new, :sent, :canceled, :complete @conn = nil args.flatten! @callbacks = Array.new if block_given? @callbacks.push(callback) elsif args.length > 0 && args[args.length-1].is_a?(Proc) @callbacks.push(args.pop) end super(&nil) ## Add command to the request sentence list: self.push(command) ## Add all arguments to the request sentence list: self.addargs(*args) ## Append a unique tag for the request: @tag = @@tagspace.to_s @@tagspace += 1 self.push(".tag=#{@tag}") end
Another utility method to encode a byte string as a valid API “word”
# File lib/mtik/request.rb, line 181 def self.to_tikword(str) str = str.dup if RUBY_VERSION >= '1.9.0' str.force_encoding(Encoding::BINARY) end if str.length < 0x80 return str.length.chr + str elsif str.length < 0x4000 return bytepack(str.length | 0x8000, 2) + str elsif str.length < 0x200000 return bytepack(str.length | 0xc00000, 3) + str elsif str.length < 0x10000000 return bytepack(str.length | 0xe0000000, 4) + str elsif str.length < 0x0100000000 return 0xf0.chr + bytepack(str.length, 5) + str else raise RuntimeError.new( "String is too long to be encoded for " + "the MikroTik API using a 4-byte length!" ) end end
Public Instance Methods
Append a single argument to the not-yet-sent request
# File lib/mtik/request.rb, line 129 def addarg(arg) unless /^\.tag=/.match(arg) self.push(arg) end end
Append one or more arguments to the not-yet-sent request
# File lib/mtik/request.rb, line 110 def addargs(*args) ## Add all additional arguments to the request sentence list: args.each do |arg| if arg.is_a?(Hash) ## Prepend argument keys that don't begin with the API- ## command-specific parameter character '.', nor the ## normal parameter charactre '=', nor the query character ## '?' with the ordinary parameter character '=': arg.each do |key, value| key = '=' + key unless /^[\?\=\.]/.match(key) addarg(key + '=' + value) end else addarg(arg) end end end
Add one or more callback Procs and/or a code block to the callback(s) that will be executed upon reply (either complete or partial, depending on the await_completion setting)
# File lib/mtik/request.rb, line 99 def append_callback(*callbacks, &callback) callbacks.flatten! callbacks.each do |cb| @callbacks.push(cb) end if block_given? @callbacks.push(callback) end end
Execute all callbacks, passing sentence along as the second parameter to each callback.
# File lib/mtik/request.rb, line 145 def callback(sentence) case @callbacks.length when 0 return nil when 1 return @callbacks[0].call(self, sentence) else result = Array.new @callbacks.each do |cb| result.push(cb.call(self, sentence)) end return result end end
Cancel a 'sent' request:
# File lib/mtik/request.rb, line 241 def cancel(&callback) if @state != :sent raise MTik::Error.new( "Method MTik::Request#cancel() called with state '#{@state}' " + "(should only call when state is :sent)" ) end @conn.send_request(true, '/cancel', '=tag=' + @tag, &callback) @state = :canceled end
Cancel a 'sent' request:
# File lib/mtik/request.rb, line 253 def cancel_each(&callback) if @state != :sent raise MTik::Error.new( "Method MTik::Request#cancel() called with state '#{@state}' " + "(should only call when state is :sent)" ) end @conn.send_request(false, '/cancel', '=tag=' + @tag, &callback) @state = :canceled end
Associate this request with a connection:
# File lib/mtik/request.rb, line 205 def conn(c) unless c.is_a?(MTik::Connection) raise RuntimeError.new( "Unexpected class '#{c.class}' in MTik::Request#conn() " + "(expected MTik::Connection)" ) end unless @conn.nil? raise MTik::Error.new( "Method MTik::Request#conn() called when MTik::Request " + "is already associated with an MTik::Connection object." ) end @conn = c end
Method the internal parser calls to flag this reply as completed upon receipt of a “!done” reply sentence. WARNING: If you call this manually and another sentence arrives, an exception will be raised!
# File lib/mtik/request.rb, line 268 def done! @state = :complete return true end
Return the boolean completion status of the request, true if complete, false if not-yet-complete.
# File lib/mtik/request.rb, line 137 def done? return @state == :complete end
Encode this request as a binary byte string ready for transmission to a MikroTik device
# File lib/mtik/request.rb, line 223 def request ## Encode the request for sending to the device: return self.map {|w| MTik::Request::to_tikword(w)}.join + 0x00.chr end
Send the request over the associated connection:
# File lib/mtik/request.rb, line 229 def send if @conn.nil? raise MTik::Error.new( "Method MTik::Request#send() called when MTik::Request " + "is not yet associated with an MTik::Connection object." ) end @state = :sent return @conn.xmit(self) end