Creating Ruby gems with same name executables? - ruby

I want to have a Ruby Gem that will have the same executable as another Gem.
When called with command args it will either do something, or pass the command on to the other Gem.
The first problem I have is that it isn't able to run two same named executables. I get this error:
Bundler is using a binstub that was created for a different gem. This is deprecated, in future versions you may need to bundle binstub yourgem to work around a system/bundle conflict.
How can I have Gems with the same named executables and ensure that the target one executes?

You cannot rely on Bundler or Rubygems to manage this for you. All it does it copy an executable that you specified in your gem spec to its bin/ directory.
The first problem you'll have is that the executable that runs may be dependent on the order in which the gems were installed which you can't guarantee.
Another problem that you'll have is that you cannot execute code on gem installation so you will be unable to run code that would try to automate this set up for people who install your gem.
I believe your gem should provide a non-conflicting executable. You can then supply post install instructions in your gem spec that are displayed to a user installing the gem, in the README, in a blog post, etc. you can tell the user that they need to set up an alias that points to your executable. In all shells that I'm aware of aliases will be executed before filesystem executables.
For the times when people want to bypass your alias and execute the original executable you can tell people to escape the command, e.g. \original-gem. That bypasses alias and function lookup in most shells and will allow users to have your super awesome version as the default (thru the alias) and a way to easily access the original.

Related

How do I get rrdtool from homebrew to work with ruby on macOS

In our Rails application we do require 'RRD' at some point, but that results in a cannot load such file -- RRD. So obviously I used homebrew to install rrdtool, but the error remains.
The docs at https://oss.oetiker.ch/rrdtool/prog/rrdruby.en.html provide two options:
Either:
$: << '/path/to/rrdtool/lib/ruby/1.8/i386-linux'
require "RRD"
In my /opt/homebrew/Cellar/rrdtool/1.8.0/lib directory there's no mention of ruby, which is because of the --disable-ruby-site-install flag in the formula, because when I skip that flag I do actually get something: /opt/homebrew/Cellar/rrdtool/1.8.0/lib/ruby/2.6.0/universal-darwin21. However replacing the path/to string with this path still gives the error.
Or:
If you use the --ruby-site-install configure option you can drop the $: line since the RRDtool module will be found automatically.
Which is a little confusing (and probably outdated) because here it seems that ruby site install is disabled by default and you have to enable it proactively, whereas in the formula it's actually actively disabled.
Either way: both options didn't do the trick for me and if there's a solution without homebrew that's also fine.
For good measure: I'm on macOS Monterey
TL;DR
For the most part, I'd say that using a non-standard gem without a Ruby version manager is your main issue. There are instructions on the rrdruby site for installing it, but they don't follow typical conventions, so your mileage will vary.
Some Practical Suggestions
The require keyword is for gems, not binaries. You need to have an rrdtool-related gem installed, available to your Ruby instance (usually through a Bundler Gemfile or gemspec, or via the RUBYOPTS environment variable or your in-process Ruby $LOAD_PATH), and then require the correct name of the gem in your code. For example, using the older rrd-ffi gem:
# use sudo if you're installing it to the system,
# but I would strongly recommend a ruby version
# manager instead
gem install rrd-ffi
# in your Ruby class/module file
require "rrd"
For the gem you seem to be using, you have to compile the gem first to make it usable, and then ensure it's available in your Ruby $LOAD_PATH (or other gem lookup mechanism) before trying to require it. The error message you're seeing is basically telling you that a gem with that name is not available as called within any of the standard lookup locations.
Again, I'd suggest reading the build documentation for your gem, and then seeing if you can install it as part of a Bundler bundle, RVM gemset, or other non-system approach if you can. Otherwise, follow the directions for the rrdruby tool, which is not available as a standard Rubygems.org gem, in order to make it available before trying to require it.
Beware of Outdated or Non-Standard Gems
Most of the RRD gems I found were quite old; most were 7-8 years old or older, so their compatibility with current Rubies is potentially suspect. The gem-builder you're using is newer, but doesn't seem to be designed as a standard gem, so you need to build it and install it in a suitable lookup path before it can be required. Installing gems as system gems is almost always a bad idea, so I'd strongly recommend building it from source and using a ruby version manager rather than following the rrdtool author's atypical suggestions. YMMV.

Are bundle exec and require 'bundler/setup' equivalent?

Do these things accomplish exactly the same?
starting a ruby process with bundle exec ruby foo.rb
having require "bundler/setup" as the first line of foo.rb
In your specific example they can be considered the same, however in reality they are not the same.
bundle exec makes some changes to the environment that bundler/setup does not make. If your foo.rb never runs a subshell, or never tries to run other ruby executables in subshells, then both versions are equivalent (they will both load bundled gems correctly and work exactly the same).
The whole idea with bundle exec is to enable you to run executables that were not originally designed with bundler in mind. Like rspec, rails, rackup. If your own app (foo.rb) does not try to run such executables that might be dependent on your bundles, then it makes no difference either way. Since all you want to make sure with bundler is that you load the correct gems, and for that bundler/setup works exactly as expected in your case.
From the bundler docs when talking about running ruby system executables:
In some cases, running executables without bundle exec may work, if the executable happens to be installed in your system and does not pull in any gems that conflict with your bundle.
However, this is unreliable and is the source of considerable pain. Even if it looks like it works, it may not work in the future or on another machine.
Then from the manpage of bundle exec you can get some additional clues as to what bundle exec actually does:
ENVIRONMENT MODIFICATIONS
make sure that it's still possible to shell out to bundle from inside a command invoked by bundle exec (using $BUNDLE_BIN_PATH)
put the directory containing executables (like rails, rspec, rackup) for your bundle on $PATH
make sure that if bundler is invoked in the subshell, it uses the same Gemfile (by setting BUNDLE_GEMFILE)
add -rbundler/setup to $RUBYOPT, which makes sure that Ruby programs invoked in the subshell can see the gems in the bundle
So if you build your app with bundler support in mind, then you never need to bundle exec your app.
But if you need to use other tools that load your app code that might load gems before they load your app code (which then might pull in a wrong non-bundled gem), then you need to use bundle exec.

Self contained ruby "binary"?

[Ruby Noob]
I have a small (command line) utility written in Ruby, which requires a few gems. Is there a way to create a self contained bundle of my program such that I can run it on another machine that has Ruby installed (but not necessarily the gems)?
FWIW, the target machine runs Linux/Ubuntu.
You can use the gem bundle http://gembundler.com/
With bundle you create a Gemfile in your project root - a text that contains all your dependencies, very similar to Maven concept
In order to fetch all your dependencies simply tun
bundle install
The only issue is that you need to have the bundle gem itself installed, so you are back with the chicken-or-Egg problem :-)
I've used:
http://www.erikveen.dds.nl/rubyscript2exe/
before, but it was a while ago. Seemed to work okay for simple programs.
You can download it here:
http://rubyforge.org/projects/rubyscript2exe/

Getting started with gems and jeweler

With Jeweler I created a gem folder structure with ease.
However, I still have some questions:
Why are params like --gemcutter and --rubyforge still available for Jeweler. Aren't these replaced by RubyGems? Do I have to specify anything to create a gem for RubyGems?
In the Rakefile I have information about the gem, and when I run "rake install" it created a gemspec. Why is the same information in two places?
What is a manifest? Just read about it, haven't seen such file.
How do I make my gem callable from the shell once I have installed it, like rails. Cause right now it's just accessible through a Ruby script using require.
Should I use "jeweler release" or "gem push" to push my gem to RubyGems.org?
I have to specify "handle" when signing up in RubyGems. What is that?
Thanks.
jeweler was created before RubyGems became what it is, so it still reflects the split. I'm not sure when jeweler was last updated, either. (I think it also still recognizes building gems on Github, which is now disabled.)
I'm not sure I follow what you're saying. The specification in the Rakefile details what the spec that gets written should look like. The spec that gets written details what should be installed and how, I believe.
A manifest is a list of all the files that your gem should ship with. Not everyone uses one. See the hoe documentation for some pro-manifest discussion.
Many Ruby gems are only libraries. If you want yours to also have a program like jeweler or rake or rails that you can call, you have to write the callable program, put it in bin in your gem's layout and specify (in your gemspec) that it should be packaged and installed. See the Gem::Specification reference under files and executable.
Not sure. Consult both jeweler's docs and the docs for RubyGems.
You can give an email address or use a name (a 'handle', like I use Telemachus here), which is all they mean by 'handle'.
For the record, if you are just learning how to write gems, you do not need to upload your first attempts using RubyGems or anything like it. You can simply install the gem on your machine only.

Unable to find a tab completion for Rubygem in Bash/Zsh

I have not found a tab-completion script for RubyGem in Bash/Zsh.
Where can you get the tab-completion script for RubyGem in Bahs/Zsh?
Executable RubyGem commands are installed into /usr/bin or /usr/local/bin or whatever prefix Ruby is found under. Tab completion for commands should just work just like any other executable file in your PATH. If it's not working, verify that the directory RubyGems installs commands into is in your PATH.
There is currently no official method of doing tab completion for the parameters to any of these commands. If you need tab completion for a command installed via RubyGems, you'll need to write and/or install it yourself. See the Bash reference manual for details. There may already be a completion system for the specific command you're interested in, so be sure to google for it, but most commands installed via RubyGems do not have any auto-complete written for them.
Try:
http://github.com/oggy/ruby-bash-completion
To install it just copy gem file to /etc/bash_completion.d/
I had errors when trying to tab:
gem install
Because I didn't have some cache files in .gem directory. So I simply disabled it by editing gem file. I changed _gem_all_names function:
function _gem_all_names {
echo ""
}
That github-hosted bash completion is now part of Ubuntu Lucid Lynx. Sweet! If you apt-get install rubygems1.8 then the completion (commands and options!) should just work.
Unfortunately, as installed, it will only complete if it sees invocation of gem1.8, and not the more conventient gem. You can fix that manually if so inclined by extending the last line of /etc/bash_completion.d/gem1.8 to include gem as part of the complete name list. I'm sure there's a cleaner way, but that works.
This is (years) late, but as I was highly unsatisfied with the various completion scripts for gem that seem to be floating around the net, I decided to write my own based (somewhat) on the rather nice git completion script available in git-sh.
https://github.com/pdkl95/rubygems-completion

Resources