Ruby gem equivalent of "pip install -e"? - ruby

In Python I can install a package from source in "editable" mode using pip install -e. Then I can carry on editing the code, and any changes will be automatically picked by other Python scripts that import library
Is there a comparable workflow for developing Ruby gems? What is the "Ruby way" of using libs as they are being developed rather than, for example, compiling and installing a gem every time I make a change to the source?

There are two common approaches one might use with bundler:
one executes bundle install --path vendor/bundle and does not run bundle update unless everything is tested.
one tells a bundler to use a local version of the gem:
in Gemfile (this is not supported in mymaingem.gemspec due to rubygems maintainence issues): gem 'mycutegem', :git => 'git://github.com/myname/mycutegem', :branch => 'master';
in command line: bundle config local.mycutegem /path_to_local_git/mycutegem.
The first approach will download everything into subfolder of your current project (here it’d be vendor/bundle.) Feel free to modify everything there, it’ll be reflected.
The second approach is likely better. You are to clone the gem from github and instruct bundle to use your local clone of the respective git repository. This approach provides you with an ability to publish the changes to your main gem into the repository. As soon as dependent repo is published as well, the up-to-date version will be retrieven by your gem subscribers, assuming they have not instructed their bundlers to use their locals.
Hope this helps.

Let's assume you have your gem code residing in a folder (say my_project/mygem/lib). You have some Ruby code in my_project that you want using the mygem code.
What I'd do is add mygem/lib to the $LOAD_PATH global variable in the beginning of said files. Kinda like this:
$LOAD_PATH << File.expand_path('lib', './mygem') # Resolve global paths
require 'a_file' # Would require "mygem/lib/a_file.rb"

I am not sure if this is exactly what you want to achieve, but from the description I infer that you want to have a local copy of some gem and reference that gem in your project.
If this is the case, you can (usually) achieve it in two steps:
Clone the gem from VCS (in most cases: git), e.g.
git clone url-of-the-gem-repo
Reference the local copy using Bundler :path, e.g.
gem "some-gem", :path => "/path/to/local/copy"
If the gem is stored at github, an even better way is to first fork it at github and then clone your own copy. Then, if you provide any improvements to the code in the local repo, you can easily share it with the world using a pull request.

I realize this is a 5 year old question, but I found all of these answers unsatisfying. Since I use ruby to develop CLI tools, using bundler for testing is not ideal. I need to be able to execute my test commands anywhere, and get the actual equivalent of pip install --editable.
Here's my solution.
Use RVM to setup a new gemset for development
rvm gemset use tools-dev --create
Install your gems
gem install mygem.gem
Locate the installation directory
gem list tool -d
In the output, it will say installed at ${rvm_gemset_path}. Copy that path. It will also be an environment variable GEM_HOME, but I include this step for completeness and clarity.
Delete the installed copy
rm -Rf ${rvm_gemset_path}/gems/mygem-${version}
Create a symlink to the git repository working directory.
ln -s ${PWD} ${rvm_gemset_path}/gems/mygem-${version}
This bash script will do it in a more automated fashion assuming that you are in the git directory and you've set GEM_NAME as your gem's name.
rvm gemset use --create ${GEM_NAME}-dev
gem build ${GEM_NAME}.gemspec
gem install ${GEM_NAME}*.gem
gem_install_path=$(ruby -e "puts Gem::Specification.find_by_name('${GEM_NAME}').full_gem_path")
mv $gem_install_path "${gem_install_path}.bak"
ln -s $PWD $gem_install_path

Related

Bundler config to either look for gems in custom path or download from custom source

How do I configure bundler so that when I run bundle install it looks for gems under /my/custom/path first and if it doesn't find there then try to fetch them from a ruby gem remote repository hosted under https://a.nice.host and downloads those into ./local/relative/path (relative to cwd for example). I would like to avoid the bundler looking at default gem installation system path or rubygems.org
The syntax for sourcing a gem from a local folder is:
gem 'some-gem-name', path: '/my/custom/path'
And the syntax for specifying a custom source is:
gem 'another-gem-name', source: 'https://a.nice.host'
And to install gems into a specific local folder, you can run:
bundle install --path ./local/relative/path
Now, that's probably all the tools you need, in truth... (And in fact, especially for that last requirement, you may instead wish to look into rvm gemsets, or using bundle install --deployment.)
But you did also ask about "looking in a local folder first, and only falling back to a remote source if it doesn't exist". That's quite an odd requirement (usually you'd only want to explicitly opt-in to fetching gems from a local path?!), but to answer this question as you've asked it...
A Gemfile is literally just ruby code! So you can define this logic using... You guessed it, ruby! For example:
if File.exists?('/my/custom/path')
gem 'some-gem-name', path: '/my/custom/path'
else
gem 'some-gem-name', source: 'https://a.nice.host'
end
If this (unusual) pattern needs to be repeated in multiple places, you could wrap it into some helper method.
For more information on the configuration options of bundler, please see the documentation.

