Trait ruru::Object [] [src]

pub trait Object: From<Value> {
    fn value(&self) -> Value;

    fn class(&self) -> Class { ... }
    fn singleton_class(&self) -> Class { ... }
    fn get_data<'a, T>(&'a self, wrapper: &'a DataTypeWrapper<T>) -> &mut T { ... }
    fn define<F: Fn(&mut Self)>(&mut self, f: F) -> &Self { ... }
    fn define_method<I: Object, O: Object>(
        &mut self,
        name: &str,
        callback: Callback<I, O>
    ) { ... } fn define_singleton_method<I: Object, O: Object>(
        &mut self,
        name: &str,
        callback: Callback<I, O>
    ) { ... } fn def<I: Object, O: Object>(
        &mut self,
        name: &str,
        callback: Callback<I, O>
    ) { ... } fn def_self<I: Object, O: Object>(
        &mut self,
        name: &str,
        callback: Callback<I, O>
    ) { ... } fn send(&self, method: &str, arguments: Vec<AnyObject>) -> AnyObject { ... } fn respond_to(&self, method: &str) -> bool { ... } fn is_nil(&self) -> bool { ... } fn to_any_object(&self) -> AnyObject { ... } fn instance_variable_get(&self, variable: &str) -> AnyObject { ... } fn instance_variable_set<T: Object>(
        &mut self,
        variable: &str,
        value: T
    ) -> AnyObject { ... } fn is_frozen(&self) -> bool { ... } fn freeze(&mut self) -> Self { ... } unsafe fn to<T: Object>(&self) -> T { ... } fn try_convert_to<T: VerifiedObject>(&self) -> Result<T> { ... } fn ty(&self) -> ValueType { ... } }

Object

Trait consists methods of Ruby Object class. Every struct like Array, Hash etc implements this trait.

class! macro automatically implements this trait for custom classes.

Required Methods

Returns internal value of current object.

Examples

use ruru::types::Value;
use ruru::Object;

struct Array {
    value: Value
}

impl From<Value> for Array {
    fn from(value: Value) -> Self {
        Array {
            value: value
        }
    }
}

impl Object for Array {
    fn value(&self) -> Value {
        self.value
    }
}

Provided Methods

Returns a class of current object.

Examples

use ruru::{Array, Object, VM};

assert_eq!(Array::new().class(), Array::new().class());

Returns a singleton class of current object.

Examples

Getting singleton class

use ruru::{Array, Object, VM};

let array = Array::new();
let another_array = Array::new();

assert!(array.singleton_class() != another_array.singleton_class());

Ruby:

array = []
another_array = []

array.singleton_class != another_array.singleton_class

Modifying singleton class

use ruru::{Array, Object, VM};

let array = Array::new();
let another_array = Array::new();

array.singleton_class().define(|itself| {
    itself.attr_reader("modified");
});

assert!(array.respond_to("modified"));
assert!(!another_array.respond_to("modified"));

Ruby:

array = []

class << array
  attr_reader :modified
end

array.respond_to?(:modified)

Gets a Rust structure that is wrapped into a Ruby object.

See the documentation for wrappable_struct! macro for more information.

Examples

Wrap Server structs to RubyServer objects

#[macro_use] extern crate ruru;
#[macro_use] extern crate lazy_static;

use ruru::{AnyObject, Class, Fixnum, Object, RString, VM};

// The structure which we want to wrap
pub struct Server {
    host: String,
    port: u16,
}

impl Server {
    fn new(host: String, port: u16) -> Self {
        Server {
            host: host,
            port: port,
        }
    }

    fn host(&self) -> &str {
        &self.host
    }

    fn port(&self) -> u16 {
        self.port
    }
}

wrappable_struct!(Server, ServerWrapper, SERVER_WRAPPER);

class!(RubyServer);

methods!(
    RubyServer,
    itself,

    fn ruby_server_new(host: RString, port: Fixnum) -> AnyObject {
        let server = Server::new(host.unwrap().to_string(),
                                 port.unwrap().to_i64() as u16);

        Class::from_existing("RubyServer").wrap_data(server, &*SERVER_WRAPPER)
    }

    fn ruby_server_host() -> RString {
        let host = itself.get_data(&*SERVER_WRAPPER).host();

        RString::new(host)
    }

    fn ruby_server_port() -> Fixnum {
        let port = itself.get_data(&*SERVER_WRAPPER).port();

        Fixnum::new(port as i64)
    }
);

fn main() {
    let data_class = Class::from_existing("Data");

    Class::new("RubyServer", Some(&data_class)).define(|itself| {
        itself.def_self("new", ruby_server_new);

        itself.def("host", ruby_server_host);
        itself.def("port", ruby_server_port);
    });
}

