Page: UMI code samples
v.4 by Roman Uskov
2018-11-01 15:11
2018-11-01 15:11
UMI samples
Ruby
You will need to install farcall gem first.
The sample assumes UMI archive is unpacked into ./umi
directory.
Simple UMI connection
This sample generates new private key and saves it to a file:
require 'farcall'
require 'open3'
require 'base64'
# Start UMI in default mode. We need only stdin/out
UMI_in,UMI_out = Open3.popen3('./umi/bin/umi')
# Now create endpoint with it. Our UMI uses \n as delimiter, so we should specify it:
UMI = Farcall::Endpoint.new(
Farcall::JsonTransport.create(delimiter: "\n", input: UMI_out,output: UMI_in)
)
# call key creation
private_key_ref = UMI.sync_call("instantiate", "PrivateKey", 2048)
# call pack for created key object
packed_key_64 = UMI.sync_call("invoke", private_key_ref.id, "pack")[:base64]
# And save in universa binary format
open('new_key.private.unikey', 'wb') { |f| f << Base64.decode64(packed_key_64) }
Very simple, but it could be ever simpler if we create some class to incapsulate UMI boilerplate and reference lifetime tracking. For example:
u = UMI.new
pk = u.instantiate "PrivateKey", 2048
contract = u.instantiate "Contract", pk
contract.seal()
p contract.getCreatedAt() # => ruby Time instance
p contract.check() # => true
p contract.isOk() # => true
# we can chain Java method calls:
address_str1 = contract.getOwner().getAllAddresses()[0]
address_str2 = pk.getPublicKey().getShortAddress().toString()
assert address_str1 == address_str2
pk2 = contract.getOwner().getKeys()[0]
Here is a smaple code that incapulates remote kitchen for the sample above:
require 'open3'
require 'farcall'
require 'base64'
class UMI
class Error < IOError
end
def initialize(path = "./umi")
@in, @out, @wtr = Open3.popen2("#{path}/bin/umi")
@endpoint = UMI = Farcall::Endpoint.new(
Farcall::JsonTransport.create(delimiter: "\n", input: @out, output: @in)
)
@closed = false
end
def new *args
end
def instantiate object_class, *args
ensure_open
Ref.new(self, call("instantiate", object_class, *prepare_args(args)))
end
def invoke ref, method, *args
ensure_open
encode_result call("invoke", ref.id, method, *prepare_args(args))
end
def close
# Process.kill("INT", @wtr.pid)
# p @wtr.join
@endpoint.close
@in.close
@out.close
@closed = true
@wtr.value.exited?
end
private
def prepare_args args
args.map {|x| x.respond_to?(:as_UMI_arg) ? x.as_UMI_arg : x}
end
def encode_result value
case value
when Hashie::Mash
type = value.__type
case type
when 'RemoteObject';
Ref.new(self, value)
when 'binary';
Base64.decode64(value.base64)
when 'unixtime';
Time.at(value.seconds)
else
value
end
when Hashie::Array
value.map {|x| encode_result x}
else
value
end
end
def ensure_open
raise Error, "UMI interface is closed" if @closed
end
def call(command, *args)
@endpoint.sync_call(command, *args)
end
end
# A reference to any Java-object that can call its methods direcly
# as if these are local ones:
class Ref
def initialize UMI, ref
@UMI, @ref = UMI, ref
@id = ref.id
end
def respond_to_missing?(method_name, include_private = false)
true
end
# this is a magick: call remote method instead of the local one
def method_missing(method_name, *args, &block)
@UMI.invoke @ref, method_name, *args
end
# This allow Ref instance to be an argument to the remote call
def as_UMI_arg
@ref
end
def inspect
"<umiRef:#{@UMI.__id__}:#{@ref.className}:#{@id})>"
end
def ==(other)
other.is_a?(Ref) && other.UMI == @UMI && other.id == id
end
end
More samples are under way with.