geminstaller 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +1 -0
- data/History.txt +4 -0
- data/LICENSE +21 -0
- data/Manifest.txt +62 -0
- data/README.txt +60 -0
- data/Rakefile +92 -0
- data/TODO.txt +88 -0
- data/bin/geminstaller +30 -0
- data/geminstaller.yml +34 -0
- data/lib/geminstaller/application.rb +105 -0
- data/lib/geminstaller/arg_parser.rb +162 -0
- data/lib/geminstaller/autogem.rb +43 -0
- data/lib/geminstaller/config.rb +69 -0
- data/lib/geminstaller/config_builder.rb +40 -0
- data/lib/geminstaller/dependency_injector.rb +129 -0
- data/lib/geminstaller/enhanced_stream_ui.rb +57 -0
- data/lib/geminstaller/exact_match_list_command.rb +16 -0
- data/lib/geminstaller/file_reader.rb +31 -0
- data/lib/geminstaller/gem_arg_processor.rb +44 -0
- data/lib/geminstaller/gem_command_line_proxy.rb +32 -0
- data/lib/geminstaller/gem_command_manager.rb +62 -0
- data/lib/geminstaller/gem_interaction_handler.rb +30 -0
- data/lib/geminstaller/gem_list_checker.rb +55 -0
- data/lib/geminstaller/gem_runner_proxy.rb +43 -0
- data/lib/geminstaller/gem_source_index_proxy.rb +21 -0
- data/lib/geminstaller/gem_spec_manager.rb +42 -0
- data/lib/geminstaller/geminstaller_error.rb +13 -0
- data/lib/geminstaller/hoe_extensions.rb +5 -0
- data/lib/geminstaller/install_processor.rb +56 -0
- data/lib/geminstaller/missing_dependency_finder.rb +41 -0
- data/lib/geminstaller/missing_file_error.rb +4 -0
- data/lib/geminstaller/noninteractive_chooser.rb +107 -0
- data/lib/geminstaller/output_filter.rb +49 -0
- data/lib/geminstaller/output_listener.rb +33 -0
- data/lib/geminstaller/output_observer.rb +32 -0
- data/lib/geminstaller/output_proxy.rb +36 -0
- data/lib/geminstaller/requires.rb +59 -0
- data/lib/geminstaller/rogue_gem_finder.rb +195 -0
- data/lib/geminstaller/ruby_gem.rb +44 -0
- data/lib/geminstaller/rubygems_exit.rb +5 -0
- data/lib/geminstaller/rubygems_extensions.rb +5 -0
- data/lib/geminstaller/unauthorized_dependency_prompt_error.rb +4 -0
- data/lib/geminstaller/unexpected_prompt_error.rb +4 -0
- data/lib/geminstaller/valid_platform_selector.rb +44 -0
- data/lib/geminstaller/version_specifier.rb +24 -0
- data/lib/geminstaller/yaml_loader.rb +22 -0
- data/lib/geminstaller.rb +102 -0
- data/start_local_gem_server.sh +1 -0
- data/test/test_all.rb +31 -0
- data/website/config.yaml +2 -0
- data/website/metainfo.yaml +72 -0
- data/website/src/code/index.page +25 -0
- data/website/src/community/index.page +14 -0
- data/website/src/community/links.page +9 -0
- data/website/src/default.css +168 -0
- data/website/src/default.template +41 -0
- data/website/src/documentation/documentation.page +417 -0
- data/website/src/documentation/index.page +88 -0
- data/website/src/documentation/tutorials.page +94 -0
- data/website/src/download.page +12 -0
- data/website/src/faq.page +7 -0
- data/website/src/index.page +38 -0
- metadata +107 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class Autogem
|
3
|
+
attr_writer :gem_command_manager, :gem_spec_manager, :gem_source_index_proxy
|
4
|
+
def autogem(gems)
|
5
|
+
@gem_source_index_proxy.refresh!
|
6
|
+
reversed_gems = gems.reverse!
|
7
|
+
@completed_names = []
|
8
|
+
@completed_gems = []
|
9
|
+
reversed_gems.each do |gem|
|
10
|
+
process_gem(gem)
|
11
|
+
end
|
12
|
+
@completed_gems
|
13
|
+
end
|
14
|
+
|
15
|
+
def process_gem(gem)
|
16
|
+
unless @completed_names.index(gem.name) or gem.no_autogem
|
17
|
+
invoke_require_gem_command(gem.name, gem.version)
|
18
|
+
@completed_names << gem.name
|
19
|
+
@completed_gems << gem
|
20
|
+
end
|
21
|
+
process_gem_dependencies(gem)
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_gem_dependencies(gem)
|
25
|
+
# TODO: this method is duplicated in rogue_gem_finder. Should abstract and take a block
|
26
|
+
matching_gems = @gem_spec_manager.local_matching_gems(gem)
|
27
|
+
matching_gems.each do |matching_gem|
|
28
|
+
dependency_gems = @gem_command_manager.dependency(matching_gem.name, matching_gem.version.to_s)
|
29
|
+
dependency_gems.each do |dependency_gem|
|
30
|
+
process_gem(dependency_gem)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def invoke_require_gem_command(name, version)
|
36
|
+
if Gem::RubyGemsVersion.index('0.8') == 0
|
37
|
+
require_gem(name, version)
|
38
|
+
else
|
39
|
+
gem(name, version)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class Config
|
3
|
+
def initialize(yaml)
|
4
|
+
@yaml = yaml
|
5
|
+
@default_install_options_string = nil
|
6
|
+
end
|
7
|
+
|
8
|
+
def gems
|
9
|
+
parse_defaults
|
10
|
+
gem_defs = @yaml["gems"]
|
11
|
+
gems = []
|
12
|
+
gem_defs.each do |gem_def|
|
13
|
+
name = gem_def['name']
|
14
|
+
version = gem_def['version'].to_s
|
15
|
+
platform = gem_def['platform'].to_s
|
16
|
+
# get install_options for specific gem, if specified
|
17
|
+
install_options_string = gem_def['install_options'].to_s
|
18
|
+
# if no install_options were specified for specific gem, and default install_options were specified...
|
19
|
+
if install_options_string.empty? &&
|
20
|
+
!@default_install_options_string.nil? &&
|
21
|
+
!@default_install_options_string.empty? then
|
22
|
+
# then use the default install_options
|
23
|
+
install_options_string = @default_install_options_string
|
24
|
+
end
|
25
|
+
install_options_array = []
|
26
|
+
# if there was an install options string specified, default or gem-specific, parse it to an array
|
27
|
+
install_options_array = install_options_string.split(" ") unless install_options_string.empty?
|
28
|
+
|
29
|
+
check_for_upgrade = gem_def['check_for_upgrade']
|
30
|
+
if check_for_upgrade.nil? && defined? @default_check_for_upgrade then
|
31
|
+
check_for_upgrade = @default_check_for_upgrade
|
32
|
+
end
|
33
|
+
|
34
|
+
fix_dependencies = gem_def['fix_dependencies']
|
35
|
+
if fix_dependencies.nil? && defined? @default_fix_dependencies then
|
36
|
+
fix_dependencies = @default_fix_dependencies
|
37
|
+
end
|
38
|
+
|
39
|
+
no_autogem = gem_def['no_autogem']
|
40
|
+
if no_autogem.nil? && defined? @default_no_autogem then
|
41
|
+
no_autogem = @default_no_autogem
|
42
|
+
end
|
43
|
+
|
44
|
+
gem = GemInstaller::RubyGem.new(
|
45
|
+
name,
|
46
|
+
:version => version,
|
47
|
+
:platform => platform,
|
48
|
+
:install_options => install_options_array,
|
49
|
+
:check_for_upgrade => check_for_upgrade,
|
50
|
+
:no_autogem => no_autogem,
|
51
|
+
:fix_dependencies => fix_dependencies)
|
52
|
+
gems << gem
|
53
|
+
end
|
54
|
+
gems.sort!
|
55
|
+
return gems
|
56
|
+
end
|
57
|
+
|
58
|
+
protected
|
59
|
+
|
60
|
+
def parse_defaults
|
61
|
+
defaults = @yaml["defaults"]
|
62
|
+
return if defaults.nil?
|
63
|
+
@default_install_options_string = defaults['install_options']
|
64
|
+
@default_check_for_upgrade = defaults['check_for_upgrade']
|
65
|
+
@default_fix_dependencies = defaults['fix_dependencies']
|
66
|
+
@default_no_autogem = defaults['no_autogem']
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
require File.expand_path("#{dir}/requires.rb")
|
3
|
+
|
4
|
+
module GemInstaller
|
5
|
+
class ConfigBuilder
|
6
|
+
attr_reader :config_file_paths_array
|
7
|
+
attr_writer :file_reader
|
8
|
+
attr_writer :yaml_loader
|
9
|
+
attr_writer :config_file_paths
|
10
|
+
|
11
|
+
def self.default_config_file_path
|
12
|
+
'geminstaller.yml'
|
13
|
+
end
|
14
|
+
|
15
|
+
def build_config
|
16
|
+
@config_file_paths ||= GemInstaller::ConfigBuilder.default_config_file_path
|
17
|
+
@config_file_paths_array = @config_file_paths.split(",")
|
18
|
+
merged_defaults = {}
|
19
|
+
merged_gems = {}
|
20
|
+
@config_file_paths_array.each do |path|
|
21
|
+
file_contents = @file_reader.read(path)
|
22
|
+
next unless file_contents
|
23
|
+
next if file_contents.empty?
|
24
|
+
file_contents_erb = ERB.new(%{#{file_contents}})
|
25
|
+
yaml = @yaml_loader.load(file_contents_erb.result)
|
26
|
+
merged_defaults.merge!(yaml['defaults']) unless yaml['defaults'].nil?
|
27
|
+
next unless yaml['gems']
|
28
|
+
yaml['gems'].each do |gem|
|
29
|
+
gem_key = [gem['name'],gem['version'],gem['platform']].join('-')
|
30
|
+
merged_gems[gem_key] = gem
|
31
|
+
end
|
32
|
+
end
|
33
|
+
merged_yaml = {}
|
34
|
+
merged_yaml['defaults'] = merged_defaults
|
35
|
+
merged_yaml['gems'] = merged_gems.values
|
36
|
+
config = GemInstaller::Config.new(merged_yaml)
|
37
|
+
config
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class DependencyInjector
|
3
|
+
def registry
|
4
|
+
@registry ||= create_registry
|
5
|
+
end
|
6
|
+
|
7
|
+
def create_registry
|
8
|
+
# define the service registry
|
9
|
+
@registry = GemInstaller::Registry.new
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Registry
|
14
|
+
attr_accessor :file_reader, :yaml_loader, :output_proxy, :config_builder, :gem_source_index, :gem_runner_proxy
|
15
|
+
attr_accessor :gem_runner, :gem_command_manager, :gem_list_checker, :app, :arg_parser, :options, :noninteractive_chooser
|
16
|
+
attr_accessor :gem_interaction_handler, :install_processor, :missing_dependency_finder, :valid_platform_selector
|
17
|
+
attr_accessor :output_listener, :outs_output_observer, :errs_output_observer, :output_filter, :autogem, :rogue_gem_finder
|
18
|
+
attr_accessor :gem_spec_manager
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@options = {}
|
22
|
+
@arg_parser = GemInstaller::ArgParser.new
|
23
|
+
@arg_parser.options = @options
|
24
|
+
|
25
|
+
@file_reader = GemInstaller::FileReader.new
|
26
|
+
@yaml_loader = GemInstaller::YamlLoader.new
|
27
|
+
@gem_arg_processor = GemInstaller::GemArgProcessor.new
|
28
|
+
@version_specifier = GemInstaller::VersionSpecifier.new
|
29
|
+
@output_proxy = GemInstaller::OutputProxy.new
|
30
|
+
@output_proxy.options = @options
|
31
|
+
@output_filter = GemInstaller::OutputFilter.new
|
32
|
+
@output_filter.options = @options
|
33
|
+
@output_filter.output_proxy = @output_proxy
|
34
|
+
|
35
|
+
|
36
|
+
@output_listener = GemInstaller::OutputListener.new
|
37
|
+
@output_listener.output_filter = @output_filter
|
38
|
+
|
39
|
+
@valid_platform_selector = GemInstaller::ValidPlatformSelector.new
|
40
|
+
@valid_platform_selector.output_filter = @output_filter
|
41
|
+
@valid_platform_selector.options = @options
|
42
|
+
|
43
|
+
@config_builder = GemInstaller::ConfigBuilder.new
|
44
|
+
@config_builder.file_reader = @file_reader
|
45
|
+
@config_builder.yaml_loader = @yaml_loader
|
46
|
+
|
47
|
+
@gem_source_index = Gem::SourceIndex.new
|
48
|
+
@gem_source_index_proxy = GemInstaller::GemSourceIndexProxy.new
|
49
|
+
@gem_source_index_proxy.gem_source_index = @gem_source_index
|
50
|
+
|
51
|
+
@gem_interaction_handler = GemInstaller::GemInteractionHandler.new
|
52
|
+
@noninteractive_chooser_class = GemInstaller::NoninteractiveChooser
|
53
|
+
@gem_interaction_handler.noninteractive_chooser_class = @noninteractive_chooser_class
|
54
|
+
@gem_interaction_handler.valid_platform_selector = @valid_platform_selector
|
55
|
+
|
56
|
+
@outs_output_observer = GemInstaller::OutputObserver.new
|
57
|
+
@outs_output_observer.stream = :stdout
|
58
|
+
@outs_output_observer.register(@output_listener)
|
59
|
+
@errs_output_observer = GemInstaller::OutputObserver.new
|
60
|
+
@errs_output_observer.stream = :stderr
|
61
|
+
@errs_output_observer.register(@output_listener)
|
62
|
+
|
63
|
+
@enhanced_stream_ui = GemInstaller::EnhancedStreamUI.new
|
64
|
+
@enhanced_stream_ui.outs = @outs_output_observer
|
65
|
+
@enhanced_stream_ui.errs = @errs_output_observer
|
66
|
+
@enhanced_stream_ui.gem_interaction_handler = @gem_interaction_handler
|
67
|
+
|
68
|
+
@gem_runner_class = Gem::GemRunner
|
69
|
+
@gem_cmd_manager_class = Gem::CommandManager
|
70
|
+
@exact_match_list_command = GemInstaller::ExactMatchListCommand.new
|
71
|
+
@gem_runner_proxy = GemInstaller::GemRunnerProxy.new
|
72
|
+
@gem_runner_proxy.gem_runner_class = @gem_runner_class
|
73
|
+
@gem_runner_proxy.gem_cmd_manager_class = @gem_cmd_manager_class
|
74
|
+
@gem_runner_proxy.output_listener = @output_listener
|
75
|
+
@gem_runner_proxy.enhanced_stream_ui = @enhanced_stream_ui
|
76
|
+
@gem_runner_proxy.options = @options
|
77
|
+
@gem_runner_proxy.output_filter = @output_filter
|
78
|
+
@gem_runner_proxy.exact_match_list_command = @exact_match_list_command
|
79
|
+
|
80
|
+
@gem_spec_manager = GemInstaller::GemSpecManager.new
|
81
|
+
@gem_spec_manager.valid_platform_selector = @valid_platform_selector
|
82
|
+
@gem_spec_manager.gem_source_index_proxy = @gem_source_index_proxy
|
83
|
+
@gem_spec_manager.output_filter = @output_filter
|
84
|
+
|
85
|
+
@gem_command_manager = GemInstaller::GemCommandManager.new
|
86
|
+
@gem_command_manager.gem_spec_manager = @gem_spec_manager
|
87
|
+
@gem_command_manager.gem_runner_proxy = @gem_runner_proxy
|
88
|
+
@gem_command_manager.gem_interaction_handler = @gem_interaction_handler
|
89
|
+
|
90
|
+
@rogue_gem_finder = GemInstaller::RogueGemFinder.new
|
91
|
+
@rogue_gem_finder.gem_command_manager = @gem_command_manager
|
92
|
+
@rogue_gem_finder.gem_spec_manager = @gem_spec_manager
|
93
|
+
@rogue_gem_finder.output_proxy = @output_proxy
|
94
|
+
|
95
|
+
@autogem = GemInstaller::Autogem.new
|
96
|
+
@autogem.gem_command_manager = @gem_command_manager
|
97
|
+
@autogem.gem_source_index_proxy = @gem_source_index_proxy
|
98
|
+
@autogem.gem_spec_manager = @gem_spec_manager
|
99
|
+
|
100
|
+
@gem_list_checker = GemInstaller::GemListChecker.new
|
101
|
+
@gem_list_checker.gem_command_manager = @gem_command_manager
|
102
|
+
@gem_list_checker.gem_arg_processor = @gem_arg_processor
|
103
|
+
@gem_list_checker.version_specifier = @version_specifier
|
104
|
+
|
105
|
+
@missing_dependency_finder = GemInstaller::MissingDependencyFinder.new
|
106
|
+
@missing_dependency_finder.gem_command_manager = @gem_command_manager
|
107
|
+
@missing_dependency_finder.gem_spec_manager = @gem_spec_manager
|
108
|
+
@missing_dependency_finder.gem_arg_processor = @gem_arg_processor
|
109
|
+
@missing_dependency_finder.output_filter = @output_filter
|
110
|
+
|
111
|
+
@install_processor = GemInstaller::InstallProcessor.new
|
112
|
+
@install_processor.options = @options
|
113
|
+
@install_processor.gem_list_checker = @gem_list_checker
|
114
|
+
@install_processor.gem_command_manager = @gem_command_manager
|
115
|
+
@install_processor.gem_spec_manager = @gem_spec_manager
|
116
|
+
@install_processor.missing_dependency_finder = @missing_dependency_finder
|
117
|
+
@install_processor.output_filter = @output_filter
|
118
|
+
|
119
|
+
@app = GemInstaller::Application.new
|
120
|
+
@app.autogem = @autogem
|
121
|
+
@app.rogue_gem_finder = @rogue_gem_finder
|
122
|
+
@app.options = @options
|
123
|
+
@app.arg_parser = @arg_parser
|
124
|
+
@app.config_builder = @config_builder
|
125
|
+
@app.install_processor = @install_processor
|
126
|
+
@app.output_filter = @output_filter
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class EnhancedStreamUI < Gem::StreamUI
|
3
|
+
attr_writer :gem_interaction_handler, :outs, :errs
|
4
|
+
|
5
|
+
def initialize()
|
6
|
+
# override default constructor to have no args
|
7
|
+
end
|
8
|
+
|
9
|
+
def ask_yes_no(question, default=nil)
|
10
|
+
begin
|
11
|
+
@gem_interaction_handler.handle_ask_yes_no(question)
|
12
|
+
rescue Exception => e
|
13
|
+
@outs.print(question)
|
14
|
+
@outs.flush
|
15
|
+
raise e
|
16
|
+
end
|
17
|
+
raise_unexpected_prompt_error(question)
|
18
|
+
end
|
19
|
+
|
20
|
+
def ask(question)
|
21
|
+
raise_unexpected_prompt_error(question)
|
22
|
+
end
|
23
|
+
|
24
|
+
def choose_from_list(question, list)
|
25
|
+
@gem_interaction_handler.handle_choose_from_list(question, list)
|
26
|
+
end
|
27
|
+
|
28
|
+
def terminate_interaction!(status=-1)
|
29
|
+
raise_error(status)
|
30
|
+
end
|
31
|
+
|
32
|
+
def terminate_interaction(status=0)
|
33
|
+
raise_error(status) unless status == 0
|
34
|
+
raise GemInstaller::RubyGemsExit.new(status)
|
35
|
+
end
|
36
|
+
|
37
|
+
def alert_error(statement, question=nil)
|
38
|
+
# if alert_error got called due to a GemInstaller::UnexpectedPromptError, re-throw it
|
39
|
+
last_exception = $!
|
40
|
+
if last_exception.class == GemInstaller::UnauthorizedDependencyPromptError || last_exception.class == GemInstaller::RubyGemsExit
|
41
|
+
raise last_exception
|
42
|
+
end
|
43
|
+
# otherwise let alert_error continue normally...
|
44
|
+
super(statement, question)
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
def raise_error(status)
|
49
|
+
raise GemInstaller::GemInstallerError.new("RubyGems exited abnormally. Status: #{status}\n")
|
50
|
+
end
|
51
|
+
|
52
|
+
def raise_unexpected_prompt_error(question)
|
53
|
+
raise GemInstaller::UnexpectedPromptError.new("GemInstaller Internal Error - Unexpected prompt received from RubyGems: '#{question}'.")
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class ExactMatchListCommand < Gem::ListCommand
|
3
|
+
def execute
|
4
|
+
string = get_one_optional_argument || ''
|
5
|
+
# This overrides the default RubyGems ListCommand behavior of doing a wildcard match. This caused problems
|
6
|
+
# when some gems (ActiveRecord-JDBC) caused exceptions during a remote list, causing a remote list
|
7
|
+
# of other gems (activerecord) to fail as well
|
8
|
+
options[:name] = /^#{string}$/i
|
9
|
+
# Do a little metaprogramming magic to avoid calling the problematic execute method on the ListCommand
|
10
|
+
# superclass, and instead directly call the method on the QueryCommand grandparent 'supersuperclass'
|
11
|
+
unbound_execute_method = Gem::QueryCommand.instance_method(:execute)
|
12
|
+
bound_execute_method = unbound_execute_method.bind(self)
|
13
|
+
bound_execute_method.call
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class FileReader
|
3
|
+
def read(file_path)
|
4
|
+
file_contents = nil
|
5
|
+
if !File.exist?(file_path) then
|
6
|
+
raise GemInstaller::MissingFileError.new("#{file_path}")
|
7
|
+
end
|
8
|
+
|
9
|
+
file = nil
|
10
|
+
begin
|
11
|
+
file = do_open(file_path)
|
12
|
+
rescue
|
13
|
+
raise GemInstaller::GemInstallerError.new("Error: Unable open file #{file_path}. Please ensure that this file can be opened.\n")
|
14
|
+
end
|
15
|
+
|
16
|
+
begin
|
17
|
+
do_read(file)
|
18
|
+
rescue
|
19
|
+
raise GemInstaller::GemInstallerError.new("Error: Unable read file #{file_path}. Please ensure that this file can be read.\n")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def do_open(file_path)
|
24
|
+
File.open(file_path)
|
25
|
+
end
|
26
|
+
|
27
|
+
def do_read(file)
|
28
|
+
file.read
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemArgProcessor
|
3
|
+
#Common Options:
|
4
|
+
# --source URL Use URL as the remote source for gems
|
5
|
+
# -p, --[no-]http-proxy [URL] Use HTTP proxy for remote operations
|
6
|
+
# -h, --help Get help on this command
|
7
|
+
# -v, --verbose Set the verbose level of output
|
8
|
+
# --config-file FILE Use this config file instead of default
|
9
|
+
# --backtrace Show stack backtrace on errors
|
10
|
+
# --debug Turn on Ruby debugging
|
11
|
+
GEM_COMMON_OPTIONS_WITHOUT_ARG = ['--no-http-proxy','-v','--verbose','--backtrace','--debug']
|
12
|
+
GEM_COMMON_OPTIONS_WITH_ARG = ['--source','-p','--http_proxy','--config-file']
|
13
|
+
|
14
|
+
# take an array of args, and strip all args that are not common gem command args
|
15
|
+
def strip_non_common_gem_args(args)
|
16
|
+
# I can't figure out a way to elegantly do this, so I hardcoded the options. Here's how you print them
|
17
|
+
# Gem::Command.common_options.each do |option|
|
18
|
+
# p option[0]
|
19
|
+
# end
|
20
|
+
|
21
|
+
common_args = []
|
22
|
+
i = 0
|
23
|
+
loop do
|
24
|
+
break if i == args.size
|
25
|
+
arg = args[i]
|
26
|
+
if GEM_COMMON_OPTIONS_WITHOUT_ARG.include?(arg)
|
27
|
+
common_args << arg
|
28
|
+
else
|
29
|
+
GEM_COMMON_OPTIONS_WITH_ARG.each do |option|
|
30
|
+
if arg.include?(option)
|
31
|
+
common_args << arg
|
32
|
+
unless arg.include?('=')
|
33
|
+
i += 1
|
34
|
+
common_args << args[i]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
i += 1
|
40
|
+
end
|
41
|
+
common_args
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemCommandLineProxy
|
3
|
+
GEM_ERROR_REGEXP = /^ERROR.*/
|
4
|
+
def run(args)
|
5
|
+
args_string = args.join(" ")
|
6
|
+
output = `gem #{args_string} 2>&1`
|
7
|
+
return_value = $?
|
8
|
+
lines = output.split("\n")
|
9
|
+
# return value is always zero on windows (from gem.bat), so we have to resort to parsing the output
|
10
|
+
error_in_output = false
|
11
|
+
lines.each do |line|
|
12
|
+
if line.match(GemInstaller::GemCommandLineProxy::GEM_ERROR_REGEXP)
|
13
|
+
error_in_output = true
|
14
|
+
break
|
15
|
+
end
|
16
|
+
end
|
17
|
+
if (return_value != 0 or error_in_output)
|
18
|
+
error_message = "Error: gem command failed. Command was 'gem #{args_string}', return value was '#{return_value}'. Output was:\n"
|
19
|
+
lines.each do |line|
|
20
|
+
error_message += ' ' + line + '\n'
|
21
|
+
end
|
22
|
+
raise GemInstaller::GemInstallerError.new(error_message)
|
23
|
+
end
|
24
|
+
return lines
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemCommandManager
|
3
|
+
attr_writer :gem_spec_manager, :gem_runner_proxy, :gem_interaction_handler
|
4
|
+
|
5
|
+
def list_remote_gem(gem, additional_options)
|
6
|
+
run_args = ["list",gem.name,"--remote"]
|
7
|
+
run_args += additional_options
|
8
|
+
@gem_runner_proxy.run(run_args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def uninstall_gem(gem)
|
12
|
+
return if !@gem_spec_manager.is_gem_installed?(gem)
|
13
|
+
@gem_interaction_handler.dependent_gem = gem
|
14
|
+
run_gem_command('uninstall',gem)
|
15
|
+
end
|
16
|
+
|
17
|
+
def install_gem(gem)
|
18
|
+
return if @gem_spec_manager.is_gem_installed?(gem)
|
19
|
+
@gem_interaction_handler.dependent_gem = gem
|
20
|
+
run_gem_command('install',gem)
|
21
|
+
end
|
22
|
+
|
23
|
+
def dependency(name, version, additional_options = [])
|
24
|
+
# it would be great to use the dependency --pipe option, but unfortunately, rubygems has a bug
|
25
|
+
# up to at least 0.9.2 where the pipe options uses 'puts', instead of 'say', so we can't capture it
|
26
|
+
# with enhanced_stream_ui. Patch submitted:
|
27
|
+
# http://rubyforge.org/tracker/index.php?func=detail&aid=9020&group_id=126&atid=577
|
28
|
+
run_args = ["dependency",/^#{name}$/,"--version",version]
|
29
|
+
run_args += additional_options
|
30
|
+
output_lines = @gem_runner_proxy.run(run_args)
|
31
|
+
# dependency output has all lines in the first element
|
32
|
+
output_array = output_lines[0].split("\n")
|
33
|
+
# drop the first line which just echoes the dependent gem
|
34
|
+
output_array.shift
|
35
|
+
# drop the line containing 'requires' (rubygems < 0.9.0)
|
36
|
+
if output_array[0] == ' Requires'
|
37
|
+
output_array.shift
|
38
|
+
end
|
39
|
+
# drop all empty lines
|
40
|
+
output_array.reject! { |line| line == "" }
|
41
|
+
# strip leading space
|
42
|
+
output_array.each { |line| line.strip! }
|
43
|
+
# convert into gems
|
44
|
+
output_gems = output_array.collect do |line|
|
45
|
+
name = line.split(' ')[0]
|
46
|
+
version_spec = line.split(/[()]/)[1]
|
47
|
+
GemInstaller::RubyGem.new(name, :version => version_spec)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def run_gem_command(gem_command,gem)
|
52
|
+
run_args = [gem_command,gem.name,"--version", "#{gem.version}"]
|
53
|
+
run_args += gem.install_options
|
54
|
+
@gem_runner_proxy.run(run_args)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemInteractionHandler
|
3
|
+
attr_writer :dependent_gem, :noninteractive_chooser_class, :valid_platform_selector
|
4
|
+
DEPENDENCY_PROMPT = 'Install required dependency'
|
5
|
+
|
6
|
+
def handle_ask_yes_no(question)
|
7
|
+
return unless question.index(DEPENDENCY_PROMPT)
|
8
|
+
message = "Error: RubyGems is prompting to install a required dependency, and you have not " +
|
9
|
+
"specified the '--install-dependencies' option for the current gem. You must modify your " +
|
10
|
+
"geminstaller config file to either specify the '--install-depencencies' (-y) " +
|
11
|
+
"option, or explicitly add an entry for the dependency gem earlier in the file.\n"
|
12
|
+
raise GemInstaller::UnauthorizedDependencyPromptError.new(message)
|
13
|
+
end
|
14
|
+
|
15
|
+
def handle_choose_from_list(question, list, noninteractive_chooser = nil)
|
16
|
+
noninteractive_chooser ||= @noninteractive_chooser_class.new
|
17
|
+
valid_platforms = nil
|
18
|
+
if dependent_gem_with_platform_specified?(list, noninteractive_chooser) or noninteractive_chooser.uninstall_list_type?(question)
|
19
|
+
valid_platforms = [@dependent_gem.platform]
|
20
|
+
else
|
21
|
+
valid_platforms = @valid_platform_selector.select(@dependent_gem.platform)
|
22
|
+
end
|
23
|
+
noninteractive_chooser.choose(question, list, @dependent_gem.name, @dependent_gem.version, valid_platforms)
|
24
|
+
end
|
25
|
+
|
26
|
+
def dependent_gem_with_platform_specified?(list, noninteractive_chooser)
|
27
|
+
noninteractive_chooser.dependent_gem?(@dependent_gem.name, list) and @dependent_gem.platform
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemListChecker
|
3
|
+
attr_writer :gem_command_manager, :gem_arg_processor, :version_specifier
|
4
|
+
|
5
|
+
def find_remote_matching_gem(gem)
|
6
|
+
gem_list_match_regexp = /^#{gem.regexp_escaped_name} \(.*/
|
7
|
+
common_args = @gem_arg_processor.strip_non_common_gem_args(gem.install_options)
|
8
|
+
remote_list = @gem_command_manager.list_remote_gem(gem, common_args)
|
9
|
+
|
10
|
+
matched_lines = []
|
11
|
+
remote_list.each do |line|
|
12
|
+
if line.match(gem_list_match_regexp)
|
13
|
+
matched_lines << line
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
if matched_lines.size > 1
|
18
|
+
error_message = "Error: more than one remote gem matches (this should not happen and is probably a bug in geminstaller). Gem name = '#{gem.name}', install options = '#{gem.install_options}'. Matching remote gems: \n"
|
19
|
+
matched_lines.each do |line|
|
20
|
+
error_message += line + "\n"
|
21
|
+
end
|
22
|
+
raise GemInstaller::GemInstallerError.new(error_message)
|
23
|
+
end
|
24
|
+
|
25
|
+
if matched_lines.size == 0
|
26
|
+
error_message = "Error: Could not find remote gem to install. Gem name = '#{gem.name}', install options = '#{gem.install_options}'"
|
27
|
+
raise GemInstaller::GemInstallerError.new(error_message)
|
28
|
+
end
|
29
|
+
|
30
|
+
return matched_lines[0]
|
31
|
+
end
|
32
|
+
|
33
|
+
def verify_and_specify_remote_gem!(gem)
|
34
|
+
# TODO: this seems like it is a hack, but we must have a non-ambiguous version on the gem in order for
|
35
|
+
# noninteractive_chooser to be able to parse the gem list for multi-platform gems. This will not be necessary
|
36
|
+
# if a future RubyGems release allows specification/searching of the platform, because then we won't need noninteractive_chooser
|
37
|
+
|
38
|
+
version_list = available_versions(gem)
|
39
|
+
specified_version = @version_specifier.specify(gem.version, version_list, gem.name)
|
40
|
+
gem.version = specified_version
|
41
|
+
end
|
42
|
+
|
43
|
+
def available_versions(gem)
|
44
|
+
remote_match_line = find_remote_matching_gem(gem)
|
45
|
+
version_list = parse_out_version_list(remote_match_line)
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_out_version_list(line)
|
49
|
+
# return everything between first set of parenthesis
|
50
|
+
version_list = line.split(/[()]/)[1]
|
51
|
+
version_list
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemRunnerProxy
|
3
|
+
attr_writer :gem_runner_class, :gem_cmd_manager_class, :output_listener, :exact_match_list_command
|
4
|
+
attr_writer :options, :enhanced_stream_ui, :output_filter
|
5
|
+
|
6
|
+
def run(args = [], stdin = [])
|
7
|
+
@output_filter.geminstaller_output(:commandecho,"'gem #{args.join(' ')}'\n")
|
8
|
+
gem_runner = create_gem_runner
|
9
|
+
# We have to manually initialize the configuration here, or else the GemCommandManager singleton
|
10
|
+
# will initialize with the (incorrect) default args when we call GemRunner.run.
|
11
|
+
gem_runner.do_configuration(args)
|
12
|
+
gem_cmd_manager = @gem_cmd_manager_class.instance
|
13
|
+
gem_cmd_manager.ui = @enhanced_stream_ui
|
14
|
+
gem_cmd_manager.register_command @exact_match_list_command
|
15
|
+
|
16
|
+
exit_status = nil
|
17
|
+
begin
|
18
|
+
gem_runner.run(args)
|
19
|
+
rescue GemInstaller::RubyGemsExit => normal_exit
|
20
|
+
exit_status = normal_exit.message
|
21
|
+
rescue GemInstaller::GemInstallerError => exit_error
|
22
|
+
raise_error_with_output(exit_error, args, @output_listener)
|
23
|
+
end
|
24
|
+
output_lines = @output_listener.read!
|
25
|
+
output_lines.push(exit_status) if exit_status
|
26
|
+
return output_lines
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_gem_runner
|
30
|
+
rubygems_version = Gem::RubyGemsVersion
|
31
|
+
if rubygems_version.index('0.8') == 0
|
32
|
+
@gem_runner_class.new()
|
33
|
+
else
|
34
|
+
@gem_runner_class.new(:command_manager => @gem_cmd_manager_class)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def raise_error_with_output(exit_error, args, listener)
|
39
|
+
descriptive_exit_message = exit_error.descriptive_exit_message(exit_error.message, 'gem', args, listener)
|
40
|
+
raise exit_error.class.new(descriptive_exit_message)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class GemSourceIndexProxy
|
3
|
+
attr_writer :gem_source_index
|
4
|
+
|
5
|
+
def refresh!
|
6
|
+
@gem_source_index.refresh!
|
7
|
+
end
|
8
|
+
|
9
|
+
# NOTE: We will require an exact version requirement, rather than the standard default Gem version_requirement of ">= 0"
|
10
|
+
# GemInstaller has it's own default defined elsewhere
|
11
|
+
def search(gem_pattern, version_requirement)
|
12
|
+
# Returns an array of Gem::Specification objects
|
13
|
+
@gem_source_index.search(gem_pattern, version_requirement)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|