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,44 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class RubyGem
|
3
|
+
include Comparable
|
4
|
+
attr_accessor :name, :version, :platform, :install_options, :check_for_upgrade, :fix_dependencies, :no_autogem, :prefer_binary_platform
|
5
|
+
|
6
|
+
def initialize(name, opts={})
|
7
|
+
@name = name
|
8
|
+
#TODO: the following logic could probably be more concise, but I'm not sure right now
|
9
|
+
@version = GemInstaller::RubyGem.default_version
|
10
|
+
if opts[:version] != "" && opts[:version] != nil
|
11
|
+
@version = opts[:version]
|
12
|
+
end
|
13
|
+
@platform = 'ruby'
|
14
|
+
if opts[:platform] != "" && opts[:platform] != nil
|
15
|
+
@platform = opts[:platform]
|
16
|
+
end
|
17
|
+
@install_options = opts[:install_options] ||= []
|
18
|
+
@check_for_upgrade = opts[:check_for_upgrade] == true ? true : false
|
19
|
+
@fix_dependencies = opts[:fix_dependencies] == true ? true : false
|
20
|
+
@no_autogem = opts[:no_autogem] == true ? true : false
|
21
|
+
@prefer_binary_platform = opts[:prefer_binary_platorm] == false ? false : true
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.default_version
|
25
|
+
'> 0.0.0'
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.default_platform
|
29
|
+
Gem::Platform::RUBY
|
30
|
+
end
|
31
|
+
|
32
|
+
def <=>(other)
|
33
|
+
compare = @name <=> other.name
|
34
|
+
return compare if compare != 0
|
35
|
+
compare = @version <=> other.version
|
36
|
+
return compare if compare != 0
|
37
|
+
return @platform <=> other.platform
|
38
|
+
end
|
39
|
+
|
40
|
+
def regexp_escaped_name
|
41
|
+
Regexp.escape(@name)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class ValidPlatformSelector
|
3
|
+
attr_writer :options, :ruby_platform, :output_filter
|
4
|
+
|
5
|
+
def select(gem_platform = nil, exact_platform_match = false)
|
6
|
+
@ruby_platform ||= RUBY_PLATFORM
|
7
|
+
return [@ruby_platform] if gem_platform == Gem::Platform::CURRENT
|
8
|
+
return [gem_platform] if exact_platform_match
|
9
|
+
valid_platforms = []
|
10
|
+
valid_platforms += binary_platform_substring if binary_platform_substring
|
11
|
+
if @options[:prefer_binary_platform] == false
|
12
|
+
# put ruby first if prefer_binary_platform is false
|
13
|
+
valid_platforms.unshift('ruby')
|
14
|
+
else
|
15
|
+
# leave binary platform first if prefer_binary_platform is false or nil
|
16
|
+
valid_platforms << 'ruby'
|
17
|
+
end
|
18
|
+
if gem_platform and
|
19
|
+
!valid_platforms.include?(gem_platform)
|
20
|
+
# only prepend the gem_platform as the first choice if
|
21
|
+
# 1. it is not nil
|
22
|
+
# 2. it is not already in the list
|
23
|
+
# 3. it is not 'ruby'
|
24
|
+
valid_platforms.unshift(gem_platform)
|
25
|
+
end
|
26
|
+
message = "Selecting valid platform(s): @ruby_platform='#{@ruby_platform}', gem_platform='#{gem_platform}', valid_platforms='#{valid_platforms.inspect}'"
|
27
|
+
@output_filter.geminstaller_output(:debug,"#{message}\n")
|
28
|
+
valid_platforms
|
29
|
+
end
|
30
|
+
|
31
|
+
def binary_platform_substring
|
32
|
+
return ['686-darwin'] if @ruby_platform =~ /686-darwin/
|
33
|
+
return ['cygwin'] if @ruby_platform =~ /cygwin/
|
34
|
+
return ['powerpc'] if @ruby_platform =~ /powerpc/
|
35
|
+
return ['i386-mswin32','mswin32'] if @ruby_platform =~ /mswin/
|
36
|
+
return ['386-linux'] if @ruby_platform =~ /386-linux/
|
37
|
+
return ['486-linux'] if @ruby_platform =~ /486-linux/
|
38
|
+
return ['586-linux'] if @ruby_platform =~ /586-linux/
|
39
|
+
return ['686-linux'] if @ruby_platform =~ /686-linux/
|
40
|
+
return ['solaris'] if @ruby_platform =~ /solaris/
|
41
|
+
return nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class VersionSpecifier
|
3
|
+
# NOTE: available_versions should be sorted in descending order (highest versions first)
|
4
|
+
# This method will return the first matching version
|
5
|
+
def specify(version_requirement, available_versions, gem_name = nil)
|
6
|
+
rubygems_version_requirement = Gem::Version::Requirement.new [version_requirement]
|
7
|
+
if available_versions.respond_to? :to_str
|
8
|
+
available_versions = available_versions.split(', ')
|
9
|
+
end
|
10
|
+
available_versions.each do |available_version_string|
|
11
|
+
available_version = Gem::Version.new(available_version_string)
|
12
|
+
if rubygems_version_requirement.satisfied_by?(available_version)
|
13
|
+
return available_version.to_s
|
14
|
+
end
|
15
|
+
end
|
16
|
+
gem_name_msg = ''
|
17
|
+
if gem_name
|
18
|
+
gem_name_msg = "for gem '#{gem_name}' "
|
19
|
+
end
|
20
|
+
error_msg = "The specified version requirement '#{version_requirement}' #{gem_name_msg}is not met by any of the available versions: #{available_versions.join(', ')}."
|
21
|
+
raise GemInstaller::GemInstallerError.new(error_msg)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module GemInstaller
|
2
|
+
class YamlLoader
|
3
|
+
def load(yaml_text)
|
4
|
+
yaml = nil
|
5
|
+
begin
|
6
|
+
yaml = YAML.load(yaml_text)
|
7
|
+
rescue Exception => e
|
8
|
+
message = e.message
|
9
|
+
error = <<-STRING_END
|
10
|
+
Error: Received error while attempting to parse YAML from yaml text:
|
11
|
+
#{message}
|
12
|
+
Please ensure this is valid YAML:
|
13
|
+
|
14
|
+
#{yaml_text}\n\n"
|
15
|
+
STRING_END
|
16
|
+
|
17
|
+
raise GemInstaller::GemInstallerError.new(error)
|
18
|
+
end
|
19
|
+
return yaml
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/geminstaller.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
require File.expand_path("#{dir}/geminstaller/requires.rb")
|
3
|
+
|
4
|
+
module GemInstaller
|
5
|
+
def self.run(args = [], geminstaller_executable = nil)
|
6
|
+
args_copy = args.dup
|
7
|
+
args_copy = args_copy.split(' ') unless args.respond_to? :join
|
8
|
+
# recursively call script with sudo, if --sudo option is specified
|
9
|
+
if platform_supports_sudo? and (args_copy.include?("-s") or args_copy.include?("--sudo"))
|
10
|
+
result = reinvoke_with_sudo(args_copy, geminstaller_executable)
|
11
|
+
if result != 0 and (args_copy.include?("-e") or args_copy.include?("--exceptions"))
|
12
|
+
message = "Error: GemInstaller failed while being invoked with --sudo option. See prior output for error, and use '--geminstaller-output=all --rubygems-output=all' options to get more details."
|
13
|
+
raise GemInstaller::GemInstallerError.new(message)
|
14
|
+
end
|
15
|
+
return result
|
16
|
+
else
|
17
|
+
app = create_application(args_copy)
|
18
|
+
app.run
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.autogem(args = [])
|
23
|
+
args_copy = args.dup
|
24
|
+
args_copy = args_copy.split(' ') unless args.respond_to? :join
|
25
|
+
# TODO: should explicitly remove all args not applicable to autogem (anything but config, silent, and geminstaller_output)
|
26
|
+
args_without_sudo = strip_sudo(args_copy)
|
27
|
+
app = create_application(args_without_sudo)
|
28
|
+
app.autogem
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.version
|
32
|
+
"0.2.0"
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.create_application(args = [], registry = nil)
|
36
|
+
registry ||= create_registry
|
37
|
+
app = registry.app
|
38
|
+
app.args = args
|
39
|
+
app
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.create_registry
|
43
|
+
dependency_injector = GemInstaller::DependencyInjector.new
|
44
|
+
dependency_injector.registry
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.parse_config_paths(config_paths)
|
48
|
+
return nil unless config_paths
|
49
|
+
return config_paths unless config_paths.respond_to? :join
|
50
|
+
return config_paths.join(',')
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.platform_supports_sudo?
|
54
|
+
return true unless RUBY_PLATFORM =~ /mswin/
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.reinvoke_with_sudo(args, geminstaller_executable)
|
59
|
+
# TODO: this sudo support seems like a hack, but I don't have a better idea right now. Ideally, we would
|
60
|
+
# invoke rubygems from the command line inside geminstaller. However, we can't do that because rubygems
|
61
|
+
# currently doesn't provide a way to specify platform from the command line - it always pops up the list
|
62
|
+
# for multi-platform gems, and we have to extend/hack rubygems to manage that.
|
63
|
+
# Feel free to comment or improve it, this seems to work for now...
|
64
|
+
geminstaller_executable ||= find_geminstaller_executable
|
65
|
+
args_without_sudo = strip_sudo(args)
|
66
|
+
args_without_sudo << '--redirect-stderr-to-stdout'
|
67
|
+
cmd = "sudo ruby #{geminstaller_executable} #{args_without_sudo.join(' ')}"
|
68
|
+
# TODO: this eats any output. There currently is no standard way to get a return code AND stdin AND stdout.
|
69
|
+
# Some non-standard packages like Open4 handle this, but not synchronously. The Simplest Thing That Could
|
70
|
+
# Possibly Work s to have a command line option which will cause all stderr output to be redirected to stdout.
|
71
|
+
result = system(cmd)
|
72
|
+
return 1 unless result
|
73
|
+
return $?.exitstatus
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.strip_sudo(args)
|
77
|
+
return args.reject {|arg| arg == "-s" || arg == "--sudo"}
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.find_geminstaller_executable
|
81
|
+
possible_locations = [
|
82
|
+
'ruby -S geminstaller',
|
83
|
+
'ruby /usr/local/bin/geminstaller',
|
84
|
+
'ruby /usr/bin/geminstaller',
|
85
|
+
'ruby ./bin/geminstaller'
|
86
|
+
]
|
87
|
+
path_key = 'geminstaller_exec_path='
|
88
|
+
possible_locations.each do |possible_location|
|
89
|
+
cmd = "#{possible_location} --geminstaller-exec-path"
|
90
|
+
|
91
|
+
$stderr_backup = $stderr.dup
|
92
|
+
$stderr.reopen("/dev/null", "w")
|
93
|
+
io = IO.popen(cmd)
|
94
|
+
$stderr = $stderr_backup.dup
|
95
|
+
output = io.read
|
96
|
+
next unless output =~ /#{path_key}/
|
97
|
+
path = output.sub(path_key,'')
|
98
|
+
return path
|
99
|
+
end
|
100
|
+
raise GemInstaller::GemInstallerError.new("Error: Unable to locate the geminstaller executable. Specify it explicitly, or don't use the sudo option.")
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
gem_server --dir=./spec/fixture/gems --port=9909
|
data/test/test_all.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# TODO: This is the only thing I could hack together in order to run the specs after upgrading
|
2
|
+
# to Rspec 0.9.3. Need to be back to all specs being runnable via 'rake' or 'test_all'
|
3
|
+
# Are there any good examples of running rspec for a non-rails project, and allowing post-suite hooks?
|
4
|
+
|
5
|
+
dir = File.dirname(__FILE__)
|
6
|
+
specdir = File.expand_path("#{dir}/../spec")
|
7
|
+
require File.expand_path("#{specdir}/helper/spec_helper")
|
8
|
+
|
9
|
+
spec_files = Dir.glob("#{specdir}/**/*_spec.rb")
|
10
|
+
# put test_gem_home_spec first so we only have to perform the install once
|
11
|
+
spec_files.unshift(Dir.glob("#{specdir}/unit/test_gem_home_spec.rb")[0])
|
12
|
+
spec_files.uniq
|
13
|
+
|
14
|
+
args = spec_files
|
15
|
+
args << "--color"
|
16
|
+
args << "--format"
|
17
|
+
args << "specdoc"
|
18
|
+
args << "--diff"
|
19
|
+
args << "unified"
|
20
|
+
|
21
|
+
$behaviour_runner = ::Spec::Runner::OptionParser.new.create_behaviour_runner(args, STDERR, STDOUT, false)
|
22
|
+
|
23
|
+
retval = 1
|
24
|
+
begin
|
25
|
+
retval = $behaviour_runner.run(args, false)
|
26
|
+
ensure
|
27
|
+
$server_was_stopped = GemInstaller::EmbeddedGemServer.stop
|
28
|
+
GemInstaller::TestGemHome.reset
|
29
|
+
end
|
30
|
+
retval ||= 0
|
31
|
+
raise "Specs failed, return value = #{retval}" unless retval == 0
|
data/website/config.yaml
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
index.page:
|
2
|
+
inMenu: true
|
3
|
+
orderInfo: 10
|
4
|
+
|
5
|
+
documentation:
|
6
|
+
title: Documentation
|
7
|
+
orderInfo: 20
|
8
|
+
|
9
|
+
download.page:
|
10
|
+
inMenu: true
|
11
|
+
orderInfo: 30
|
12
|
+
|
13
|
+
faq.page:
|
14
|
+
inMenu: true
|
15
|
+
orderInfo: 40
|
16
|
+
|
17
|
+
community:
|
18
|
+
title: Community
|
19
|
+
orderInfo: 50
|
20
|
+
|
21
|
+
code:
|
22
|
+
title: Code
|
23
|
+
orderInfo: 60
|
24
|
+
|
25
|
+
documentation/index.page:
|
26
|
+
inMenu: true
|
27
|
+
orderInfo: 10
|
28
|
+
|
29
|
+
documentation/tutorials.page:
|
30
|
+
title: Tutorials
|
31
|
+
inMenu: true
|
32
|
+
orderInfo: 20
|
33
|
+
|
34
|
+
documentation/documentation.page:
|
35
|
+
title: Documentation
|
36
|
+
inMenu: true
|
37
|
+
orderInfo: 30
|
38
|
+
|
39
|
+
community/index.page:
|
40
|
+
inMenu: true
|
41
|
+
orderInfo: 10
|
42
|
+
|
43
|
+
community/links.page:
|
44
|
+
title: Links
|
45
|
+
inMenu: true
|
46
|
+
orderInfo: 30
|
47
|
+
|
48
|
+
code/index.page:
|
49
|
+
title: Design
|
50
|
+
inMenu: true
|
51
|
+
orderInfo: 10
|
52
|
+
|
53
|
+
---
|
54
|
+
community/rubyforge.html:
|
55
|
+
url: http://rubyforge.org/projects/geminstaller/
|
56
|
+
inMenu: true
|
57
|
+
title: Rubyforge
|
58
|
+
orderInfo: 40
|
59
|
+
|
60
|
+
code/rdoc:
|
61
|
+
url: rdoc/index.html
|
62
|
+
inMenu: true
|
63
|
+
title: Rdoc
|
64
|
+
orderInfo: 20
|
65
|
+
|
66
|
+
code/coverage:
|
67
|
+
url: coverage/index.html
|
68
|
+
inMenu: true
|
69
|
+
title: Coverage
|
70
|
+
orderInfo: 30
|
71
|
+
|
72
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
---
|
2
|
+
title: Design
|
3
|
+
---
|
4
|
+
h1. Design Notes
|
5
|
+
|
6
|
+
These are some notes on the underlying design of GemInstaller, and the current state of development:
|
7
|
+
|
8
|
+
* GemInstaller was developed from nothing but a concept, using "Behavior-Driven Development":http://behaviour-driven.org and "Rspec":http://rspec.rubyforge.org/.
|
9
|
+
* GemInstaller uses "Dependency Injection", an architecture which has many benefits, including testability and enabling "loose coupling and high cohesion":http://www.c2.com/cgi/wiki?CouplingAndCohesion. I originally started with "Needle":http://rubyforge.org/projects/needle/, a Ruby Dependency Injection framework, but switched to a simple home-grown approach in other to not have a dependency on the Needle gem. Read more about Dependency Injection here:
|
10
|
+
** "http://onestepback.org/index.cgi/Tech/Ruby/DependencyInjectionInRuby.rdoc":http://onestepback.org/index.cgi/Tech/Ruby/DependencyInjectionInRuby.rdoc
|
11
|
+
** "http://onestepback.org/articles/depinj/":http://onestepback.org/articles/depinj/
|
12
|
+
** "http://martinfowler.com/bliki/InversionOfControl.html":http://martinfowler.com/bliki/InversionOfControl.html
|
13
|
+
** "http://martinfowler.com/articles/injection.html":http://martinfowler.com/articles/injection.html
|
14
|
+
** "http://www.theserverside.com/tt/articles/article.tss?l=IOCBeginners":http://www.theserverside.com/tt/articles/article.tss?l=IOCBeginners
|
15
|
+
** "http://www.ibm.com/developerworks/edu/j-dw-java-springswing-i.html":http://www.ibm.com/developerworks/edu/j-dw-java-springswing-i.html
|
16
|
+
* A lot of effort has gone into supporting isolated functional testing. Every run of a functional spec or the spec suite creates a new "Test Gem Home" sandbox installation of RubyGems. This creates a new GEM_HOME on the fly for each run. This allows me to test actual gem installation without being connected to a network, and avoid mucking with (or having false failures due to) my actual local RubyGems gem repository. Unfortunately, the Ruby load path still uses the executables from the system installation of RubyGems. I plan on fixing that too (which will allow me to test multiple RubyGems versions), but that seems to be a much trickier task than just having a different GEM_HOME.
|
17
|
+
* Spec/Test Philosophy:
|
18
|
+
** GemInstaller's specs are grouped into distinct categories. This and other testing approaches I use are heavily influenced by this fine article at GroboUtils: "Naming Your Junit Tests":http://groboutils.sourceforge.net/testing-junit/art_naming.html
|
19
|
+
** Unit vs. Functional: Many classes have two identically-named spec files associated with them, under unit and functional.
|
20
|
+
*** Unit Specs: The Unit specs for the most part test only a single class in isolation, usually using mock objects, so they run very fast. Many of them are vestiges of my initial BDD approach when I started from nothing. I incur a little bit of overhead cost to maintain them as the app evolves, but I don't mind that as much as some people :). They also come in very handy when I want to BDD some new behavior and don't want to have the high "Test Gem Home" fixture creation overhead that the functional specs have.
|
21
|
+
*** Functional Specs: These also have a one-to-one relationship with classes for the most part - geminstaller_spec is an exception. Most of these test how groups of classes interact together at various levels. Most of them use the "Test Gem Home" fixture approach. This is effective, but adds several seconds to the startup of each run. There is also overlap between some of them, especially at high levels of the API, which adds some maintenance overhead, but is worth it to me since it helps catch integration bugs.
|
22
|
+
*** Smoke Tests: There are some tests under /spec/smoketests which I run manually. These are really coarse grained scripts, and don't use Rspec or Test::Unit. They hit the live rubygems.org gem repository, and install/uninstall actual gems. They are used as a periodic check to ensure that my "Test Gem Home" fixture approach is not masking any real-life bugs.
|
23
|
+
* I'm a proponent of high code coverage. I check periodically to ensure I maintain close to 100% code coverage, not counting platform- and version-specific code blocks (and I'll get around to those some day). Also, the sudo recursive invocation stuff still needs some tests, but that's a bit tricky to automate.
|
24
|
+
* The tests are flaky on Windows (but run fine on mac/linux). The app itself runs fine, so I believe it's something related to spawning processes for the test GEM_HOME and EmbeddedGemServer. I plan to look at this too, eventually.
|
25
|
+
* One of my motivations for creating GemInstaller was as an exercise to help me learn Ruby better. If you see me doing anything obviously dumb or inefficient in the code or design, please let me know. Some of them I already know about, but haven't fixed, most I'm probably not aware of :)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
---
|
2
|
+
title: Feedback
|
3
|
+
---
|
4
|
+
h1. Feedback, Bugs, and Source Code
|
5
|
+
|
6
|
+
h2. Rubyforge Bug/Feature Tracker and Mailing Lists
|
7
|
+
|
8
|
+
* "RubyForge Page":http://rubyforge.org/projects/geminstaller/
|
9
|
+
* "Bug/Feature Tracker":http://rubyforge.org/tracker/?group_id=1902
|
10
|
+
* "Mailing Lists":http://rubyforge.org/mail/?group_id=1902
|
11
|
+
|
12
|
+
h2. Source Code
|
13
|
+
|
14
|
+
* Subversion Repository: svn://rubyforge.org/var/svn/geminstaller/trunk
|
@@ -0,0 +1,9 @@
|
|
1
|
+
|
2
|
+
h1. Links
|
3
|
+
|
4
|
+
* Chad's links:
|
5
|
+
** "Chad's Home Page":http://www.thewoolleyweb.com
|
6
|
+
** "Pivotal Labs":http://pivotallabs.com - where Chad works
|
7
|
+
** "DiabeticMommy.com":http://www.diabeticmommy.com - Support site for women with diabetes, created and edited by Chad's wife
|
8
|
+
* RubyGems
|
9
|
+
** "RubyGems Homepage":http://rubygems.org
|
@@ -0,0 +1,168 @@
|
|
1
|
+
/* andreas09 v1.0 (Dec 10th 2005) - An open source template by Andreas Viklund - http://andreasviklund.com. Free to use for any purpose as long as the proper credits are given to the original author. */
|
2
|
+
|
3
|
+
body {
|
4
|
+
background:#FFFFFF;
|
5
|
+
color:#303030;
|
6
|
+
font:90% Verdana,Tahoma,Arial,sans-serif;
|
7
|
+
margin:0;
|
8
|
+
padding:0;
|
9
|
+
text-align:center;
|
10
|
+
}s
|
11
|
+
|
12
|
+
a {
|
13
|
+
color:#505050;
|
14
|
+
font-weight:bold;
|
15
|
+
text-decoration:none;
|
16
|
+
}
|
17
|
+
|
18
|
+
a:hover {
|
19
|
+
color:#808080;
|
20
|
+
text-decoration:underline;
|
21
|
+
}
|
22
|
+
|
23
|
+
p {
|
24
|
+
line-height:1.5em;
|
25
|
+
margin:0 0 15px;
|
26
|
+
}
|
27
|
+
|
28
|
+
code,pre {
|
29
|
+
font-size:1.3em;
|
30
|
+
}
|
31
|
+
|
32
|
+
/*** Main container ***/
|
33
|
+
|
34
|
+
#container {
|
35
|
+
background:#CCCCCC;
|
36
|
+
color:#303030;
|
37
|
+
margin:0;
|
38
|
+
min-width:770px;
|
39
|
+
padding:0;
|
40
|
+
text-align:left;
|
41
|
+
width:100%;
|
42
|
+
}
|
43
|
+
|
44
|
+
/*** Header section ***/
|
45
|
+
|
46
|
+
#header {
|
47
|
+
background:#000000;
|
48
|
+
padding-bottom:10px;
|
49
|
+
}
|
50
|
+
|
51
|
+
#sitename {
|
52
|
+
color:#ffffff;
|
53
|
+
margin:0 20px 10px 40px;
|
54
|
+
text-align:left;
|
55
|
+
}
|
56
|
+
|
57
|
+
#sitename h1,#sitename h2 {
|
58
|
+
letter-spacing:7px;
|
59
|
+
margin:0;
|
60
|
+
padding:0;
|
61
|
+
}
|
62
|
+
|
63
|
+
#sitename h1 {
|
64
|
+
font-size:2.6em;
|
65
|
+
padding-top:18px;
|
66
|
+
}
|
67
|
+
|
68
|
+
#sitename h2 {
|
69
|
+
font-size:1.8em;
|
70
|
+
}
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
#navigation {
|
75
|
+
text-align:left;
|
76
|
+
margin:0 18px 10px auto;
|
77
|
+
padding:0;
|
78
|
+
font-size:1.4em;
|
79
|
+
line-height:.3em;
|
80
|
+
}
|
81
|
+
#navigation li {
|
82
|
+
margin:0;
|
83
|
+
padding-right: 10px;
|
84
|
+
list-style:none;
|
85
|
+
display:inline;
|
86
|
+
}
|
87
|
+
#navigation li a {
|
88
|
+
margin:0 1px 0 1px;
|
89
|
+
color:#666;
|
90
|
+
text-decoration:none;
|
91
|
+
font-weight:bold;
|
92
|
+
}
|
93
|
+
#navigation li a:hover {
|
94
|
+
color:#AA2233;
|
95
|
+
}
|
96
|
+
#navigation li.webgen-menu-item-selected a, #naviation li.webgen-menu-item-selected a:hover {
|
97
|
+
color: #000;
|
98
|
+
}
|
99
|
+
|
100
|
+
|
101
|
+
/*** Content wrap ***/
|
102
|
+
|
103
|
+
#wrap {
|
104
|
+
background:#FFFFFF;
|
105
|
+
clear:both;
|
106
|
+
font-size:1.1em;
|
107
|
+
padding-top:10px;
|
108
|
+
}
|
109
|
+
|
110
|
+
/*** Content ***/
|
111
|
+
#content {
|
112
|
+
padding:0px 20px 5px;
|
113
|
+
}
|
114
|
+
|
115
|
+
#content {
|
116
|
+
margin-top: 10px;
|
117
|
+
margin-left: 20px;
|
118
|
+
margin-right: 20px;
|
119
|
+
}
|
120
|
+
|
121
|
+
#content h1,#content h2,#content h3 {
|
122
|
+
background-color:inherit;
|
123
|
+
color:#606060;
|
124
|
+
font-size:1.8em;
|
125
|
+
font-weight:bold;
|
126
|
+
letter-spacing:-1px;
|
127
|
+
margin:0 0 10px;
|
128
|
+
padding:0;
|
129
|
+
}
|
130
|
+
|
131
|
+
#content h2 {
|
132
|
+
font-size:1.6em;
|
133
|
+
margin-bottom:10px;
|
134
|
+
}
|
135
|
+
|
136
|
+
#content h3 {
|
137
|
+
font-size:1.4em;
|
138
|
+
margin-bottom:10px;
|
139
|
+
}
|
140
|
+
|
141
|
+
#content img {
|
142
|
+
border:1px solid #b0b0b0;
|
143
|
+
float:left;
|
144
|
+
margin:5px 15px 6px;
|
145
|
+
padding:5px;
|
146
|
+
}
|
147
|
+
|
148
|
+
/*** Footer ***/
|
149
|
+
|
150
|
+
#footer {
|
151
|
+
background:#FFFFFF;
|
152
|
+
clear:both;
|
153
|
+
color:#d0d0d0;
|
154
|
+
font-size:1.0em;
|
155
|
+
font-weight:bold;
|
156
|
+
margin:0;
|
157
|
+
padding:20px 0;
|
158
|
+
text-align:center;
|
159
|
+
width:100%;
|
160
|
+
}
|
161
|
+
|
162
|
+
#footer a {
|
163
|
+
color:#d0d0d0;
|
164
|
+
font-weight:bold;
|
165
|
+
}
|
166
|
+
|
167
|
+
|
168
|
+
/*** End of file ***/
|
@@ -0,0 +1,41 @@
|
|
1
|
+
--- content, html
|
2
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
3
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
4
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="{lang:}">
|
5
|
+
<head>
|
6
|
+
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
7
|
+
<meta name="description" content="geminstaller.rubyforge.org" />
|
8
|
+
<meta name="keywords" content="ruby,gems,rubygems" />
|
9
|
+
<meta name="author" content="Chad Woolley / Original design by Andreas Viklund - http://andreasviklund.com" />
|
10
|
+
<meta name="generator" content="webgen - http://webgen.rubyforge.org" />
|
11
|
+
<link rel="stylesheet" href="{relocatable: default.css}" type="text/css" media="screen" />
|
12
|
+
<link rel="stylesheet" href="{resource: webgen-css}" type="text/css" media="screen" />
|
13
|
+
<title>{title:}</title>
|
14
|
+
</head>
|
15
|
+
|
16
|
+
<body>
|
17
|
+
<div id="container">
|
18
|
+
|
19
|
+
<div id="header">
|
20
|
+
<div id="sitename">
|
21
|
+
<h1>GemInstaller</h1>
|
22
|
+
<h2>Automate Ruby Gems!</h2>
|
23
|
+
</div>
|
24
|
+
</div>
|
25
|
+
|
26
|
+
<div id="navigation">
|
27
|
+
{menu: {menuStyle: horizontal}}
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<div id="wrap">
|
31
|
+
|
32
|
+
<div id="content">
|
33
|
+
{block:}
|
34
|
+
</div>
|
35
|
+
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
|
39
|
+
<div id="footer">© 2007 Chad Woolley | Generated by <a href="http://webgen.rubyforge.org">webgen</a></div>
|
40
|
+
</body>
|
41
|
+
</html>
|