How to install pry/pry-rails globally for all projects? - ruby-on-rails

How can replace irb and rails console with pry/pry-console globally for every project without having to include it in a project?

Easy mode is to hack your .irbrc, so that when anything tries to load IRB, you take over and force it to load Pry instead:
begin
gem "pry"
rescue => ex
$stderr.puts ex.message
else
require "pry"
Pry.start
exit!
end
If you're using Bundler, however, that'll still only work if Pry is available in the current bundle.
To make it work even when Bundler doesn't think Pry should be allowed to activate, you'll need to monkey with gem loading -- Bundler tries very hard to make it impossible.
To do that, you'll need a ~/.rubyrc.rb file, which you ensure always gets loaded for all ruby commands by exporting RUBYOPT=$HOME/.rubyrc in your .bashrc / .zshrc.
That file can then hack into Bundler's internals to force loading a non-bundled gem. You can also monkey-patch Bundler.require, which is how Rails loads all the gems in the Gemfile, to similarly force pry-rails in any application that contains rails.
(This strategy also allows you to globally support binding.pry, without needing to explicitly require anything, or add the gem to the project.)

Related

How to require a gem in .irbrc without needing to also add it to a Rails Gemfile?

I've added awesome_print to my ~/.irbrc file like so:
require 'ap'
Inside a Rails project directory, if I run irb it loads the gem fine, because I've already installed the gem locally. But if I run rails console, it spits out this error:
cannot load such file -- ap
How can I resolve this? I am guessing that it's looking for the gem in the app's Gemfile, but I don't want to add it to the Gemfile because I don't want other developers requiring that dependency. I only want to use awesome_print on my machine.
I am also using rbenv, if that is of any help.
There is this trick.
What you need to do is
# Copy the definition of the debundle! method into your ~/.irbrc
# Call 'debundle!' from IRB when you need to.
(as explained at the top of the file)
The text as it appears on the referred to site:
debundle.rb allows you to require gems that are not in your Gemfile when inspecting
programs that are run with Bundler.
Use at your own risk!
Paste the code of debundle.rb and you are done! A good place would be your .irbrc file
before requiring irbtools.
The code is directly taken from pry-debundle.
Please look there for any further information. This repo exists to simplify debundling
without using the pry repl.

How am I supposed to use bundle install --standalone with a Rails app?

bundle install --standalone seems like a wonderful idea, but I'm having a hard time wrapping my head around how to use it that doesn't wind up requiring you to have rubygems or bundler installed.
It generates a bundler/setup.rb which adds the lib and ext directories of my gems, seemingly in order. Presumably, all I'd need to do is add it to the load path, and all's well.
But bundler/setup.rb doesn't seem to actually require anything.
Now, that's fine, because the normal bundler/setup doesn't require anything either and leaves it to the app to call Bundler.require
Rails by default does the requiring with this little ditty:
if defined?(Bundler)
Bundler.require(*Rails.groups(:assets => %w(development test)))
end
At the point it hits this, Bundler isn't defined (bundler/setup.rb doesn't define it), so it skips over the block.
So how exactly do I require bundler. If bundle install --standalone actually bundled bundler, presumably, I could manually call require bundler and then have Bundler defined, but it seems to exclude itself from the bundle.
Is there an app out there that actually uses bundle install --standalone, and if so, how?
To get this to work with Rails, you remove the Bundler.require call and manually add all of the require lines where they're needed.
This has pros and cons. On the plus side, it can make loading your app faster as not all the gems have to be required at load time. It also makes clear what gems are being used where.
On the down side, you have to add all the require calls to your application.
Take a look at Myron Marstons blog post for a better explanation.
bundle install --deployment #install ruby & bundled gem into project's directory

Bypassing bundler for auxiliary development gems

