Class: Chef::Provider::Package::Rubygems

Inherits:
Chef::Provider::Package show all
Includes:
Mixin::GetSourceFromPackage, Mixin::ShellOut
Defined in:
lib/chef/provider/package/rubygems.rb

Defined Under Namespace

Classes: AlternateGemEnvironment, CurrentGemEnvironment, GemEnvironment

Constant Summary

Constants included from Mixin::ShellOut

Mixin::ShellOut::DEPRECATED_OPTIONS

Instance Attribute Summary collapse

Attributes inherited from Chef::Provider

#action, #current_resource, #new_resource, #run_context

Instance Method Summary collapse

Methods included from Mixin::ShellOut

#run_command_compatible_options, #shell_out, #shell_out!

Methods inherited from Chef::Provider::Package

#action_install, #action_purge, #action_reconfig, #action_remove, #action_upgrade, #define_resource_requirements, #expand_options, #get_preseed_file, #preseed_package, #preseed_resource, #reconfig_package, #removing_package?, #whyrun_supported?

Methods included from Mixin::Command

#chdir_or_tmpdir, #handle_command_failures, #output_of_command, #run_command, #run_command_with_systems_locale

Methods included from Mixin::Command::Windows

#popen4

Methods included from Mixin::Command::Unix

#popen4

Methods inherited from Chef::Provider

#action_nothing, #cookbook_name, #define_resource_requirements, #events, #node, #process_resource_requirements, #requirements, #resource_collection, #run_action, #set_updated_status, #whyrun_mode?, #whyrun_supported?

Methods included from DSL::Recipe

#method_missing

Methods included from Mixin::ConvertToClassName

#convert_to_class_name, #convert_to_snake_case, #filename_to_qualified_string, #snake_case_basename

Constructor Details

#initialize(new_resource, run_context = nil) ⇒ Rubygems

Returns a new instance of Rubygems.



334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/chef/provider/package/rubygems.rb', line 334

def initialize(new_resource, run_context=nil)
  super
  @cleanup_gem_env = true
  if new_resource.gem_binary
    if new_resource.options && new_resource.options.kind_of?(Hash)
      msg =  "options cannot be given as a hash when using an explicit gem_binary\n"
      msg << "in #{new_resource} from #{new_resource.source_line}"
      raise ArgumentError, msg
    end
    @gem_env = AlternateGemEnvironment.new(new_resource.gem_binary)
    Chef::Log.debug("#{@new_resource} using gem '#{new_resource.gem_binary}'")
  elsif is_omnibus? && (!@new_resource.instance_of? Chef::Resource::ChefGem)
    # Opscode Omnibus - The ruby that ships inside omnibus is only used for Chef
    # Default to installing somewhere more functional
    if new_resource.options && new_resource.options.kind_of?(Hash)
      msg = "options should be a string instead of a hash\n"
      msg << "in #{new_resource} from #{new_resource.source_line}"
      raise ArgumentError, msg
    end
    gem_location = find_gem_by_path
    @new_resource.gem_binary gem_location
    @gem_env = AlternateGemEnvironment.new(gem_location)
    Chef::Log.debug("#{@new_resource} using gem '#{gem_location}'")
  else
    @gem_env = CurrentGemEnvironment.new
    @cleanup_gem_env = false
    Chef::Log.debug("#{@new_resource} using gem from running ruby environment")
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Chef::DSL::Recipe

Instance Attribute Details

#cleanup_gem_envObject (readonly)

Returns the value of attribute cleanup_gem_env.



326
327
328
# File 'lib/chef/provider/package/rubygems.rb', line 326

def cleanup_gem_env
  @cleanup_gem_env
end

#gem_envObject (readonly)

Returns the value of attribute gem_env.



325
326
327
# File 'lib/chef/provider/package/rubygems.rb', line 325

def gem_env
  @gem_env
end

Instance Method Details

#all_installed_versionsObject



422
423
424
425
426
# File 'lib/chef/provider/package/rubygems.rb', line 422

def all_installed_versions
  @all_installed_versions ||= begin
    @gem_env.installed_versions(Gem::Dependency.new(gem_dependency.name, '>= 0'))
  end
end

#candidate_versionObject



448
449
450
451
452
453
454
455
456
457
458
# File 'lib/chef/provider/package/rubygems.rb', line 448

