开发者

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 tho开发者_如何学Pythonse 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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