Skip to content

Instantly share code, notes, and snippets.

@tommeier
Last active December 18, 2015 13:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tommeier/5794153 to your computer and use it in GitHub Desktop.
Save tommeier/5794153 to your computer and use it in GitHub Desktop.
Append a gem dependency on installation of gem (example: RubyWMQ). The example being the dependency can only installed on deployed systems, and unable to install on development systems (ie: WMQ and developing on Mac)
require 'rubygems'
require 'bundler'
Bundler.module_eval do
def self.append_gem(gem_name, options = {})
return if gem_already_loaded?(gem_name, options[:version])
version = options[:version] || '*'
spec_path = Dir.glob("#{Gem.dir}/specifications/#{gem_name}-#{version}.gemspec").last
if spec_path.nil?
warn "Couldn't find #{gem_name}@version:#{version}"
return
end
spec = Gem::Specification.load spec_path
spec.activate
begin
Kernel.require options[:require] || gem_name
yield if block_given?
rescue Exception => err
warn "Couldn't load #{gem_name}: #{err}"
end
end
def self.gem_already_loaded?(gem_name, version = nil)
spec = Gem.loaded_specs[gem_name]
version ? spec && spec.version == version : !!spec
end
end
describe Bundler do
describe "#gem_already_loaded?" do
let(:loaded_specs) { {} }
before do
Gem.stub(:loaded_specs) { loaded_specs }
end
context 'versioning' do
let(:loaded_specs) {
{
'my_test' => mock('Gem::Specification', name: 'my_test', version: '0.0.5'),
'test_gem' => mock('Gem::Specification', name: 'test_gem', version: '1.2.3'),
'another_gem' => mock('Gem::Specification', name: 'another_gem', version: '1.2.4')
}
}
context "with specific version" do
it 'should return true when already loaded' do
Bundler.gem_already_loaded?('test_gem', '1.2.3').should be_true
end
it 'should return false when not loaded' do
Bundler.gem_already_loaded?('test_gem', '1.2.4').should be_false
end
end
context "with no specific version" do
it 'should return true when already loaded' do
Bundler.gem_already_loaded?('test_gem').should be_true
end
it 'should return false when not loaded' do
Bundler.gem_already_loaded?('unknown_gem').should be_false
end
end
end
end
describe "#append_gem" do
let(:gem_name) { 'test_gem_name' }
let(:gem_path) { '/some/file/location/test_gem_name-0.01.gem' }
let(:spec_path) { double('gem_spec', :activate => true) }
let(:options) { {} }
context 'with a single call' do
context 'cannot find a gem' do
before do
Dir.stub(:glob) { mock('ReturnedFileArray', :last => nil) }
end
context "with no version set" do
it 'should return nothing and make warning' do
Bundler.should_receive(:warn).with("Couldn't find #{gem_name}@version:*").once
end
end
context "with version info" do
let(:options) { {version: '1.2.3'} }
it 'should return nothing and make warning with version info' do
Bundler.should_receive(:warn).with("Couldn't find #{gem_name}@version:1.2.3").once
end
end
end
context "with a found gem" do
before do
Dir.stub(:glob) { mock('ReturnedFileArray', :last => gem_path) }
Gem.stub(:loaded_specs) { {} }
Gem::Specification.stub(:load) { spec_path }
end
context 'fail to load' do
it 'warns when loading' do
Kernel.stub(:require).and_raise(StandardError.new('Gem unable to load'))
Bundler.should_receive(:warn).with("Couldn't load #{gem_name}: Gem unable to load").once
end
end
context 'loading' do
it 'successfully loads and requires gem' do
Kernel.should_receive(:require).with(gem_name) { true }
end
it 'should return nothing if it is already loaded' do
Gem.stub(:loaded_specs) { { gem_name => double('Gem::Specification') } }
Kernel.should_not_receive(:require).with(gem_name)
Bundler.should_not_receive(:warn)
end
end
end
after do
Bundler.append_gem(gem_name, options)
end
end
context 'with optional require statement' do
before do
Dir.stub(:glob) { mock('ReturnedFileArray', :last => gem_path) }
Gem::Specification.stub(:load) { spec_path }
end
it 'should require a different name to the gem' do
Kernel.should_receive(:require).with('some_different_name') { true }
Bundler.append_gem(gem_name, :require => 'some_different_name')
end
end
context 'version specification' do
let(:root_path) { '/some/gem/location' }
let(:spec_path) { mock('GemSpecPath', last: "#{root_path}/#{gem_name}-1.2.3", activate: true )}
before do
Kernel.should_receive(:require).with(gem_name) { true }
end
context "with no version set" do
let(:options) {{}}
it 'should search for all versions of that gem' do
Dir.should_receive(:glob).with("#{Gem.dir}/specifications/#{gem_name}-*.gemspec").once { spec_path }
Gem::Specification.should_receive(:load).with(spec_path.last) { spec_path }
end
end
context "with specific version set" do
let(:options) {{ version: '1.2.3' }}
it 'should search and load the specific version of the gem' do
Dir.should_receive(:glob).with("#{Gem.dir}/specifications/#{gem_name}-1.2.3.gemspec").once { spec_path }
Gem::Specification.should_receive(:load).with(spec_path.last) { spec_path }
end
end
after do
Bundler.append_gem(gem_name, options)
end
end
context 'with an optional block' do
let(:passed_object_mock) { mock('SomeKlass') }
before do
Dir.stub(:glob) { mock('ReturnedFileArray', :last => gem_path) }
Gem::Specification.stub(:load) { spec_path }
Kernel.should_receive(:require).with(gem_name) { true }
end
it 'should yield any passed block' do
passed_object_mock.should_receive(:trigger_event).once { true }
Bundler.append_gem(gem_name) do
passed_object_mock.trigger_event
end
end
end
end
end
# -*- encoding: utf-8 -*-
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
Gem::Specification.new do |gem|
...
gem.extensions = ['ext/mkrf_conf.rb']
...
end
require 'rubygems'
require 'rubygems/command.rb'
require 'rubygems/dependency.rb'
require 'rubygems/dependency_installer.rb'
def gem_cached?(gem_name, gem_version, dependency_options = {})
Dir.chdir(File.join(Gem.dir, 'cache')) do
found_locally = Gem::DependencyInstaller.new(dependency_options).find_spec_by_name_and_version(gem_name, gem_version)
found_locally.any? { |gem_spec_array| gem_spec_array.first.version.to_s == gem_version.to_s }
end
rescue Gem::GemNotFoundException
false
end
def already_cached_locally?(gem_name, gem_version)
gem_cached?(gem_name, gem_version, {domain: :local}) || gem_cached?(gem_name, gem_version, {domain: :local, prerelease: true})
end
def gem_required?
ENV['SKIP_GEM_APPEND'].nil? && RUBY_PLATFORM =~ /linux|mswin|windows|sunos|solaris/i
end
def print_out(output)
STDOUT.puts "[Appending Gem] #{output}"
end
begin
Gem::Command.build_args = ARGV
rescue NoMethodError
end
append_gem_name = 'example_gem'
append_gem_version = '2.0.4'
begin
dep = Gem::Dependency.new(append_gem_name, append_gem_version)
print_out "Installing Gem : #{dep.name} @ #{dep.requirement}"
inst = Gem::DependencyInstaller.new
inst.install dep
print_out "Gem Installed"
begin
#Initialise and activate correct version
gem dep.name, dep.requirement
require 'example_gem'
rescue LoadError
print_out "Error loading Gem (require 'example_gem')."
end
rescue Exception => e
print_out e
print_out e.backtrace.join "\n "
exit(1)
end if gem_required? && !already_cached_locally?(append_gem_name, append_gem_version)
f = File.open(File.join(File.dirname(__FILE__), "Rakefile"), "w") # create dummy rakefile to indicate success
f.write("task :default\n")
f.close
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment