How to install a gem so that it executes independent of currently active RVM gemset? - ruby

I am writing a dev command-line tool gem (let's say called "tool") which I would like to use in any place in the system. I use RVM for different projects, but this tool should be available from the command-line inside those projects.
If I simply install the gem globally using "gem install" (let's say it installs to /usr/local/bin/tool), the executable is of course available inside of other projects, but since the gemset changed when executing "tool" it fails saying that it cannot find the "tool" gem (because "tool" was never installed to that gemset). I don't want to add the gem to all the Gemfiles of all the projects.
What's the best way of dealing with this? Perhaps there is a way to "lock" the gem_path/gem_home in the executable at the time of installation?

It turned out that https://github.com/sportngin/brew-gem does what I want, installing a Rubygems gem via brew and locking its GEM_HOME and GEM_PATH in a bin wrapper it generates, so that everything works even when executed in the context of a different RVM gemset.
I forked it into https://github.com/gtmax/brew-gem, adding support for installing a brew gem from a local.gem file or from a gem project on github.

Related

Is it recomment to install ruby dependencies into local folder?

I'm learning ruby recently and I'm a front-end developer, I found the dependecies are installed to C:/Ruby31-x64/lib/ruby/gems/3.1.0 folder when I use bundle install. it seems dependencies are installed globally by default like the npm install -g. I know that Bundler also have a way to install the gems to the local folder, so I wonder is
it recommend to install the dependencies to the local folder like npm does?
Similar to Composer, Bundler takes care of maintaining your dependencies, but rather than installing everything in the project folder, it distributes your gems system-wide, which are then shared by all of your projects. Using the Gemfile in your project folder, it keeps track of which libraries each project needs. Bundler is the default package manager for Rails, so you should simply let it do its thing. It does it quite well.
Hope this helps.
Use a Version Manager for Ruby, and Bundler for Gem Version Management
There are occasionally reasons to install gems in from one another isolation, but most often you should use a Ruby version manager to manage your non-system Rubies and Bundler to handle multiple gem versions and locations. Avoid modifying your system Ruby or its gems whenever possible. Also, be aware that even using RVM gemsets (or similar features in other version managers) without tweaking some poorly documented features like rvm gemset globalcache enable can create a level of isolation that is likely unnecessary and can bloat disk usage dramatically.
If You Need Gem Isolation...
That said, if you want to ensure you are isolating your gems or localizing them to your application and aren't using direnv's Ruby layout to manage all the various environment variables involved, you can use Bundler's BUNDLE_PATH environment variable to "vendor" your gems into the local application directory (these days usually under vendor/bundle). For deployments or containerization, you can also invoke bundle-install with the --deployment or --standalone flags and a list of gem groups to install if you don't want to rely on the availability of Bundler itself after installing the gems.
This sort of thing might be useful for building containers or tarballs, but isn't really very useful for development. Don't do it unless you have a really strong reason.

Cleaning gems from older gem version

I recently upgraded my distro (Ubuntu from 16.04 to 18.04), and I guess either on the upgrade or on the regular sudo update/upgrade calls gem was upgraded and I currently have the 2.7.6 version. I barely use Ruby lately, but just now I had to run a jekyll/bundle command which returned me that it had a bad interpreter (it was looking for ruby2.3 and it's currently installed on the 2.5 version).
My next step would be updating the jekyll and bundle gems (so they update the interpreter version), but to my surprise they aren't listed when I run gem list. They were obviously installed since there's a /usr/local/bin/jekyll executable and a /usr/local/bin/bundle one as well. But I think when gem was upgraded it started checking gems on a different folder (/var/lib/gems/2.5.0 I'm guessing) but jekyll/bundle apparently are in /var/lib/gems/2.3.0. I'd be fine just installing jekyll/bundle on the 2.5.0 version, but is there a way to remove all gem files from the older version since they are useless now?
I highly recommend you to use a ruby version manager like rvm or rbenv to manage different ruby versions installed on your system.
If you just want to remove the gems from your disk, you can find the location of each gem with the command bundle show $gem_name and delete the parent directory to delete all of them. You may need to delete the Gemfile.lock as well to reset the locked gem versions.
Here's what I recommend:
Use rbenv for multiple Ruby version management, no customizations needed
a ruby installer plugin is now included with rbenv
it also handles ruby executable shims automatically, don't need to rbenv rehash anymore
it loads really fast (rvm has a noticable load time on shell startup)
Use bundler to dynamically resolve gems at runtime (options below)
it's fast enough anyways
don't need a special gem solution, bundler comes included /w Ruby now
Options to invoke bundler dynamically (I recommend the last one):
use bundle exec in front of every ruby executable
variant: create alias be='bundle exec'
create bundle binstubs <LIST GEM EXECUTABLES YOU WANT> for each project
use bin/ in front of every ruby executable to call the binstubs
do #2 and then set up .git/safe
lets you manually allow PATH lookups to the bin/ folder while in that project root
don't need to type bin/ anymore
Now multiple gem versions will all be installed into the same Ruby version bucket, and you let bundler dynamically add the right versions to the load path before every startup.
Removing a Ruby version will also mean removing all the gems (and versions of those gems) installed for that Ruby version as well.
I'd like to thank both answers. I upvoted them because even though they're not a direct solution to my problem they give good directions to prevent it.
NOTE: These are the steps I took, which doesn't mean they are the correct way, so don't take more as a reference than a guide.
Here's what I did to clean up those old Ruby version gems:
First I needed to add an environment variable for gem to be able to find those obsolete gems, by running GEM_PATH=/var/lib/gems/2.3.0.
Then it's possible to get the contents and specification of the gems with gem contents name_of_gem and gem specification name_of_gem.
I individually uninstalled those gems that were obsolete with sudo GEM_PATH=/var/lib/gems/2.3.0 gem uninstall -i /var/lib/gems/2.3.0 name_of_gem. In my case I did a sudo install, so I needed sudo which also required me to set the environment variable again because of sudo's security policy. Also, I needed to manually set the install directory for some reason.
Some gems will have executables, and the uninstall will ask if you want to remove it. I wasn't able to get the gem uninstall to remove it because it claimed I didn't have write permission to /var/lib/gems/2.3.0/bin (which is weird because the EXECUTABLE DIRECTORY gem environment variable was /usr/local/bin). I asked gem not to remove the executables, wrote down their names and removed them manually from the EXECUTABLE DIRECTORY folder. You can also use which gem_executable_name to find out where it's located.
After uninstalling all the gems I believe it's safe to remove the /var/lib/gems/2.3.0 folder and its contents. Running gem contents on all of them only returned me files on this folder so I believe the only external files were the script/executables that were added to /usr/local/bin.

Can't install gem using Bundler's Rakefile install task when developing a custom gem

I'm developing a couple of private gems and I think I don't understand correctly the PATH/GEM_PATH and/or Bundler/RVM installation flow, would love if someone could chip in.
I have a repository with two gems (A & B for simplicity sake). I've developed the gems using the scaffolding + following the guidelines provided by this bundler tutorial.
Thanks to the Bundler project I have a few Rakefile tasks like rake build, rake install, rake install:local and rake release. Because of the private nature of these gems I can't release them to RubyGems (and we haven't looked into hosting our rubygems).
My machines are using RVM to manage ruby versions and Bundler version 1.15.1
What I want to do: Assuming a new machine/developer trying out the project, ideally we would cd into each of the subfolders (currently 2, gem A and gem B), run rake install and after that we should have the gems available system wide for the current user.
What is happening: The gems are built and work properly, but they are only available inside the subfolder of each gem i.e. gem A is only available inside the subfolder A and gem B is only available inside subfolder B.
What I've tried: So, after rake build/install/install:local a new .gem file is generated under pkg. I've tried to manually install the "compiled" file using gem install pkg/A.gem, gem install --local pkg/A.gem and gem install --local --user-install pkg/A.gem without success. (there are plenty of SO questions/answers about this)
I believe this has something to do with the PATH variables, but like I said before I don't fully understand the way they are managed. I get the following results from these commands:
# Our gem
> gem which A
/home/ubuntu/.rvm/gems/ruby-2.4.0/gems/A-0.1.8/lib/A.rb
# Pry, available globally
> gem which pry
/home/ubuntu/.rvm/gems/ruby-2.4.0/gems/pry-0.11.1/lib/pry.rb
I've been lost and frustrated for far too long now, any help is appreciated. Also open to hear suggestions of better private gem installation flows :)
Yes, it has something to do with your PATH variables. Your installation seems to be good.
I advise you to first affirm your gems installation path with:
echo $GEM_HOME
The double check your PATH to ensure its present and also confirm that the GEM home is also where the gem got installed into from the rake install
echo $PATH
If not, put it in your path and you should be fine with something like this:
echo PATH=$PATH:$GEM_HOME >> ~/.bashrc
source ~/.bashrc
Build your gem as per that guide you linked. You should end up with a gem file. Distribute this as you see fit (I use rsync/crontab to download newer gem versions but anything goes). User can install the gem as follows:
gem install --user-install /path/to/your/file.gem
This will install the gem in the user's ~/.gem/ruby/<version>/gems/<your-gem-name> directory.
Tried it with an empty gem (foodie, as in that example guide) and it works fine. But if you don't specify the --user-install parameter it will try to install in the system ruby dir (/usr/lib/ruby/gems...)

Ruby command line tool crash when switch ruby version in rvm

I am trying to make an CLI tool with ruby.
My tool require some library in bundle (log4r, ...). So problem appear when i switch my ruby version (2.0.0 -> 2.1.2) or when switch gemset, some gem are not install in new ruby environment.
So how can i make my app work like vagrant, which work in every version of ruby i am using?
If you package your application as a Gem, you can include a Gemspec that describes your application. One of the things you can specify is its runtime dependencies; when the user runs gem install myapp, then the gem command will make sure it includes everything you specified (like log4r).
It will be harder to make this happen without Rubygems. You can package your application along with a defined version of Ruby and all its requirements - that's what Vagrant does - but that makes your application a larger download and means you have more to maintain. It's going to be hard work if you want to install your app system-wide and have it work with every single Ruby environment. It's far better to let the gem application install your app (whether systemwide, or via rbenv/rvm) and let that manage your dependencies for you. There's the default gems plugin for rbenv and rvm gemsets to help manage this.

rbenv or bundler gemset sandboxing?

The rbenv documentation states that you can use the gemsets plugin to sandbox your gems if you aren't using bundler:
Manage gemsets. Bundler is a better way to manage application dependencies. If you have projects that are not yet using Bundler you can install the rbenv-gemset plugin.
However, I am using bundler and when I switch between projects that have different gemsets I need to re-bundle. When I was using rvm, this was simple due to the .gemsets file.
Is there a sandboxing functionality in bundler that I am not aware of or have I misunderstood the documentation?
I found this clear, well-documented solution:
Setting up and installing rbenv, ruby-build, rubies, rbenv-gemset, and bundler
Now when you gem install (see “Bundler” section below) something it will go to your helloset gem area.
You can combine use of multiple gemsets with it.
The big benefit to this setup is enabling each project to use a different set of differently versioned gems.
Read the section entitled Enable RVM-style “gemsets” (optional).
It uses the rbenv plugin for gemsets: https://github.com/jamis/rbenv-gemset.
Update 8/12/2012 I am using this to manage two projects with different versions of Ruby, and separate sets of gems. It works well.
bundler uses the Gemfile and Gemfile.lock files in, in essense, to make a "gemset" for that particular project that you access via commands prefixed with bundle exec
It may have to do with the way rbevn uses shims. You might want check out
https://github.com/carsomyr/rbenv-bundler
Try using chgems with Bundler. chgems is like chroot for RubyGems. It spawns a new sub-shell with PATH, GEM_HOME and GEM_PATH updated to use $project/.gem/$ruby/$version as the primary GEM_HOME. Since all gems are installed inside of the project directory, cleaning up after a project is as easy as rm -rf $project/.

Resources