pub struct VM;
Expand description
Virtual Machine and helpers
Implementations§
source§impl VM
impl VM
sourcepub fn init()
pub fn init()
Initializes Ruby virtual machine.
This function should ONLY be used if you write a standalone application which calls Ruby itself, for example:
-
Sidekiq-like background processing
-
Unicorn-like web server
In these cases it should be called before any interaction with Ruby.
If you write a library which is being connected to Ruby in runtime (e.g. some gem), this function should not be used.
Examples
use rutie::{Class, VM};
VM::init();
// VM started, able to use Ruby now
// ...
Class::new("SomeClass", None); // etc
Examples found in repository?
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
fn main() {
VM::init();
let args: Vec<String> = env::args().collect();
if args.len() > 1 {
match VM::eval(&args[1]) {
Ok(_) => (),
Err(e) => {
println!("{}", e);
process::exit(1);
}
}
} else {
eprintln!(r#"Usage: eval "puts 'Put ruby code to be evaluated in a string after eval.' ""#);
process::exit(1);
}
}
sourcepub fn init_loadpath()
pub fn init_loadpath()
Initializes Ruby load path.
This enables more of Ruby’s internal features such as making additional encodings available.
This function, like VM::init
, should ONLY be used if you write a standalone
application which calls Ruby itself.
If you write a library which is being connected to Ruby in runtime (e.g. some gem), this function should not be used.
Examples
use rutie::{RString, Encoding, EncodingSupport, VM, Object};
VM::init_loadpath(); // Needed for alternate encodings
VM::require("enc/encdb");
VM::require("enc/trans/transdb");
let bytes = [254, 255, 1, 65, 0, 97, 1, 66] ;
let enc = Encoding::find("UTF-16").unwrap();
let mut string = RString::from_bytes(&bytes, &enc);
assert_eq!(string.to_bytes_unchecked(), bytes);
assert!(string.encoding().equals(&enc), "not equal!");
sourcepub fn require(name: &str)
pub fn require(name: &str)
Requires Ruby source file.
Examples
use rutie::VM;
VM::require("some_ruby_file");
Ruby:
require 'some_ruby_file'
sourcepub fn raise(exception: Class, message: &str)
pub fn raise(exception: Class, message: &str)
Raises an exception.
Examples
Built-in exceptions
use rutie::{Class, VM};
VM::raise(Class::from_existing("ArgumentError"), "Wrong argument");
Ruby:
raise ArgumentError, 'Wrong argument'
Custom exceptions
use rutie::{Class, VM};
let standard_error = Class::from_existing("StandardError");
let custom_exception = Class::new("CustomException", Some(&standard_error));
VM::raise(custom_exception, "Something went wrong");
Ruby:
class CustomException < StandardError
end
raise CustomException, 'Something went wrong'
sourcepub fn raise_ex<E>(exception: E)where
E: Into<AnyException>,
pub fn raise_ex<E>(exception: E)where
E: Into<AnyException>,
Raises an exception from a native AnyException
object.
Examples
Built-in exceptions
use rutie::{Class, VM, Exception, AnyException};
VM::raise_ex(AnyException::new("StandardError", Some("something went wrong")));
Ruby:
raise StandardError, 'something went wrong'
Custom exceptions
use rutie::{Class, VM, Exception, AnyException};
let standard_error = Class::from_existing("StandardError");
Class::new("CustomException", Some(&standard_error));
let exception = AnyException::new("CustomException", Some("something went wrong"));
VM::raise_ex(exception);
Ruby:
class CustomException < StandardError
end
raise CustomException, 'Something went wrong'
sourcepub fn eval(string: &str) -> Result<AnyObject, AnyException>
pub fn eval(string: &str) -> Result<AnyObject, AnyException>
Evals string and returns an Result<AnyObject, AnyException>
Examples
#[macro_use]
extern crate rutie;
use rutie::{Class, Fixnum, Object, VM};
fn main() {
// Successful example
let result = VM::eval("2+2").ok().unwrap().try_convert_to::<Fixnum>();
assert_eq!(result, Ok(Fixnum::new(4)));
// Error example
let result = VM::eval("raise 'flowers'");
assert!(result.is_err());
}
Err
will return an AnyObject
of the exception class raised.
#[macro_use]
extern crate rutie;
use rutie::{Class, Fixnum, Object, Exception, RString, VM};
fn main() {
let result = VM::eval("raise IndexError, 'flowers'");
match result {
Err(ao) => {
let err = ao.message();
assert_eq!(err, "flowers");
},
_ => { unreachable!() }
}
}
Be aware when checking for equality amongst types like strings, that even with the same content in Ruby, they will evaluate to different values in C/Rust.
Examples found in repository?
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
fn main() {
VM::init();
let args: Vec<String> = env::args().collect();
if args.len() > 1 {
match VM::eval(&args[1]) {
Ok(_) => (),
Err(e) => {
println!("{}", e);
process::exit(1);
}
}
} else {
eprintln!(r#"Usage: eval "puts 'Put ruby code to be evaluated in a string after eval.' ""#);
process::exit(1);
}
}
sourcepub unsafe fn eval_str(string: &str) -> AnyObject
pub unsafe fn eval_str(string: &str) -> AnyObject
Evals string and returns an AnyObject
Examples
#[macro_use]
extern crate rutie;
use rutie::{Class, Fixnum, Object, VM};
fn main() {
let result = unsafe { VM::eval_str("2+2").try_convert_to::<Fixnum>() };
assert_eq!(result, Ok(Fixnum::new(4)));
}
Be aware when checking for equality amongst types like strings, that even with the same content in Ruby, they will evaluate to different values in C/Rust.
Marked unsafe because “evaluation can raise an exception.”
sourcepub fn block_proc() -> Proc
pub fn block_proc() -> Proc
Converts a block given to current method to a Proc
It works similarly to def method(&block)
which converts block to Proc
Examples
#[macro_use]
extern crate rutie;
use rutie::{Class, Object, Proc, RString, VM};
class!(Greeter);
methods!(
Greeter,
rtself,
fn greet_rust_with() -> RString {
let greeting_template = VM::block_proc();
let name = RString::new_utf8("Rust").to_any_object();
greeting_template.call(&[name]).try_convert_to::<RString>().unwrap()
}
);
fn main() {
Class::new("Greeter", None).define(|klass| {
klass.def_self("greet_rust_with", greet_rust_with);
});
}
Ruby:
class Greeter
def self.greet_rust_with(&greeting_template)
greeting_template.call('Rust')
end
end
Greeter.greet_rust_with do |name|
"Hello, #{name}!"
end
# => "Hello, Rust!"
sourcepub fn is_block_given() -> bool
pub fn is_block_given() -> bool
Checks if a block is given to current method.
Examples
#[macro_use] extern crate rutie;
use rutie::{Class, Fixnum, Object, VM};
class!(Calculator);
methods!(
Calculator,
rtself,
fn calculate(a: Fixnum, b: Fixnum) -> Fixnum {
let a = a.unwrap();
let b = b.unwrap();
if VM::is_block_given() {
let arguments = [a.to_any_object(), b.to_any_object()];
let result = VM::block_proc().call(&arguments);
result.try_convert_to::<Fixnum>().unwrap()
} else {
Fixnum::new(a.to_i64() + b.to_i64())
}
}
);
fn main() {
Class::new("Calculator", None).define(|klass| {
klass.def("calculate", calculate);
});
}
Ruby:
class Calculator
def calculate(a, b, &block)
if block_given?
block.call(a, b)
else
a + b
end
end
end
sourcepub fn yield_object(object: impl Object) -> AnyObject
pub fn yield_object(object: impl Object) -> AnyObject
Yield object to block
Examples
#[macro_use] extern crate rutie;
use rutie::{Class, Fixnum, Object, VM};
class!(Calculator);
methods!(
Calculator,
rtself,
fn calculate(a: Fixnum) -> Fixnum {
let a = a.map_err(|e| VM::raise_ex(e) ).unwrap();
if VM::is_block_given() {
let argument = a.to_any_object();
let result = VM::yield_object(a);
result.try_convert_to::<Fixnum>().unwrap()
} else {
VM::raise(Class::from_existing("LocalJumpError"), "no block given (yield)");
unreachable!();
}
}
);
fn main() {
Class::new("Calculator", None).define(|klass| {
klass.def("calculate", calculate);
});
let result = VM::eval(" Calculator.new().calculate(4) { |n| n * n } ").unwrap();
let num = result.try_convert_to::<Fixnum>().unwrap().to_i64();
assert_eq!(num, 16);
}
Ruby:
class Calculator
def calculate(a)
if block_given?
yield a
else
raise LocalJumpError, "no block given (yield)"
end
end
end
result = Calculator.new.calculate(4) { |n| n * n }
result == 16
sourcepub fn yield_splat(objects: Array) -> AnyObject
pub fn yield_splat(objects: Array) -> AnyObject
Yield splat from array of Ruby objects to block
Examples
#[macro_use] extern crate rutie;
use rutie::{Array, Class, Fixnum, Object, VM};
class!(Calculator);
methods!(
Calculator,
rtself,
fn calculate(a: Array) -> Fixnum {
let a = a.map_err(|e| VM::raise_ex(e) ).unwrap();
if VM::is_block_given() {
let argument = a.to_any_object();
let result = VM::yield_splat(a);
result.try_convert_to::<Fixnum>().unwrap()
} else {
VM::raise(Class::from_existing("LocalJumpError"), "no block given (yield)");
unreachable!();
}
}
);
fn main() {
Class::new("Calculator", None).define(|klass| {
klass.def("calculate", calculate);
});
let result = VM::eval(" Calculator.new().calculate([4,6,8]) { |a,b,c| a*b-c } ").unwrap();
let num = result.try_convert_to::<Fixnum>().unwrap().to_i64();
assert_eq!(num, 16);
}
Ruby:
class Calculator
def calculate(a)
if block_given?
yield a
else
raise LocalJumpError, "no block given (yield)"
end
end
end
result = Calculator.new.calculate([4,6,8]) { |a,b,c| a*b-c }
result == 16
sourcepub fn protect<F>(func: F) -> Result<AnyObject, i32>
pub fn protect<F>(func: F) -> Result<AnyObject, i32>
Run a closure
and protect from panic during raised exceptions
by returning Err<i32>
.
Examples
fn protect_send(&self, method: &str, arguments: &[AnyObject]) -> Result<AnyObject, AnyException> {
let closure = || self.send(&method, arguments.as_ref());
let result = VM::protect(closure);
result.map_err(|_| {
let output = VM::error_info().unwrap();
// error cleanup
VM::clear_error_info();
output
})
}
sourcepub fn error_info() -> Result<AnyException, NilClass>
pub fn error_info() -> Result<AnyException, NilClass>
Get current VM error info.
Examples
fn protect_send(&self, method: &str, arguments: &[AnyObject]) -> Result<AnyObject, AnyException> {
let closure = || self.send(&method, arguments.as_ref()).into();
let result = VM::protect(closure);
result.map_err(|_| {
let output = VM::error_info().unwrap();
// error cleanup
VM::clear_error_info();
output
})
}
sourcepub fn error_pop() -> Result<AnyException, NilClass>
pub fn error_pop() -> Result<AnyException, NilClass>
Get current VM error info and reset it. If no error exists
then Err(NilClass::new())
is returned.
use rutie::{VM, Exception, AnyException, Object};
let closure = || unsafe { VM::eval_str("raise 'hello world!'").into() };
let result = VM::protect(closure);
let exception = VM::error_pop().expect("nil should not have occurred here!");
assert_eq!("hello world!", exception.message());
sourcepub fn clear_error_info()
pub fn clear_error_info()
Clear current VM error info.
Examples
fn protect_send(&self, method: &str, arguments: &[AnyObject]) -> Result<AnyObject, AnyException> {
let closure = || self.send(&method, arguments.as_ref()).into();
let result = VM::protect(closure);
result.map_err(|_| {
let output = VM::error_info().unwrap();
// error cleanup
VM::clear_error_info();
output
})
}
sourcepub unsafe fn exit_bang(arguments: &[AnyObject])
pub unsafe fn exit_bang(arguments: &[AnyObject])
Exits the process immediately. No exit handlers are
run. status
is returned to the underlying system as the
exit status.
call-seq:
Process.exit!(status=false)
Note: Because the VM is exiting — having a return object is not a viable option and therefore you must account for any exceptions that may arise yourself.
Examples
extern crate rutie;
use rutie::{VM,Boolean};
unsafe { VM::exit_bang(&[Boolean::new(true).into()]) }
Process.exit!(true)
Since invalid arguments can raise an exception this is marked as unsafe. Simply use VM::protect
and VM::error_pop
to handle potential exceptions.
extern crate rutie;
use rutie::{VM, Symbol, NilClass, Object, AnyException, Exception};
VM::protect(|| {
unsafe { VM::exit_bang(&[Symbol::new("asdf").into()]) };
NilClass::new().into()
});
let error = VM::error_pop();
assert_eq!(error.unwrap().inspect(), "#<TypeError: no implicit conversion of Symbol into Integer>");
sourcepub unsafe fn abort(arguments: &[AnyObject])
pub unsafe fn abort(arguments: &[AnyObject])
Terminate execution immediately, effectively by calling
Kernel.exit(false)
. If msg is given, it is written
to STDERR prior to terminating.
call-seq:
abort
Kernel::abort([msg])
Process.abort([msg])
Note: Because the VM is aborting — having a return object is not a viable option and therefore you must account for any exceptions that may arise yourself.
Examples
extern crate rutie;
use rutie::{VM, NilClass, AnyException, Exception, RString};
VM::protect(|| {
unsafe { VM::abort(&[RString::new_utf8("Goodbye cruel world!").into()]) }
NilClass::new().into()
});
let error = VM::error_pop();
assert_eq!(error.unwrap().inspect(), "#<SystemExit: Goodbye cruel world!>");
abort "Goodbye cruel world!"
Since invalid arguments can raise an exception this is marked as unsafe. Simply use VM::protect
and VM::error_pop
to handle potential exceptions.
extern crate rutie;
use rutie::{VM, Symbol, NilClass, Object, AnyException, Exception};
VM::protect(|| {
unsafe { VM::abort(&[Symbol::new("asdf").into()]) };
NilClass::new().into()
});
let error = VM::error_pop();
assert_eq!(error.unwrap().inspect(), "#<TypeError: no implicit conversion of Symbol into String>");
sourcepub fn trap(arguments: &[AnyObject]) -> Result<AnyObject, AnyException>
pub fn trap(arguments: &[AnyObject]) -> Result<AnyObject, AnyException>
Specifies the handling of signals. The first parameter is a signal name (a string such as “SIGALRM”, “SIGUSR1”, and so on) or a signal number. The characters “SIG” may be omitted from the signal name. The command or block specifies code to be run when the signal is raised. If the command is the string “IGNORE” or “SIG_IGN”, the signal will be ignored. If the command is “DEFAULT” or “SIG_DFL”, the Ruby’s default handler will be invoked. If the command is “EXIT”, the script will be terminated by the signal. If the command is “SYSTEM_DEFAULT”, the operating system’s default handler will be invoked. Otherwise, the given command or block will be run. The special signal name “EXIT” or signal number zero will be invoked just prior to program termination. trap returns the previous handler for the given signal.
Signal.trap(0, proc { puts "Terminating: #{$$}" })
Signal.trap("CLD") { puts "Child died" }
fork && Process.wait
produces:
Terminating: 27461
Child died
Terminating: 27460
sourcepub fn at_exit<F>(func: F)
pub fn at_exit<F>(func: F)
at_exit
is run AFTER the VM is shut down
Examples
use rutie::VM;
let closure = |_vm| {
println!("at_exit worked!");
};
VM::at_exit(closure);
sourcepub unsafe fn call_super(arguments: &[AnyObject]) -> AnyObject
pub unsafe fn call_super(arguments: &[AnyObject]) -> AnyObject
Call super
Examples
#[macro_use] extern crate rutie;
use rutie::{Class, Fixnum, Object, VM, Exception};
class!(Adder);
methods!(
Adder,
rtself,
fn adder_add(a: Fixnum, b: Fixnum) -> Fixnum {
if let Err(ref error) = a {
VM::raise(error.class(), &error.message());
}
if let Err(ref error) = b {
VM::raise(error.class(), &error.message());
}
// We can safely unwrap here
let a = a.unwrap().to_i64();
// We can safely unwrap here
let b = b.unwrap().to_i64();
Fixnum::new(a + b)
}
);
class!(DoAdder);
methods!(
DoAdder,
rtself,
fn do_adder_add(a: Fixnum, b: Fixnum) -> Fixnum {
if let Err(ref error) = a {
VM::raise(error.class(), &error.message());
}
if let Err(ref error) = b {
VM::raise(error.class(), &error.message());
}
unsafe {
VM::call_super(&[
a.unwrap().into(),
b.unwrap().into()
]).to::<Fixnum>()
}
}
);
fn main() {
Class::new("Adder", None).define(|klass| {
klass.def("add", adder_add);
});
Class::new("DoAdder", Some(&Class::from_existing("Adder"))).define(|klass| {
klass.def("add", do_adder_add);
});
let result = VM::eval(" DoAdder.new().add(4, 4) ").unwrap();
let num = result.try_convert_to::<Fixnum>().unwrap().to_i64();
assert_eq!(num, 8);
}
Ruby:
class Adder
def add(a, b)
a + b
end
end
class DoAdder < Adder
def add(a, b)
super(a, b)
end
end
result = DoAdder.new.add(4, 4)
result == 8