Skip to content

Commit

Permalink
Improved, applied fixes from bundler team
Browse files Browse the repository at this point in the history
  • Loading branch information
mpapis committed Jun 7, 2011
1 parent a2f2bbc commit 58875b5
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 69 deletions.
85 changes: 81 additions & 4 deletions README.md
@@ -1,6 +1,7 @@
# About

Rubygems and Bundler integration, makes executable wrappers generated by rubygems aware of bundler.
Rubygems and Bundler integration, makes executable wrappers
generated by rubygems aware of bundler.

# Installation

Expand All @@ -9,13 +10,89 @@ Rubygems and Bundler integration, makes executable wrappers generated by rubygem

# Description

This gem is intended to fill in the integration gap between Bundler and Rubygems
This gem is intended to fill in the integration gap between
Bundler and Rubygems

With this gem rubygems generated wrappers will allow to
use bundler when asked for.

Using this gem solves problem of calling `bundle exec ...`
with your every command.

<h3 style="color: red; letter-spacing: 3px;">NOTE</h3>

Please note that method introduced by this gem is not approved
by rubygems neither bundler teams becouse of the following problem:

Beware, it is important to understund that this gem can make
your gem execetables load in versions specified in Gemfile!

The problem with Gemfile is that you can have few of them,
so expect that gem version for executable will be taken from
`~/Gemfile` when your project is in `~/projects/my_project`
and does not contain `Gemfile`

Last note is that bundler handling can be used only when bundler is installed.

# Controlling the wrapper

Wrappers generated by this gem will replace original rubygems wrapper,
but it will not change wrapper behavior unless explicitly asked for.

To allow using bundler when available, but fallback to rubygems when not:

export USE_BUNDLER=try

To force usage of bundler:

export USE_BUNDLER=force

To make your choice persistent put this into `~/.rvmrc` or `~/.bashrc`.

# How it works

Installation of gem will make any new installed gem use new bundler
aware wrapper:

gem install rubygems-bundler

To make gems already installed aware of bundler call the following command,
it will detect gems with executables and regenerate those executables:

gem regenerate_binstubs [-V]

Now for running haml can be controlled if it will using bundler code or not:

mpapis@niczsoft:~/test> USE_BUNDLER=no haml -v
Haml 3.1.1 (Separated Sally)

mpapis@niczsoft:~/test> USE_BUNDLER=try haml -v
Haml/Sass 3.0.0 (Classy Cassidy)

mpapis@niczsoft:~/test> gem uninstall bundler
Remove executables:
bundle
in addition to the gem? [Yn] y
Removing bundle
Successfully uninstalled bundler-1.0.14

mpapis@niczsoft:~/test> USE_BUNDLER=force haml -v
/home/mpapis/.rvm/gems/ruby-1.9.2-p180@test-bundler/bin/haml:25:in `rescue in <main>': (RuntimeError)

Please install bundler first.

from /home/mpapis/.rvm/gems/ruby-1.9.2-p180@test-bundler/bin/haml:18:in `<main>'

mpapis@niczsoft:~/test> USE_BUNDLER=try haml -v
Haml 3.1.1 (Separated Sally)

# Author

- Michal Papis <mpapis@gmail.com>

# Thanks

- Yehuda Katz : the initial patch code
- Wayne E. Seguin : support in writing the code
- Yehuda Katz : the initial patch code
- Wayne E. Seguin : support in writing good code
- Evan Phoenix : support on rubygems internalls
- Andre Arko : claryfications how rubygems/bundler works
28 changes: 21 additions & 7 deletions lib/rubygems/commands/regenerate_binstubs_command.rb
@@ -1,7 +1,7 @@
require 'yaml'
require 'rubygems/installer'

class Gem::Commands::RegenerateBinstubsCommand < Gem::Command
class RegenerateBinstubsCommand < Gem::Command
def initialize
super 'regenerate_binstubs', 'Re run generation of executable wrappers for gems.'
end
Expand All @@ -28,16 +28,30 @@ def description # :nodoc:

def execute
name = get_one_optional_argument || ''
specs = Gem.source_index.search Gem::Dependency.new /^#{name}/i, Gem::Requirement.default
specs = installed_gems.select{|spec| spec.name =~ /^#{name}/i }
specs.each do |spec|
unless spec.executables.empty?
puts "#{spec.name} #{spec.version}"
inst = Gem::Installer.new Dir[File.join(Gem.dir, 'cache', spec.file_name)].first, :wrappers => true, :force => true
class << inst
include Gem::BundlerInstaller
cache_gem = File.join(Gem.dir, 'cache', spec.file_name)
if File.exist? cache_gem
puts "#{spec.name} #{spec.version}"
inst = Gem::Installer.new Dir[cache_gem].first, :wrappers => true, :force => true
RubyGemsBundlerInstaller.bundler_generate_bin(inst)
else
puts "##{spec.name} #{spec.version} not found in GEM_HOME"
end
inst.bundler_generate_bin
end
end
end

private
def installed_gems
if Gem::VERSION > '1.8' then
Gem::Specification.to_a
else
Gem.source_index.map{|name,spec| spec}
end

Gem::VERSION > '1.8' ? Gem::Specification._all : Gem.source_index.map{|name,spec| spec}

end
end
83 changes: 27 additions & 56 deletions lib/rubygems/fix_wrapper.rb
@@ -1,93 +1,64 @@
module Gem::BundlerInstaller
def bundler_generate_bin
return if @spec.executables.nil? or @spec.executables.empty?

# If the user has asked for the gem to be installed in a directory that is
# the system gem directory, then use the system bin directory, else create
# (or use) a new bin dir under the gem_home.
bindir = @bin_dir ? @bin_dir : Gem.bindir(@gem_home)

Dir.mkdir bindir unless File.exist? bindir
raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir

@spec.executables.each do |filename|
module RubyGemsBundlerInstaller
# Iterate through executables and generate wrapper for each one,
# extract of rubygems code
def self.bundler_generate_bin(inst)
return if inst.spec.executables.nil? or inst.spec.executables.empty?
bindir = inst.bin_dir ? inst.bin_dir : Gem.bindir(inst.gem_home)
inst.spec.executables.each do |filename|
filename.untaint
bin_path = File.expand_path "#{@spec.bindir}/#{filename}", @gem_dir
if File.exist?(bin_path)
mode = File.stat(bin_path).mode | 0111
File.chmod mode, bin_path
end

if @wrappers then
bundler_generate_bin_script filename, bindir
bin_script_path = File.join bindir, inst.formatted_program_filename(filename)
FileUtils.rm_f bin_script_path
File.open bin_script_path, 'wb', 0755 do |file|
file.print bundler_app_script_text(inst, filename)
end
inst.say bin_script_path if Gem.configuration.really_verbose
end
end

##
# Creates the scripts to run the applications in the gem.
#--
# The Windows script is generated in addition to the regular one due to a
# bug or misfeature in the Windows shell's pipe. See
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/193379

def bundler_generate_bin_script(filename, bindir)
bin_script_path = File.join bindir, formatted_program_filename(filename)

FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers

File.open bin_script_path, 'wb', 0755 do |file|
file.print bundler_app_script_text(filename)
end

say bin_script_path if Gem.configuration.really_verbose
end

##
# Return the text for an application file.
def bundler_app_script_text(bin_file_name)
def self.bundler_app_script_text(inst, bin_file_name)
<<-TEXT
#{shebang bin_file_name}
#{inst.shebang bin_file_name}
#
# This file was generated by RubyGems extended wrapper.
#
# The application '#{spec.name}' is installed as part of a gem, and
# The application '#{inst.spec.name}' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
gemfile = ENV['BUNDLE_GEMFILE']
use_bundler = (ENV['USE_BUNDLER']||'').downcase
try_bundler = %w{try check possibly}.include? use_bundler
force_bundler = %w{force require yes true on}.include? use_bundler
version = "#{Gem::Requirement.default}"
if !gemfile.nil? && File.exist?(gemfile)
if try_bundler || force_bundler
begin
require 'bundler/setup'
rescue LoadError
gemfile = nil
raise '\n\nPlease install bundler first.\n\n' if force_bundler
try_bundler = false
end
else
gemfile = nil
end
if gemfile.nil?
unless try_bundler
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
version = $1
ARGV.shift
end
gem '#{spec.name}', version
gem '#{inst.spec.name}', version
end
load Gem.bin_path('#{spec.name}', '#{bin_file_name}', version)
load Gem.bin_path('#{inst.spec.name}', '#{bin_file_name}', version)
TEXT
end

end

module Gem
post_install do |inst|
class << inst
include Gem::BundlerInstaller
end
inst.bundler_generate_bin
RubyGemsBundlerInstaller.bundler_generate_bin(inst)
end
end
4 changes: 2 additions & 2 deletions rubygems-bundler.gemspec
@@ -1,7 +1,7 @@
Gem::Specification.new do |s|
s.name = "rubygems-bundler"
s.version = "0.1.0"
s.date = "2011-06-07"
s.version = "0.1.1"
s.date = "2011-06-08"
s.summary = "Make rubygems generate bundler aware executable wrappers"
s.email = "mpapis@gmail.com"
s.homepage = "https://github.com/mpapis/rubygems-bundler"
Expand Down

0 comments on commit 58875b5

Please sign in to comment.