To use the RubyServer class in Ruby:

server = RubyServer.new("127.0.0.1", 3000)

server.host == "127.0.0.1"
server.port == 3000

Wraps calls to the object.

Mostly used to have Ruby-like class definition DSL.

Examples

Defining class

#[macro_use] extern crate ruru;

use ruru::{Class, Fixnum, Object, RString};

class!(Hello);
class!(Nested);

methods!(
    Hello,
    itself,

    fn greeting() -> RString {
        RString::new("Greeting from class")
    }

    fn many_greetings() -> RString {
        RString::new("Many greetings from instance")
    }
);

methods!(
    Nested,
    itself,

    fn nested_greeting() -> RString {
        RString::new("Greeting from nested class")
    }
);

fn main() {
    Class::new("Hello", None).define(|itself| {
        itself.attr_reader("reader");

        itself.def_self("greeting", greeting);
        itself.def("many_greetings", many_greetings);

        itself.define_nested_class("Nested", None).define(|itself| {
            itself.def_self("nested_greeting", nested_greeting);
        });
    });
}

Ruby:

class Hello
  attr_reader :reader

  def self.greeting
    'Greeting from class'
  end

  def many_greetings
    'Many greetings from instance'
  end

  class Nested
    def self.nested_greeting
      'Greeting from nested class'
    end
  end
end

Defining singleton method for an object

#[macro_use] extern crate ruru;

use ruru::{AnyObject, Class, Fixnum, Object, RString, VM};

methods!(
    RString,
    itself,

    fn greeting() -> RString {
        RString::new("Greeting!")
    }
);

fn main() {
    let mut string = RString::new("Some string");

    // The same can be done by modifying `string.singleton_class()`
    // or using `string.define_singleton_method("greeting", greeting)`
    string.define(|itself| {
        itself.define_singleton_method("greeting", greeting);
    });

    assert!(string.respond_to("greeting"));
}

Ruby:

string = "Some string"

class << string
  def greeting
    'Greeting!'
  end
end

string.respond_to?("greeting")

Defines an instance method for the given class or object.

Use methods! macro to define a callback.

You can also use def() alias for this function combined with Class::define() a for nicer DSL.

Panics

Ruby can raise an exception if you try to define instance method directly on an instance of some class (like Fixnum, String, Array etc).

Use this method only on classes (or singleton classes of objects).

Examples

The famous String#blank? method

#[macro_use] extern crate ruru;

use ruru::{Boolean, Class, Object, RString, VM};

methods!(
   RString,
   itself,

   fn is_blank() -> Boolean {
       Boolean::new(itself.to_string().chars().all(|c| c.is_whitespace()))
   }
);

fn main() {
    Class::from_existing("String").define(|itself| {
        itself.def("blank?", is_blank);
    });
}

Ruby:

class String
  def blank?
    # simplified
    self.chars.all? { |c| c == ' ' }
  end
end

Receiving arguments

Raise Fixnum to the power of exp.

#[macro_use] extern crate ruru;

use std::error::Error;

use ruru::{Class, Fixnum, Object, VM};

methods!(
    Fixnum,
    itself,

    fn pow(exp: Fixnum) -> Fixnum {
        // `exp` is not a valid `Fixnum`, raise an exception
        if let Err(ref error) = exp {
            VM::raise(error.to_exception(), error.description());
        }

        // We can safely unwrap here, because an exception was raised if `exp` is `Err`
        let exp = exp.unwrap().to_i64() as u32;

        Fixnum::new(itself.to_i64().pow(exp))
    }

    fn pow_with_default_argument(exp: Fixnum) -> Fixnum {
        let default_exp = 0;
        let exp = exp.map(|exp| exp.to_i64()).unwrap_or(default_exp);

        let result = itself.to_i64().pow(exp as u32);

        Fixnum::new(result)
    }
);

fn main() {
    Class::from_existing("Fixnum").define(|itself| {
        itself.def("pow", pow);
        itself.def("pow_with_default_argument", pow_with_default_argument);
    });
}

Ruby:

class Fixnum
  def pow(exp)
    raise ArgumentError unless exp.is_a?(Fixnum)

    self ** exp
  end

  def pow_with_default_argument(exp)
    default_exp = 0
    exp = default_exp unless exp.is_a?(Fixnum)

    self ** exp
  end
end

Defines a class method for given class or singleton method for object.

Use methods! macro to define a callback.

You can also use def_self() alias for this function combined with Class::define() a for nicer DSL.

