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

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.

Related

Creating Ruby gems with same name executables?

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.

How do I run Rails/Rake from another directory?

Without cd-ing into the root directory of my Rails application, how can I execute a Rails or Rake command for that application.
I tried:
bundle exec rake my_tasks:do_stuff BUNDLE_GEMFILE=/PATH/TO/RAILS_APP/Gemfile
among other combinations, to no avail.
[Update]
The issue is actually two-fold, bundle doesn't know where the gemfile is and rake doesn't know what to run.
To use Bundler:
BUNDLE_GEMFILE=/PATH/TO/RAILS_APP/Gemfile bundle exec ...
Note that BUNDLE_GEMFILE has to go before 'bundle exec'.
To use Rake:
rake -f /PATH/TO/RAILS_APP/Rakefile my_task:do_stuff
To use Rails console:
????
Have yet to figure out how to enter the Rails console from another directory. Looking at the source, I think it may not be possible, because it eventually does File.join('script','rails') to kick off the rails process.
Without you showing the error message you are getting, I'm assuming it has less to do with Bundler than it does Rake. When the rake command is run, it searches for a Rakefile starting in the current directory and traversing up the tree until it finds one. You can override this behavior by explicitly specifying a Rakefile in the options to the rake command. This is done using the -f <RAKEFILE> option.
eg.
bundle exec rake -f /PATH/TO/RAILS_APP/Rakefile my_task:do_stuff
Bear in mind that your Rake tasks have to be "CWD agnostic". Most tasks and scripts are as they tend to get the project directory based on a path relative to a known file in the directory tree. You probably already understand that, but it's worth mentioning in case the tasks are expecting the current working directory to actually be the rails root. That would be a case where running them from outside the project could potentially be dangerous.

bundle exec thin start vs ./bundle/bin/thin start

I'm trying to understand how bundle exec works and what it does. I've installed the gems using bundle install like so:
bundle install --binstubs ./bundle/bin --path ./bundle/lib'
This creates a script ./bundle/bin/thin that I can use to start my Rails application using thin like so:
./bundle/bin/thin start -p 8080
However I see most articles on the internet recommend starting thin using bundle exec like this:
bundle exec thin start -p 8080
What is the difference between the two? My tests show that bundle exec doesn't call the ./bundle/bin/thin script, so how does bundle exec differ from the script?
There's no significant difference: they're two ways to accomplish the same thing, which is to run the correct version of the command for your bundle, with the environment set up to ensure that other bundled gens can be loaded by the command. The choice comes down to a matter of convenience.
The benefit of bundle exec is that you don't need to generate binstubs to use it: it just works with the existing Gemfile. This explains why you don't see it invoking the binstub you do have.
Some people don't like having to type bundle exec before every command, so the goal with binstubs is that you can add the directory to the front of your PATH and call the command normally. The drawback is that there is a potential security or usability concern if a bundled gem contains a command that shadows an important system command (e.g., ls).
If you don't put it in your PATH and always call it as bundle/bin/thin, you don't have the security concern, but it also gives you no particular benefit over using bundle exec thin.
In that specific case, there is no difference. bundle exec thin start -p 8080 will end up calling .bundle/bin/thin, but what if you you installed binstubs in a different path? bundle exec thin start will read your .bundle/config to find where your binstubs folder is. If you don't have binstubs installed and say you have 3 versions of thin installed, bundle exec will execute the one that's defined in your Gemfile.
Edit: #tadman also made a good point that I initially missed. When you use bundle exec, the gem environment from your Gemfile will be used, without it, it'll load the latest version of each gem currently installed.
The bundle exec method is the "official" way to do it. If you want to make use of the --binstubs feature, which is unusual, you can.
Remember that ./bundle/bin/thin is not the script, but a wrapper or "stub" that calls the script with the proper bundle exec environment loaded.

How to develop a Ruby GEM without having to install it first?

I'm developing a GEM that I've forked and I'm trying to modify it slightly for my app.
I'm finding it difficult and time consuming because for every change I make I have to
uninstall
build
re-install
run the app
Is there an easier way of which doesn't require repeating all steps above?
To use it in some app using bundler
If what you mean is for using it in a app to test it / use it, you can just specify a path for your gem or even point to a git repo in the Gemfile http://gembundler.com/gemfile.html
Like
gem "mygem", :path => "~/code/gems/mygem"
To use it as a standalone gem. i.e: like rspec or rake that can run outside of an app.
Just specify the path to your gem binary when running the gem command, like:
$ ~/path_to_my_gem/bin/mygem some args
If you can execute inside your gem directory (i.e: the command does not create files in the current directory, or needs any specific files from the current directory), just do this:
$ ./bin/mygem some args
Note that this last one is just for future reference, I think it's not applicable in the OP context.
use require_relative to include your files:
require_relative 'yourgem/yourclass'
This is the documentation for the function.

Guard running outside of Bundler warning

When I run the guard command it gives the following warning:
Guard here! It looks like your project has a Gemfile, yet you are
running guard outside of Bundler. If this is your intent, feel free
to ignore this message. Otherwise, consider using bundle exec guard
to ensure your dependencies are loaded correctly.
Is this hinting to me that Rails is not configured to work with Bundler correctly, or is it normal? It's not the expected behavior in the tutorial I'm following.
You should run bundle exec guard instead. Or, alternatively, run bundle install --binstubs, then you may run guard with bin/guard (it creates a script at this location). This is the recommended way of running all commands coming from gems installed with bundle install.
(If I understand it correctly) It ensures that you run the specific version of the gem specified in the bundle as well as that this gem will not be able to run gems which are installed on you computer but not included in the Gemfile (which could fool you into believing that you project is fine until you try to run it on a different computer, or a production server, where the other gem would be missing). It also does a lot of of stuff which, frankly, I have no idea about.
More info in the docs.

Resources