In the Gemfile of my Rails project, I am starting to have auxiliary gems like "ruby-debug19", "perftools.rb", or "irbtools". All of these really have nothing to do with the project, but rather are part of my local development setup. But since I'm using bundler, I cannot load these gems (even though they are installed system-wide) unless I add them to the Gemfile. In my view that is a bit of a code smell.
For example, I would like to be able to require 'irbtools' in rails console without adding "irbtools" to my Gemfile.
Is there a way to keep auxiliary gems out of the Gemfile and still be able to load them for debugging, profiling, etc. when I need them?
Actually, you can create a group in you Gemfile like:
group :auxiliary do
gem 'irbtools'
end
And then use bundle install --without auxiliary if you don't want to use irbtools. Why do you think adding them to Gemfile is a code smell? And if it possible to do this without adding gems to the Gemfile it will be many more code smell I think.
Thanks to this post I have a great solution.
Add this line at the end of your Gemfile:
eval(File.read(File.dirname(__FILE__) + '/Gemfile.local'), binding) rescue nil
Create a file called Gemfile.local.
Add your development gems to Gemfile local. For example:
group :development do
gem 'cucumber'
end
Add Gemfile.local to .gitignore.
Now you can add your auxiliary development gems without changing the Gemfile for other folks on the team. Very cool.
I put the code below in a file in my app root, so it's easy to load from irb.
If you want it in something like a rails server, you probably need to add the load statement to environments/development.rb etc. That still creates problems if you accidentally check that in, but it's less annoying than having to add it to the Gemfile and causing your Gemfile.lock to change also.
# Example usage:
# add_more_gems("ruby-debug-base19-0.11.26", "linecache19-0.5.13")
# or
# add_more_gems(%w(ruby-debug-base19-0.11.26 linecache19-0.5.13))
#
# Note that you are responsible for:
# - adding all gem dependencies manually
# - making sure manually-added gem versions don't conflict with Gemfile.lock
# To list deps, run e.g. "gem dep ruby-debug-base19 -v 0.11.26"
#
def add_more_gems(*gem_names_and_vers)
gem_names_and_vers.flatten!
gem_base = File.expand_path(Gem.dir)
gem_names_and_vers.each do |gem_name_and_ver|
# uncomment if desired
###puts "Adding lib paths for #{gem_name_and_ver.inspect}"
spec_file = File.join(gem_base, 'specifications', "#{gem_name_and_ver}.gemspec")
spec = Gem::Specification.load spec_file
this_gem_dir = File.join(gem_base, 'gems', gem_name_and_ver)
spec.require_paths.each {|path|
dir_to_add = File.join(this_gem_dir, path)
$: << dir_to_add unless $:.member?(dir_to_add)
}
end
end
# put your often-used gems here
add_more_gems(
%w(
ruby-debug-base19-0.11.26
ruby-debug-ide19-0.4.12
linecache19-0.5.13
)
)
Not sure if this would work for you. It depends on whether or not you're using RVM. If you are, then you could install those auxiliary gems into the #global gemset that is created automatically for every Ruby interpreter. The gems in the #global gemset are available to all project-specific gemsets by default. This way you won't need to clutter up your Gemfiles.

Rails 3, Bundler, LoadError

I'm writing an app that will run scripts in a specified folder, and then record the numbers and graph them.
My problem is that if the script is a ruby file, the require statements fail inside the script because bundler seems to have done something funky with the load path.
Running rails runner Datasource.run_jobs fails:
class Datasource < ActiveRecord::Base
def self.run_jobs
require 'aws_sdb'
access_key_id = "REDACTED"
secret_key = "REDACTED" # In all actuality these woudln't be here.
#sdb = AwsSdb::Service.new(:access_key_id => access_key_id, :secret_access_key => secret_key)
Datasource.all.each do |ds|
puts "Updating #{ds.name}..."
unless #sdb.list_domains.include? ds.name
puts "#{ds.name} doesn't exist in SDB, creating a domain for it..."
#sdb.create_domain ds.name
end
#data = "TEST"
data = `#{RAILS_ROOT}/lib/plugins/#{ds.name}`
#sdb.put_attributes(ds.name, Time.now.to_i, data)
puts "Data Collected: #{data.inspect}"
end
end
has_many :graphs
end
(Basing this off your comments on the question)
You will need to add hpricot (and any other gem this needs) to your Gemfile so that they are made available by Bundler. Bundler is by far the easiest way to avoid gem conflicts and tomfoolery.
Imagine this situation: You somehow lose the gems that you have currently. Be this happening through a format or system change or any other reason. Whatever it is, you've lost your gems. How are you going to re-install all your gems? You could keep a list of them somewhere else yourself, but is this truly likely?
Bundler solves this problem by making you state what gems your application requires and only adding those gems to the load path, which is why you can't find hpricot. When you run bundle install the first time, this creates a Gemfile.lock which contains something like this:
GEM
remote: http://rubygems.org/
specs:
abstract (1.0.0)
actionmailer (3.0.0)
...
Because you commit this file to your source control "solution" of choice (be it Git, SVN, FTP, whatever, it's not important) you have a solid way of specifying the precise gems and precise versions of those gems that your application uses.
When/If your gems are wiped, you can simply clone your project again and run bundle install. Because the Gemfile.lock file exists, you'll have exactly the same gems you had originally, even if there were updates.
If you don't want the exact same gems, just run bundle update and this will ignore the specifications in Gemfile.lock and instead revert to depending on Gemfile to define them. This will check for new versions of gems and install them, updating the Gemfile.lock when it's done.
Honestly, I don't understand the Bundler hate. If you could explain in wider terms than "OMG IT SUCKS YEHUDA IS SATAN", I'd be much obliged.
Edit: WedTM asked for a sample Gemfile and related code:
In the Gemfile you'd have this:
group(:scripts) do
gem 'gem1'
end
To require these gems for your scripts:
require 'bundler'
Bundler.require(:scripts)
You may also wish to require the default gems too which you can do by just adding default anywhere to the arguments of require:
Bundler.require(:default, :scripts)
If this for some reason doesn't work I would imagine it would be because it can't locate the Gemfile. This can be fixed by setting the ENV['BUNDLE_GEMFILE'] to the path to the Gemfile.
I wonder if you might be able to use RVM to set up the ruby environment before running your scripts. Maybe something with a gemset like:
data = `rvm gemset use scripts; #{RAILS_ROOT}/lib/plugins/#{ds.name}`

Rails optional gem config

What do you do when you want to use a gem for development/testing that you don't want to force other devs to use? Right now I have
begin
require 'redgreen'
rescue LoadError
end
in test_helper.rb and no gem config, but that seems like a clumsy approach, albeit a functional one. I'd like to do something like the following:
config.gem "redgreen", :optional => true
Any other suggestions? Or should I just vendor those pretty superficial gems...?
EDIT
To be clear, I am only talking about those specific gems, like redgreen, which aren't actually used in the functional code, but only in the coding process. There is no need to vendor these at all, except to avoid the conditional require.
Gems that are specific to your development environment should be installed in your gemset or local gems, but not in the Gemfile.
A classic example is the ruby-debug-base19x which Rubymine needs for debugging. This is installed in your local gemset, but not in the Gemfile because not all coders use Rubymine.
[EDIT]
Indeed, everything is run in the context of the bundle, and outside gems are not reachable. There do exist some workarounds indeed. Most of them are dirty :)
I found a lot of good solutions in this bundler issue.
The nicest solution was to add this to your .irbrc :
# Add all gems in the global gemset to the $LOAD_PATH so they can be used even
# in places like 'rails console'.
if defined?(::Bundler)
global_gemset = ENV['GEM_PATH'].split(':').grep(/ruby.*#global/).first
if global_gemset
all_global_gem_paths = Dir.glob("#{global_gemset}/gems/*")
all_global_gem_paths.each do |p|
gem_path = "#{p}/lib"
$LOAD_PATH << gem_path
end
end
end
require 'irb/completion'
require 'rubygems'
require 'wirble'
Wirble.init
Wirble.colorize
If you then install wirble to the global gemset, it can then be found.
Original source: https://gist.github.com/794915
Hope this helps.
I answered a similar question of my own here
User-level bundler Gemfile
One way to do this is to create different environments:
group :scott do
end
Then
bundle --with-env=scott
Ok, I think I've come up with something. Basically, the idea is to only execute a secondary Gemfile when a Rails app is executing. To do this we add two things:
First, we alter the rails script a little:
# in ./script/rails
Kernel::IN_RAILS_APP = true
APP_PATH = File.expand_path('../../config/application', __FILE__)
require File.expand_path('../../config/boot', __FILE__)
require 'rails/commands'
Second, we tell bundler to pull in the secondary Gemfile if we're in a rails app and a secondary file exists:
# Gemfile
if Kernel.const_defined?(:IN_RAILS_APP)
local_gemfile = File.dirname(__FILE__) + "/Gemfile.local"
if File.exists?(local_gemfile)
puts 'using local gemfile'
self.instance_eval(Bundler.read_file(local_gemfile))
end
end
Now you can add a Gemfile.local to your project and run specific gems on a per-machine basis. bundle install works normally since the IN_RAILS_APP constant doesn't exist.
** Make sure to add Gemfile.local to your .gitignore.
In my opinions this is what environments are for. Fortunately there is also a way provided to do it with what is in your Gemfile, this is also how rails use it: groups
Pretty much use the environments the same way rails use it. Here is what you could find in your Gemfile:
group :test do
# Pretty printed test output
gem 'turn', :require => false
end
And here is what you can find in your config/application.rb
Bundler.require(:default, Rails.env) if defined?(Bundler)
All you would need to do is to change your local environment settings and the others working with you won't be affected unless they decide to. Everything gets committed and nothing gets lost.
Here some links :
http://yehudakatz.com/2010/05/09/the-how-and-why-of-bundler-groups/
http://gembundler.com/groups.html
If you want it to be optional, it's better to freeze the gem as a plugin. However, it's not a good idea to use different gems than the rest of a development team, as it creates some inconsistencies in the codebase that can be hard to track down later. I would say add it to config.gem, and just tell the other developers to do:
rake gems:install
And you're done.
This is how I tackled the same problem under Rails 3.1. In my Gemfile:
if File.exists? './tmp/eric_dev_gems'
gem 'redgreen'
gem 'awesome_print'
gem 'wirble'
gem 'wirb'
gem 'hirb'
end
Create a file in ./tmp/ (or in some folder which is in your .gitignore) of your choosing. I used eric_dev_gems. This should be ignored by git, and will only exist on your system unless one of your teammates decides he wants to create that file too.
I solved it by putting this in my gem file:
$gem_names ||= ENV['GEM_PATH'].split(':').map{|g| Dir.glob("#{g}/gems/*").map{|p|p.split('/gems/').last}}.flatten
gem 'redgreen' if $gem_names.any?{|n| n=~/redgreen/ }
That way the gem will only be used if you manually installed it on your system.
This works well but has the downside that it puts the gem name in the Gemfile.lock. This is of little consequence because the gem does not get installed with bundle install but it does make your lock file a bit messy and can cause the lock file to change a bit from one developer to the next.
If that is an issue for you another option is to keep the gemfile clean and require the gem by its full path, or you can add the path for just that gem. Like this:
$gem_paths ||= ENV['GEM_PATH'].split(':').map{|g| Dir.glob("#{g}/gems/*")}.flatten
$gem_paths.grep(/redgreen/).each {|p|$LOAD_PATH << p+'/lib'}
require 'redgreen' rescue nil

Resources