Examples

Defining a class method

#[macro_use] extern crate ruru;

use std::error::Error;

use ruru::{Class, Object, RString, Symbol, VM};

methods!(
    Symbol,
    itself,

    fn from_string(string: RString) -> Symbol {
        // `string` is not a valid `String`, raise an exception
        if let Err(ref error) = string {
            VM::raise(error.to_exception(), error.description());
        }

        Symbol::new(&string.unwrap().to_string())
    }
);

fn main() {
    Class::from_existing("Symbol").define(|itself| {
        itself.def_self("from_string", from_string);
    });
}

Ruby:

class Symbol
  def self.from_string(string)
    raise ArgumentError unless string.is_a?(String)

    # simplified
    string.to_sym
  end
end

Defining a singleton method for an object

#[macro_use] extern crate ruru;

use ruru::{AnyObject, Class, Fixnum, Object, RString, VM};

methods!(
    RString,
    itself,

    fn greeting() -> RString {
        RString::new("Greeting!")
    }
);

fn main() {
    let mut string = RString::new("Some string");

    // The same can be done by modifying `string.singleton_class()`
    // or using `string.define_singleton_method("greeting", greeting)`
    string.define(|itself| {
        itself.define_singleton_method("greeting", greeting);
    });

    assert!(string.respond_to("greeting"));
}

Ruby:


string = "Some string"

class << string
  def greeting
    'Greeting!'
  end
end

string.respond_to?("greeting")

An alias for define_method (similar to Ruby syntax def some_method).

An alias for define_singleton_method (similar to Ruby def self.some_method).

Calls a given method on an object similarly to Ruby Object#send method

Examples

use ruru::{Array, Fixnum, Object, RString, VM};

let array = Array::new().push(Fixnum::new(1));
let array_to_str =
    array
        .send("to_s", vec![])
        .try_convert_to::<RString>()
        .unwrap()
        .to_string();

assert_eq!(array_to_str, "[1]".to_string());

Checks whether the object responds to given method

Examples

use ruru::{Array, Object, VM};

let array = Array::new();

assert!(array.respond_to("push"));
assert!(!array.respond_to("something_else"));

Checks whether the object is nil

Examples

use ruru::{Hash, NilClass, Object, VM};

assert!(NilClass::new().is_nil());
assert!(!Hash::new().is_nil());

Ruby:

nil.nil? == true
{}.nil? == false

Converts struct to AnyObject

See docs for AnyObject class for more details.

Examples

use ruru::{Array, Fixnum, Object, VM};

let array = Array::new().push(Fixnum::new(1));
let args = vec![Fixnum::new(1).to_any_object()];
let index =
    array
        .send("find_index", args)
        .try_convert_to::<Fixnum>();

assert_eq!(index, Ok(Fixnum::new(0)));

Gets an instance variable of object

Examples

#[macro_use]
extern crate ruru;

use ruru::{AnyObject, Class, Fixnum, Object, VM};

class!(Counter);

methods!(
    Counter,
    itself,

    fn counter_initialize() -> AnyObject {
        itself.instance_variable_set("@state", Fixnum::new(0))
    }

    fn counter_increment() -> AnyObject {
        // Using unsafe conversion, because we are sure that `@state` is always a `Fixnum`
        // and we don't provide an interface to set the value externally
        let state = unsafe {
            itself.instance_variable_get("@state").to::<Fixnum>().to_i64()
        };

        itself.instance_variable_set("@state", Fixnum::new(state + 1))
    }

    fn counter_state() -> Fixnum {
        unsafe { itself.instance_variable_get("@state").to::<Fixnum>() }
    }
);

fn main() {
    let counter = Class::new("Counter", None).define(|itself| {
        itself.def("initialize", counter_initialize);
        itself.def("increment!", counter_increment);
        itself.def("state", counter_state);
    }).new_instance(vec![]);

    counter.send("increment!", vec![]);

    let new_state = counter.send("state", vec![]).try_convert_to::<Fixnum>();

    assert_eq!(new_state, Ok(Fixnum::new(1)));
}

Ruby:

class Counter
  def initialize
    @state = 0
  end

  def increment!
    @state += 1
  end

  def state
    @state
  end
end

counter = Counter.new
counter.increment!

new_state = counter.state

new_state == 1

Sets an instance variable for object

Examples

#[macro_use]
extern crate ruru;

use ruru::{AnyObject, Class, Fixnum, Object, VM};

class!(Counter);