def candidate_version
  @candidate_version ||= begin
    if target_version_already_installed?
      nil
    elsif source_is_remote?
      @gem_env.candidate_version_from_remote(gem_dependency, *gem_sources).to_s
    else
      @gem_env.candidate_version_from_file(gem_dependency, @new_resource.source).to_s
    end
  end
end

#cleanup_after_convergeObject



441
442
443
444
445
446
# File 'lib/chef/provider/package/rubygems.rb', line 441

def cleanup_after_converge
  if @cleanup_gem_env
    logger.debug { "#{@new_resource} resetting gem environment to default" }
    Gem.clear_paths
  end
end

#current_versionObject



398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/chef/provider/package/rubygems.rb', line 398

def current_version
  #raise 'todo'
  # If one or more matching versions are installed, the newest of them
  # is the current version
  if !matching_installed_versions.empty?
    gemspec = matching_installed_versions.last
    logger.debug { "#{@new_resource} found installed gem #{gemspec.name} version #{gemspec.version} matching #{gem_dependency}"}
    gemspec
  # If no version matching the requirements exists, the latest installed
  # version is the current version.
  elsif !all_installed_versions.empty?
    gemspec = all_installed_versions.last
    logger.debug { "#{@new_resource} newest installed version of gem #{gemspec.name} is #{gemspec.version}" }
    gemspec
  else
    logger.debug { "#{@new_resource} no installed version found for #{gem_dependency.to_s}"}
    nil
  end
end

#find_gem_by_pathObject



378
379
380
381
382
383
384
# File 'lib/chef/provider/package/rubygems.rb', line 378

def find_gem_by_path
  Chef::Log.debug("#{@new_resource} searching for 'gem' binary in path: #{ENV['PATH']}")
  separator = ::File::ALT_SEPARATOR ? ::File::ALT_SEPARATOR : ::File::SEPARATOR
  path_to_first_gem = ENV['PATH'].split(::File::PATH_SEPARATOR).select { |path| ::File.exists?(path + separator + "gem") }.first
  raise Chef::Exceptions::FileNotFound, "Unable to find 'gem' binary in path: #{ENV['PATH']}" if path_to_first_gem.nil?
  path_to_first_gem + separator + "gem"
end

#gem_binary_pathObject



492
493
494
# File 'lib/chef/provider/package/rubygems.rb', line 492

def gem_binary_path
  @new_resource.gem_binary || 'gem'
end

#gem_dependencyObject



386
387
388
# File 'lib/chef/provider/package/rubygems.rb', line 386

def gem_dependency
  Gem::Dependency.new(@new_resource.package_name, @new_resource.version)
end

#gem_sourcesObject



428
429
430
# File 'lib/chef/provider/package/rubygems.rb', line 428

def gem_sources
  @new_resource.source ? Array(@new_resource.source) : nil
end

#install_package(name, version) ⇒ Object

Installs the gem, using either the gems API or shelling out to ‘gem` according to the following criteria:

  1. Use gems API (Gem::DependencyInstaller) by default

  2. shell out to ‘gem install` when a String of options is given

  3. use gems API with options if a hash of options is given



473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
# File 'lib/chef/provider/package/rubygems.rb', line 473

def install_package(name, version)
  if source_is_remote? && @new_resource.gem_binary.nil?
    if @new_resource.options.nil?
      @gem_env.install(gem_dependency, :sources => gem_sources)
    elsif @new_resource.options.kind_of?(Hash)
      options = @new_resource.options
      options[:sources] = gem_sources
      @gem_env.install(gem_dependency, options)
    else
      install_via_gem_command(name, version)
    end
  elsif @new_resource.gem_binary.nil?
    @gem_env.install(@new_resource.source)
  else
    install_via_gem_command(name,version)
  end
  true
end

#install_via_gem_command(name, version) ⇒ Object



496
497
498
499
500
501
502
503
504
505
506
507
# File 'lib/chef/provider/package/rubygems.rb', line 496

