{"id":13412165,"url":"https://github.com/danielpclark/rutie","last_synced_at":"2025-05-13T21:08:13.594Z","repository":{"id":32607141,"uuid":"137946784","full_name":"danielpclark/rutie","owner":"danielpclark","description":"“The Tie Between Ruby and Rust.”","archived":false,"fork":false,"pushed_at":"2025-02-18T21:32:27.000Z","size":735,"stargazers_count":984,"open_issues_count":39,"forks_count":62,"subscribers_count":19,"default_branch":"master","last_synced_at":"2025-04-28T13:59:48.257Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danielpclark.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-06-19T21:19:04.000Z","updated_at":"2025-04-24T14:57:48.000Z","dependencies_parsed_at":"2024-06-18T15:22:17.091Z","dependency_job_id":"8a7b2f89-5bd7-41dd-a745-7f4038346c47","html_url":"https://github.com/danielpclark/rutie","commit_stats":null,"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielpclark%2Frutie","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielpclark%2Frutie/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielpclark%2Frutie/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielpclark%2Frutie/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danielpclark","download_url":"https://codeload.github.com/danielpclark/rutie/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254028946,"owners_count":22002282,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T20:01:21.700Z","updated_at":"2025-05-13T21:08:08.566Z","avatar_url":"https://github.com/danielpclark.png","language":"Rust","funding_links":[],"categories":["Rust","Development tools","Ruby \u003c3 Rust","开发工具 Development tools","开发工具","FFI Bindings"],"sub_categories":["FFI","FFI FFI","示例 FFI"],"readme":"## Rutie\n\n[![GitHub Actions Status](https://github.com/danielpclark/rutie/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/danielpclark/rutie/actions/workflows/ci.yml)\n[![Ruby 2 Compatible](https://img.shields.io/badge/Supports-Ruby%202-brightgreen)](https://travis-ci.org/danielpclark/rutie)\n[![Maintenance](https://img.shields.io/maintenance/yes/2025.svg)](https://github.com/danielpclark/rutie/commits/master)\n[![GitHub contributors](https://img.shields.io/github/contributors/danielpclark/rutie.svg)](https://github.com/danielpclark/rutie/graphs/contributors)\n[![license](https://img.shields.io/github/license/danielpclark/rutie.svg)](https://github.com/danielpclark/rutie/blob/master/LICENSE)\n[![crates.io version](https://img.shields.io/crates/v/rutie.svg)](https://crates.io/crates/rutie)\n\n\u003e Rutie — /ro\u0026#x035E;oˈˌtī/rOOˈˌtɪ/rüˈˌtaɪ/\n\nIntegrate Ruby with your Rust application.  Or integrate Rust with your Ruby application.\nThis project allows you to do either with relative ease.\n\nYou are highly encouraged to read the source code for this project.  Every method that has been\nmapped from Ruby for public use in `src/class/*` is **very well documented** with example code.\nThis is the best way to take off running with Rutie.  There are also integration examples in the\nexamples directory which are based off of this README.\n\nThis project is a continuation of:\n* [ruru](https://github.com/d-unseductable/ruru/) (licensed MIT)\n* [ruby-sys](https://github.com/steveklabnik/ruby-sys/) (licensed MIT)\n\n## Index\n\n* [Using Ruby in Rust](https://github.com/danielpclark/rutie#using-ruby-in-rust)\n* [Using Rust in Ruby](https://github.com/danielpclark/rutie#using-rust-in-ruby)\n* [Custom Ruby Objects in Rust](https://github.com/danielpclark/rutie#custom-ruby-objects-in-rust)\n* [Variadic Functions / Splat Operator](https://github.com/danielpclark/rutie#variadic-functions--splat-operator)\n* [Migrating from Ruru to Rutie](https://github.com/danielpclark/rutie#migrating-from-ruru-to-rutie)\n* [Safety — The Rutie Philosophy vs The Rust Philosophy on Safety](https://github.com/danielpclark/rutie/blob/master/README.md#safety--the-rutie-philosophy-vs-the-rust-philosophy-on-safety)\n* [Troubleshooting](https://github.com/danielpclark/rutie#troubleshooting)\n  * [It panics for some Rubies on CI server tests](https://github.com/danielpclark/rutie#it-panics-for-some-rubies-on-ci-server-tests)\n  * [Rust signal: 11, SIGSEGV: invalid memory reference](https://github.com/danielpclark/rutie#rust-signal-11-sigsegv-invalid-memory-reference)\n  * [Error while loading shared libraries: libruby.so.#.#: cannot open shared object file: No such file or directory](https://github.com/danielpclark/rutie#error-while-loading-shared-libraries-librubyso-cannot-open-shared-object-file-no-such-file-or-directory)\n  * [Calling methods from other methods within the `methods!` macro doesn't work](https://github.com/danielpclark/rutie#calling-methods-from-other-methods-within-the-methods-macro-doesnt-work)\n  * [Handling exceptions raised from Ruby in Rust code](https://github.com/danielpclark/rutie#handling-exceptions-raised-from-ruby-in-rust-code)\n  * [Segfault during GC when using a Ruby method written in C](https://github.com/danielpclark/rutie/blob/master/README.md#segfault-during-gc-when-using-a-ruby-method-written-in-c)\n* [Operating System Requirements](https://github.com/danielpclark/rutie#operating-system-requirements)\n* [Contributing](https://github.com/danielpclark/rutie#contributing)\n* [Rutie's Future](https://github.com/danielpclark/rutie#ruties-future)\n  * [SemVer](https://github.com/danielpclark/rutie#semver)\n* [Additional Project History](https://github.com/danielpclark/rutie#additional-project-history)\n* [LICENSE](https://github.com/danielpclark/rutie#license)\n\n## Using Ruby in Rust\n\nFirst add the dependency to your `Cargo.toml` file.\n\n```toml\n[dependencies]\nrutie = { version = \"0.8\", features = [\"link-ruby\"] }\n```\n\nThen in your Rust program add `VM::init()` to the beginning of its code execution path\nand begin to use Rutie.\n\n```rust\nuse rutie::{Object, RString, VM};\n\nfn try_it(s: \u0026str) -\u003e String {\n    let a = RString::new_utf8(s);\n\n    // The `send` method returns an AnyObject type.\n    let b = unsafe { a.send(\"reverse\", \u0026[]) };\n\n    // We must try to convert the AnyObject\n    // type back to our usable type.\n    match b.try_convert_to::\u003cRString\u003e() {\n        Ok(ruby_string) =\u003e ruby_string.to_string(),\n        Err(_) =\u003e \"Fail!\".to_string(),\n    }\n}\n\n#[test]\nfn it_works() {\n\n    // Rust projects must start the Ruby VM\n    VM::init();\n\n    assert_eq!(\"selppa\", try_it(\"apples\"));\n}\n\nfn main() {}\n```\n\nRunning `cargo test` should have this test pass.\n\n## Using Rust in Ruby\n\nYou can start a Ruby project with `bundle gem rutie_ruby_example` and then once\nyou change into that directory run `cargo init --lib`.  Remove the TODOs from the gemspec\nfile. Add Rutie to the `Cargo.toml` file and define the lib type.\n\n```toml\n[dependencies]\nrutie = { version = \"xxx\" }\n\n[lib]\nname = \"rutie_ruby_example\"\ncrate-type = [\"cdylib\"]\n```\n\nThen edit your `src/lib.rs` file for your Rutie code.\n\n```rust\nuse rutie::{class, methods, Class, Object, RString, VM};\n\nclass!(RutieExample);\n\nmethods!(\n    RutieExample,\n    _rtself,\n    fn pub_reverse(input: RString) -\u003e RString {\n        let ruby_string = input.map_err(VM::raise_ex).unwrap();\n\n        RString::new_utf8(\u0026ruby_string.to_string().chars().rev().collect::\u003cString\u003e())\n    }\n);\n\n#[allow(non_snake_case)]\n#[no_mangle]\npub extern \"C\" fn Init_rutie_ruby_example() {\n    Class::new(\"RutieExample\", None).define(|klass| {\n        klass.def_self(\"reverse\", pub_reverse);\n    });\n}\n```\n\nAnd that's it for the Rust side.  When using the `methods!` macro or `extern` functions\nmake sure the method name won't clash with any others.  This is why this example is prefixed with `pub_`.\n\nNow you just need to load the library in Ruby.  Add the `rutie` gem to your gemspec or Gemfile.\n\n```ruby\n# gemspec\nspec.add_dependency 'rutie', '~\u003e 0.0.4'\n\n# Gemfile\ngem 'rutie', '~\u003e 0.0.4'\n```\n\nAnd then load the library in your main project file `lib/rutie_ruby_example.rb`.\n\n```ruby\nrequire_relative \"rutie_ruby_example/version\"\nrequire \"rutie\"\n\nmodule RutieRubyExample\n  Rutie.new(:rutie_ruby_example).init \"Init_rutie_ruby_example\", __dir__\nend\n```\n\nThat's all you need to load your Ruby things from Rust.  Now to write the test in\n`test/rutie_ruby_example_test.rb`:\n\n```ruby\nrequire_relative \"test_helper\"\nrequire \"rutie_ruby_example\"\n\nclass TestRutieRubyExample \u003c Minitest::Test\n  def test_it_reverses\n    assert_equal \"selppa\", RutieExample.reverse(\"apples\")\n  end\nend\n```\n\nWrite the following in `test/test_helper.rb`:\n\n```ruby\nrequire 'minitest/autorun'\n```\n\nAdd the following snippet to your already scaffolded `Rakefile`:\n\n```ruby\nrequire \"rake/testtask\"\n\nRake::TestTask.new do |t|\n  t.libs \u003c\u003c 'test'\n  t.pattern = 'test/**/*_test.rb'\n  t.verbose = true\nend\n```\n\nAnd to properly test it you will always need to run `cargo build --release` whenever\nyou make **any** changes to the Rust code.  Run the test with:\n\n```bash\ncargo build --release; rake test\n```\n\nOr better yet change your `Rakefile` to always run the `cargo build --release` before\nevery test suite run.  Feel free to change the test input to prove it fails because\nthe above test works as is.\n\n## Custom Ruby Objects in Rust\n\nTo create a Ruby object in Rust that can be returned directly to Ruby\nit needs just a few simple things.\n\nHere's an example excerpt of code from [FasterPath](https://github.com/danielpclark/faster_path).\n\n```rust\nuse rutie::types::{ Value, ValueType };\nuse rutie::{ RString, AnyObject, Object, Class, VerifiedObject };\n\npub struct Pathname {\n    value: Value\n}\n\nimpl Pathname {\n    pub fn new(path: \u0026str) -\u003e Pathname {\n        let arguments = [RString::new_utf8(path).to_any_object()];\n        let instance = Class::from_existing(\"Pathname\").new_instance(Some(\u0026arguments));\n\n        Pathname { value: instance.value() }\n    }\n\n    pub fn to_any_object(\u0026self) -\u003e AnyObject {\n        AnyObject::from(self.value())\n    }\n}\n\nimpl From\u003cValue\u003e for Pathname {\n    fn from(value: Value) -\u003e Self {\n        Pathname { value }\n    }\n}\n\nimpl Object for Pathname {\n    #[inline]\n    fn value(\u0026self) -\u003e Value {\n        self.value\n    }\n}\n\nimpl VerifiedObject for Pathname {\n    fn is_correct_type\u003cT: Object\u003e(object: \u0026T) -\u003e bool {\n        Class::from_existing(\"Pathname\").case_equals(object)\n    }\n\n    fn error_message() -\u003e \u0026'static str {\n        \"Error converting to Pathname\"\n    }\n}\n```\n\nIf the class does not yet exist in Ruby you'll need to account for creating\nit before generating a new instance of it.  This object is now compatible to\nbe returned into Ruby directly from Rust/Rutie.  _Note that this definition is\nmerely a Rust compatible representation of the Ruby object and doesn't define\nany Ruby methods which can be used from Ruby._\n\n## Variadic Functions / Splat Operator\n\nA preferred way to integrate a dynamic amount of parameters has not yet been implemented in Rutie,\nbut you can still manage to get it done in the following way.\n\n```rust\nuse rutie::{AnyObject, Array};\nuse rutie::types::{Argc, Value};\nuse rutie::util::str_to_cstring;\nuse rutie::rubysys::class;\nuse std::mem;\n\npub extern fn example_method(argc: Argc, argv: *const AnyObject, _rtself: AnyObject) -\u003e AnyObject {\n    let args = Value::from(0);\n\n    unsafe {\n        let p_argv: *const Value = mem::transmute(argv);\n\n        class::rb_scan_args(\n            argc,\n            p_argv,\n            str_to_cstring(\"*\").as_ptr(),\n            \u0026args\n        )\n    };\n\n    let arguments = Array::from(args);\n\n    let output = // YOUR CODE HERE.  Use arguments as you see fit.\n\n    output.to_any_object()\n}\n```\n\nThis style of code is meant to be used outside of the `methods!` macro for now.\nYou may place this method on a class or module as you normally would from a `methods!` macro definition.\n\n```rust\nuse rutie::{Class, Object, VM};\n\nclass!(Example);\n\n// Code from above\nfn main() {\n    VM::init();\n    Class::new(\"Example\", None).define(|klass| {\n        klass.def(\"example_method\", example_method);\n    });\n}\n```\n\nThe Rutie project has in its plans to remove the need for anyone to write unsafe code for\nvariadic support and will likely be updating the `methods!` macro to support this natively.\n\n## Migrating from Ruru to Rutie\n\n#### \u0026lt;0.1\n\nFor using Rutie versions less than 0.1 the change is simple.  Replace all occurrences\nof the string `ruru` with `rutie` in your program.  And if you would like to use\n`ruby-sys` code from Rutie rather than requiring `ruby-sys` you can change all existing\nreferences to `ruby_sys` to `rutie::rubysys`.\n\n#### 0.1\n\nYou will have additional considerations to change like `Error` being removed.  For that; change instances of type `ruru::result::Error` to `rutie::AnyException`.\n\n#### 0.2\n\nMigrated `parse_arguments` from `VM` to `util`.\n\n#### 0.3\n\nInternal changes `util` from `binding` and `rubysys` have been replaced to reduce confusion and reduce duplication.\n\n### 0.10\n\n`crate::util::parse_arguments` has been marked as unsafe, this is considered a breaking change in Rust.\n\n## Safety — The Rutie Philosophy vs The Rust Philosophy on Safety\n\nI'm writing this section to bring to light that, as of this writing, the safety that Rust likes to guarantee for its crates and the Rutie crate aren't currently aligned.  The typical Rust safety for libraries wrapping C code is to have one unsafe crate with a `-sys` extension in its name and then a crate that wraps that to make it safe.\n\nRutie is an official fork of the project Ruru and because of this a great deal of the decisions in design for the project remain unchanged.  Rutie also brought in the `ruby-sys` crate and treats it as an internal private API/module; and yet shares it openly for other developers to have full control to design their own API on top of it.\n\nOne of the glaring things that Rutie has that goes against the Rust Philosophy on Safety is that any of the methods that call Ruby code, can potentially raise an exception, and don't return the type `Option\u003cAnyObject, AnyException\u003e` will panic when an exception is raised from Ruby… which kills the application process running.  The way to avoid panics is simple: either guarantee the Ruby code you're running will never raise an exception, or [Handling exceptions raised from Ruby in Rust code](https://github.com/danielpclark/rutie#handling-exceptions-raised-from-ruby-in-rust-code) with \"protect\" methods that return the type `Option\u003cAnyObject, AnyException\u003e`.  Anyone can implement this safety by reading and understanding how the **protect** methods are written in this library and working with them.\n\nThe important thing to consider as to **“why doesn't every method guarantee the safety as Rust would prescribe to?”** is that exception handling in Ruby is **not a zero cost abstraction**.  So there is a cost in performance when you need to implement it.  One can easily argue that the guarantee of safety is far more important than leaving the risk in the hands of inexperienced developers.  But one could also argue that it is better to leave the choice of performance cost, and the fact that exception capturing is occasionally unnecessary, up to the developer.  Seeing how the legacy of design decisions is largely inherited this project leans towards the latter argument where the choice of being absolutely safe everywhere vs some extra speed in performance is up to the developer.\n\nI'm not opposed to this project being 100% safe, but that will be a major change and a totally different API with many decisions that need to come into play.  Also since this project doesn't strictly adhere to Rust safety principles, as a crate library would be expected to be, this project will not reach the stable 1.0 release as the idea of stability and safety are hand in hand.\n\nI do like safety guarantees and as much as possible new features and language APIs will be built toward this.  You can see what the design of a safe API would look like by examining the [Enumerator features](https://github.com/danielpclark/rutie/blob/master/src/class/enumerator.rs) that have been implemented in this way (largely wrapping method names with calls to `protect_send`).\n\n## Troubleshooting\n\n#### It panics for some Rubies on CI server tests\n\nSometimes the Ruby binary built isn't the best for the system.  Be sure to compile Ruby\nfor that system if this is the issue.   With RVM do `rvm reinstall --disable-binary` with\nyour choice of Ruby version.\n\n#### Rust signal: 11, SIGSEGV: invalid memory reference\n\nThis is an indication that you haven't started a Ruby VM in Rust yet with `VM::init();`.  Do this once\nbefore using Ruby code from Rust.\n\n#### Error while loading shared libraries: libruby.so.#.#: cannot open shared object file: No such file or directory\n\nThis happens when the Rutie build is trying to link with `libruby`,\nbut it's not found on your library search path. Either add it to\n`LD_LIBRARY_PATH`/`DYLD_LIBRARY_PATH` if you're building a standalone\nprogram that calls `VM::init()`, or if you're building a library to\nload into a running Ruby VM then you can disable linking by either\nsetting the environment variable `NO_LINK_RUTIE`, or enabling the\ncargo feature `no-link` for Rutie in your `Cargo.toml` like this:\n\n```toml\n[dependencies]\nrutie = {version=\"xxx\", features=[\"no-link\"]}\n```\n\n#### Calling methods from other methods within the `methods!` macro doesn't work\n\nThe way the macro is designed doesn't use the same parameter signatures you've provided and\ntherefore it is recommended to implement any methods you want to re-use in Rust with\nfunctions outside of the `methods!` macro.  You can simply call that new external\nmethod in the `methods!` macro when defining methods for Ruby to use.\n\n#### Handling exceptions raised from Ruby in Rust code\n\nIf you're using any method that doesn't return a `Result\u003cAnyObject, AnyException\u003e` then\nany exception raised from the Ruby side will interfere with that Ruby thread and cause\nRust to panic and stop.  Ruby internally uses exceptions to effect the entire thread through\nan internal thread global value.  To handle places where Ruby may raise an exception during Rust\ncode execution you should use methods that are designed to handle that.\n\n* `VM::eval`\n* `Object.protect_send`\n* `Object.protect_public_send`\n\nIf you are writing lower level code and want to work more directly with the internal Ruby\nexception you may use `VM::protect` and read the source code for `Object.protect_send` to\nsee how it's done.\n\n#### Segfault during GC when using a Ruby method written in C\n\nOne possible issue that may cause this is when you store an item in Rust in heap memory rather than the stack.\n\nAn example case that caused this issue is the following:\n\n```rust\nClass::from_existing(\"Pathname\").new_instance(\u0026vec![RString::new_utf8(path).to_any_object()])\n```\n\n\u003e Ruby's GC traces objects from the stack. Rust's Vec, on the other hand, stores elements in the heap. So Ruby's GC may not be able to find the string you created and may release it. — @irxground\n\nTo rememdy the issue it required not using Vec but rather Rust's array type to store the argument on the stack rather than the heap.\n\n```rust\nlet arguments = [RString::new_utf8(path).to_any_object()];\nClass::from_existing(\"Pathname\").new_instance(\u0026arguments)\n```\n\n## Operating System Requirements\n\nEverything is tested against 64 bit operating systems with 64 bit Ruby \u0026 Rust builds.  32 bit isn't currently supported.  \n\n### Ruby 2 Notes\n\nRuby 2 is supported up through 0.8 of Rutie.  For usage with Ruby 2 you need to install libssl1.1 and point to it when you install.  For example:\n\n```\nwget https://www.openssl.org/source/openssl-1.1.1l.tar.gz\ntar xf openssl-1.1.1l.tar.gz\ncd openssl-1.1.1l\n./config --prefix=/usr/local/openssl-1.1.1l --openssldir=/usr/local/openssl-1.1.1l\nmake\nsudo make install\ncd ..\nrvm install ruby-2.7.7 --with-openssl-dir=/usr/local/openssl-1.1.1l\nrvm use 2.7.7\n```\n\n#### Linux \u0026 Mac\n\n- Rust 1.26 or later\n- Ruby (64 bit) 2.5 or later\n\nNOTE: Known issues with Ruby 3.0 compatility with the GC. `GC#mark`, `GC#is_marked`, `GC#marked_locations` do not work with Ruby 3.\n\n#### Windows\n- Rust 1.26 or later\n- Ruby 2.5+ built with MingW (64 bit)\n- MS Visual Studio (Build Tools)\n\n#### Dynamic vs Static Builds\n\nRuby needs to be compiled with the `--enable shared` option.  Dynamic linking to the Ruby library provides the best performance and best support.  Static build support is incomplete for now.\n\nIf using RBENV then the following is recommended:\n\n    CONFIGURE_OPTS=--enable-shared rbenv install 2.7.1\n\nYou can check if your Ruby is compiled to be dynamically linked to by running the following and getting a `\"yes\"` response.\n\n    ruby -e \"pp RbConfig::CONFIG['ENABLE_SHARED']\"\n\nIf you still run into `ld: library not found for -lruby-static` issue, try running `cargo clean`. This'll clean any artifacts from previous attempts.\n\nIf you'd like to make a pull request for adding static build support there are currently 3 methods not working with it and linking to the proper name of the ruby static lib file \u0026 path needs to be updated.\n\n## Contributing\n\nContributors are welcome!\n\nThe code is organized in 3 main layers.  The `rubysys` folder is the raw mapping to Ruby C code and\nall the methods from there are unsafe.  The `binding` folder is where we wrap those methods to abstract\naway all the unsafe methods to safe methods.  The `class` folder is where the public API is implemented\nfor using Ruby with Rust code.  These methods in the `class` folder must all be documented and tested within\nthe documentation.  There is a subfolder under `class` for traits called `traits`.\n\nMacros for abstracting away complexity are in `src/dsl.rs`.\n\nRuby's helper gem is in the submodule folder `gem`.\n\n## Rutie's Future\n\nRutie will continue to be improved upon to be more and more compatible with every aspect of Ruby.  It\nwill also gradually change toward Rust safety, semantics, and best practices.\n\nI imagine a future where Rutie is the stepping stone that helps Ruby switch from C to Rust.\n\n#### SemVer\n\nAs this package has taken 1.0 to mean both stable and safe and won't likely make a 1.0, then there can\nbe breaking changes expected in each MINOR version update.  These MINOR version breaking changes will\noccur in the public API of `src/class/*` and `src/helpers/*`.  For private APIs there can be breaking\nchanges in each PATCH version update which includes `src/rubysys/*`, `src/binding/*`, and\n`src/util.rs`.\n\n## Additional Project History\n\nIf you need some more examples of usage or the git blame history please look at the [Ruru](https://github.com/d-unseductable/ruru)\nproject as Rutie has had the README completely rewritten and this first git commit is from Ruru.\nNote that there are some fundamental changes which that README won't account for.\nThis project also had [ruby-sys](https://github.com/steveklabnik/ruby-sys) merged in which may have some additional beneficial git history.\n\n## LICENSE\n\nBoth projects that were merged into this project contained identifiers under the MIT license.\nThis project follows with the same licensing.  **ruby-sys** marked MIT as the license in the\n`Cargo.toml` file whereas **ruru** had that and included a LICENSE file.  This projects LICENSE\nhas credited the original author by preserving the MIT license author line and appending new\nauthor(s) which is permitted by the MIT LICENSE.\n\nMIT LICENSE — see [LICENSE](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielpclark%2Frutie","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanielpclark%2Frutie","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielpclark%2Frutie/lists"}