methods!(
    Counter,
    itself,

    fn counter_initialize() -> AnyObject {
        itself.instance_variable_set("@state", Fixnum::new(0))
    }

    fn counter_increment() -> AnyObject {
        // Using unsafe conversion, because we are sure that `@state` is always a `Fixnum`
        // and we don't provide an interface to set the value externally
        let state = unsafe {
            itself.instance_variable_get("@state").to::<Fixnum>().to_i64()
        };

        itself.instance_variable_set("@state", Fixnum::new(state + 1))
    }

    fn counter_state() -> Fixnum {
        unsafe { itself.instance_variable_get("@state").to::<Fixnum>() }
    }
);

fn main() {
    let counter = Class::new("Counter", None).define(|itself| {
        itself.def("initialize", counter_initialize);
        itself.def("increment!", counter_increment);
        itself.def("state", counter_state);
    }).new_instance(vec![]);

    counter.send("increment!", vec![]);

    let new_state = counter.send("state", vec![]).try_convert_to::<Fixnum>();

    assert_eq!(new_state, Ok(Fixnum::new(1)));
}

Ruby:

class Counter
  def initialize
    @state = 0
  end

  def increment!
    @state += 1
  end

  def state
    @state
  end
end

counter = Counter.new
counter.increment!

new_state = counter.state

new_state == 1

Returns the freeze status of the object.

Examples

use ruru::{Object, RString, VM};

let frozen_string = RString::new("String").freeze();

assert!(frozen_string.is_frozen());

Ruby:

frozen_string = 'String'.freeze

frozen_string.frozen? == true

Prevents further modifications to the object.

Examples

use ruru::{Object, RString, VM};

let frozen_string = RString::new("String").freeze();

assert!(frozen_string.is_frozen());

Ruby:

frozen_string = 'String'.freeze

frozen_string.frozen? == true

Unsafely casts current object to the specified Ruby type

This operation in unsafe, because it does not perform any validations on the object, but it is faster than try_convert_to().

Use it when:

  • you own the Ruby code which passes the object to Rust;
  • you are sure that the object always has correct type;
  • Ruby code has a good test coverage.

This function is used by unsafe_methods! macro for argument casting.

Examples

use ruru::{AnyObject, Fixnum, Object, VM};

let fixnum_as_any_object = Fixnum::new(1).to_any_object();

let fixnum = unsafe { fixnum_as_any_object.to::<Fixnum>() };

assert_eq!(fixnum.to_i64(), 1);

Safely casts current object to the specified Ruby type

This function is used by methods! macro for argument casting.

See documentation for VerifiedObject trait to enable safe conversions for custom classes.

Examples

Basic conversions

use ruru::result::Error;
use ruru::{Fixnum, Object, RString, VM};

let fixnum_as_any_object = Fixnum::new(1).to_any_object();
let converted_fixnum = fixnum_as_any_object.try_convert_to::<Fixnum>();

assert_eq!(converted_fixnum, Ok(Fixnum::new(1)));

let string = RString::new("string");
let string_as_fixnum = string.try_convert_to::<Fixnum>();
let expected_error = Error::TypeError("Error converting to Fixnum".to_string());

assert_eq!(string_as_fixnum, Err(expected_error));

Method arguments

To launch a server in Rust, you plan to write a simple Server class

class Server
  def start(address)
    # ...
  end
end

The address must be Hash with the following structure:

{
  host: 'localhost',
  port: 8080,
}

You want to extract port from it. Default port is 8080 in case when:

  • address is not a Hash
  • address[:port] is not present
  • address[:port] is not a Fixnum
#[macro_use]
extern crate ruru;

use ruru::{Class, Fixnum, Hash, NilClass, Object, Symbol, VM};

class!(Server);

methods!(
    Server,
    itself,

    fn start(address: Hash) -> NilClass {
        let default_port = 8080;

        let port = address
            .map(|hash| hash.at(Symbol::new("port")))
            .and_then(|port| port.try_convert_to::<Fixnum>())
            .map(|port| port.to_i64())
            .unwrap_or(default_port);

        // Start server...

        NilClass::new()
    }
);

fn main() {
    Class::new("Server", None).define(|itself| {
        itself.def("start", start);
    });
}

Ruby:

class Server
  def start(address)
    default_port = 8080

    port =
      if address.is_a?(Hash) && address[:port].is_a?(Fixnum)
        address[:port]
      else
        default_port
      end

    # Start server...
  end
end

Determines the value type of the object

Example

use ruru::{AnyObject, Fixnum, Object, VM};
use ruru::types::ValueType;

let any_object = Fixnum::new(1).to_any_object();

assert_eq!(any_object.ty(), ValueType::Fixnum);

Implementors