def install_via_gem_command(name, version)
  if @new_resource.source =~ /\.gem$/i
    name = @new_resource.source
  else
    src = @new_resource.source && "  --source=#{@new_resource.source} --source=http://rubygems.org"
  end
  if version
    shell_out!("#{gem_binary_path} install #{name} -q --no-rdoc --no-ri -v \"#{version}\"#{src}#{opts}", :env=>nil)
  else
    shell_out!("#{gem_binary_path} install #{name} -q --no-rdoc --no-ri #{src}#{opts}", :env=>nil)
  end
end

#is_omnibus?Boolean

Returns:

  • (Boolean)


364
365
366
367
368
369
370
371
372
373
374
375
376
# File 'lib/chef/provider/package/rubygems.rb', line 364

def is_omnibus?
  if RbConfig::CONFIG['bindir'] =~ %r!/opt/(opscode|chef)/embedded/bin!
    Chef::Log.debug("#{@new_resource} detected omnibus installation in #{RbConfig::CONFIG['bindir']}")
    # Omnibus installs to a static path because of linking on unix, find it.
    true
  elsif RbConfig::CONFIG['bindir'].sub(/^[\w]:/, '')  == "/opscode/chef/embedded/bin"
    Chef::Log.debug("#{@new_resource} detected omnibus installation in #{RbConfig::CONFIG['bindir']}")
    # windows, with the drive letter removed
    true
  else
    false
  end
end

#load_current_resourceObject



432
433
434
435
436
437
438
439
# File 'lib/chef/provider/package/rubygems.rb', line 432

def load_current_resource
  @current_resource = Chef::Resource::Package::GemPackage.new(@new_resource.name)
  @current_resource.package_name(@new_resource.package_name)
  if current_spec = current_version
    @current_resource.version(current_spec.version.to_s)
  end
  @current_resource
end

#loggerObject



328
329
330
# File 'lib/chef/provider/package/rubygems.rb', line 328

def logger
  Chef::Log.logger
end

#matching_installed_versionsObject



418
419
420
# File 'lib/chef/provider/package/rubygems.rb', line 418

def matching_installed_versions
  @matching_installed_versions ||= @gem_env.installed_versions(gem_dependency)
end

#purge_package(name, version) ⇒ Object



535
536
537
# File 'lib/chef/provider/package/rubygems.rb', line 535

def purge_package(name, version)
  remove_package(name, version)
end

#remove_package(name, version) ⇒ Object



513
514
515
516
517
518
519
520
521
522
523
524
525
# File 'lib/chef/provider/package/rubygems.rb', line 513

def remove_package(name, version)
  if @new_resource.gem_binary.nil?
    if @new_resource.options.nil?
      @gem_env.uninstall(name, version)
    elsif @new_resource.options.kind_of?(Hash)
      @gem_env.uninstall(name, version, @new_resource.options)
    else
      uninstall_via_gem_command(name, version)
    end
  else
    uninstall_via_gem_command(name, version)
  end
end

#source_is_remote?Boolean

Returns:

  • (Boolean)


390
391
392
393
394
395
396
# File 'lib/chef/provider/package/rubygems.rb', line 390

def source_is_remote?
  return true if @new_resource.source.nil?
  scheme = URI.parse(@new_resource.source).scheme
  # URI.parse gets confused by MS Windows paths with forward slashes.
  scheme = nil if scheme =~ /^[a-z]$/
  %w{http https}.include?(scheme)
end

#target_version_already_installed?Boolean

Returns:

  • (Boolean)


460
461
462
463
464
465
# File 'lib/chef/provider/package/rubygems.rb', line 460

def target_version_already_installed?
  return false unless @current_resource && @current_resource.version
  return false if @current_resource.version.nil?

  Gem::Requirement.new(@new_resource.version).satisfied_by?(Gem::Version.new(@current_resource.version))
end

#uninstall_via_gem_command(name, version) ⇒ Object



527
528
529
530
531
532
533
# File 'lib/chef/provider/package/rubygems.rb', line 527

def uninstall_via_gem_command(name, version)
  if version
    shell_out!("#{gem_binary_path} uninstall #{name} -q -x -I -v \"#{version}\"#{opts}", :env=>nil)
  else
    shell_out!("#{gem_binary_path} uninstall #{name} -q -x -I -a#{opts}", :env=>nil)
  end
end

#upgrade_package(name, version) ⇒ Object



509
510
511
# File 'lib/chef/provider/package/rubygems.rb', line 509

def upgrade_package(name, version)
  install_package(name, version)
end