How to embed gems in my plugin?

I need to use Protocol Buffer ruby. How can I embed it into my plugin?

I have used rubyzip gem as below:

dirname = File.dirname(__FILE__)
$: << dirname + '/lib/rubyzip'
require 'zip'

But the method does not work for protobuf lib/google. It requires external packages. What is the proper approach to require gems in a plugin?

Typically, include all of the Gem’s ruby files within your extension’s folder, edit all files to wrap the content in your namespace module, and update any require paths to reference these new locations.

This makes the RBZ slightly bigger, but the user can install the extension from a single RBZ file and fully uninstall it using SketchUp’s own UI.

There are some Gems that requires local compiling and can’t be used this way, but for pure Ruby gems it should work.

1 Like

Just FYI, the above is not embedding the Zip library within your extension.
It is merely distributing the gem with your extension.

This does not ensure that you are not overloading a newer load of the Zip library. (Ie, you don’t check first if it’s loaded or what version it is if so.)

You might just as well do …

begin
  require "zip"
rescue
  Gem.install("rubyzip")
  require "zip"
end

… and just use it from the user’s ENV['GEMHOME'] folder rather than your extension subfolder.

If choosing this approach, you also should provide some sort of user feedback explaining the delay when the files are fetched and installed. When trying this approach myself some years back this could take well over a minute but I don’t know if that is still the case.

1 Like

Gems bundled with an extension that isn’t wrapped into the extension namespace ends up being rejected on Extension Warehouse. All extensions are expected to isolate themselves. Multiple extensions trying to install the same gem might have different version requirement and thus clash. Installing gems is as Christina mentioned a poor user experience given how it blocks the main thread for a significant time.

Treat gems like any other code in your extension; bundle it within your extension namespace. Only way to ensure it doesn’t clash with other extensions and provides the best user experience.

1 Like

The gem you would like to use (google-protobuf) is often referred to as an ‘extension gem’. @ene_su mentioned them above as a gem that ‘requires local compiling’.

An easy way to determine if they are extension gems is to look at the gem’s ‘all versions’ page at rubygems.org and see if they distribute gems with descriptions like ‘x64-mingw32’ or ‘universal-darwin’. If not, one has to look at the git repository and check for ‘non-ruby’ code, typically this would be c, c++, and/or java code, depending on what’s supported.

Anyway, these gems are very difficult to embed in a SU extension/plugin, especially one that can be used in Windows and macOS. Also, Windows issues with SU using MSYS2 mingw Ruby in previous versions, and using MSVC/mswin Ruby in more recent ones.

It can be done, but it requires knowledge of how to build extension gems, and they can only be built with external stand-alone Ruby versions…

4 Likes