Xot - Shared utility classes and functions for C++ and Ruby

Ask DeepWiki License Build Status Gem Version

⚠️ Notice

This repository is a read-only mirror of our monorepo. We do not accept pull requests or direct contributions here.

🔄 Where to Contribute?

All development happens in our xord/all monorepo, which contains all our main libraries. If you'd like to contribute, please submit your changes there.

For more details, check out our Contribution Guidelines.

Thanks for your support! 🙌

🚀 About

Xot is the foundational utility layer used by every other library in the xord/* family — Rucy, Beeps, Rays, Reflex, Processing, RubySketch, and Reight.

It is split into two layers that ship in a single gem:

  • C++ headers and library (include/xot/, src/) — building blocks such as reference counting, the pimpl idiom, non-copyable base, plus thin wrappers for strings, time, exceptions, and debug output. These are linked into the native extensions of the other xord/* gems.
  • Ruby helpers (lib/xot/) — small meta-programming mixins (Hookable, Inspectable, accessor builders, ...), bit-flag utilities, and shared Rake / test scaffolding used by our gems.

Xot exists primarily to keep these patterns consistent across our own projects. It is not designed as a general-purpose dependency, and its API is not promised to be stable for outside use. You are welcome to read and learn from it, but pin a specific version if you depend on it directly.

📋 Requirements

  • Ruby 3.0.0 or later
  • A C++ compiler with C++20 support (Clang on macOS / iOS, GCC or MSVC on Linux / Windows)
  • Rake and test-unit (development only)

📦 Installation

Add this line to your Gemfile:

gem 'xot'

Then install:

$ bundle install

Or install it directly:

$ gem install xot

When installed via gem install, the C++ headers under include/xot/ are placed inside the gem directory so that other xord/* extensions can locate them at build time (via Xot::Extension.inc_dir).

📚 What's Included

C++ headers (include/xot/)

Header Provides
defs.h Type aliases (uint, ushort, ulong, schar, longlong, ...)
noncopyable.h Xot::NonCopyable — base class that disables copy and assignment
ref.h Xot::RefCountable<> and Xot::Ref<T> — intrusive reference counting
pimpl.h Xot::PImpl<T> / Xot::PSharedImpl<T> — pimpl idiom on top of smart ptrs
string.h Xot::String (extends std::string), stringf, split, to_s
time.h Xot::time() (seconds since epoch, double), Xot::sleep(seconds)
exception.h XotError hierarchy and xot_error / argument_error / ... throw helpers
debug.h Xot::dout / doutln — printf-style debug output (no-op in release)
util.h Bit / flag helpers, random, deg2rad, rad2deg, memory-usage hints
windows.h Win32 helpers used by other libraries

Ruby modules (lib/xot/)

Module / class Purpose
Xot::Hookable Attach on_* hook methods, or before / after wrappers around an existing method, on a single object
Xot::Inspectable Compact default inspect (class + object_id only) — safe under circular references
Xot::BitFlag Build symbolic bit-flag sets and convert between symbols and bitmasks
Xot::BitFlagAccessor Class-level accessor generator backed by a BitFlag
Xot::BitUtil Bit manipulation helpers (bit(n), etc.)
Xot::BlockUtil instance_eval-or-block-call dispatching
Xot::ConstSymbolAccessor Define accessors that translate symbols to module constants
Xot::UniversalAccessor Single-method getter / setter (obj.x reads, obj.x value writes)
Xot::Setter Bulk attribute setter mixin
Xot::Invoker Helper for invoking methods / blocks safely
Xot::Util Misc Ruby utilities
Xot::Extension Path / name / version helpers used by every xord/* gem's build script
Xot::Rake Rake DSL (default_tasks, build_native_library, build_ruby_extension, test_ruby_extension, build_ruby_gem, ...)
Xot::Test Test-unit helpers

💡 Usage

C++ — reference counting

#include <xot/ref.h>

class Thing : public Xot::RefCountable<>
{
    protected:
        ~Thing () = default;   // destructor is protected; Ref<> handles deletion
};

Xot::Ref<Thing> a = new Thing;   // refcount = 1
{
    Xot::Ref<Thing> b = a;       // refcount = 2
}                                // refcount = 1
// when `a` goes out of scope, refcount = 0 and the object is deleted

C++ — pimpl

#include <xot/pimpl.h>

// in the header
class Widget
{
    public:
        Widget ();
        int value () const;
    private:
        struct Data;
        Xot::PImpl<Data> self;
};

// in the .cpp
struct Widget::Data { int n = 42; };
Widget::Widget () {}
int Widget::value () const { return self->n; }

Ruby — BitFlag

require 'xot/bit_flag'

flags = Xot::BitFlag.new(read: 0x1, write: 0x2, exec: 0x4)
mask  = flags.symbols2bits(:read, :exec)   # => 5
flags.bits2symbols(mask)                   # => [:read, :exec]

Ruby — Hookable

require 'xot/hookable'

class Greeter
  include Xot::Hookable
  def greet(name) = "hello, #{name}"
end

g = Greeter.new
g.before(:greet) {|name| puts "about to greet #{name}" }
g.greet 'world'
# => about to greet world
# => "hello, world"

# `on` creates a brand-new on_* method instead of wrapping an existing one
g.on(:click) {|x, y| puts "clicked at #{x}, #{y}" }
g.on_click 1, 2

Ruby — UniversalAccessor

require 'xot/universal_accessor'

class Box
  attr_accessor :width
  universal_accessor :width
end

box = Box.new
box.width 10    # writes
box.width       # => 10 (reads)

🛠️ Development

Xot uses a shared Rakefile pattern. From the gem root:

$ rake lib    # build the native C++ library (libxot)
$ rake ext    # build the Ruby C extension
$ rake test   # run the test suite (test/test_*.rb)
$ rake       # default: builds the extension

In the xord/all monorepo you can also scope by module, e.g. rake xot test.

📜 License

Xot is licensed under the MIT License. See the LICENSE file for details.