User-level bundler Gemfile - bundler

I'd love to have a Gemfile in Bundler that just sets my own personal Gemfiles to always be bult into bundles...
aka ruby-debug, interactive-editor, and so forth.
Any idea how to do this?

We use this technique.
Puth this in your Gemfile:
eval File.read(File.expand_path("Gemfile.personal")) if File.exists?(File.expand_path("Gemfile.personal"))
And then add your personal gems to Gemfile.personal. Of course exclude Gemfile.personal from your version control.

One way to do this is to create different evnironments
group :scott do
end
Then
bundle --with-env=scott

I'm not 100% sure what it is you are trying to achieve, but;
If you just want to specify a number of development-only gems, you can specify a development group that can be excluded from deployments:
group :development do
gem "ruby-debug"
gem "interactive-editor"
end
Then on production or test you would do:
bundle install --without development

The cleanest solution I found so far is to use a separate Gemfile.personal and use a custom Gemfile path. I like this solution because you can use it in any project without modifying project code at all.
1. Add Gemfile.personal in to project root dir
# /path/to/your_ruby_project/Gemfile.personal
eval File.read('Gemfile') # use all gems from Gemfile
gem 'personal-gem1'
gem 'personal-gem2'
2. Install gems using Gemfile.personal file
BUNDLE_GEMFILE="Gemfile.personal" bundle install
# or
bundle install --gemfile=Gemfile.personal
Just remember to specify BUNDLE_GEMFILE every time you execute commands with bundler.
I personally put BUNDLE_GEMFILE=Gemfile.personal env variable in .env file using dotenv which ensures that Gemfile.personal is always used when I execute any command with bundler so I do not need to put it manually every time.
3. Put Gemfile.personal and Gemfile.personal.lock to .gitignore
For linux users:
touch ~/.gitignore
echo "Gemfile.personal\nGemfile.personal.lock" >> ~/.gitignore
This will affect all projects, so you do not need to update each project .gitignore separately.

My proposition does not depend on Bundler. As such does not clutter Gemfile* with your private gems for the price being a bit less convenient than answer by #ScottSchulthess.
How Bundler works
There is an array stored in $LOAD_PATH global variable which is a "load path for scripts and binary modules by load or require" (see Ruby docs) and Bundler modifies this array.
If you're developing a gem, $LOAD_PATH it will contain paths to all gems in the system. You can simply write e.g. require "pry" somewhere and pry gem will be loaded properly even if it's not mentioned in gemspec nor Gemfile. You don't have to add it to dependencies. (Of course it has to be already installed with gem install pry.)
A very different strategy Bundler takes when you're developing an application. In such case most of $LOAD_PATH will be removed on require bundler/setup (Rails calls it in config/boot.rb). Only essential paths and those pointing to gems specified in Gemfile.lock will remain there. So if you want to use pry without adding it to Gemfile, you got to append it to $LOAD_PATH before requiring it.
Solution for applications
gems_root = $LOAD_PATH.detect{ |p| %r{/bundler-} =~ p}.sub(%r{/bundler-.*}, "")
additional_gems = {
"pry" => "pry-0.10.1/lib",
"pry-rails" => "pry-rails-0.3.2/lib",
}
load_paths = additional_gems.values.map{ |p| File.join gems_root, p }
$LOAD_PATH.unshift *load_paths
additional_gems.keys.each{ |r| require r }
If you're using Rails, then save it in /config/initializers/00_custom_gems.rb and that's all. Outside Rails you additionally need to require it, preferably right after require "bundler/setup":
require "path/to/it" if File.exists? "path/to/it"
Remember to mention this file in .gitignore.
Sometimes proper gem path does not end with /lib but with architecture name. The easiest way to learn it is to add it for a moment to Gemfile and do puts $LOAD_PATH in aforementioned initializer. You can also learn those dirs from gemspec.
Solution for gems
When developing gem, you don't need to enhance $LOAD_PATH, only to require gems you want. If you need custom gems in tests and you're using RSpec, it can be done somewhere in /spec/support.
Another (less sane) idea is to add a file lib/development.rb:
require_relative "my_gem_name"
require "path/to/private/requires" if File.exists? "path/to/private/requires"
and refer to this file instead of to "my_gem_name" in your tests, demo application etc..

