-
Notifications
You must be signed in to change notification settings - Fork 10
Handling references to Ruby values
Anyolite allows not only for handling Crystal values from within Ruby, but also Ruby values from within Crystal. This can be used, for example, to build containers of Ruby values or to call instance methods on them for scripting purposes.
To avoid memory leaks, Ruby values are generally handled using the Anyolite::RbRef
struct, which makes sure that the Ruby object stays alive as long as it is alive in Crystal.
An easy way to access these references is by using them as function arguments:
module Test
def self.check_for_string(ref : Anyolite::RbRef)
ref.is_a_string?
end
end
In this code, ref
translates to an arbitrary Ruby object and thus the function accepts everything. The is_a_string?
method then checks whether the value is a Ruby String value and returns either true
or false
.
It is also possible to test for arbitrary other classes:
class TestClass
end
module Test
def check_for_test_class(ref : Anyolite::RbRef)
ref.is_a_custom?(TestClass)
end
end
This way, references can be safely checked for their types without throwing any exceptions. If you want to actually cast a value, you can use the following approach (which returns nil
if the given value is not a TestClass
instance:
class TestClass
end
module Test
def self.convert_to_test_class(ref : Anyolite::RbRef)
if ref.is_a_custom?(TestClass)
Anyolite.cast_to_crystal(ref, TestClass)
else
nil
end
end
end
You can also call instance methods of the passed objects:
class TestClass
def greet(name : String)
"Hello world, #{name}!"
end
end
module Test
def self.call_method_for_test_class(ref : Anyolite::RbRef, method_name : String)
Anyolite.call_rb_method_of_object(ref, method_name, ["Some guy"], cast_to: String)
end
end
Then, the following Ruby code will result in the output "Hello world, Some guy!":
tc = TestClass.new
puts Test.call_method_for_test_class(ref: tc, method_name: "greet")
The relevant arguments for Anyolite.call_rb_method_of_object
here are an RbRef
object (alternatively, a Crystal object can be passed as well, in which case a temporary Ruby object will be created instead), the method name (as Symbol or String variable), an Array with the arguments (can be Crystal values or RbRef
values) or nil
for no arguments and the optional cast_to
keyword argument, which specifies the type of the returned value (if nothing was given, it will become a new RbRef
instance).
Furthermore, you can even access instance variables:
class Person
attr_accessor :name
end
module Test
def self.get_name_of_person(ref : Anyolite::RbRef)
if ref.is_a_custom?(Person)
Anyolite.get_iv(ref, :name, cast_to: String)
else
nil
end
end
def self.set_name_of_person(ref : Anyolite::RbRef, new_name : String)
if ref.is_a_custom?(Person)
Anyolite.set_iv(ref, :name, new_name)
end
end
end
Returning RbRef
values is also completely valid and will simply pass their content directly to Ruby. Only if the reference to the object is removed from both Crystal and Ruby, the object will be actually deleted, so passing RbRef
between Crystal and Ruby is completely safe.
Beginner topics:
Advanced topics:
Modifications of Anyolite:
Other useful links: