diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5f098cd4a16..866a2495d7f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -594,6 +594,31 @@ jobs: working-directory: tests/nim run: python3 testnim.py + build-ruby: + name: Build Ruby + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v5 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ruby + - name: flatc + run: | + cmake \ + -S . \ + -B build \ + -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DFLATBUFFERS_BUILD_FLATHASH=OFF \ + -DFLATBUFFERS_BUILD_FLATLIB=OFF \ + -DFLATBUFFERS_BUILD_TESTS=OFF \ + -DFLATBUFFERS_INSTALL=OFF \ + -DFLATBUFFERS_STRICT_MODE=ON + ninja -C build + - name: test + run: | + BUILD_DIR=build tests/RubyTest.rb + bazel: name: Bazel runs-on: ubuntu-24.04 diff --git a/CMakeLists.txt b/CMakeLists.txt index 93dac0aaf98..0d90cd4b7cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -183,6 +183,7 @@ set(FlatBuffers_Compiler_SRCS src/bfbs_gen.h src/bfbs_gen_lua.h src/bfbs_gen_nim.h + src/bfbs_gen_ruby.h src/bfbs_namer.h include/codegen/idl_namer.h include/codegen/namer.h @@ -195,6 +196,7 @@ set(FlatBuffers_Compiler_SRCS src/annotated_binary_text_gen.cpp src/bfbs_gen_lua.cpp src/bfbs_gen_nim.cpp + src/bfbs_gen_ruby.cpp src/code_generators.cpp grpc/src/compiler/schema_interface.h grpc/src/compiler/cpp_generator.h diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 1c551e999db..fd41c545ded 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -753,6 +753,7 @@ struct IDLOptions { kNim = 1 << 17, kProto = 1 << 18, kKotlinKmp = 1 << 19, + kRuby = 1 << 20, kMAX }; diff --git a/ruby/flatbuffers.rb b/ruby/flatbuffers.rb new file mode 100644 index 00000000000..c39720b9ff5 --- /dev/null +++ b/ruby/flatbuffers.rb @@ -0,0 +1,20 @@ +# Copyright 2025 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require_relative "flatbuffers/enum" +require_relative "flatbuffers/flags" +require_relative "flatbuffers/struct" +require_relative "flatbuffers/table" +require_relative "flatbuffers/union" +require_relative "flatbuffers/version" diff --git a/ruby/flatbuffers/enum.rb b/ruby/flatbuffers/enum.rb new file mode 100644 index 00000000000..7d32d5a8fd1 --- /dev/null +++ b/ruby/flatbuffers/enum.rb @@ -0,0 +1,49 @@ +# Copyright 2025 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module FlatBuffers + class Enum + class << self + def try_convert(value) + case value + when Symbol, String + @name_to_enum[value.to_s] + when Integer + @value_to_enum[value] + when self + value + else + nil + end + end + + def register(name, value) + object = new(name, value) + (@name_to_enum ||= {})[name] = object + (@value_to_enum ||= {})[value] = object + object + end + end + + attr_reader :name + attr_reader :value + def initialize(name, value) + @name = name + @value = value + end + + alias_method :to_i, :value + alias_method :to_int, :value + end +end diff --git a/ruby/flatbuffers/flags.rb b/ruby/flatbuffers/flags.rb new file mode 100644 index 00000000000..1263ca36ebb --- /dev/null +++ b/ruby/flatbuffers/flags.rb @@ -0,0 +1,69 @@ +# Copyright 2025 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module FlatBuffers + class Flags + class << self + def try_convert(value) + case value + when Array + value.inject(new) do |previous, v| + flag = try_convert(v) + return nil if flag.nil? + previous | flag + end + when Symbol, String + try_convert(@name_to_value[value.to_s]) + when Integer + new(value) + when self + value + else + nil + end + end + + def register(name, value) + (@name_to_value ||= {})[name] = value + new(value) + end + + def names + @name_to_value.keys + end + + def resolve_names(value) + @name_to_value.select do |name, v| + not (value & v).zero? + end + end + end + + attr_reader :value + def initialize(value=0) + @value = value + end + + def names + @names ||= self.class.resolve_names(@value) + end + + alias_method :to_i, :value + alias_method :to_int, :value + + def |(other) + self.class.new(@value | Integer(other)) + end + end +end diff --git a/ruby/flatbuffers/inspectable.rb b/ruby/flatbuffers/inspectable.rb new file mode 100644 index 00000000000..017cdbf0d2a --- /dev/null +++ b/ruby/flatbuffers/inspectable.rb @@ -0,0 +1,41 @@ +# Copyright 2025 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module FlatBuffers + module Inspectable + def inspect + inspected = +"<#{self.class}:" + public_methods(false).each do |name| + next unless method(name).arity.zero? + inspected << " #{name}=#{__send__(name).inspect}" + end + inspected << ">" + inspected + end + + def pretty_print(q) + q.object_group(self) do + targets = public_methods(false).select do |name| + method(name).arity.zero? + end + q.seplist(targets, lambda {q.text(",")}) do |name| + q.breakable + q.text(name.to_s) + q.text("=") + q.pp(__send__(name)) + end + end + end + end +end diff --git a/ruby/flatbuffers/struct.rb b/ruby/flatbuffers/struct.rb new file mode 100644 index 00000000000..b5a348042ee --- /dev/null +++ b/ruby/flatbuffers/struct.rb @@ -0,0 +1,26 @@ +# Copyright 2025 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require_relative "inspectable" +require_relative "view" + +module FlatBuffers + class Struct + include Inspectable + + def initialize(view) + @view = view + end + end +end diff --git a/ruby/flatbuffers/table.rb b/ruby/flatbuffers/table.rb new file mode 100644 index 00000000000..ea3ec9df68c --- /dev/null +++ b/ruby/flatbuffers/table.rb @@ -0,0 +1,34 @@ +# Copyright 2025 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require_relative "inspectable" +require_relative "view" + +module FlatBuffers + class Table + include Inspectable + + def initialize(input) + if input.is_a?(View) + @view = input + else + if input.is_a?(String) + input = IO::Buffer.for(input) + end + offset = input.get_value(:u32, 0) + @view = View.new(input, offset, have_vtable: true) + end + end + end +end diff --git a/ruby/flatbuffers/union.rb b/ruby/flatbuffers/union.rb new file mode 100644 index 00000000000..a0084fa8426 --- /dev/null +++ b/ruby/flatbuffers/union.rb @@ -0,0 +1,62 @@ +# Copyright 2025 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module FlatBuffers + class Union + class << self + def try_convert(value) + case value + when Symbol, String + @name_to_union[value.to_s] + when Integer + @value_to_union[value] + when self + value + else + nil + end + end + + def register(name, value, table_class_name, require_path) + object = new(name, value, table_class_name, require_path) + (@name_to_union ||= {})[name] = object + (@value_to_union ||= {})[value] = object + object + end + end + + attr_reader :name + attr_reader :value + def initialize(name, value, table_class_name, require_path) + @name = name + @value = value + @table_class_name = table_class_name + @require_path = require_path + end + + def table_class + @table_class ||= resolve_table_class + end + + private def resolve_table_class + return nil if @table_class_name.nil? + + require_table_class + Object.const_get(@table_class_name) + end + + alias_method :to_i, :value + alias_method :to_int, :value + end +end diff --git a/ruby/flatbuffers/version.rb b/ruby/flatbuffers/version.rb new file mode 100644 index 00000000000..e63f7bbb340 --- /dev/null +++ b/ruby/flatbuffers/version.rb @@ -0,0 +1,17 @@ +# Copyright 2025 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module FlatBuffers + VERSION = "25.9.23" +end diff --git a/ruby/flatbuffers/view.rb b/ruby/flatbuffers/view.rb new file mode 100644 index 00000000000..30229498afd --- /dev/null +++ b/ruby/flatbuffers/view.rb @@ -0,0 +1,176 @@ +# Copyright 2025 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module FlatBuffers + class View + OFFSET_BYTE_SIZE = 4 + VIRTUAL_OFFSET_BYTE_SIZE = 2 + + def initialize(data, offset, have_vtable: false) + @data = data + @offset = offset + if have_vtable + vtable_offset = unpack_signed_offset(0) + @vtable_start = @offset - vtable_offset + # We assume vtable must have length. + @vtable_max_offset = VIRTUAL_OFFSET_BYTE_SIZE + @vtable_length = unpack_virtual_offset(0) + @vtable_max_offset = @vtable_length - VIRTUAL_OFFSET_BYTE_SIZE + @table_length = unpack_virtual_offset(VIRTUAL_OFFSET_BYTE_SIZE) + end + end + + def unpack_virtual_offset(vtable_offset) + return 0 if vtable_offset > @vtable_max_offset + @data.get_value(:u16, @vtable_start + vtable_offset) + end + + def resolve_indirect(offset) + @offset + offset + unpack_offset(offset) + end + + def unpack_offset_raw(offset) + @data.get_value(:u32, offset) + end + + def unpack_offset(offset) + unpack_offset_raw(@offset + offset) + end + + def unpack_signed_offset(offset) + @data.get_value(:s32, @offset + offset) + end + + def unpack_bool(offset) + unpack_byte(offset) != 0 + end + + def unpack_utype(offset) + unpack_ubyte(offset) + end + + def unpack_byte(offset) + @data.get_value(:S8, @offset + offset) + end + + def unpack_ubyte(offset) + @data.get_value(:U8, @offset + offset) + end + + def unpack_short(offset) + @data.get_value(:s16, @offset + offset) + end + + def unpack_ushort(offset) + @data.get_value(:u16, @offset + offset) + end + + def unpack_int(offset) + @data.get_value(:s32, @offset + offset) + end + + def unpack_uint(offset) + @data.get_value(:u32, @offset + offset) + end + + def unpack_long(offset) + @data.get_value(:s64, @offset + offset) + end + + def unpack_ulong(offset) + @data.get_value(:u64, @offset + offset) + end + + def unpack_float(offset) + @data.get_value(:f32, @offset + offset) + end + + def unpack_double(offset) + @data.get_value(:f64, @offset + offset) + end + + def unpack_string(offset) + value_offset = resolve_indirect(offset) + length = unpack_offset_raw(value_offset) + @data.get_string(value_offset + OFFSET_BYTE_SIZE, + length) + end + + def unpack_table(klass, offset) + sub_view = self.class.new(@data, + resolve_indirect(offset), + have_vtable: true) + klass.new(sub_view) + end + + def unpack_struct(klass, offset) + sub_view = self.class.new(@data, + @offset + offset, + have_vtable: false) + klass.new(sub_view) + end + + def unpack_union(klass, offset) + unpack_table(klass, offset) + end + + def unpack_vector_length(offset) + vector_offset = resolve_indirect(offset) + unpack_offset_raw(vector_offset) + end + + def unpack_vector(offset, element_size) + relative_vector_offset = offset + unpack_offset(offset) + length = unpack_offset(relative_vector_offset) + return nil if length.zero? + + relative_vector_body_offset = relative_vector_offset + OFFSET_BYTE_SIZE + length.times.collect do |i| + relative_element_offset = + relative_vector_body_offset + (element_size * i) + yield(relative_element_offset) + end + end + + def unpack_bool_vector(offset, element_size) + unpack_vector(offset, element_size) do |relative_element_offset| + unpack_bool(relative_element_offset) + end + end + + def unpack_ubyte_vector(offset, element_size) + unpack_vector(offset, element_size) do |relative_element_offset| + unpack_ubyte(relative_element_offset) + end + end + + def unpack_string_vector(offset, element_size) + unpack_vector(offset, element_size) do |relative_element_offset| + unpack_string(relative_element_offset) + end + end + + def unpack_table_vector(offset, element_size, klass) + unpack_vector(offset, element_size) do |relative_element_offset| + unpack_table(klass, relative_element_offset) + end + end + + def unpack_struct_vector(offset, element_size, klass) + unpack_vector(offset, element_size) do |relative_element_offset| + unpack_struct(klass, relative_element_offset) + end + end + end +end diff --git a/scripts/generate_code.py b/scripts/generate_code.py index c1a4448c06d..16c6c01b657 100755 --- a/scripts/generate_code.py +++ b/scripts/generate_code.py @@ -505,6 +505,13 @@ def glob(path, pattern): flatc(NIM_OPTS, schema="more_defaults.fbs") flatc(NIM_OPTS, schema="MutatingBool.fbs") +# Ruby Tests +RUBY_OPTS = BASE_OPTS + ["--ruby"] +flatc(RUBY_OPTS, schema="monster_test.fbs", include="include_test") +flatc(RUBY_OPTS, schema="optional_scalars.fbs") +flatc(RUBY_OPTS, schema="more_defaults.fbs") +flatc(RUBY_OPTS, schema="MutatingBool.fbs") + # --filename-suffix and --filename-ext tests flatc( CPP_OPTS + NO_INCL_OPTS + ["--grpc", "--filename-ext", "hpp"], diff --git a/scripts/release.sh b/scripts/release.sh index c1315aa6ad7..f2e7af44667 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -94,6 +94,11 @@ sed -i \ -e "s/\(version='\).*/\1$version',/" \ python/setup.py +echo "Updating ruby/flatbuffers/version.rb..." +sed -i \ + "s/^ VERSION = \".*\"$/ VERSION = \"$version\"/g" \ + ruby/flatbuffers/version.rb + echo "Updating rust/flatbuffers/Cargo.toml..." sed -i \ "s/^version = \".*\"$/version = \"$version\"/g" \ diff --git a/src/BUILD.bazel b/src/BUILD.bazel index 4b9fb7a8156..4a9f9adcb1d 100644 --- a/src/BUILD.bazel +++ b/src/BUILD.bazel @@ -76,6 +76,8 @@ cc_library( "bfbs_gen_lua.h", "bfbs_gen_nim.cpp", "bfbs_gen_nim.h", + "bfbs_gen_ruby.cpp", + "bfbs_gen_ruby.h", "bfbs_namer.h", "binary_annotator.cpp", "binary_annotator.h", diff --git a/src/bfbs_gen.h b/src/bfbs_gen.h index afae9c45f0f..641ab0ada4a 100644 --- a/src/bfbs_gen.h +++ b/src/bfbs_gen.h @@ -26,7 +26,7 @@ namespace flatbuffers { namespace { -static void ForAllEnums( +static inline void ForAllEnums( const flatbuffers::Vector>* enums, std::function func) { for (auto it = enums->cbegin(); it != enums->cend(); ++it) { @@ -34,7 +34,7 @@ static void ForAllEnums( } } -static void ForAllObjects( +static inline void ForAllObjects( const flatbuffers::Vector>* objects, std::function func) { for (auto it = objects->cbegin(); it != objects->cend(); ++it) { @@ -42,7 +42,7 @@ static void ForAllObjects( } } -static void ForAllEnumValues( +static inline void ForAllEnumValues( const reflection::Enum* enum_def, std::function func) { for (auto it = enum_def->values()->cbegin(); it != enum_def->values()->cend(); @@ -51,7 +51,7 @@ static void ForAllEnumValues( } } -static void ForAllDocumentation( +static inline void ForAllDocumentation( const flatbuffers::Vector>* documentation, std::function func) { @@ -65,7 +65,7 @@ static void ForAllDocumentation( // Maps the field index into object->fields() to the field's ID (the ith element // in the return vector). -static std::vector FieldIdToIndex(const reflection::Object* object) { +static inline std::vector FieldIdToIndex(const reflection::Object* object) { std::vector field_index_by_id; field_index_by_id.resize(object->fields()->size()); @@ -78,23 +78,23 @@ static std::vector FieldIdToIndex(const reflection::Object* object) { return field_index_by_id; } -static bool IsStructOrTable(const reflection::BaseType base_type) { +static inline bool IsStructOrTable(const reflection::BaseType base_type) { return base_type == reflection::Obj; } -static bool IsFloatingPoint(const reflection::BaseType base_type) { +static inline bool IsFloatingPoint(const reflection::BaseType base_type) { return base_type == reflection::Float || base_type == reflection::Double; } -static bool IsBool(const reflection::BaseType base_type) { +static inline bool IsBool(const reflection::BaseType base_type) { return base_type == reflection::Bool; } -static bool IsSingleByte(const reflection::BaseType base_type) { +static inline bool IsSingleByte(const reflection::BaseType base_type) { return base_type >= reflection::UType && base_type <= reflection::UByte; } -static bool IsVector(const reflection::BaseType base_type) { +static inline bool IsVector(const reflection::BaseType base_type) { return base_type == reflection::Vector; } diff --git a/src/bfbs_gen_ruby.cpp b/src/bfbs_gen_ruby.cpp new file mode 100644 index 00000000000..70d0af8c3b8 --- /dev/null +++ b/src/bfbs_gen_ruby.cpp @@ -0,0 +1,656 @@ +/* + * Copyright 2025 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "bfbs_gen_ruby.h" + +#include +#include +#include +#include +#include +#include + +// Ensure no includes to flatc internals. bfbs_gen.h and generator.h are OK. +#include "bfbs_gen.h" +#include "bfbs_namer.h" + +// The intermediate representation schema. +#include "flatbuffers/code_generator.h" +#include "flatbuffers/reflection.h" +#include "flatbuffers/reflection_generated.h" + +namespace flatbuffers { +namespace { + +// To reduce typing +namespace r = ::reflection; + +std::set RubyKeywords() { + return { + "__ENCODING__", "__LINE__", "__FILE__", "BEGIN", "END", "alias", + "and", "begin", "break", "case", "class", "def", + "defined?", "do", "else", "elsif", "end", "ensure", + "false", "for", "if", "in", "module", "next", + "nil", "not", "or", "redo", "rescue", "retry", + "return", "self", "super", "then", "true", "undef", + "unless", "until", "when", "while", "yield", + }; +} + +Namer::Config RubyDefaultConfig() { + return {/*types=*/Case::kUpperCamel, + /*constants=*/Case::kUpperCamel, + /*methods=*/Case::kSnake, + /*functions=*/Case::kSnake, + /*fields=*/Case::kSnake, + /*variables=*/Case::kSnake, + /*variants=*/Case::kScreamingSnake, + /*enum_variant_seperator=*/"::", + /*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase, + /*namespaces=*/Case::kUpperCamel, + /*namespace_seperator=*/"::", + /*object_prefix=*/"op", + /*object_suffix=*/"os", + /*keyword_prefix=*/"kp", + /*keyword_suffix=*/"ks", + /*filenames=*/Case::kSnake, + /*directories=*/Case::kSnake, + /*output_path=*/"", + /*filename_suffix=*/"", + /*filename_extension=*/".rb"}; +} + +template +typename std::enable_if::value || + std::is_floating_point::value, + std::string>::type +ToRuby(Value value) { + return NumToString(value); +} + +// We can use std::optional when we require C++17. +std::string ToRuby(const std::string &value, bool is_nil = false) { + if (is_nil) { + return "nil"; + } else { + return std::string("\"") + value + "\""; + } +} + +// We can use std::pair<> with C++17 or later. +struct RubyResolvedClassName { + std::string path; + std::string class_name; +}; + +class RubyBfbsGenerator : public BaseBfbsGenerator { + public: + explicit RubyBfbsGenerator(const std::string &flatc_version) + : BaseBfbsGenerator(), + requires_(), + current_obj_(nullptr), + current_enum_(nullptr), + flatc_version_(flatc_version), + namer_(RubyDefaultConfig(), RubyKeywords()), + indent_("") {} + + Status GenerateFromSchema(const r::Schema *schema, + const CodeGenOptions &options) override { + options_ = options; + if (!GenerateEnums(schema->enums())) { + return ERROR; + } + if (!GenerateObjects(schema->objects(), schema->root_table())) { + return ERROR; + } + return OK; + } + + using BaseBfbsGenerator::GenerateCode; + + Status GenerateCode(const Parser &, const std::string &, + const std::string &) override { + return Status::NOT_IMPLEMENTED; + } + + Status GenerateMakeRule(const Parser &parser, const std::string &path, + const std::string &filename, + std::string &output) override { + (void)parser; + (void)path; + (void)filename; + (void)output; + return Status::NOT_IMPLEMENTED; + } + + Status GenerateGrpcCode(const Parser &parser, const std::string &path, + const std::string &filename) override { + (void)parser; + (void)path; + (void)filename; + return Status::NOT_IMPLEMENTED; + } + + Status GenerateRootFile(const Parser &parser, + const std::string &path) override { + (void)parser; + (void)path; + return Status::NOT_IMPLEMENTED; + } + + bool IsSchemaOnly() const override { return true; } + + bool SupportsBfbsGeneration() const override { return true; } + + bool SupportsRootFileGeneration() const override { return false; } + + IDLOptions::Language Language() const override { return IDLOptions::kRuby; } + + std::string LanguageName() const override { return "Ruby"; } + + uint64_t SupportedAdvancedFeatures() const override { + return r::AdvancedArrayFeatures | r::AdvancedUnionFeatures | + r::OptionalScalars | r::DefaultVectorsAndStrings; + } + + private: + void Indent() { indent_ += " "; } + + void Unindent() { indent_ = indent_.substr(0, indent_.size() - 2); } + + std::vector SplitNamespace(const std::string &ns) { + std::vector components; + if (ns.empty()) { + return components; + } + + // We can use std::views::split() with C++20. + std::string::size_type start = 0; + while (true) { + auto next_start = ns.find_first_of(".", start); + components.push_back(ns.substr(start, next_start - start)); + if (next_start == std::string::npos) { + break; + } + start = next_start + 1; + } + return components; + } + + bool IsEnumType(const r::Type *type) { + const auto base_type = type->base_type(); + if (IsInteger(base_type)) { + return type->index() >= 0; + } else { + return false; + } + } + + bool GenerateEnums( + const flatbuffers::Vector> *enums) { + ForAllEnums(enums, [&](const r::Enum *enum_def) { + std::string code; + + StartCodeBlock(enum_def); + + std::string ns; + const auto enum_name = namer_.Type(namer_.Denamespace(enum_def, ns)); + const auto ns_components = SplitNamespace(ns); + + for (const auto &ns_component : ns_components) { + code += indent_ + "module " + namer_.Namespace(ns_component) + "\n"; + Indent(); + } + + GenerateDocumentation(enum_def->documentation(), code); + if (enum_def->is_union()) { + code += indent_ + "class " + enum_name + " < ::FlatBuffers::Union\n"; + } else { + const auto attributes = enum_def->attributes(); + if (attributes && attributes->LookupByKey("bit_flags")) { + code += indent_ + "class " + enum_name + " < ::FlatBuffers::Flags\n"; + } else { + code += indent_ + "class " + enum_name + " < ::FlatBuffers::Enum\n"; + } + } + Indent(); + + ForAllEnumValues(enum_def, [&](const reflection::EnumVal *enum_val) { + GenerateDocumentation(enum_val->documentation(), code); + auto name = enum_val->name()->str(); + auto ruby_name = ToRuby(name); + auto ruby_constant_name = namer_.Variant(name); + auto ruby_value = ToRuby(enum_val->value()); + if (enum_def->is_union()) { + auto resolved = ResolveClassName(enum_val->union_type(), + ns_components, false, true); + auto ruby_resolved_class_name = + ToRuby(resolved.class_name, resolved.class_name.empty()); + auto ruby_resolved_path = + ToRuby(resolved.path, resolved.path.empty()); + // NAME = register("Name", value, "ClassName", "path") + code += indent_ + ruby_constant_name + " = register(" + ruby_name + + ", " + ruby_value + ", " + ruby_resolved_class_name + ", " + + ruby_resolved_path + ")\n"; + } else { + // NAME = register("Name", value) + code += indent_ + ruby_constant_name + " = register(" + ruby_name + + ", " + ruby_value + ")\n"; + } + }); + if (enum_def->is_union()) { + code += "\n"; + code += indent_ + "private def require_table_class\n"; + code += indent_ + " require_relative @require_path\n"; + code += indent_ + "end\n"; + } + + Unindent(); + code += indent_ + "end\n"; + for (const auto &_ : ns_components) { + (void)_; // To suppress unused-variable warning + Unindent(); + code += indent_ + "end\n"; + } + + EmitCodeBlock(code, enum_name, ns_components, + enum_def->declaration_file()->str()); + }); + return true; + } + + bool GenerateObjects( + const flatbuffers::Vector> *objects, + const r::Object *root_object) { + (void)root_object; + ForAllObjects(objects, [&](const r::Object *object) { + std::string code; + + StartCodeBlock(object); + + std::string ns; + const auto object_name = namer_.Type(namer_.Denamespace(object, ns)); + auto ns_components = SplitNamespace(ns); + + for (const auto &ns_component : ns_components) { + code += indent_ + "module " + namer_.Namespace(ns_component) + "\n"; + Indent(); + } + + GenerateDocumentation(object->documentation(), code); + + if (object->is_struct()) { + code += indent_ + "class " + object_name + " < ::FlatBuffers::Struct\n"; + } else { + code += indent_ + "class " + object_name + " < ::FlatBuffers::Table\n"; + } + Indent(); + + // Create all the field accessors. + size_t n_processed_fields = 0; + ForAllFields(object, /*reverse=*/false, [&](const r::Field *field) { + // Skip writing deprecated fields altogether. + if (field->deprecated()) { + return; + } + + const auto &field_name = namer_.Field(*field); + const auto type = field->type(); + const auto base_type = type->base_type(); + + if (n_processed_fields > 0) { + code += "\n"; + } + + GenerateDocumentation(field->documentation(), code); + + if (base_type == r::Bool) { + auto predicate = field_name + "?"; + if (predicate.substr(0, 3) == "is_") { + predicate = predicate.substr(3); + } + code += indent_ + "def " + predicate + "\n"; + } else { + code += indent_ + "def " + field_name + "\n"; + } + Indent(); + + const auto ruby_field_offset = ToRuby(field->offset()); + auto field_offset_direct_code = + indent_ + "field_offset = " + ruby_field_offset + "\n"; + std::string field_offset_virtual_code; + field_offset_virtual_code += + indent_ + "field_offset = @view.unpack_virtual_offset(" + + ruby_field_offset + ")\n"; + field_offset_virtual_code += indent_ + "return " + DefaultValue(field) + + " if field_offset.zero?\n"; + field_offset_virtual_code += "\n"; + + const auto unpack_method_name = + "@view.unpack_" + + namer_.Method(std::string(r::EnumNameBaseType(base_type))); + if (IsScalar(base_type)) { + if (object->is_struct()) { + code += field_offset_direct_code; + if (IsEnumType(type)) { + code += indent_ + "enum_value = " + unpack_method_name + + "(field_offset)\n"; + const auto klass = RegisterRequires(field->type(), ns_components); + code += + indent_ + klass + ".try_convert(enum_value) || enum_value\n"; + } else { + code += indent_ + unpack_method_name + "(field_offset)\n"; + } + } else { + // Table accessors + if (IsEnumType(type)) { + const auto klass = RegisterRequires(field->type(), ns_components); + code += indent_ + "field_offset = @view.unpack_virtual_offset(" + + ruby_field_offset + ")\n"; + code += indent_ + "if field_offset.zero?\n"; + code += indent_ + " enum_value = " + DefaultValue(field) + "\n"; + code += indent_ + "else\n"; + code += indent_ + " enum_value = " + unpack_method_name + + "(field_offset)\n"; + code += indent_ + "end\n"; + code += + indent_ + klass + ".try_convert(enum_value) || enum_value\n"; + } else { + code += field_offset_virtual_code; + code += indent_ + unpack_method_name + "(field_offset)\n"; + } + } + } else { + switch (base_type) { + case r::String: { + code += field_offset_virtual_code; + code += indent_ + unpack_method_name + "(field_offset)\n"; + break; + } + case r::Obj: { + if (object->is_struct()) { + code += field_offset_direct_code; + const auto klass = + RegisterRequires(field->type(), ns_components); + code += indent_ + "@view.unpack_struct(" + klass + + ", field_offset)\n"; + } else { + code += field_offset_virtual_code; + const auto field_object = GetObject(type); + const auto klass = + RegisterRequires(field->type(), ns_components); + if (field_object->is_struct()) { + code += indent_ + "@view.unpack_struct(" + klass + + ", field_offset)\n"; + } else { + code += indent_ + "@view.unpack_table(" + klass + + ", field_offset)\n"; + } + } + break; + } + case r::Union: { + code += indent_ + "type = " + field_name + "_type\n"; + code += indent_ + "return if type.nil?\n"; + code += "\n"; + code += field_offset_virtual_code; + code += indent_ + + "@view.unpack_union(type.table_class, field_offset)\n"; + break; + } + case r::Array: + case r::Vector: { + const r::BaseType element_base_type = type->element(); + int32_t element_size = type->element_size(); + const auto ruby_element_size = ToRuby(element_size); + std::string unpack_vector_code; + unpack_vector_code += + indent_ + "@view.unpack_vector(field_offset, " + + ruby_element_size + ") do |element_offset|\n"; + if (element_base_type == r::Obj) { + const auto klass = + RegisterRequires(field->type(), ns_components, true); + const auto element_object = GetObjectByIndex(type->index()); + if (element_object->is_struct()) { + code += field_offset_virtual_code; + code += unpack_vector_code; + code += indent_ + " @view.unpack_struct(" + klass + + ", element_offset)\n"; + code += indent_ + "end\n"; + } else { + code += field_offset_virtual_code; + code += indent_ + "@view.unpack_vector(field_offset, " + + ruby_element_size + ") do |element_offset|\n"; + code += indent_ + " @view.unpack_table(" + klass + + ", element_offset)\n"; + code += indent_ + "end\n"; + } + } else { + code += field_offset_virtual_code; + code += unpack_vector_code; + std::string element_unpack_method_name; + element_unpack_method_name += + "@view.unpack_" + + namer_.Method( + std::string(r::EnumNameBaseType(element_base_type))); + code += indent_ + " " + element_unpack_method_name + + "(element_offset)\n"; + code += indent_ + "end\n"; + } + break; + } + default: { + return; + } + } + } + Unindent(); + code += indent_ + "end\n"; + + n_processed_fields++; + }); + + Unindent(); + code += indent_ + "end\n"; + + for (const auto &_ : ns_components) { + (void)_; // To suppress unused-variable warning + Unindent(); + code += indent_ + "end\n"; + } + + EmitCodeBlock(code, object_name, ns_components, + object->declaration_file()->str()); + }); + return true; + } + + private: + void GenerateDocumentation( + const flatbuffers::Vector> + *documentation, + std::string &code) const { + flatbuffers::ForAllDocumentation( + documentation, [&](const flatbuffers::String *str) { + code += indent_ + "#" + str->str() + "\n"; + }); + } + + std::string DefaultValue(const r::Field *field) const { + const r::BaseType base_type = field->type()->base_type(); + if (IsFloatingPoint(base_type)) { + const auto default_value = field->default_real(); + if (std::isnan(default_value)) { + return "Float::NAN"; + } else if (std::isinf(default_value)) { + if (default_value > 0) { + return "Float::INFINITY"; + } else { + return "-Float::INFINITY"; + } + } else { + return ToRuby(default_value); + } + } + if (IsBool(base_type)) { + return field->default_integer() ? "true" : "false"; + } + if (IsScalar(base_type)) { + return ToRuby(field->default_integer()); + } + return "nil"; + } + + void StartCodeBlock(const reflection::Enum *enum_def) { + current_enum_ = enum_def; + current_obj_ = nullptr; + requires_.clear(); + } + + void StartCodeBlock(const reflection::Object *object) { + current_obj_ = object; + current_enum_ = nullptr; + requires_.clear(); + } + + RubyResolvedClassName ResolveClassName( + const r::Type *type, const std::vector &base_ns_components, + bool use_element = false, bool always_use_absolute_class_name = false) { + const r::BaseType base_type = + use_element ? type->element() : type->base_type(); + + if (base_type == r::None) { + return RubyResolvedClassName{std::string(""), std::string("")}; + } + + std::string name; + std::string ns; + if (IsStructOrTable(base_type)) { + const r::Object *object = GetObjectByIndex(type->index()); + name = namer_.Denamespace(object, ns); + if (object == current_obj_) { + return RubyResolvedClassName{std::string(""), namer_.Type(name)}; + } + } else { + const r::Enum *enum_def = GetEnumByIndex(type->index()); + name = namer_.Denamespace(enum_def, ns); + if (enum_def == current_enum_) { + return RubyResolvedClassName{std::string(""), namer_.Type(name)}; + } + } + + auto ns_components = SplitNamespace(ns); + auto absolute_ns_components = ns_components; + for (size_t i = 0; i < base_ns_components.size(); ++i) { + const auto &base_ns_component = base_ns_components[i]; + if (ns_components.empty() || ns_components[0] != base_ns_component) { + for (; i < base_ns_components.size(); ++i) { + ns_components.insert(ns_components.begin(), ".."); + } + break; + } + ns_components.erase(ns_components.begin()); + } + const auto path = namer_.Directories(ns_components) + + namer_.File(name, SkipFile::Extension); + + if (!always_use_absolute_class_name && + (ns_components.empty() || ns_components[0] != "..")) { + // We can use relative hierarchy + return RubyResolvedClassName{path, + namer_.NamespacedType(ns_components, name)}; + } else { + // We need to use absolute hierarchy + return RubyResolvedClassName{ + path, std::string("::") + + namer_.NamespacedType(absolute_ns_components, name)}; + } + } + + std::string RegisterRequires( + const r::Type *type, const std::vector &base_ns_components, + bool use_element = false) { + auto resolved = ResolveClassName(type, base_ns_components, use_element); + + if (!resolved.path.empty()) { + RegisterRequires(resolved.path); + } + return resolved.class_name; + } + + void RegisterRequires(const std::string &path) { + if (std::find(requires_.cbegin(), requires_.cend(), path) == + requires_.cend()) { + requires_.push_back(path); + } + } + + void EmitCodeBlock(const std::string &code_block, const std::string &name, + const std::vector &ns_components, + const std::string &declaring_file) const { + std::string code = + "# Automatically generated by the FlatBuffers compiler, " + "do not modify.\n"; + code += "#\n"; + code += "# flatc version: " + flatc_version_ + "\n"; + if (!declaring_file.empty()) { + code += "# Declared by: " + declaring_file + "\n"; + } + auto root_table = schema_->root_table(); + if (root_table) { + const auto root_type = root_table->name()->str(); + const auto root_file = root_table->declaration_file()->str(); + code += "# Rooting type: " + root_type + " (" + root_file + ")\n"; + } + code += "\n"; + + code += "require \"flatbuffers\"\n"; + if (!requires_.empty()) { + for (const auto &path : requires_) { + code += "require_relative \"" + path + "\"\n"; + } + } + code += "\n"; + + code += code_block; + + auto file_name = namer_.File(name); + const auto path = options_.output_path + namer_.Directories(ns_components); + if (!path.empty()) { + EnsureDirExists(path); + file_name = path + "/" + file_name; + } + SaveFile(file_name.c_str(), code, false); + } + + std::vector requires_; + CodeGenOptions options_; + + const r::Object *current_obj_; + const r::Enum *current_enum_; + const std::string flatc_version_; + const BfbsNamer namer_; + std::string indent_; +}; +} // namespace + +std::unique_ptr NewRubyBfbsGenerator( + const std::string &flatc_version) { + return std::unique_ptr( + new RubyBfbsGenerator(flatc_version)); +} + +} // namespace flatbuffers diff --git a/src/bfbs_gen_ruby.h b/src/bfbs_gen_ruby.h new file mode 100644 index 00000000000..ff5694006a9 --- /dev/null +++ b/src/bfbs_gen_ruby.h @@ -0,0 +1,33 @@ +/* + * Copyright 2025 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_BFBS_GEN_RUBY_H_ +#define FLATBUFFERS_BFBS_GEN_RUBY_H_ + +#include +#include + +#include "flatbuffers/code_generator.h" + +namespace flatbuffers { + +// Constructs a new Ruby Code generator. +std::unique_ptr NewRubyBfbsGenerator( + const std::string &flatc_version); + +} // namespace flatbuffers + +#endif // FLATBUFFERS_BFBS_GEN_RUBY_H_ diff --git a/src/flatc_main.cpp b/src/flatc_main.cpp index bcbb55d8376..4fb3813964f 100644 --- a/src/flatc_main.cpp +++ b/src/flatc_main.cpp @@ -19,6 +19,7 @@ #include "bfbs_gen_lua.h" #include "bfbs_gen_nim.h" +#include "bfbs_gen_ruby.h" #include "flatbuffers/base.h" #include "flatbuffers/code_generator.h" #include "flatbuffers/flatc.h" @@ -160,6 +161,11 @@ int main(int argc, const char* argv[]) { "Generate PHP files for tables/structs"}, flatbuffers::NewPhpCodeGenerator()); + flatc.RegisterCodeGenerator( + flatbuffers::FlatCOption{"", "ruby", "", + "Generate Ruby files for tables/structs"}, + flatbuffers::NewRubyBfbsGenerator(flatbuffers_version)); + flatc.RegisterCodeGenerator( flatbuffers::FlatCOption{"r", "rust", "", "Generate Rust files for tables/structs"}, diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index c0f96ef0be8..0006d5fe6bd 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -2731,7 +2731,7 @@ bool Parser::SupportsOptionalScalars(const flatbuffers::IDLOptions& opts) { IDLOptions::kKotlin | IDLOptions::kKotlinKmp | IDLOptions::kCpp | IDLOptions::kJava | IDLOptions::kCSharp | IDLOptions::kTs | IDLOptions::kBinary | IDLOptions::kGo | IDLOptions::kPython | - IDLOptions::kJson | IDLOptions::kNim; + IDLOptions::kJson | IDLOptions::kNim | IDLOptions::kRuby; unsigned long langs = opts.lang_to_generate; return (langs > 0 && langs < IDLOptions::kMAX) && !(langs & ~supported_langs); } @@ -2742,7 +2742,8 @@ bool Parser::SupportsOptionalScalars() const { bool Parser::SupportsDefaultVectorsAndStrings() const { static FLATBUFFERS_CONSTEXPR unsigned long supported_langs = - IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kNim; + IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kNim | + IDLOptions::kRuby; return !(opts.lang_to_generate & ~supported_langs); } diff --git a/tests/RubyTest.rb b/tests/RubyTest.rb new file mode 100755 index 00000000000..e5a49740f62 --- /dev/null +++ b/tests/RubyTest.rb @@ -0,0 +1,77 @@ +#!/usr/bin/env ruby +# +# Copyright 2014 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +$VERBOSE = true + +require "fileutils" +require "tmpdir" + +require "test/unit" + +top_dir = File.expand_path(File.join(__dir__, "..")) +lib_dir = File.join(top_dir, "ruby") +$LOAD_PATH.unshift(lib_dir) + +build_dir = ENV["BUILD_DIR"] || top_dir +flatc = File.join(build_dir, "flatc") + +run_flatc = lambda do |*args| + unless system(flatc, *args) + raise "Failed to run flatc: #{flatc} #{args.join(" ")}" + end +end + +module Helper + module Path + class << self + attr_accessor :tmp_dir + attr_accessor :reflection_dir + attr_accessor :monster_dir + end + end +end + +tests_dir = File.join(top_dir, "tests") +Dir.mktmpdir do |tmp_dir| + Helper::Path.tmp_dir = tmp_dir + + reflection_dir = File.join(tmp_dir, "reflection") + Helper::Path.reflection_dir = reflection_dir + FileUtils.mkdir_p(reflection_dir) + reflection_fbs = File.join(top_dir, "reflection", "reflection.fbs") + run_flatc.call("-o", reflection_dir, + "--ruby", + reflection_fbs) + run_flatc.call("-o", reflection_dir, + "--bfbs-builtins", + "--bfbs-comments", + "--binary", + "--schema", + reflection_fbs) + $LOAD_PATH.unshift(reflection_dir) + + monster_dir = File.join(tmp_dir, "monster") + Helper::Path.monster_dir = monster_dir + FileUtils.mkdir_p(monster_dir) + run_flatc.call("-o", monster_dir, + "-I", File.join(tests_dir, "include_test"), + "--ruby", + File.join(tests_dir, "monster_test.fbs")) + $LOAD_PATH.unshift(monster_dir) + + test_dir = File.join(tests_dir, "ruby") + exit(Test::Unit::AutoRunner.run(true, test_dir)) +end diff --git a/tests/abc.rb b/tests/abc.rb new file mode 100644 index 00000000000..bcf9541d541 --- /dev/null +++ b/tests/abc.rb @@ -0,0 +1,11 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 + +require "flatbuffers" + +class Abc < ::FlatBuffers::Enum + A = register("A", 0) + B = register("B", 1) + C = register("C", 2) +end diff --git a/tests/more_defaults.rb b/tests/more_defaults.rb new file mode 100644 index 00000000000..dde9fecde96 --- /dev/null +++ b/tests/more_defaults.rb @@ -0,0 +1,57 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 + +require "flatbuffers" + +class MoreDefaults < ::FlatBuffers::Table + def ints + field_offset = @view.unpack_virtual_offset(4) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 4) do |element_offset| + @view.unpack_int(element_offset) + end + end + + def floats + field_offset = @view.unpack_virtual_offset(6) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 4) do |element_offset| + @view.unpack_float(element_offset) + end + end + + def empty_string + field_offset = @view.unpack_virtual_offset(8) + return nil if field_offset.zero? + + @view.unpack_string(field_offset) + end + + def some_string + field_offset = @view.unpack_virtual_offset(10) + return nil if field_offset.zero? + + @view.unpack_string(field_offset) + end + + def abcs + field_offset = @view.unpack_virtual_offset(12) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 4) do |element_offset| + @view.unpack_int(element_offset) + end + end + + def bools + field_offset = @view.unpack_virtual_offset(14) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 1) do |element_offset| + @view.unpack_bool(element_offset) + end + end +end diff --git a/tests/my_game/example/ability.rb b/tests/my_game/example/ability.rb new file mode 100644 index 00000000000..eac2835e22d --- /dev/null +++ b/tests/my_game/example/ability.rb @@ -0,0 +1,22 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" + +module MyGame + module Example + class Ability < ::FlatBuffers::Struct + def id + field_offset = 0 + @view.unpack_uint(field_offset) + end + + def distance + field_offset = 4 + @view.unpack_uint(field_offset) + end + end + end +end diff --git a/tests/my_game/example/any.rb b/tests/my_game/example/any.rb new file mode 100644 index 00000000000..9260e103b9e --- /dev/null +++ b/tests/my_game/example/any.rb @@ -0,0 +1,21 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" + +module MyGame + module Example + class Any < ::FlatBuffers::Union + NONE = register("NONE", 0, nil, nil) + MONSTER = register("Monster", 1, "::MyGame::Example::Monster", "monster") + TEST_SIMPLE_TABLE_WITH_ENUM = register("TestSimpleTableWithEnum", 2, "::MyGame::Example::TestSimpleTableWithEnum", "test_simple_table_with_enum") + MY_GAME_EXAMPLE_2_MONSTER = register("MyGame_Example2_Monster", 3, "::MyGame::Example2::Monster", "../example_2/monster") + + private def require_table_class + require_relative @require_path + end + end + end +end diff --git a/tests/my_game/example/any_ambiguous_aliases.rb b/tests/my_game/example/any_ambiguous_aliases.rb new file mode 100644 index 00000000000..8c0f11c8a6f --- /dev/null +++ b/tests/my_game/example/any_ambiguous_aliases.rb @@ -0,0 +1,21 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" + +module MyGame + module Example + class AnyAmbiguousAliases < ::FlatBuffers::Union + NONE = register("NONE", 0, nil, nil) + M_1 = register("M1", 1, "::MyGame::Example::Monster", "monster") + M_2 = register("M2", 2, "::MyGame::Example::Monster", "monster") + M_3 = register("M3", 3, "::MyGame::Example::Monster", "monster") + + private def require_table_class + require_relative @require_path + end + end + end +end diff --git a/tests/my_game/example/any_unique_aliases.rb b/tests/my_game/example/any_unique_aliases.rb new file mode 100644 index 00000000000..8229253fea4 --- /dev/null +++ b/tests/my_game/example/any_unique_aliases.rb @@ -0,0 +1,21 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" + +module MyGame + module Example + class AnyUniqueAliases < ::FlatBuffers::Union + NONE = register("NONE", 0, nil, nil) + M = register("M", 1, "::MyGame::Example::Monster", "monster") + TS = register("TS", 2, "::MyGame::Example::TestSimpleTableWithEnum", "test_simple_table_with_enum") + M_2 = register("M2", 3, "::MyGame::Example2::Monster", "../example_2/monster") + + private def require_table_class + require_relative @require_path + end + end + end +end diff --git a/tests/my_game/example/color.rb b/tests/my_game/example/color.rb new file mode 100644 index 00000000000..901f7dfa52a --- /dev/null +++ b/tests/my_game/example/color.rb @@ -0,0 +1,20 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" + +module MyGame + module Example + # Composite components of Monster color. + class Color < ::FlatBuffers::Enum + RED = register("Red", 1) + # \brief color Green + # Green is bit_flag with value (1u << 1) + GREEN = register("Green", 2) + # \brief color Blue (1u << 3) + BLUE = register("Blue", 8) + end + end +end diff --git a/tests/my_game/example/long_enum.rb b/tests/my_game/example/long_enum.rb new file mode 100644 index 00000000000..c75ef6bbe87 --- /dev/null +++ b/tests/my_game/example/long_enum.rb @@ -0,0 +1,16 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" + +module MyGame + module Example + class LongEnum < ::FlatBuffers::Enum + LONG_ONE = register("LongOne", 2) + LONG_TWO = register("LongTwo", 4) + LONG_BIG = register("LongBig", 1099511627776) + end + end +end diff --git a/tests/my_game/example/monster.rb b/tests/my_game/example/monster.rb new file mode 100644 index 00000000000..87bfef2aa42 --- /dev/null +++ b/tests/my_game/example/monster.rb @@ -0,0 +1,524 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" +require_relative "vec_3" +require_relative "color" +require_relative "any" +require_relative "test" +require_relative "stat" +require_relative "ability" +require_relative "../in_parent_namespace" +require_relative "referrable" +require_relative "any_unique_aliases" +require_relative "any_ambiguous_aliases" +require_relative "race" +require_relative "long_enum" + +module MyGame + module Example + # an example documentation comment: "monster object" + class Monster < ::FlatBuffers::Table + def pos + field_offset = @view.unpack_virtual_offset(4) + return nil if field_offset.zero? + + @view.unpack_struct(Vec3, field_offset) + end + + def mana + field_offset = @view.unpack_virtual_offset(6) + return 150 if field_offset.zero? + + @view.unpack_short(field_offset) + end + + def hp + field_offset = @view.unpack_virtual_offset(8) + return 100 if field_offset.zero? + + @view.unpack_short(field_offset) + end + + def name + field_offset = @view.unpack_virtual_offset(10) + return nil if field_offset.zero? + + @view.unpack_string(field_offset) + end + + def inventory + field_offset = @view.unpack_virtual_offset(14) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 1) do |element_offset| + @view.unpack_ubyte(element_offset) + end + end + + def color + field_offset = @view.unpack_virtual_offset(16) + if field_offset.zero? + enum_value = 8 + else + enum_value = @view.unpack_ubyte(field_offset) + end + Color.try_convert(enum_value) || enum_value + end + + def test_type + field_offset = @view.unpack_virtual_offset(18) + if field_offset.zero? + enum_value = 0 + else + enum_value = @view.unpack_utype(field_offset) + end + Any.try_convert(enum_value) || enum_value + end + + def test + type = test_type + return if type.nil? + + field_offset = @view.unpack_virtual_offset(20) + return nil if field_offset.zero? + + @view.unpack_union(type.table_class, field_offset) + end + + def test_4 + field_offset = @view.unpack_virtual_offset(22) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 4) do |element_offset| + @view.unpack_struct(Test, element_offset) + end + end + + def testarrayofstring + field_offset = @view.unpack_virtual_offset(24) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 4) do |element_offset| + @view.unpack_string(element_offset) + end + end + + # an example documentation comment: this will end up in the generated code + # multiline too + def testarrayoftables + field_offset = @view.unpack_virtual_offset(26) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 4) do |element_offset| + @view.unpack_table(Monster, element_offset) + end + end + + def enemy + field_offset = @view.unpack_virtual_offset(28) + return nil if field_offset.zero? + + @view.unpack_table(Monster, field_offset) + end + + def testnestedflatbuffer + field_offset = @view.unpack_virtual_offset(30) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 1) do |element_offset| + @view.unpack_ubyte(element_offset) + end + end + + def testempty + field_offset = @view.unpack_virtual_offset(32) + return nil if field_offset.zero? + + @view.unpack_table(Stat, field_offset) + end + + def testbool? + field_offset = @view.unpack_virtual_offset(34) + return false if field_offset.zero? + + @view.unpack_bool(field_offset) + end + + def testhashs_32_fnv_1 + field_offset = @view.unpack_virtual_offset(36) + return 0 if field_offset.zero? + + @view.unpack_int(field_offset) + end + + def testhashu_32_fnv_1 + field_offset = @view.unpack_virtual_offset(38) + return 0 if field_offset.zero? + + @view.unpack_uint(field_offset) + end + + def testhashs_64_fnv_1 + field_offset = @view.unpack_virtual_offset(40) + return 0 if field_offset.zero? + + @view.unpack_long(field_offset) + end + + def testhashu_64_fnv_1 + field_offset = @view.unpack_virtual_offset(42) + return 0 if field_offset.zero? + + @view.unpack_ulong(field_offset) + end + + def testhashs_32_fnv_1a + field_offset = @view.unpack_virtual_offset(44) + return 0 if field_offset.zero? + + @view.unpack_int(field_offset) + end + + def testhashu_32_fnv_1a + field_offset = @view.unpack_virtual_offset(46) + return 0 if field_offset.zero? + + @view.unpack_uint(field_offset) + end + + def testhashs_64_fnv_1a + field_offset = @view.unpack_virtual_offset(48) + return 0 if field_offset.zero? + + @view.unpack_long(field_offset) + end + + def testhashu_64_fnv_1a + field_offset = @view.unpack_virtual_offset(50) + return 0 if field_offset.zero? + + @view.unpack_ulong(field_offset) + end + + def testarrayofbools + field_offset = @view.unpack_virtual_offset(52) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 1) do |element_offset| + @view.unpack_bool(element_offset) + end + end + + def testf + field_offset = @view.unpack_virtual_offset(54) + return 3.14159 if field_offset.zero? + + @view.unpack_float(field_offset) + end + + def testf_2 + field_offset = @view.unpack_virtual_offset(56) + return 3.0 if field_offset.zero? + + @view.unpack_float(field_offset) + end + + def testf_3 + field_offset = @view.unpack_virtual_offset(58) + return 0.0 if field_offset.zero? + + @view.unpack_float(field_offset) + end + + def testarrayofstring_2 + field_offset = @view.unpack_virtual_offset(60) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 4) do |element_offset| + @view.unpack_string(element_offset) + end + end + + def testarrayofsortedstruct + field_offset = @view.unpack_virtual_offset(62) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 8) do |element_offset| + @view.unpack_struct(Ability, element_offset) + end + end + + def flex + field_offset = @view.unpack_virtual_offset(64) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 1) do |element_offset| + @view.unpack_ubyte(element_offset) + end + end + + def test_5 + field_offset = @view.unpack_virtual_offset(66) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 4) do |element_offset| + @view.unpack_struct(Test, element_offset) + end + end + + def vector_of_longs + field_offset = @view.unpack_virtual_offset(68) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 8) do |element_offset| + @view.unpack_long(element_offset) + end + end + + def vector_of_doubles + field_offset = @view.unpack_virtual_offset(70) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 8) do |element_offset| + @view.unpack_double(element_offset) + end + end + + def parent_namespace_test + field_offset = @view.unpack_virtual_offset(72) + return nil if field_offset.zero? + + @view.unpack_table(::MyGame::InParentNamespace, field_offset) + end + + def vector_of_referrables + field_offset = @view.unpack_virtual_offset(74) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 4) do |element_offset| + @view.unpack_table(Referrable, element_offset) + end + end + + def single_weak_reference + field_offset = @view.unpack_virtual_offset(76) + return 0 if field_offset.zero? + + @view.unpack_ulong(field_offset) + end + + def vector_of_weak_references + field_offset = @view.unpack_virtual_offset(78) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 8) do |element_offset| + @view.unpack_ulong(element_offset) + end + end + + def vector_of_strong_referrables + field_offset = @view.unpack_virtual_offset(80) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 4) do |element_offset| + @view.unpack_table(Referrable, element_offset) + end + end + + def co_owning_reference + field_offset = @view.unpack_virtual_offset(82) + return 0 if field_offset.zero? + + @view.unpack_ulong(field_offset) + end + + def vector_of_co_owning_references + field_offset = @view.unpack_virtual_offset(84) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 8) do |element_offset| + @view.unpack_ulong(element_offset) + end + end + + def non_owning_reference + field_offset = @view.unpack_virtual_offset(86) + return 0 if field_offset.zero? + + @view.unpack_ulong(field_offset) + end + + def vector_of_non_owning_references + field_offset = @view.unpack_virtual_offset(88) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 8) do |element_offset| + @view.unpack_ulong(element_offset) + end + end + + def any_unique_type + field_offset = @view.unpack_virtual_offset(90) + if field_offset.zero? + enum_value = 0 + else + enum_value = @view.unpack_utype(field_offset) + end + AnyUniqueAliases.try_convert(enum_value) || enum_value + end + + def any_unique + type = any_unique_type + return if type.nil? + + field_offset = @view.unpack_virtual_offset(92) + return nil if field_offset.zero? + + @view.unpack_union(type.table_class, field_offset) + end + + def any_ambiguous_type + field_offset = @view.unpack_virtual_offset(94) + if field_offset.zero? + enum_value = 0 + else + enum_value = @view.unpack_utype(field_offset) + end + AnyAmbiguousAliases.try_convert(enum_value) || enum_value + end + + def any_ambiguous + type = any_ambiguous_type + return if type.nil? + + field_offset = @view.unpack_virtual_offset(96) + return nil if field_offset.zero? + + @view.unpack_union(type.table_class, field_offset) + end + + def vector_of_enums + field_offset = @view.unpack_virtual_offset(98) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 1) do |element_offset| + @view.unpack_ubyte(element_offset) + end + end + + def signed_enum + field_offset = @view.unpack_virtual_offset(100) + if field_offset.zero? + enum_value = -1 + else + enum_value = @view.unpack_byte(field_offset) + end + Race.try_convert(enum_value) || enum_value + end + + def testrequirednestedflatbuffer + field_offset = @view.unpack_virtual_offset(102) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 1) do |element_offset| + @view.unpack_ubyte(element_offset) + end + end + + def scalar_key_sorted_tables + field_offset = @view.unpack_virtual_offset(104) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 4) do |element_offset| + @view.unpack_table(Stat, element_offset) + end + end + + def native_inline + field_offset = @view.unpack_virtual_offset(106) + return nil if field_offset.zero? + + @view.unpack_struct(Test, field_offset) + end + + def long_enum_non_enum_default + field_offset = @view.unpack_virtual_offset(108) + if field_offset.zero? + enum_value = 0 + else + enum_value = @view.unpack_ulong(field_offset) + end + LongEnum.try_convert(enum_value) || enum_value + end + + def long_enum_normal_default + field_offset = @view.unpack_virtual_offset(110) + if field_offset.zero? + enum_value = 2 + else + enum_value = @view.unpack_ulong(field_offset) + end + LongEnum.try_convert(enum_value) || enum_value + end + + def nan_default + field_offset = @view.unpack_virtual_offset(112) + return Float::NAN if field_offset.zero? + + @view.unpack_float(field_offset) + end + + def inf_default + field_offset = @view.unpack_virtual_offset(114) + return Float::INFINITY if field_offset.zero? + + @view.unpack_float(field_offset) + end + + def positive_inf_default + field_offset = @view.unpack_virtual_offset(116) + return Float::INFINITY if field_offset.zero? + + @view.unpack_float(field_offset) + end + + def infinity_default + field_offset = @view.unpack_virtual_offset(118) + return Float::INFINITY if field_offset.zero? + + @view.unpack_float(field_offset) + end + + def positive_infinity_default + field_offset = @view.unpack_virtual_offset(120) + return Float::INFINITY if field_offset.zero? + + @view.unpack_float(field_offset) + end + + def negative_inf_default + field_offset = @view.unpack_virtual_offset(122) + return -Float::INFINITY if field_offset.zero? + + @view.unpack_float(field_offset) + end + + def negative_infinity_default + field_offset = @view.unpack_virtual_offset(124) + return -Float::INFINITY if field_offset.zero? + + @view.unpack_float(field_offset) + end + + def double_inf_default + field_offset = @view.unpack_virtual_offset(126) + return Float::INFINITY if field_offset.zero? + + @view.unpack_double(field_offset) + end + end + end +end diff --git a/tests/my_game/example/race.rb b/tests/my_game/example/race.rb new file mode 100644 index 00000000000..81e9b63a181 --- /dev/null +++ b/tests/my_game/example/race.rb @@ -0,0 +1,17 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" + +module MyGame + module Example + class Race < ::FlatBuffers::Enum + NONE = register("None", -1) + HUMAN = register("Human", 0) + DWARF = register("Dwarf", 1) + ELF = register("Elf", 2) + end + end +end diff --git a/tests/my_game/example/referrable.rb b/tests/my_game/example/referrable.rb new file mode 100644 index 00000000000..7fbb58d7a04 --- /dev/null +++ b/tests/my_game/example/referrable.rb @@ -0,0 +1,19 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" + +module MyGame + module Example + class Referrable < ::FlatBuffers::Table + def id + field_offset = @view.unpack_virtual_offset(4) + return 0 if field_offset.zero? + + @view.unpack_ulong(field_offset) + end + end + end +end diff --git a/tests/my_game/example/stat.rb b/tests/my_game/example/stat.rb new file mode 100644 index 00000000000..77a5a4133c7 --- /dev/null +++ b/tests/my_game/example/stat.rb @@ -0,0 +1,33 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" + +module MyGame + module Example + class Stat < ::FlatBuffers::Table + def id + field_offset = @view.unpack_virtual_offset(4) + return nil if field_offset.zero? + + @view.unpack_string(field_offset) + end + + def val + field_offset = @view.unpack_virtual_offset(6) + return 0 if field_offset.zero? + + @view.unpack_long(field_offset) + end + + def count + field_offset = @view.unpack_virtual_offset(8) + return 0 if field_offset.zero? + + @view.unpack_ushort(field_offset) + end + end + end +end diff --git a/tests/my_game/example/struct_of_structs.rb b/tests/my_game/example/struct_of_structs.rb new file mode 100644 index 00000000000..7d755c4489d --- /dev/null +++ b/tests/my_game/example/struct_of_structs.rb @@ -0,0 +1,29 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" +require_relative "ability" +require_relative "test" + +module MyGame + module Example + class StructOfStructs < ::FlatBuffers::Struct + def a + field_offset = 0 + @view.unpack_struct(Ability, field_offset) + end + + def b + field_offset = 8 + @view.unpack_struct(Test, field_offset) + end + + def c + field_offset = 12 + @view.unpack_struct(Ability, field_offset) + end + end + end +end diff --git a/tests/my_game/example/struct_of_structs_of_structs.rb b/tests/my_game/example/struct_of_structs_of_structs.rb new file mode 100644 index 00000000000..759af33bb1e --- /dev/null +++ b/tests/my_game/example/struct_of_structs_of_structs.rb @@ -0,0 +1,18 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" +require_relative "struct_of_structs" + +module MyGame + module Example + class StructOfStructsOfStructs < ::FlatBuffers::Struct + def a + field_offset = 0 + @view.unpack_struct(StructOfStructs, field_offset) + end + end + end +end diff --git a/tests/my_game/example/test.rb b/tests/my_game/example/test.rb new file mode 100644 index 00000000000..303a6db76d3 --- /dev/null +++ b/tests/my_game/example/test.rb @@ -0,0 +1,22 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" + +module MyGame + module Example + class Test < ::FlatBuffers::Struct + def a + field_offset = 0 + @view.unpack_short(field_offset) + end + + def b + field_offset = 2 + @view.unpack_byte(field_offset) + end + end + end +end diff --git a/tests/my_game/example/test_simple_table_with_enum.rb b/tests/my_game/example/test_simple_table_with_enum.rb new file mode 100644 index 00000000000..aea020b3d9b --- /dev/null +++ b/tests/my_game/example/test_simple_table_with_enum.rb @@ -0,0 +1,23 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" +require_relative "color" + +module MyGame + module Example + class TestSimpleTableWithEnum < ::FlatBuffers::Table + def color + field_offset = @view.unpack_virtual_offset(4) + if field_offset.zero? + enum_value = 2 + else + enum_value = @view.unpack_ubyte(field_offset) + end + Color.try_convert(enum_value) || enum_value + end + end + end +end diff --git a/tests/my_game/example/type_aliases.rb b/tests/my_game/example/type_aliases.rb new file mode 100644 index 00000000000..2950a172ab2 --- /dev/null +++ b/tests/my_game/example/type_aliases.rb @@ -0,0 +1,100 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" + +module MyGame + module Example + class TypeAliases < ::FlatBuffers::Table + def i_8 + field_offset = @view.unpack_virtual_offset(4) + return 0 if field_offset.zero? + + @view.unpack_byte(field_offset) + end + + def u_8 + field_offset = @view.unpack_virtual_offset(6) + return 0 if field_offset.zero? + + @view.unpack_ubyte(field_offset) + end + + def i_16 + field_offset = @view.unpack_virtual_offset(8) + return 0 if field_offset.zero? + + @view.unpack_short(field_offset) + end + + def u_16 + field_offset = @view.unpack_virtual_offset(10) + return 0 if field_offset.zero? + + @view.unpack_ushort(field_offset) + end + + def i_32 + field_offset = @view.unpack_virtual_offset(12) + return 0 if field_offset.zero? + + @view.unpack_int(field_offset) + end + + def u_32 + field_offset = @view.unpack_virtual_offset(14) + return 0 if field_offset.zero? + + @view.unpack_uint(field_offset) + end + + def i_64 + field_offset = @view.unpack_virtual_offset(16) + return 0 if field_offset.zero? + + @view.unpack_long(field_offset) + end + + def u_64 + field_offset = @view.unpack_virtual_offset(18) + return 0 if field_offset.zero? + + @view.unpack_ulong(field_offset) + end + + def f_32 + field_offset = @view.unpack_virtual_offset(20) + return 0.0 if field_offset.zero? + + @view.unpack_float(field_offset) + end + + def f_64 + field_offset = @view.unpack_virtual_offset(22) + return 0.0 if field_offset.zero? + + @view.unpack_double(field_offset) + end + + def v_8 + field_offset = @view.unpack_virtual_offset(24) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 1) do |element_offset| + @view.unpack_byte(element_offset) + end + end + + def vf_64 + field_offset = @view.unpack_virtual_offset(26) + return nil if field_offset.zero? + + @view.unpack_vector(field_offset, 8) do |element_offset| + @view.unpack_double(element_offset) + end + end + end + end +end diff --git a/tests/my_game/example/vec_3.rb b/tests/my_game/example/vec_3.rb new file mode 100644 index 00000000000..ca4e3f2606f --- /dev/null +++ b/tests/my_game/example/vec_3.rb @@ -0,0 +1,45 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" +require_relative "color" +require_relative "test" + +module MyGame + module Example + class Vec3 < ::FlatBuffers::Struct + def x + field_offset = 0 + @view.unpack_float(field_offset) + end + + def y + field_offset = 4 + @view.unpack_float(field_offset) + end + + def z + field_offset = 8 + @view.unpack_float(field_offset) + end + + def test_1 + field_offset = 16 + @view.unpack_double(field_offset) + end + + def test_2 + field_offset = 24 + enum_value = @view.unpack_ubyte(field_offset) + Color.try_convert(enum_value) || enum_value + end + + def test_3 + field_offset = 26 + @view.unpack_struct(Test, field_offset) + end + end + end +end diff --git a/tests/my_game/example_2/monster.rb b/tests/my_game/example_2/monster.rb new file mode 100644 index 00000000000..8d87dcd6531 --- /dev/null +++ b/tests/my_game/example_2/monster.rb @@ -0,0 +1,13 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" + +module MyGame + module Example2 + class Monster < ::FlatBuffers::Table + end + end +end diff --git a/tests/my_game/in_parent_namespace.rb b/tests/my_game/in_parent_namespace.rb new file mode 100644 index 00000000000..679132d0f1a --- /dev/null +++ b/tests/my_game/in_parent_namespace.rb @@ -0,0 +1,11 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" + +module MyGame + class InParentNamespace < ::FlatBuffers::Table + end +end diff --git a/tests/my_game/other_name_space/from_include.rb b/tests/my_game/other_name_space/from_include.rb new file mode 100644 index 00000000000..c6f46b2c8fa --- /dev/null +++ b/tests/my_game/other_name_space/from_include.rb @@ -0,0 +1,14 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" + +module MyGame + module OtherNameSpace + class FromInclude < ::FlatBuffers::Enum + INCLUDE_VAL = register("IncludeVal", 0) + end + end +end diff --git a/tests/my_game/other_name_space/table_b.rb b/tests/my_game/other_name_space/table_b.rb new file mode 100644 index 00000000000..f12805f6b22 --- /dev/null +++ b/tests/my_game/other_name_space/table_b.rb @@ -0,0 +1,20 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" +require_relative "../../table_a" + +module MyGame + module OtherNameSpace + class TableB < ::FlatBuffers::Table + def a + field_offset = @view.unpack_virtual_offset(4) + return nil if field_offset.zero? + + @view.unpack_table(::TableA, field_offset) + end + end + end +end diff --git a/tests/my_game/other_name_space/unused.rb b/tests/my_game/other_name_space/unused.rb new file mode 100644 index 00000000000..9fd0d997f19 --- /dev/null +++ b/tests/my_game/other_name_space/unused.rb @@ -0,0 +1,17 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" + +module MyGame + module OtherNameSpace + class Unused < ::FlatBuffers::Struct + def a + field_offset = 0 + @view.unpack_int(field_offset) + end + end + end +end diff --git a/tests/optional_scalars/optional_byte.rb b/tests/optional_scalars/optional_byte.rb new file mode 100644 index 00000000000..8361b5cd083 --- /dev/null +++ b/tests/optional_scalars/optional_byte.rb @@ -0,0 +1,14 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: optional_scalars.ScalarStuff () + +require "flatbuffers" + +module OptionalScalars + class OptionalByte < ::FlatBuffers::Enum + NONE = register("None", 0) + ONE = register("One", 1) + TWO = register("Two", 2) + end +end diff --git a/tests/optional_scalars/scalar_stuff.rb b/tests/optional_scalars/scalar_stuff.rb new file mode 100644 index 00000000000..4c88840c98f --- /dev/null +++ b/tests/optional_scalars/scalar_stuff.rb @@ -0,0 +1,272 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: optional_scalars.ScalarStuff () + +require "flatbuffers" +require_relative "optional_byte" + +module OptionalScalars + class ScalarStuff < ::FlatBuffers::Table + def just_i_8 + field_offset = @view.unpack_virtual_offset(4) + return 0 if field_offset.zero? + + @view.unpack_byte(field_offset) + end + + def maybe_i_8 + field_offset = @view.unpack_virtual_offset(6) + return 0 if field_offset.zero? + + @view.unpack_byte(field_offset) + end + + def default_i_8 + field_offset = @view.unpack_virtual_offset(8) + return 42 if field_offset.zero? + + @view.unpack_byte(field_offset) + end + + def just_u_8 + field_offset = @view.unpack_virtual_offset(10) + return 0 if field_offset.zero? + + @view.unpack_ubyte(field_offset) + end + + def maybe_u_8 + field_offset = @view.unpack_virtual_offset(12) + return 0 if field_offset.zero? + + @view.unpack_ubyte(field_offset) + end + + def default_u_8 + field_offset = @view.unpack_virtual_offset(14) + return 42 if field_offset.zero? + + @view.unpack_ubyte(field_offset) + end + + def just_i_16 + field_offset = @view.unpack_virtual_offset(16) + return 0 if field_offset.zero? + + @view.unpack_short(field_offset) + end + + def maybe_i_16 + field_offset = @view.unpack_virtual_offset(18) + return 0 if field_offset.zero? + + @view.unpack_short(field_offset) + end + + def default_i_16 + field_offset = @view.unpack_virtual_offset(20) + return 42 if field_offset.zero? + + @view.unpack_short(field_offset) + end + + def just_u_16 + field_offset = @view.unpack_virtual_offset(22) + return 0 if field_offset.zero? + + @view.unpack_ushort(field_offset) + end + + def maybe_u_16 + field_offset = @view.unpack_virtual_offset(24) + return 0 if field_offset.zero? + + @view.unpack_ushort(field_offset) + end + + def default_u_16 + field_offset = @view.unpack_virtual_offset(26) + return 42 if field_offset.zero? + + @view.unpack_ushort(field_offset) + end + + def just_i_32 + field_offset = @view.unpack_virtual_offset(28) + return 0 if field_offset.zero? + + @view.unpack_int(field_offset) + end + + def maybe_i_32 + field_offset = @view.unpack_virtual_offset(30) + return 0 if field_offset.zero? + + @view.unpack_int(field_offset) + end + + def default_i_32 + field_offset = @view.unpack_virtual_offset(32) + return 42 if field_offset.zero? + + @view.unpack_int(field_offset) + end + + def just_u_32 + field_offset = @view.unpack_virtual_offset(34) + return 0 if field_offset.zero? + + @view.unpack_uint(field_offset) + end + + def maybe_u_32 + field_offset = @view.unpack_virtual_offset(36) + return 0 if field_offset.zero? + + @view.unpack_uint(field_offset) + end + + def default_u_32 + field_offset = @view.unpack_virtual_offset(38) + return 42 if field_offset.zero? + + @view.unpack_uint(field_offset) + end + + def just_i_64 + field_offset = @view.unpack_virtual_offset(40) + return 0 if field_offset.zero? + + @view.unpack_long(field_offset) + end + + def maybe_i_64 + field_offset = @view.unpack_virtual_offset(42) + return 0 if field_offset.zero? + + @view.unpack_long(field_offset) + end + + def default_i_64 + field_offset = @view.unpack_virtual_offset(44) + return 42 if field_offset.zero? + + @view.unpack_long(field_offset) + end + + def just_u_64 + field_offset = @view.unpack_virtual_offset(46) + return 0 if field_offset.zero? + + @view.unpack_ulong(field_offset) + end + + def maybe_u_64 + field_offset = @view.unpack_virtual_offset(48) + return 0 if field_offset.zero? + + @view.unpack_ulong(field_offset) + end + + def default_u_64 + field_offset = @view.unpack_virtual_offset(50) + return 42 if field_offset.zero? + + @view.unpack_ulong(field_offset) + end + + def just_f_32 + field_offset = @view.unpack_virtual_offset(52) + return 0.0 if field_offset.zero? + + @view.unpack_float(field_offset) + end + + def maybe_f_32 + field_offset = @view.unpack_virtual_offset(54) + return 0.0 if field_offset.zero? + + @view.unpack_float(field_offset) + end + + def default_f_32 + field_offset = @view.unpack_virtual_offset(56) + return 42.0 if field_offset.zero? + + @view.unpack_float(field_offset) + end + + def just_f_64 + field_offset = @view.unpack_virtual_offset(58) + return 0.0 if field_offset.zero? + + @view.unpack_double(field_offset) + end + + def maybe_f_64 + field_offset = @view.unpack_virtual_offset(60) + return 0.0 if field_offset.zero? + + @view.unpack_double(field_offset) + end + + def default_f_64 + field_offset = @view.unpack_virtual_offset(62) + return 42.0 if field_offset.zero? + + @view.unpack_double(field_offset) + end + + def just_bool? + field_offset = @view.unpack_virtual_offset(64) + return false if field_offset.zero? + + @view.unpack_bool(field_offset) + end + + def maybe_bool? + field_offset = @view.unpack_virtual_offset(66) + return false if field_offset.zero? + + @view.unpack_bool(field_offset) + end + + def default_bool? + field_offset = @view.unpack_virtual_offset(68) + return true if field_offset.zero? + + @view.unpack_bool(field_offset) + end + + def just_enum + field_offset = @view.unpack_virtual_offset(70) + if field_offset.zero? + enum_value = 0 + else + enum_value = @view.unpack_byte(field_offset) + end + OptionalByte.try_convert(enum_value) || enum_value + end + + def maybe_enum + field_offset = @view.unpack_virtual_offset(72) + if field_offset.zero? + enum_value = 0 + else + enum_value = @view.unpack_byte(field_offset) + end + OptionalByte.try_convert(enum_value) || enum_value + end + + def default_enum + field_offset = @view.unpack_virtual_offset(74) + if field_offset.zero? + enum_value = 1 + else + enum_value = @view.unpack_byte(field_offset) + end + OptionalByte.try_convert(enum_value) || enum_value + end + end +end diff --git a/tests/property.rb b/tests/property.rb new file mode 100644 index 00000000000..01c67867d46 --- /dev/null +++ b/tests/property.rb @@ -0,0 +1,12 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 + +require "flatbuffers" + +class Property < ::FlatBuffers::Struct + def property? + field_offset = 0 + @view.unpack_bool(field_offset) + end +end diff --git a/tests/ruby/test_monster.rb b/tests/ruby/test_monster.rb new file mode 100644 index 00000000000..3dc999b88ac --- /dev/null +++ b/tests/ruby/test_monster.rb @@ -0,0 +1,354 @@ +# Copyright 2025 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require "my_game/example/monster" + +class TestMonster < Test::Unit::TestCase + def setup + data_mon = File.join(__dir__, "..", "monsterdata_test.mon") + data = File.open(data_mon, "rb", &:read) + @monster = MyGame::Example::Monster.new(data) + end + + def test_pos + assert_equal(1.0, @monster.pos.x) + end + + def test_hp + assert_equal(80, @monster.hp) + end + + def test_mana + assert_equal(150, @monster.mana) + end + + def test_name + assert_equal("MyMonster", @monster.name) + end + + def test_color + assert_equal("Blue", @monster.color.name) + end + + def test_inventory + assert_equal([0, 1, 2, 3, 4], @monster.inventory) + end + + def test_friendly? + # Deprecated field isn't generated. + assert do + not @monster.respond_to?(:friendly?) + end + end + + def test_test_array_of_tables + assert_nil(@monster.testarrayoftables) + end + + def test_test_array_of_strings + assert_equal(["test1", "test2"], @monster.testarrayofstring) + end + + def test_test_array_of_strings2 + assert_nil(@monster.testarrayofstring_2) + end + + def test_test_array_of_bools + assert_equal([true, false, true], @monster.testarrayofbools) + end + + def test_test_array_of_sorted_structs + assert_equal([45, 21, 12], + @monster.testarrayofsortedstruct.collect(&:distance)) + end + + def test_enemy + assert_equal("Fred", @monster.enemy.name) + end + + def test_test_type + assert_equal(MyGame::Example::Any::MONSTER, @monster.test_type) + end + + def test_test + assert_equal("Fred", @monster.test.name) + end + + def test_test_4 + assert_equal([ + {a: 10, b: 20}, + {a: 30, b: 40}, + ], + @monster.test_4.collect {|test| {a: test.a, b: test.b}}) + end + + def test_test_5 + assert_equal([ + {a: 10, b: 20}, + {a: 30, b: 40}, + ], + @monster.test_5.collect {|test| {a: test.a, b: test.b}}) + end + + def test_test_nested_flatbuffer + assert_nil(@monster.testnestedflatbuffer) + end + + def test_test_empty + assert_nil(@monster.testempty) + end + + def test_test_bool? + assert do + @monster.testbool? + end + end + + def test_test_hash_s32_fnv1 + assert_equal(-579221183, @monster.testhashs_32_fnv_1) + end + + def test_test_hash_u32_fnv1 + assert_equal(3715746113, @monster.testhashu_32_fnv_1) + end + + def test_test_hash_s64_fnv1 + assert_equal(7930699090847568257, @monster.testhashs_64_fnv_1) + end + + def test_test_hash_u64_fnv1 + assert_equal(7930699090847568257, @monster.testhashu_64_fnv_1) + end + + def test_test_hash_s32_fnv1a + assert_equal(-1904106383, @monster.testhashs_32_fnv_1a) + end + + def test_test_hash_u32_fnv1a + assert_equal(2390860913, @monster.testhashu_32_fnv_1a) + end + + def test_test_hash_s64_fnv1a + assert_equal(4898026182817603057, @monster.testhashs_64_fnv_1a) + end + + def test_test_hash_u64_fnv1a + assert_equal(4898026182817603057, @monster.testhashu_64_fnv_1a) + end + + def test_test_f + assert_equal(3.14159, @monster.testf) + end + + def test_test_f2 + assert_equal(3.0, @monster.testf_2) + end + + def test_test_f3 + assert_equal(0.0, @monster.testf_3) + end + + def test_flex + assert_nil(@monster.flex) + end + + def test_vector_of_longs + assert_equal([1, 100, 10000, 1000000, 100000000], + @monster.vector_of_longs) + end + + def test_vector_of_double + assert_equal([-1.7976931348623157e+308, 0.0, 1.7976931348623157e+308], + @monster.vector_of_doubles) + end + + def test_parent_namespace_test + assert_nil(@monster.parent_namespace_test) + end + + def test_vector_of_referrables + assert_nil(@monster.vector_of_referrables) + end + + def test_single_weak_reference + assert_equal(0, @monster.single_weak_reference) + end + + def test_vector_of_weak_references + assert_nil(@monster.vector_of_weak_references) + end + + def test_vector_of_strong_referrables + assert_nil(@monster.vector_of_strong_referrables) + end + + def test_co_owning_reference + assert_equal(0, @monster.co_owning_reference) + end + + def test_vector_of_co_owning_references + assert_nil(@monster.vector_of_co_owning_references) + end + + def test_non_owning_reference + assert_equal(0, @monster.non_owning_reference) + end + + def test_vector_of_non_owning_references + assert_nil(@monster.vector_of_non_owning_references) + end + + def test_any_unique + assert_nil(@monster.any_unique) + end + + def test_any_ambiguous + assert_nil(@monster.any_ambiguous) + end + + def test_vector_of_enums + assert_nil(@monster.vector_of_enums) + end + + def test_signed_enum + assert_equal(MyGame::Example::Race::NONE, + @monster.signed_enum) + end + + def test_test_required_nested_flatbuffer + assert_nil(@monster.testrequirednestedflatbuffer) + end + + def test_scalar_key_sorted_tables + stats = @monster.scalar_key_sorted_tables + assert_equal([[0, "miss"], [10, "hit"]], + stats.collect {|stat| [stat.val, stat.id]}) + end + + def test_native_inline + test = @monster.native_inline + assert_equal([1, 2], + [test.a, test.b]) + end + + def test_long_enum_non_enum_default + assert_equal(0, @monster.long_enum_non_enum_default) + end + + def test_long_enum_normal_default + assert_equal(MyGame::Example::LongEnum::LONG_ONE, + @monster.long_enum_normal_default) + end + + def test_nan_default + assert do + @monster.nan_default.nan? + end + end + + def test_inf_default + value = @monster.inf_default + assert do + value.positive? and value.infinite? + end + end + + def test_positive_inf_default + value = @monster.positive_inf_default + assert do + value.positive? and value.infinite? + end + end + + def test_infinity_default + value = @monster.infinity_default + assert do + value.positive? and value.infinite? + end + end + + def test_positive_infinity_default + value = @monster.positive_infinity_default + assert do + value.positive? and value.infinite? + end + end + + def test_negative_inf_default + value = @monster.negative_inf_default + assert do + !value.positive? and value.infinite? + end + end + + def test_negative_infinity_default + value = @monster.negative_infinity_default + assert do + !value.positive? and value.infinite? + end + end + + def test_double_inf_default + value = @monster.double_inf_default + assert do + value.positive? and value.infinite? + end + end + + class TestVec3 < self + def setup + super + @vec3 = @monster.pos + end + + def test_x + assert_equal(1.0, @vec3.x) + end + + def test_y + assert_equal(2.0, @vec3.y) + end + + def test_z + assert_equal(3.0, @vec3.z) + end + + def test_test_1 + assert_equal(3.0, @vec3.test_1) + end + + def test_test_2 + assert_equal("Green", @vec3.test_2.name) + end + + def test_test_3 + assert_equal(5, @vec3.test_3.a) + end + end + + class TestTest < self + def setup + super + @test = @monster.pos.test_3 + end + + def test_a + assert_equal(5, @test.a) + end + + def test_b + assert_equal(6, @test.b) + end + end +end diff --git a/tests/ruby/test_reflection.rb b/tests/ruby/test_reflection.rb new file mode 100644 index 00000000000..5796f7b917b --- /dev/null +++ b/tests/ruby/test_reflection.rb @@ -0,0 +1,388 @@ +# Copyright 2025 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require "reflection/schema" + +class TestReflection < Test::Unit::TestCase + def setup + reflection_bfbs = File.join(Helper::Path.reflection_dir, "reflection.bfbs") + data = File.open(reflection_bfbs, "rb", &:read) + @schema = Reflection::Schema.new(data) + end + + class TestSchema < self + def test_objects + assert_equal([ + "reflection.Enum", + "reflection.EnumVal", + "reflection.Field", + "reflection.KeyValue", + "reflection.Object", + "reflection.RPCCall", + "reflection.Schema", + "reflection.SchemaFile", + "reflection.Service", + "reflection.Type", + ], + @schema.objects.collect(&:name)) + end + + def test_enums + assert_equal([ + "reflection.AdvancedFeatures", + "reflection.BaseType", + ], + @schema.enums.collect(&:name)) + end + + def test_file_ident + assert_equal("BFBS", @schema.file_ident) + end + + def test_file_ext + assert_equal("bfbs", @schema.file_ext) + end + + class TestRootTable < self + def setup + super + @root_table = @schema.root_table + end + + def test_name + assert_equal("reflection.Schema", @root_table.name) + end + end + + def test_services + assert_nil(@schema.services) + end + + def test_advanced_features + assert_equal(0, + @schema.advanced_features) + end + + def test_fbs_files + assert_equal(["//reflection.fbs"], + @schema.fbs_files.collect(&:filename)) + end + end + + class TestObject < self + def setup + super + @object = @schema.objects[0] + end + + def test_name + assert_equal("reflection.Enum", @object.name) + end + + def test_fields + assert_equal([ + "attributes", + "declaration_file", + "documentation", + "is_union", + "name", + "underlying_type", + "values", + ], + @object.fields.collect(&:name)) + end + + def test_struct? + assert do + not @object.struct? + end + end + + def test_minalign + assert_equal(1, @object.minalign) + end + + def test_bytesize + assert_equal(0, @object.bytesize) + end + + def test_attributes + assert_nil(@object.attributes) + end + + def test_documentation + assert_nil(@object.documentation) + end + + def test_declaration_file + assert_equal("//reflection.fbs", @object.declaration_file) + end + end + + class TestField < self + def setup + super + @field = @schema.objects[0].fields[0] + end + + def test_name + assert_equal("attributes", @field.name) + end + + def test_type + assert_equal(Reflection::BaseType::VECTOR, + @field.type.base_type) + end + + def test_id + assert_equal(4, @field.id) + end + + def test_offset + assert_equal(12, @field.offset) + end + + def test_default_integer + assert_equal(0, @field.default_integer) + end + + def test_default_real + assert_equal(0.0, @field.default_real) + end + + def test_deprecated? + assert do + not @field.deprecated? + end + end + + def test_required? + assert do + not @field.required? + end + end + + def test_key? + assert do + not @field.key? + end + end + + def test_attributes + assert_nil(@field.attributes) + end + + def test_documentation + assert_nil(@field.documentation) + end + + def test_optional? + assert do + @field.optional? + end + end + + def test_padding + assert_equal(0, @field.padding) + end + + def test_offset_64? + assert do + not @field.offset_64? + end + end + end + + class TestType < self + def setup + super + @type = @schema.objects[0].fields[0].type + end + + def test_base_type + assert_equal(Reflection::BaseType::VECTOR, + @type.base_type) + end + + def test_element + assert_equal(Reflection::BaseType::OBJ, + @type.element) + end + + def test_index + assert_equal(3, @type.index) + end + + def test_fixed_length + assert_equal(0, @type.fixed_length) + end + + def test_base_size + assert_equal(4, @type.base_size) + end + + def test_element_size + assert_equal(4, @type.element_size) + end + end + + class TestBaseType < self + def setup + super + @base_type = @schema.objects[0].fields[0].type.base_type + end + + def test_name + assert_equal("Vector", @base_type.name) + end + + def test_value + assert_equal(14, @base_type.value) + end + + def test_to_i + assert_equal(14, @base_type.to_i) + end + + def test_to_int + assert_equal(14, Integer.try_convert(@base_type)) + end + + class TestTryConvert < self + def test_symbol + assert_equal(Reflection::BaseType::VECTOR, + Reflection::BaseType.try_convert(:Vector)) + end + + def test_string + assert_equal(Reflection::BaseType::VECTOR, + Reflection::BaseType.try_convert("Vector")) + end + + def test_integer + assert_equal(Reflection::BaseType::VECTOR, + Reflection::BaseType.try_convert(14)) + end + + def test_base_type + base_type = Reflection::BaseType::VECTOR + assert_equal(base_type, + Reflection::BaseType.try_convert(base_type)) + end + end + end + + class TestEnum < self + def setup + super + @enum = @schema.enums[0] + end + + def test_name + assert_equal("reflection.AdvancedFeatures", @enum.name) + end + + def test_values + assert_equal([ + "AdvancedArrayFeatures", + "AdvancedUnionFeatures", + "OptionalScalars", + "DefaultVectorsAndStrings", + ], + @enum.values.collect(&:name)) + end + + def test_union? + assert do + not @enum.union? + end + end + + def test_underlying_type + assert_equal(Reflection::BaseType::ULONG, + @enum.underlying_type.base_type) + end + + def test_attributes + assert_equal(["bit_flags"], + @enum.attributes.collect(&:key)) + end + + def test_documentation + assert_nil(@enum.documentation) + end + + def test_declaration_file + assert_equal("//reflection.fbs", @enum.declaration_file) + end + end + + class TestKeyValue < self + def setup + super + @key_value = @schema.enums[0].attributes[0] + end + + def test_key + assert_equal("bit_flags", @key_value.key) + end + + def test_value + assert_equal("0", @key_value.value) + end + end + + class TestEnumVal < self + def setup + super + @enum_val = @schema.enums[0].values[0] + end + + def test_name + assert_equal("AdvancedArrayFeatures", @enum_val.name) + end + + def test_value + assert_equal(1, @enum_val.value) + end + + def test_union_type + assert_equal(Reflection::BaseType::NONE, + @enum_val.union_type.base_type) + end + + def test_documentation + assert_nil(@enum_val.documentation) + end + + def test_attributes + assert_nil(@enum_val.attributes) + end + end + + class TestSchemaFile < self + def setup + super + @schema_file = @schema.fbs_files[0] + end + + def test_filename + assert_equal("//reflection.fbs", @schema_file.filename) + end + + def test_included_filenames + assert_nil(@schema_file.included_filenames) + end + end +end diff --git a/tests/table_a.rb b/tests/table_a.rb new file mode 100644 index 00000000000..75c2f6aef72 --- /dev/null +++ b/tests/table_a.rb @@ -0,0 +1,16 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 +# Rooting type: MyGame.Example.Monster () + +require "flatbuffers" +require_relative "my_game/other_name_space/table_b" + +class TableA < ::FlatBuffers::Table + def b + field_offset = @view.unpack_virtual_offset(4) + return nil if field_offset.zero? + + @view.unpack_table(MyGame::OtherNameSpace::TableB, field_offset) + end +end diff --git a/tests/test_mutating_bool.rb b/tests/test_mutating_bool.rb new file mode 100644 index 00000000000..63be61c7320 --- /dev/null +++ b/tests/test_mutating_bool.rb @@ -0,0 +1,15 @@ +# Automatically generated by the FlatBuffers compiler, do not modify. +# +# flatc version: 25.9.23 + +require "flatbuffers" +require_relative "property" + +class TestMutatingBool < ::FlatBuffers::Table + def b + field_offset = @view.unpack_virtual_offset(4) + return nil if field_offset.zero? + + @view.unpack_struct(Property, field_offset) + end +end