Rebrand all ZAP-facing identifiers off Cap'n Proto: - module Capnp→Zap, ext binding namespace ruby_capn_proto→ruby_zap, header guard RUBY_CAPN_PROTO_H→RUBY_ZAP_H, entry point Init_zap, gem capn_proto→zap, schemas .capnp→.zap, media/logo, gemspec, README, spec, examples. Zero capnp/Cap'n Proto brand prose remains. Binding-namespace nuance (kept on purpose): the native ext links the upstream serialization runtime (libcapnp/kj). Its capnp:: C++ symbols and <capnp/...> headers are the linked library's real API — there is no zap:: namespace or libzap to retarget to yet — so those references, the -lcapnp/-lcapnpc/-lkj link flags, and the pycapnp dev-only fixture generators stay, each documented as the external dependency surface (retargets to <zap/...>/-lzap when the ZAP C++ runtime ships). Build/modernization: - extconf.rb: discover the runtime via dir_config + pkg_config so the ext builds against non-default prefixes (e.g. Homebrew), not just /usr/local. Native ext now compiles + links cleanly (zap.bundle). - ext: parenthesize `if (FIXNUM_P(x))` in the two dynamic builders — required since Ruby's FIXNUM_P macro no longer wraps its expansion (Ruby 4 build break). - Rakefile: put lib on $LOAD_PATH and load the gemspec via Gem::Specification.load; pin rake-compiler ~> 0.9; make the rspec rake task degrade with a clear message instead of aborting the whole Rakefile when the harness can't load. - gemspec: bump dev deps within-major (rspec ~> 2.14, rake ~> 13.0, rake-compiler ~> 0.9); add required_ruby_version. - .gitignore: ignore native-ext build artifacts (*.o, Makefile, mkmf.log, *.bundle.dSYM, *.gem). Verified: all spec/zap_spec.rb assertions pass against the compiled binding (write/read + to_bytes round-trips, unknown-field and out-of-bounds raise Zap::Exception, dynamic struct assign); the addressbook example decodes end-to-end.
3.1 KiB
ZAP — Ruby Edition
Docs: ZAP Ruby SDK · part of the ZAP Protocol
This is the Ruby binding for ZAP — an insanely fast binary data interchange format and capability-based RPC system.
Installing
The native extension links the ZAP C++ runtime. Until the standalone libzap
runtime ships, the binding builds against the upstream-compatible serialization
library (libcapnp); install it first, then install the gem:
gem install zap --pre
The native extension requires a C++ compiler with C++11 features, so use the
same C++ compiler and flags you used to build the runtime (e.g. CXX and
CXXFLAGS). On macOS:
gem install zap --pre
Runtime dependency note. This binding currently links the upstream serialization runtime (
libcapnp/<capnp/...>,-lcapnp -lcapnpc -lkj). Those references are the external dependency surface, not ZAP brand identity; they retarget tolibzaponce the ZAP C++ runtime is published. Schema files use the.zapextension.
Example
require 'zap'
module AddressBook extend Zap::SchemaLoader
load_schema("addressbook.zap")
end
def write_address_book(file)
addresses = AddressBook::AddressBook.new_message
people = addresses.initPeople(2)
alice = people[0]
alice.id = 123
alice.name = 'Alice'
alice.email = '[email protected]'
alice_phones = alice.initPhones(1)
alice_phones[0].number = "555-1212"
alice_phones[0].type = 'mobile'
alice.employment.school = "MIT"
bob = people[1]
bob.id = 456
bob.name = 'Bob'
bob.email = '[email protected]'
bob_phones = bob.initPhones(2)
bob_phones[0].number = "555-4567"
bob_phones[0].type = 'home'
bob_phones[1].number = "555-7654"
bob_phones[1].type = 'work'
bob.employment.unemployed = nil
addresses.write(file)
end
def print_address_book(file)
addresses = AddressBook::AddressBook.read_from(file)
addresses.people.each do |person|
puts "#{person.name} : #{person.email}"
person.phones.each do |phone|
puts "#{phone.type} : #{phone.number}"
end
if person.employment.unemployed?
puts "unemployed"
if person.employment.employer?
puts "employer: #{person.employment.employer}"
if person.employment.school?
puts "student at: #{person.employment.school}"
if person.employment.selfEmployed?
puts "self employed"
end
end
end
if __FILE__ == $0
file = File.open("addressbook.bin", "wb")
write_address_book(file)
file = File.open("addressbook.bin", "rb")
print_address_book(file)
end
Status
What's implemented:
- Schema parsing/loading
- Message reading
- From byte string
- From file descriptor
- Message writing
- To byte string
- To file descriptor
What's to come:
- More reading/writing mechanisms:
- Packing/unpacking
- Extensive test coverage
- Proper support for JRuby
- Support for RPC
