diff --git a/bindings/libdnf5/advisory.i b/bindings/libdnf5/advisory.i index 7a74005e1..b867b22b9 100644 --- a/bindings/libdnf5/advisory.i +++ b/bindings/libdnf5/advisory.i @@ -6,6 +6,10 @@ %module "libdnf5/advisory" #endif +#if defined(SWIGRUBY) +%mixin libdnf5::advisory::AdvisorySet "Enumerable"; +#endif + %include %include @@ -57,3 +61,8 @@ %template(VectorAdvisoryReference) std::vector; add_iterator(AdvisorySet) + +#if defined(SWIGRUBY) +fix_swigtype_trait(libdnf5::advisory::Advisory) +#endif +add_ruby_each(libdnf5::advisory::AdvisorySet) diff --git a/bindings/libdnf5/common.i b/bindings/libdnf5/common.i index 0fd8c5875..53a42d902 100644 --- a/bindings/libdnf5/common.i +++ b/bindings/libdnf5/common.i @@ -134,6 +134,42 @@ del ClassName##__iter__ #endif %enddef +%define fix_swigtype_trait(ClassName) +%traits_swigtype(ClassName) +%fragment(SWIG_Traits_frag(ClassName)); +%enddef + +%define add_ruby_each(ClassName) +#if defined(SWIGRUBY) +/* Using swig::from method on a class instance (= $self in %extend blocks below) fails. + * We need to define these traits so that it compiles. This seems to be an issue related + * to namespacing: + * https://github.com/swig/swig/issues/973 + * and to using a pointer in some places: + * https://github.com/swig/swig/issues/2938 + */ +fix_swigtype_trait(ClassName) + +%extend ClassName { + VALUE each() { + // If no block is provided, returns Enumerator. + RETURN_ENUMERATOR(swig::from($self), 0, 0); + + VALUE r; + auto i = self->begin(); + auto e = self->end(); + + for (; i != e; ++i) { + r = swig::from(*i); + rb_yield(r); + } + + return swig::from($self); + } +} +#endif +%enddef + %define add_str(ClassName) #if defined(SWIGPYTHON) %extend ClassName { diff --git a/bindings/libdnf5/rpm.i b/bindings/libdnf5/rpm.i index 3fe5da429..80b7a66c0 100644 --- a/bindings/libdnf5/rpm.i +++ b/bindings/libdnf5/rpm.i @@ -6,6 +6,13 @@ %module "libdnf5/rpm" #endif +#if defined(SWIGRUBY) +// Mixin modules for Ruby. This has to be declared before inclusion of the +// related header file. +%mixin libdnf5::rpm::PackageSet "Enumerable"; +%mixin libdnf5::rpm::ReldepList "Enumerable"; +#endif + %include %include %include @@ -99,6 +106,13 @@ add_hash(libdnf5::rpm::Package) add_iterator(PackageSet) add_iterator(ReldepList) +add_ruby_each(libdnf5::rpm::PackageSet) +// Reldep needs special treatment so that the add_ruby_each can use it. +#if defined(SWIGRUBY) +fix_swigtype_trait(libdnf5::rpm::Reldep) +#endif +add_ruby_each(libdnf5::rpm::ReldepList) + %feature("director") TransactionCallbacks; %include "libdnf5/rpm/transaction_callbacks.hpp" wrap_unique_ptr(TransactionCallbacksUniquePtr, libdnf5::rpm::TransactionCallbacks); diff --git a/test/ruby/libdnf5/rpm/test_package_query.rb b/test/ruby/libdnf5/rpm/test_package_query.rb index ec219ce41..dfaaf3015 100644 --- a/test/ruby/libdnf5/rpm/test_package_query.rb +++ b/test/ruby/libdnf5/rpm/test_package_query.rb @@ -38,7 +38,6 @@ def test_filter_name() query.filter_name(["pkg"]) assert_equal(1, query.size()) - # TODO(dmach): implement each() so the query can be easily iterated or converted to an array actual = [] it = query.begin() while it != query.end() @@ -55,7 +54,6 @@ def test_filter_name() query.filter_name(["pk*"], Common::QueryCmp_GLOB) assert_equal(2, query.size()) - # TODO(dmach): implement each() so the query can be easily iterated or converted to an array actual = [] it = query.begin() while it != query.end() @@ -65,4 +63,32 @@ def test_filter_name() assert_equal(["pkg-1.2-3.x86_64", "pkg-libs-1:1.3-4.x86_64"], actual) end + + def test_implements_enumerable() + query = Rpm::PackageQuery.new(@base) + query.filter_name(["pkg"]) + assert_equal(1, query.size()) + + # Using each() without a block should return Enumerator. + assert_instance_of(Enumerator, query.each) + + # Using each() with a block should return the collection. + assert_instance_of(Rpm::PackageSet, query.each(&:get_name)) + + actual_nevra = query.map { |pkg| pkg.get_nevra } + + assert_equal(["pkg-1.2-3.x86_64"], actual_nevra) + + # --- + + query = Rpm::PackageQuery.new(@base) + query.filter_name(["pk*"], Common::QueryCmp_GLOB) + assert_equal(2, query.size()) + + # Test other method than each that comes with Enumerable + actual = query.select { |pkg| pkg.get_name == "pkg-libs" } + + assert_equal(1, actual.size) + assert_equal('pkg-libs-1:1.3-4.x86_64', actual.first.get_nevra) + end end