Related

Handling require in a project meant to be provided as a gem

I'm completely lost with ruby "require" (and I don't have the option for require_relative since my code is meant to run with ruby 1.8.7.
First some sample code.
requester.rb
require './column/main_column'
....
column/main_column.rb
require './helper' # helper.rb being in the root dir
....
column/segment_column
require './column/main_column'
require './helper' # helper.rb being in the root dir
....
I'm completely lost in how should I use my requires to make my code usable inside a gem and in a standalone way (without assuming a previous gem installation). I was reading at Jekyll's code and every dependencies seems to be loaded in the main rb file which to my opinion is a bit dirty and still do resolve the problem if column/segment_column.rb needs to be called independently.
Please try to argument about different approaches and why one is the best.
You should require files without the extension and relative to your gem project's lib folder.
For example, given this structure:
example-gem/
lib/
example/
gem.rb
example.rb
example-gem.gemspec
You'd require the main example module using:
require 'example'
And in the example.rb file you'd find:
require 'example/gem'
This set of conventions is adopted by the community and is documented on the RubyGems Guides. If they fail, it is often because your gem's lib directory is not in Ruby's $LOAD_PATH, which usually means the gem is not installed.
Building and installing gems during development is cumbersome. For that reason, gems like Bundler became part of the toolboxes of many Rubyists. Bundler lets you use your gem without having to build or install it locally. You can simply bundle exec a script that uses your gem or use bundle console in order to interact with it.

RubyMine 6.0.2 with "debugger" gem without modifying Gemfile.lock?

I am using RubyMine (v6.0.2), but my teammates are not, so they need the "debugger" gem in the gemfile. I can conditionally un-require the Gemfile when running RubyMine (so the Gemfile can be shared and identical), but since the 'debugger' gem is not included, the Gemfile.lock file changes depending on whether the project was last run with RubyMine or not. This creates a lot of noise in redundant Gemfile.lock changes.
I've tried using 'debugger-xml' gem; that doesn't solve the issue.
So -- how can I run RubyMine 6.0.2, with the 'debugger' gem in the Gemfile, without having Gemfile.lock change?
I've been working on this issue from the other side of the table. I use the debugger gem, but have team mates that use RubyMine.
We discussed several potential solutions but they all involved conditional checks in the Gemfile that would result in a modified Gemfile.lock.
I googled around for a better solution and found this SO post: How to use gems not in a Gemfile when working with bundler?
Combining a few of the answers in there, I came up with this solution:
Remove the debugger gem from the Gemfile.
Create a Gemfile.local with the contents below.
Add Gemfile.local to the .gitignore file if using git.
Create a function and shell alias.
Start rails with $ be rails s
How it all works!
Bundler will use the file named Gemfile by default, but this behavior can be overridden by specifying a BUNDLE_GEMFILE environment variable. Bundler will use/create the lock file with the same name as the BUNDLE_GEMFILE.
The shell function __bundle_exec_custom will check to see if there is a Gemfile.local file in the CWD. If there is, then the BUNDLE_GEMFILE variable is set and used. Otherwise, the default Gemfile is used.
This will allow a developer to use any gems that they want for local development without having to impact the application as a whole.
Gemfile.local:
source "https://rubygems.org"
gemfile = File.join(File.dirname(__FILE__), 'Gemfile')
if File.readable?(gemfile)
puts "Loading #{gemfile}..." if $DEBUG
instance_eval(File.read(gemfile))
end
gem 'debugger'
Function and shell alias:
__bundle_exec_custom () {
if [ -f Gemfile.local ]
then
BUNDLE_GEMFILE="Gemfile.local" bundle exec $#
else
bundle exec $#
fi
}
# Rails aliases
alias be='__bundle_exec_custom'
I think I found it. Apparently, RubyMine does not deal well with the debugger gem being required into the Rails app, but has no issue with the gem just being installed.
The solution then is to include the gem in the Gemfile (and Gemfile.lock) but only require it outside RubyMine.
gem 'debugger', {group: [:test, :development]}.
merge(ENV['RM_INFO'] ? {require: false} : {})
The above code is from this comment on the JetBrains bug tracker, through this comment on a similar question.
It checks for the presence of the RM_INFO environment variable, which is set by RubyMine. The important thing is that it only affects whether the gem is required and thus should not change Gemfile.lock between installs.
I may have an even better solution that seems to be working for me in my Rails 4 app...
In your Gemfile, move all your debugging-related gems to their own group, as such:
group :pry do
gem 'pry', '>= 0.10.0'
gem 'pry-debugger', '>= 0.2.3'
gem 'pry-highlight', '>= 0.0.1'
end
In config/application.rb you will a find something like the following:
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
Add the following just below that:
Bundler.require(:pry) unless ENV['RM_INFO'] || Rails.env.production?
You may wish to modify the unless condition to suit your needs, but the important part is that RubyMine will set RM_INFO, which you can use to detect and therefore exclude gems from being required.
This will eliminate the ping-pong effect of bundling in RubyMine vs. command line, so this should work well in a mixed-IDE team.
One last note, if you're deploying to Heroku, you might want to exclude the :pry group from being installed on deploy:
$ heroku config:set BUNDLE_WITHOUT="development:test:pry"

