itamae-goの実装が面白そうだったので、ソースコードを調べていたんですが、
rustでもmrubyを組み込むcrateあったなーと思い、勉強ついでにマネをさせてもらいました。
用意するもの
- rust (1.10.0)
ソースコード
/tmp/madoka/homuraというディレクトリつくって、
そこに3つファイルを作るというあまり意味のないレシピ。
Cargo.toml
[dependencies]
mrusty = "*"
main.rs
#[macro_use]
extern crate mrusty;
use mrusty::*;
// ruby
static RUBY_RECIPE: &'static str = r#"
directory '/tmp/madoka/homura'
3.times do |i|
file "/tmp/madoka/homura/#{i}"
end
"#;
#[allow(unused_must_use)]
#[allow(unused_variables)]
fn main() {
let mruby = Mruby::new();
let itamae = mruby.def_module("Itamae");
let recipe = mruby.def_class_under("Recipe", &itamae);
let context = mruby.def_class_under("Context", &recipe);
mruby.def_method(context.clone(), "directory", mrfn!(|mruby, slf: Value, v: (&str)| {
std::fs::create_dir_all(v)
.map(|_| mruby.bool(true))
.unwrap_or(mruby.bool(false))
}));
mruby.def_method(context, "file", mrfn!(|mruby, slf: Value, v: (&str)| {
std::fs::File::create(v)
.map(|_| mruby.bool(true))
.unwrap_or(mruby.bool(false))
}));
// itamae-goのように実装してみたら、mrustyだとうまくできなかったので、
// method_missingを利用。
mruby.run(r#"
class Wrapper
def initialize
@inst = Itamae::Recipe::Context.new
end
def method_missing(method, *args)
@inst.send(method, *args)
end
end
ITAMAE_CONTEXT = Wrapper.new
"#);
mruby.run(format!("ITAMAE_CONTEXT.instance_exec {{{}}}", RUBY_RECIPE).as_str());
}
まとめ
rustとmrubyでrubyのdslができたような気がします。
mrustyはrustのマクロ機能をいい感じにつかっていたので、とても勉強になりました。