Is it possible to override gemfile for local development?

We have a Gemfile currently in our git repository. However, there's a gem I use only locally in my environment (my team doesn't use it). In order to use it, I have to add it to our Gemfile, but every time I check out to our master/dev main branch, I have to remove it because of conflicts with the tracked gemfile.
What I would like is something like a Gemfile.local which would inherit the gems imported from the Gemfile but to also allow new gems to be imported there to use on my machine only. This file would be ignored in .gitignore. Is this even possible?
Set BUNDLE_GEMFILE environment variable:
BUNDLE_GEMFILE=Gemfile.local bundle exec rails c
To provide a “delta” only in the Gemfile.local put require_relative 'Gemfile' on top of it (or Bundler::Dsl#eval_gemfile as suggested by #MichaelKohl in comments.)
Put below code at the top of your Gemfile.local to load existing gemfile:
if File.exists?('Gemfile') then
eval File.read('Gemfile')
end
It will load all gems from existing Gemfile. You can add new gems also as you need.
Run below commands to install gems from new Gemfile.local:
bundle install --gemfile=Gemfile.local

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...)

Is there a preferred tool to edit Gemfiles?

I have a project that depends on Ruby to do something. I need to tell these people to install bundler, create a Gemfile (or update an existing one) and then run bundler install.
To be very clear, these people do not care about Ruby, they don't know what Ruby is and they don't need to know what Ruby is.
Currently my documentation is:
Run this command in terminal:
gem install bundler
Create a new file name Gemfile and add these contents:
source 'https://rubygems.org'
gem 'lightning_sites'
Or if there is already a Gemfile then edit that file and add the line gem 'lightning_sites' at the bottom.
Go back to the terminal and run:
bundle install --path vendor/bundle
I would like to replace the documentation for step 2 and preferable replace it with a command line. Is there a tool that ships by default with Ruby or bundler that accomplishes this?
If you want to avoid bundler you need to force-bundle all your dependencies inside your application. This is only really practical if none of your dependencies have compiled extensions, so if they're all pure Ruby you'll be able to do it.
What you end up doing is a bundle install --path gems/ for example, then package up everything including that directory as a deployable application. You may want to make a script that performs this step and creates a .zip file of the final result for distribution purposes.
This is a heavy-handed approach, so it's best to do this only if absolutely necessary.
You don't have to use Bundler to install gems, Ruby provides the gem command for installing gems individually.
You could simply run: gem install lightning_sites --install-dir lightning_sites and in whatever Ruby script is using the gem, programatically modify your GEM_PATH using Gem.paths before the require statement to include that install directory.

How to contribute to a Ruby Gem

I am trying to contribute to a Ruby gem and I don't understand how to test a local gem without using a globally installed gem.
The gem I want to contribute to is a command line interface gem. I clone the gem into a directory then cd into that directory. However, when I run commands in the terminal when I'm in the cloned project directory it still uses the global gem. I've even run
gem uninstall gemname
then while inside the newly cloned gem directory I redo
gem install gemname.
No matter what changes I make to the gem, I can't see the results or what my contributions are doing because it's always running the global gem.
When I do try to type a command line command that is supposed to interact with the gem while in the cloned gem directory I get:
-bash: ~/.gem/ruby/2.1.0/bin/githubrepo: No such file or directory
I've done a ton of research but I'm just not getting it. Help?
gem install gemname will look for a .gem file in the current directory. If not found it will look for it on the web.
gem install --local /path/to/your/gemname.gem will allow you to target a particular directory. You may need to gem build gemname.gemspec first, so it has your changes.
Instead of doing this, I would write tests in the gem directory itself. It's likely that when running code in there, you can simply require 'gemname' in Ruby to get the gem functionality.
If it's a well-written gem, it should have tests already. They will most likely be in a directory called test or spec. Have a look at these tests and try to carry on in that style to test your changes. This will make your code changes far far more likely to be accepted as a pull request.

Resources