Is it possible to require files outside the Gemfile?

For example, I'm developing a gem, and while I'm developing, I use pry instead of IRB, and debugger for debugging. However, I don't want possible contributors to have to install them (because they may not need them). My first idea was to put them in a Bundler group:
source :rubygems
gemspec
group :extras do
gem "pry"
gem "debugger"
end
And then people could use:
$ bundle install --without extras
But I want it to be a default that they're not installed. What would be perfect is that they're not in my Gemfile, but that I can still require them (if they exist on the computer). This solution would be ok because I don't care at which version they're locked. Can it be done?
You can add arbitrary load paths and then require gems from them. Check out the global variable $:
puts $:.inspect
# ["/var/myproject/releases/20200918191637/lib", "/var/myproject/releases/20200918191637/vendor", "/var/myproject/releases/20200918191637/app/assets", "/var/myproject/releases/20200918191637/app/controllers", "/var/myproject/releases/20200918191637/app/helpers", "/var/myproject/releases/20200918191637/app/mailers", "/var/myproject/releases/20200918191637/app/models", "/home/deploy/.rvm/gems/ruby-2.3.0/gems/bundler-2.0.2/lib", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/gems/2.3.0/gems/json-1.8.3/lib", "/home/deploy/.rvm/gems/ruby-2.3.0/gems/bundler-2.0.2/lib/gems/bundler-2.0.2/lib", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/site_ruby/2.3.0", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/site_ruby/2.3.0/x86_64-linux", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/site_ruby", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/vendor_ruby/2.3.0", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/vendor_ruby/2.3.0/x86_64-linux", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/vendor_ruby", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/x86_64-linux"]
Now let's append to $: and require a gem
require 'method_source'
# LoadError: cannot load such file -- method_source
$: << '/home/deploy/.rvm/gems/ruby-2.2.4#myset/gems/method_source-1.0.0/lib'
# ["/var/myproject/releases/20200918191637/lib", "/var/myproject/releases/20200918191637/vendor", "/var/myproject/releases/20200918191637/app/assets", "/var/myproject/releases/20200918191637/app/controllers", "/var/myproject/releases/20200918191637/app/helpers", "/var/myproject/releases/20200918191637/app/mailers", "/var/myproject/releases/20200918191637/app/models", "/home/deploy/.rvm/gems/ruby-2.3.0/gems/bundler-2.0.2/lib", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/gems/2.3.0/gems/json-1.8.3/lib", "/home/deploy/.rvm/gems/ruby-2.3.0/gems/bundler-2.0.2/lib/gems/bundler-2.0.2/lib", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/site_ruby/2.3.0", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/site_ruby/2.3.0/x86_64-linux", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/site_ruby", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/vendor_ruby/2.3.0", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/vendor_ruby/2.3.0/x86_64-linux", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/vendor_ruby", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0", "/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/x86_64-linux", "/home/deploy/.rvm/gems/ruby-2.2.4#myset/gems/method_source-1.0.0/lib"]
require 'method_source'
# true
That can be tedious if you have a lot of dependencies, though, so you might try this:
Dir['/home/deploy/.rvm/gems/ruby-2.2.4#myset/gems/*'].each do |gem_path|
$: << File.join(gem_path, 'lib')
end
bundle install is "opt-out"—unless you specify --without some_group, it installs everything.
If you absolutely don't want to have a given gem in your Gemfile, you could just gem install that rogue gem outside of your bundle. Then it'll be visible to you under irb and straight ruby (but obviously you'll get errors if you try to require it within code running under bundle exec).
You could add a conditional based on environment variables into the Gemfile. Example:
source :rubygems
gemspec
if ENV['WITH_EXTRAS'] == '1'
gem "pry"
gem "debugger"
end
The gems are then only installed/loaded, if you set the environment variable to '1' e.g. WITH_EXTRAS=1 bundle install.
Nowadays you can use the "optional" argument, and it will do exactly what you ask: it will not install the extras unless people bundle --with extras
group :extras, optional:true do
...
end
https://bundler.io/v2.3/guides/groups.html#optional-groups

How to use gems not in a Gemfile when working with bundler?

When using bundler with a project in general and Rails specifically, you have access only to gems defined in your Gemfile. While this makes sense, it can be limiting. Mostly I find it limiting when I want to use a certain RSpec formatter that the rest of the team doesn't use. Unless it's in the Gemfile, it isn't accessible.
Any way around it or I have to add it to Gemfile?
Update: my problem wasn't Bundler but Spork. When running RSpec without Spork I had no problem of using whatever formatter I wanted.
Update #2: it looks like that using Bundler is still the cause of the problem. The difference between using Spork and not using Spork, is that when running RSpec without Spork, it loads the formatter before loading your project and getting into the Bundler "sandbox".
With Bundler:
$ bundle exec irb
>> require 'fivemat'
LoadError: cannot load such file -- fivemat
from (irb):1:in `require'
from (irb):1
from /Users/arikfr/.rvm/rubies/ruby-1.9.3-p194/bin/irb:16:in `<main>'
Without Bundler:
$ irb
>> require 'fivemat'
=> true
In ChiliProject we allow users to create a Gemfile.local which is included into the main Gemfile on load. This allows users to specify additional gems without having to change our Gemfile to ease updates.
For that, we have included the following code at the bottom of our Gemfile.
gemfile_local = File.expand_path('Gemfile.local', __dir__)
if File.readable?(gemfile_local)
puts "Loading #{gemfile_local}..." if $DEBUG
instance_eval(File.read(gemfile_local))
end
The Gemfile.local itself is excluded from the repository via .gitignore.
I assume that none of these answers have been chosen as correct because they don't do a great job of solving the problem: having additional gems that you can use that by default don't require any changes to files already in the repository to achieve. That is, you don't have to modify any files, and you don't have to live with remembering not to check in your local changes. Here's how I do it.
The idea is basically inverting the dependencies of Holger's answer, such that there's no need to modify the shared Gemfile. Bundler allows one to specify which file is to be used as the gemfile, but strangely the documented methods do not apparently work with its configuration file and will not be fixed. There is a somewhat obscured feature of Bundler that any of the configuration options can be set in an environment variable or passed on the command line. Running all of your commands as bundle [command] --gemfile [yourgemfile] or BUNDLE_GEMFILE="[yourgemfile]" bundle [command] will cause Bundler to read whatever gemfile you want it to. I highly recommend using the environment variable approach, and either creating an alias or exporting the variable for your current session, particularly as I was unable to use the command line switch with the "exec" command.
Therefore, I run rspec like this: BUNDLE_GEMFILE="[mygemfile]" bundle exec rspec [filename], and I have the first part of this aliased as bem in my bashrc. Works like a charm.
Then, you should setup your source control to ignore your Gemfile, either in the project's .gitignore or, to keep the project entirely hygienic without changing even its .gitignore, to your personal global ignore file (which is by default in ~/.config/git/ignore and has the same format as a project's gitignore file).
One other thing to note is that Bundler will create a lockfile based on the Gemfile's name. This is super handy, as it keeps you from overwriting your project's Gemfile.lock if it's checked in, but you need to ignore this new lock file as well. If your gemfile is Foo.bar, look for Foo.bar.lock.
Finally, you can do something similar to Holger's suggestion in your custom Gemfile:
source "http://rubygems.org"
gem "fivemat"
instance_eval(File.read(File.dirname(__FILE__) + "/Gemfile"))
and you're good to go, as long as you remember to specify your Gemfile.
You can use something like this in your Gemfile:
gem 'foo' if ENV['ENABLE_FOO_GEM']
Then just set ENABLE_FOO_GEM in your environment.
export ENABLE_FOO_GEM=1
The gem will be disabled by default, but easily turned on (permanently) by anyone who wants to use it.
Add to .gitignore
Gemfile.local
Gemfile.local.lock
Add to the project a Gemfile.local.sample file with the following content:
# Include gems that are note meant to be part of the project but for development purposes
# That's why Gemfile.local and Gemfile.local.lock must be git-ignored
# To use these gems:
# 1. Create a "Gemfile.local" file (at same level of "Gemfile")
# 2. Prepend "BUNDLE_GEMFILE=Gemfile.local" before "bundle install" or "bundle exec rails c" and so forth.
eval_gemfile "./Gemfile"
group :development, :test do
# Suggested gems
gem "awesome_print", require:"ap"
gem "hirb"
gem "pry"
gem "pry-byebug"
gem "pry-rails"
gem "meta_request"
# My gems
gem "fivemat"
end
I believe the gem Devpack provides the functionality you are looking for.
The gem allows you to add a single gem to your Gemfile which will permit any developer to configure their own preferred set of development gems either for an individual project or globally by creating a .devpack file containing a list of gems.
(I am the author of this gem; I came across this post while developing it so thought it may be worth adding).
In case you still decide to do this (horrible idea):
You can add ruby code to your Gemfile to load a ~/.gemfile (or such) if it exists.
Something like:
eval(IO.read('~/.gemfile'), binding) if FileTest.exists?("~/.gemfile")

Add the gem I'm developing to be loaded automatically by rubygems without build/install?

I'm developing a gem with Jeweler in a custom directory.
I want to be able to require the gem within any app (and also the executables files from $PATH), and without needing to build and install the gem each time I modify it.
I thought about 2 ways:
I make a symlink to $GEM_HOME/gems and $GEM_HOME/bin
I add the bin directory to $PATH and the lib directory to rubygems to be loaded.
But I bet there is a proper way to do this.
You can specify a local path in the gem command:
gem 'your-gem', '1.2.3', :path => 'path/to/your-gem'
Update: As #Nick points out in the comments,
This is specific to using bundler. In general, it's just require '/path/to/your-gem.
I'd like to add, however, that if you're using a custom-developed gem, bundle will probably make your life easier if you're not already using it. This is because with bundler, when you're done developing the gem (or at a stable/release point) you can load a gem directly from a github repository like this:
gem 'your-gem', :git => 'git#github.com:you/your-gem.git'
No need to mess around with your Gemfile
require 'bundler/setup' will put the right gem into your $LOAD_PATH and allow you to require it on the next line.
#!/usr/bin/env ruby
require 'bundler/setup'
require '<gem-name>'

Resources