Skip to content

Commit

Permalink
pass &Ruby handle to functions used as Ruby blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
matsadler committed Aug 23, 2023
1 parent d7f95d1 commit 7a02106
Show file tree
Hide file tree
Showing 7 changed files with 27 additions and 28 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
inaccessible to Rust.

### Changed
- Closures/Functions used as Ruby blocks/procs take an additional first
argument of `&Ruby`.

### Deprecated

Expand Down
23 changes: 12 additions & 11 deletions src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl Ruby {
/// use magnus::{prelude::*, rb_assert, Error, Ruby};
///
/// fn example(ruby: &Ruby) -> Result<(), Error> {
/// let proc = ruby.proc_new(|args, _block| {
/// let proc = ruby.proc_new(|_ruby, args, _block| {
/// let acc = i64::try_convert(*args.get(0).unwrap())?;
/// let i = i64::try_convert(*args.get(1).unwrap())?;
/// Ok(acc + i)
Expand All @@ -66,7 +66,7 @@ impl Ruby {
/// }
/// # Ruby::init(example).unwrap()
/// ```
pub fn proc_new<R>(&self, block: fn(&[Value], Option<Proc>) -> R) -> Proc
pub fn proc_new<R>(&self, block: fn(&Ruby, &[Value], Option<Proc>) -> R) -> Proc
where
R: BlockReturn,
{
Expand All @@ -80,7 +80,8 @@ impl Ruby {
where
R: BlockReturn,
{
let func = std::mem::transmute::<VALUE, fn(&[Value], Option<Proc>) -> R>(callback_arg);
let func =
std::mem::transmute::<VALUE, fn(&Ruby, &[Value], Option<Proc>) -> R>(callback_arg);
func.call_handle_error(argc, argv as *const Value, Value::new(blockarg))
.as_rb_value()
}
Expand All @@ -107,7 +108,7 @@ impl Ruby {
/// fn example(ruby: &Ruby) -> Result<(), Error> {
/// let mut count = 0;
///
/// let proc = ruby.proc_from_fn(move |args, _block| {
/// let proc = ruby.proc_from_fn(move |_ruby, args, _block| {
/// let step = i64::try_convert(*args.get(0).unwrap())?;
/// count += step;
/// Ok(count)
Expand All @@ -123,7 +124,7 @@ impl Ruby {
/// ```
pub fn proc_from_fn<F, R>(&self, block: F) -> Proc
where
F: 'static + Send + FnMut(&[Value], Option<Proc>) -> R,
F: 'static + Send + FnMut(&Ruby, &[Value], Option<Proc>) -> R,
R: BlockReturn,
{
unsafe extern "C" fn call<F, R>(
Expand All @@ -134,7 +135,7 @@ impl Ruby {
blockarg: VALUE,
) -> VALUE
where
F: FnMut(&[Value], Option<Proc>) -> R,
F: FnMut(&Ruby, &[Value], Option<Proc>) -> R,
R: BlockReturn,
{
let closure = &mut *(callback_arg as *mut F);
Expand Down Expand Up @@ -211,7 +212,7 @@ impl Proc {
/// use magnus::{block::Proc, prelude::*, rb_assert};
/// # let _cleanup = unsafe { magnus::embed::init() };
///
/// let proc = Proc::new(|args, _block| {
/// let proc = Proc::new(|_ruby, args, _block| {
/// let acc = i64::try_convert(*args.get(0).unwrap())?;
/// let i = i64::try_convert(*args.get(1).unwrap())?;
/// Ok(acc + i)
Expand All @@ -226,7 +227,7 @@ impl Proc {
deprecated(note = "please use `Ruby::proc_new` instead")
)]
#[inline]
pub fn new<R>(block: fn(&[Value], Option<Proc>) -> R) -> Self
pub fn new<R>(block: fn(&Ruby, &[Value], Option<Proc>) -> R) -> Self
where
R: BlockReturn,
{
Expand All @@ -251,7 +252,7 @@ impl Proc {
///
/// let mut count = 0;
///
/// let proc = Proc::from_fn(move |args, _block| {
/// let proc = Proc::from_fn(move |_ruby, args, _block| {
/// let step = i64::try_convert(*args.get(0).unwrap())?;
/// count += step;
/// Ok(count)
Expand All @@ -268,7 +269,7 @@ impl Proc {
#[inline]
pub fn from_fn<F, R>(block: F) -> Self
where
F: 'static + Send + FnMut(&[Value], Option<Proc>) -> R,
F: 'static + Send + FnMut(&Ruby, &[Value], Option<Proc>) -> R,
R: BlockReturn,
{
get_ruby!().proc_from_fn(block)
Expand Down Expand Up @@ -468,7 +469,7 @@ impl TryConvert for Proc {
/// dropped when the returned `Value` is garbage collected.
fn wrap_closure<F, R>(func: F) -> (*mut F, Value)
where
F: FnMut(&[Value], Option<Proc>) -> R,
F: FnMut(&Ruby, &[Value], Option<Proc>) -> R,
R: BlockReturn,
{
struct Closure<F>(F, DataType);
Expand Down
10 changes: 2 additions & 8 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,9 @@ impl Ruby {
/// fn example(ruby: &Ruby) -> Result<(), Error> {
/// let i: i64 = ruby
/// .range_new(1, 100, false)?
/// .block_call("each", (), |args, _block| {
/// .block_call("each", (), |ruby, args, _block| {
/// let i = i64::try_convert(*args.get(0).unwrap())?;
/// if i % 3 == 0 && i % 5 == 0 {
/// // `block_call` takes a function pointer, not a
/// // closure, so can't capture `ruby`. As we know this
/// // will always be called from Ruby it's safe to get
/// // `Ruby` without the checks that we're on a Ruby
/// // thread.
/// let ruby = unsafe { Ruby::get_unchecked() };
/// Err(ruby.iter_break_value(i))
/// } else {
/// Ok(())
Expand Down Expand Up @@ -171,7 +165,7 @@ impl Error {
///
/// let i: i64 = magnus::Range::new(1, 100, false)
/// .unwrap()
/// .block_call("each", (), |args, _block| {
/// .block_call("each", (), |_ruby, args, _block| {
/// let i = i64::try_convert(*args.get(0).unwrap())?;
/// if i % 3 == 0 && i % 5 == 0 {
/// Err(Error::iter_break(i))
Expand Down
7 changes: 4 additions & 3 deletions src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ where
#[doc(hidden)]
pub trait Block<Res>
where
Self: Sized + FnMut(&[Value], Option<Proc>) -> Res,
Self: Sized + FnMut(&Ruby, &[Value], Option<Proc>) -> Res,
Res: BlockReturn,
{
#[inline]
Expand All @@ -377,8 +377,9 @@ where
argv: *const Value,
blockarg: Value,
) -> Result<Value, Error> {
let ruby = unsafe { Ruby::get_unchecked() };
let args = slice::from_raw_parts(argv, argc as usize);
(self)(args, Proc::from_value(blockarg)).into_block_return()
(self)(&ruby, args, Proc::from_value(blockarg)).into_block_return()
}

#[inline]
Expand All @@ -398,7 +399,7 @@ where

impl<Func, Res> Block<Res> for Func
where
Func: FnMut(&[Value], Option<Proc>) -> Res,
Func: FnMut(&Ruby, &[Value], Option<Proc>) -> Res,
Res: BlockReturn,
{
}
Expand Down
9 changes: 5 additions & 4 deletions src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1254,7 +1254,7 @@ pub trait ReprValue: private::ReprValue {
/// # let _cleanup = unsafe { magnus::embed::init() };
///
/// let values = eval::<RArray>(r#"["foo", 1, :bar]"#).unwrap();
/// let block = Proc::new(|args, _block| args.first().unwrap().to_r_string());
/// let block = Proc::new(|_ruby, args, _block| args.first().unwrap().to_r_string());
/// let _: Value = values.funcall_with_block("map!", (), block).unwrap();
/// assert_eq!(values.to_vec::<String>().unwrap(), vec!["foo", "1", "bar"]);
/// ```
Expand Down Expand Up @@ -1310,7 +1310,7 @@ pub trait ReprValue: private::ReprValue {
///
/// let values = eval::<RArray>(r#"["foo", 1, :bar]"#).unwrap();
/// let _: Value = values
/// .block_call("map!", (), |args, _block| {
/// .block_call("map!", (), |_ruby, args, _block| {
/// args.first().unwrap().to_r_string()
/// })
/// .unwrap();
Expand All @@ -1320,7 +1320,7 @@ pub trait ReprValue: private::ReprValue {
self,
method: M,
args: A,
block: fn(&[Value], Option<Proc>) -> R,
block: fn(&Ruby, &[Value], Option<Proc>) -> R,
) -> Result<T, Error>
where
M: IntoId,
Expand All @@ -1338,7 +1338,8 @@ pub trait ReprValue: private::ReprValue {
where
R: BlockReturn,
{
let func = std::mem::transmute::<VALUE, fn(&[Value], Option<Proc>) -> R>(callback_arg);
let func =
std::mem::transmute::<VALUE, fn(&Ruby, &[Value], Option<Proc>) -> R>(callback_arg);
func.call_handle_error(argc, argv as *const Value, Value::new(blockarg))
.as_rb_value()
}
Expand Down
2 changes: 1 addition & 1 deletion tests/block_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ fn it_can_call_method_with_block() {
ruby.into_value(3_i64),
]);
let _: Value = ary
.block_call("map!", (), |args, _| {
.block_call("map!", (), |_, args, _| {
i64::try_convert(args[0]).map(|i| i * 4)
})
.unwrap();
Expand Down
2 changes: 1 addition & 1 deletion tests/proc_new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use magnus::{block::Proc, eval, gc, value::Opaque, Ruby, Value};
fn make_proc(ruby: &Ruby) -> Proc {
let x = String::from("foo");
let y = Opaque::from(ruby.str_new("bar"));
ruby.proc_from_fn(move |_args, _block| Ok((x.clone(), y)))
ruby.proc_from_fn(move |_ruby, _args, _block| Ok((x.clone(), y)))
}

#[test]
Expand Down

0 comments on commit 7a02106

Please sign in to comment.