Skip to content

Commit

Permalink
add example of multi-file embedded project
Browse files Browse the repository at this point in the history
  • Loading branch information
matsadler committed Mar 11, 2023
1 parent 196af6e commit aa3a6e0
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 0 deletions.
25 changes: 25 additions & 0 deletions examples/embed/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use magnus::{class, embed, prelude::*, require, Value};

mod word_source;
mod word_sink;

// run with `echo foo bar baz | cargo run --example embed`
fn main() {
// start Ruby
let _cleanup = unsafe { embed::init() };

// effectivly `require`ing these modules, loading the classes in Ruby
word_source::init().unwrap();
word_sink::init().unwrap();

// require our Ruby code
// this example uses a relative path from the working directory
require("./examples/embed/transform").unwrap();

// get the class defined in Ruby and call a method on it
class::object()
.const_get::<_, Value>("Transform")
.unwrap()
.funcall::<_, _, Value>("run", ())
.unwrap();
}
10 changes: 10 additions & 0 deletions examples/embed/transform.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class Transform
def self.run
source = WordSource.new
sink = WordSink.new

source.each do |word|
sink << word.reverse
end
end
end
39 changes: 39 additions & 0 deletions examples/embed/word_sink.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use std::{
cell::RefCell,
io::{stdout, Stdout, Write},
};

use magnus::{class, define_class, exception, function, method, prelude::*, Error, RString};

#[magnus::wrap(class = "WordSink")]
struct WordSink {
started: RefCell<bool>,
sink: RefCell<Stdout>,
}

impl WordSink {
fn new() -> Self {
Self {
started: RefCell::new(false),
sink: RefCell::new(stdout()),
}
}

fn concat(&self, s: RString) -> Result<usize, Error> {
let mut sink = self.sink.borrow_mut();
if *self.started.borrow() {
let _ = sink.write(b" ");
} else {
*self.started.borrow_mut() = true;
}
let res = unsafe { sink.write(s.as_slice()) };
res.map_err(|e| Error::new(exception::io_error(), e.to_string()))
}
}

pub fn init() -> Result<(), Error> {
let class = define_class("WordSink", class::object())?;
class.define_singleton_method("new", function!(WordSink::new, 0))?;
class.define_method("<<", method!(WordSink::concat, 1))?;
Ok(())
}
41 changes: 41 additions & 0 deletions examples/embed/word_source.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use std::io::{stdin, BufRead, Stdin};

use magnus::{
block::yield_value, class, define_class, function, method, prelude::*, Error, RString, Value,
};

#[magnus::wrap(class = "WordSource")]
struct WordSource {
source: Stdin,
}

impl WordSource {
fn new() -> Self {
Self { source: stdin() }
}

fn each(&self) -> Result<(), Error> {
let mut source = self.source.lock();
let mut buf = Vec::new();
while let Ok(read) = source.read_until(b' ', &mut buf) {
if read == 0 {
break;
}
if buf[read - 1] == b' ' {
buf.pop();
}
if !buf.is_empty() {
let _: Value = yield_value(RString::from_slice(&buf))?;
}
buf.clear()
}
Ok(())
}
}

pub fn init() -> Result<(), Error> {
let class = define_class("WordSource", class::object())?;
class.define_singleton_method("new", function!(WordSource::new, 0))?;
class.define_method("each", method!(WordSource::each, 0))?;
Ok(())
}

0 comments on commit aa3a6e0

Please sign in to comment.