jwhitmire-geminstaller 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/COPYING +1 -0
  2. data/History.txt +82 -0
  3. data/LICENSE +21 -0
  4. data/Manifest.txt +78 -0
  5. data/README.txt +77 -0
  6. data/Rakefile +210 -0
  7. data/TODO.txt +138 -0
  8. data/authors +2 -0
  9. data/bin/geminstaller +30 -0
  10. data/cruise_config.rb +23 -0
  11. data/focused_spec.sh +2 -0
  12. data/focused_spec_debug.sh +2 -0
  13. data/geminstaller.yml +47 -0
  14. data/lib/geminstaller/application.rb +112 -0
  15. data/lib/geminstaller/arg_parser.rb +177 -0
  16. data/lib/geminstaller/autogem.rb +48 -0
  17. data/lib/geminstaller/backward_compatibility.rb +17 -0
  18. data/lib/geminstaller/bundler_exporter.rb +19 -0
  19. data/lib/geminstaller/config.rb +68 -0
  20. data/lib/geminstaller/config_builder.rb +65 -0
  21. data/lib/geminstaller/enhanced_stream_ui.rb +71 -0
  22. data/lib/geminstaller/exact_match_list_command.rb +16 -0
  23. data/lib/geminstaller/file_reader.rb +31 -0
  24. data/lib/geminstaller/gem_arg_processor.rb +44 -0
  25. data/lib/geminstaller/gem_command_manager.rb +71 -0
  26. data/lib/geminstaller/gem_interaction_handler.rb +41 -0
  27. data/lib/geminstaller/gem_list_checker.rb +55 -0
  28. data/lib/geminstaller/gem_runner_proxy.rb +65 -0
  29. data/lib/geminstaller/gem_source_index_proxy.rb +21 -0
  30. data/lib/geminstaller/gem_spec_manager.rb +53 -0
  31. data/lib/geminstaller/geminstaller_access_error.rb +4 -0
  32. data/lib/geminstaller/geminstaller_error.rb +13 -0
  33. data/lib/geminstaller/hoe_extensions.rb +9 -0
  34. data/lib/geminstaller/install_processor.rb +71 -0
  35. data/lib/geminstaller/missing_dependency_finder.rb +46 -0
  36. data/lib/geminstaller/missing_file_error.rb +4 -0
  37. data/lib/geminstaller/noninteractive_chooser.rb +114 -0
  38. data/lib/geminstaller/outdated_gem_finder.rb +46 -0
  39. data/lib/geminstaller/output_filter.rb +49 -0
  40. data/lib/geminstaller/output_listener.rb +33 -0
  41. data/lib/geminstaller/output_observer.rb +36 -0
  42. data/lib/geminstaller/output_proxy.rb +36 -0
  43. data/lib/geminstaller/registry.rb +137 -0
  44. data/lib/geminstaller/requires.rb +83 -0
  45. data/lib/geminstaller/rogue_gem_finder.rb +195 -0
  46. data/lib/geminstaller/ruby_gem.rb +58 -0
  47. data/lib/geminstaller/rubygems_exit.rb +5 -0
  48. data/lib/geminstaller/rubygems_extensions.rb +9 -0
  49. data/lib/geminstaller/rubygems_version_checker.rb +21 -0
  50. data/lib/geminstaller/rubygems_version_warnings.rb +38 -0
  51. data/lib/geminstaller/source_index_search_adapter.rb +41 -0
  52. data/lib/geminstaller/unauthorized_dependency_prompt_error.rb +4 -0
  53. data/lib/geminstaller/unexpected_prompt_error.rb +4 -0
  54. data/lib/geminstaller/valid_platform_selector.rb +49 -0
  55. data/lib/geminstaller/version_specifier.rb +24 -0
  56. data/lib/geminstaller/yaml_loader.rb +22 -0
  57. data/lib/geminstaller.rb +103 -0
  58. data/lib/geminstaller_rails_preinitializer.rb +54 -0
  59. data/start_local_gem_server.sh +1 -0
  60. data/website/config.yaml +11 -0
  61. data/website/src/analytics.page +6 -0
  62. data/website/src/code/ci.virtual +5 -0
  63. data/website/src/code/coverage/index.virtual +5 -0
  64. data/website/src/code/index.page +92 -0
  65. data/website/src/code/rdoc/index.virtual +6 -0
  66. data/website/src/community/index.page +14 -0
  67. data/website/src/community/links.page +11 -0
  68. data/website/src/community/rubyforge.virtual +4 -0
  69. data/website/src/default.css +175 -0
  70. data/website/src/default.template +42 -0
  71. data/website/src/documentation/documentation.page +477 -0
  72. data/website/src/documentation/index.page +53 -0
  73. data/website/src/documentation/tutorials.page +337 -0
  74. data/website/src/download.page +12 -0
  75. data/website/src/faq.page +36 -0
  76. data/website/src/index.page +103 -0
  77. data/website/src/metainfo +54 -0
  78. data/website/src/webgen.css +112 -0
  79. metadata +139 -0
@@ -0,0 +1,9 @@
1
+ module Gem
2
+ class GemRunner
3
+ public :do_configuration
4
+ end
5
+ class CommandManager
6
+ attr_reader :commands
7
+ end
8
+ end
9
+
@@ -0,0 +1,21 @@
1
+ module GemInstaller
2
+ class RubyGemsVersionChecker
3
+ def self.matches?(version_spec, options = {})
4
+ version_spec = [version_spec] unless version_spec.kind_of?(Array)
5
+ # TODO: if rubygems has already been initialized before GemInstaller overrides the rubygems version,
6
+ # (for example, by running rspec), then Gem::RubyGemsVersion could be initialized to the incorrect
7
+ # system-rubygems version instead of the geminstaller-overridden version. Need to figure out how
8
+ # to re-parse 'rubygems/rubygems_version' and let it redefine 'Gem::RubyGemsVersion'
9
+ rubygems_version = options[:rubygems_version] ||= Gem::RubyGemsVersion
10
+
11
+ # Manually check here in addition to backward_compatibility.rb, to avoid circular dependency
12
+ if defined?(Gem::Requirement)
13
+ requirement_class = Gem::Requirement
14
+ else
15
+ requirement_class = Gem::Version::Requirement
16
+ end
17
+
18
+ requirement_class.new(version_spec).satisfied_by?(Gem::Version.new(rubygems_version))
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ module GemInstaller
2
+ class RubyGemsVersionWarnings
3
+ def self.outdated_warning(options = {})
4
+ return nil if allow_unsupported?
5
+ return nil if GemInstaller::RubyGemsVersionChecker.matches?('>=0.9.5', options)
6
+ return "\n\n----------------------------------------------------------------\n" +
7
+ "WARNING: You are using RubyGems version #{Gem::RubyGemsVersion}.\n" +
8
+ "You should update to RubyGems version 1.0.1 or above,\n" +
9
+ "because gems created for newer RubyGems versions\n" +
10
+ "might be incompatible.\n" +
11
+ "To update rubygems (recommended), use 'gem update --system'.\n" +
12
+ "----------------------------------------------------------------\n\n"
13
+ end
14
+
15
+ def self.incompatible_warning(options = {})
16
+ return nil if allow_unsupported?
17
+ return nil unless (
18
+ GemInstaller::RubyGemsVersionChecker.matches?('=0.9.5', options) or
19
+ GemInstaller::RubyGemsVersionChecker.matches?('=1.1.0', options)
20
+ )
21
+ return "\n\n----------------------------------------------------------------\n" +
22
+ "WARNING: You are using RubyGems version #{Gem::RubyGemsVersion}.\n" +
23
+ "This version is known to have bugs and/or compatibility issues\n" +
24
+ "with GemInstaller. Update RubyGems, or continue at your risk.\n" +
25
+ "To update rubygems (recommended), use 'gem update --system'.\n" +
26
+ "----------------------------------------------------------------\n\n"
27
+ end
28
+
29
+ def self.allow_unsupported?
30
+ defined? ALLOW_UNSUPPORTED_RUBYGEMS_VERSION
31
+ end
32
+
33
+ def self.print_warnings(options = {})
34
+ warnings = [self.outdated_warning, self.incompatible_warning].compact!
35
+ warnings.each {|warning| print warning}
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,41 @@
1
+ module GemInstaller
2
+ class SourceIndexSearchAdapter
3
+ attr_writer :gem_source_index_proxy
4
+
5
+ def all_local_specs
6
+ if GemInstaller::RubyGemsVersionChecker.matches?('<=0.9.4')
7
+ search_less_than_or_equal_0_9_4('',GemInstaller::RubyGem.default_version)
8
+ else
9
+ dependency = Gem::Dependency.new('', GemInstaller::RubyGem.default_version)
10
+ @gem_source_index_proxy.refresh!
11
+ @gem_source_index_proxy.search(dependency, false)
12
+ end
13
+ end
14
+
15
+ def search(gem, version_requirement, platform_only = false)
16
+ if GemInstaller::RubyGemsVersionChecker.matches?('<=0.9.4')
17
+ gem_pattern = /^#{gem.regexp_escaped_name}$/
18
+ search_less_than_or_equal_0_9_4(gem_pattern, version_requirement)
19
+ else
20
+ search_greater_than_0_9_4(gem, version_requirement, platform_only)
21
+ end
22
+ end
23
+
24
+ def search_less_than_or_equal_0_9_4(gem_pattern, version_requirement)
25
+ @gem_source_index_proxy.refresh!
26
+ @gem_source_index_proxy.search(gem_pattern, version_requirement)
27
+ end
28
+
29
+ def search_greater_than_0_9_4(gem, version_requirement, platform_only = false)
30
+ dependency = Gem::Dependency.new(gem.name, version_requirement)
31
+ @gem_source_index_proxy.refresh!
32
+ @gem_source_index_proxy.search(dependency, platform_only)
33
+ end
34
+
35
+ end
36
+ end
37
+
38
+
39
+
40
+
41
+
@@ -0,0 +1,4 @@
1
+ module GemInstaller
2
+ class UnauthorizedDependencyPromptError < GemInstaller::GemInstallerError
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module GemInstaller
2
+ class UnexpectedPromptError < GemInstaller::GemInstallerError
3
+ end
4
+ end
@@ -0,0 +1,49 @@
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
+ if GemInstaller::RubyGemsVersionChecker.matches?('>=0.9.5')
7
+ # valid_platform_selector is not used for RubyGems >= 0.9.5
8
+ raise RuntimeError.new("Internal GemInstaller Error: ValidPlatformSelector should not be used for RubyGems >= 0.9.5")
9
+ end
10
+
11
+ @ruby_platform ||= RUBY_PLATFORM
12
+ return [@ruby_platform] if gem_platform == Gem::Platform::CURRENT
13
+ return [gem_platform] if exact_platform_match
14
+ valid_platforms = []
15
+ valid_platforms += binary_platform_substring if binary_platform_substring
16
+ if @options[:prefer_binary_platform] == false
17
+ # put ruby first if prefer_binary_platform is false
18
+ valid_platforms.unshift('ruby')
19
+ else
20
+ # leave binary platform first if prefer_binary_platform is false or nil
21
+ valid_platforms << 'ruby'
22
+ end
23
+ if gem_platform and
24
+ !valid_platforms.include?(gem_platform)
25
+ # only prepend the gem_platform as the first choice if
26
+ # 1. it is not nil
27
+ # 2. it is not already in the list
28
+ # 3. it is not 'ruby'
29
+ valid_platforms.unshift(gem_platform)
30
+ end
31
+ message = "Selecting valid platform(s): @ruby_platform='#{@ruby_platform}', gem_platform='#{gem_platform}', valid_platforms='#{valid_platforms.inspect}'"
32
+ @output_filter.geminstaller_output(:debug,"#{message}\n")
33
+ valid_platforms
34
+ end
35
+
36
+ def binary_platform_substring
37
+ return ['686-darwin'] if @ruby_platform =~ /686-darwin/
38
+ return ['cygwin'] if @ruby_platform =~ /cygwin/
39
+ return ['powerpc'] if @ruby_platform =~ /powerpc/
40
+ return ['i386-mswin32','mswin32'] if @ruby_platform =~ /mswin/
41
+ return ['386-linux'] if @ruby_platform =~ /386-linux/
42
+ return ['486-linux'] if @ruby_platform =~ /486-linux/
43
+ return ['586-linux'] if @ruby_platform =~ /586-linux/
44
+ return ['686-linux'] if @ruby_platform =~ /686-linux/
45
+ return ['solaris'] if @ruby_platform =~ /solaris/
46
+ return nil
47
+ end
48
+ end
49
+ 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 = GemInstaller::REQUIREMENT_CLASS.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
@@ -0,0 +1,103 @@
1
+ dir = File.dirname(__FILE__)
2
+ require File.expand_path("#{dir}/geminstaller/requires.rb")
3
+
4
+ module GemInstaller
5
+ def self.install(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.install
19
+ end
20
+ end
21
+
22
+ def self.run(args = [], geminstaller_executable = nil)
23
+ # run is now an alias for install
24
+ install(args, geminstaller_executable)
25
+ end
26
+
27
+ def self.autogem(args = [])
28
+ args_copy = args.dup
29
+ args_copy = args_copy.split(' ') unless args.respond_to? :join
30
+ # TODO: should explicitly remove all args not applicable to autogem (anything but config, silent, and geminstaller_output)
31
+ args_without_sudo = strip_sudo(args_copy)
32
+ app = create_application(args_without_sudo)
33
+ app.autogem
34
+ end
35
+
36
+ def self.version
37
+ "0.5.4"
38
+ end
39
+
40
+ def self.create_application(args = [], registry = nil)
41
+ registry ||= create_registry
42
+ app = registry.app
43
+ app.args = args
44
+ app
45
+ end
46
+
47
+ def self.create_registry
48
+ GemInstaller::Registry.new
49
+ end
50
+
51
+ def self.parse_config_paths(config_paths)
52
+ return nil unless config_paths
53
+ return config_paths unless config_paths.respond_to? :join
54
+ return config_paths.join(',')
55
+ end
56
+
57
+ def self.platform_supports_sudo?
58
+ return true unless RUBY_PLATFORM =~ /mswin/
59
+ return false
60
+ end
61
+
62
+ def self.reinvoke_with_sudo(args, geminstaller_executable)
63
+ # Sudo support is a hack, better to use other alternatives.
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 is 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.reopen($stderr_backup)
95
+ STDERR.reopen($stderr_backup) # Why are these not the same? It is an old question: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/47596
96
+ output = io.read
97
+ next unless output =~ /#{path_key}/
98
+ path = output.sub(path_key,'')
99
+ return path
100
+ end
101
+ raise GemInstaller::GemInstallerError.new("Error: Unable to locate the geminstaller executable. Specify it explicitly, or don't use the sudo option.")
102
+ end
103
+ end
@@ -0,0 +1,54 @@
1
+ ############# GemInstaller Rails Preinitializer - see http://geminstaller.rubyforge.org
2
+
3
+ # This file should be required in your Rails config/preinitializer.rb for Rails >= 2.0,
4
+ # or required in config/boot.rb before initialization for Rails < 2.0. For example:
5
+ # require 'geminstaller_rails_preinitializer'
6
+ #
7
+ # If you require different geminstaller command options, copy this file into your Rails app,
8
+ # modify it, and require your customized version. For example:
9
+ # require "#{File.expand_path(RAILS_ROOT)}/config/custom_geminstaller_rails_preinitializer.rb"
10
+
11
+ require "rubygems"
12
+ require "geminstaller"
13
+
14
+ module GemInstallerRailsPreinitializer
15
+ class << self
16
+ def preinitialize
17
+ args = ''
18
+
19
+ # Specify --geminstaller-output=all and --rubygems-output=all for maximum debug logging
20
+ # args += ' --geminstaller-output=all --rubygems-output=all'
21
+
22
+ # The 'exceptions' flag determines whether errors encountered while running GemInstaller
23
+ # should raise exceptions (and abort Rails), or just return a nonzero return code
24
+ args += " --exceptions"
25
+
26
+ # This will use sudo by default on all non-windows platforms, but requires an entry in your
27
+ # sudoers file to avoid having to type a password. It can be omitted if you don't want to use sudo.
28
+ # See http://geminstaller.rubyforge.org/documentation/documentation.html#dealing_with_sudo
29
+ # Note that environment variables will NOT be passed via sudo!
30
+ #args += " --sudo" unless RUBY_PLATFORM =~ /mswin/
31
+
32
+ # The 'install' method will auto-install gems as specified by the args and config
33
+ # IMPORTANT NOTE: Under recent RubyGems versions, this will install to ~/.gem
34
+ # The forking is a workaround to 'check_for_upgrade' in the configuration file, which causes
35
+ # script/console crashes on Mac OS X. See http://www.ruby-forum.com/topic/101243 for details
36
+ # It is probably best not to install from preinitializer - it has known problems
37
+ # under Passenger, and you should be installing your gems before you initialize rails anyway,
38
+ # via capistrano, chef, or some other mechanism. So, it is commented for now.
39
+ # pid = fork do
40
+ # GemInstaller.install(args)
41
+ # end
42
+ # Process.wait(pid)
43
+
44
+ # The 'autogem' method will automatically add all gems in the GemInstaller config to your load path,
45
+ # using the rubygems 'gem' method. Note that only the *first* version of any given gem will be loaded.
46
+ GemInstaller.autogem(args)
47
+ end
48
+ end
49
+ end
50
+
51
+ # Attempt to prevent GemInstaller from running twice, but won't work if it is executed
52
+ # in a separate interpreter (like rake tests)
53
+ GemInstallerRailsPreinitializer.preinitialize unless $geminstaller_initialized
54
+ $geminstaller_initialized = true
@@ -0,0 +1 @@
1
+ gem server --dir=./spec/fixture/gems --port=9909
@@ -0,0 +1,11 @@
1
+ # Configuration file for webgen
2
+ # Used to set the parameters of the plugins
3
+ default_meta_info:
4
+ Page:
5
+ blocks:
6
+ default: {pipeline: tags,redcloth,blocks}
7
+ Template:
8
+ blocks:
9
+ default: {pipeline: tags,redcloth,blocks}
10
+ tag.menu.show_current_subtree_only: true
11
+ tag.menu.nested: false
@@ -0,0 +1,6 @@
1
+ <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
2
+ </script>
3
+ <script type="text/javascript">
4
+ _uacct = "UA-2845076-1";
5
+ urchinTracker();
6
+ </script>
@@ -0,0 +1,5 @@
1
+ ci.html:
2
+ title: CI
3
+ url: http://ci.thewoolleyweb.com
4
+ in_menu: true
5
+ sort_info: 20
@@ -0,0 +1,5 @@
1
+ index.html:
2
+ title: Coverage
3
+ url: index.html
4
+ in_menu: true
5
+ sort_info: 40
@@ -0,0 +1,92 @@
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 order 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 are not part of the main spec suite. These are really coarse grained scripts. They hit the live rubygems.org gem repository, and install/uninstall actual gems, and exercise my sample rails app which uses GemInstaller. They are used as a 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 "harder" on Windows (but run fine on mac/linux). The app should work fine, but testing is pretty tricky. I have all tests working against the latest rubygems (1.0.1). Here's what does and doesn't work with tests against latest rubygems on windows:
25
+ * install_smoketest.rb and autogem_smoketest.rb work
26
+ * ruby test_suites/test_all.rb works
27
+ * rake default test task does not work (this seems to be a problem with hoe on windows)
28
+ * Sometimes test suite still hangs after a while, this is probably some orphaned EmbeddedGemServer process somewhere. A reboot should fix it - this is windows, after all!
29
+ * 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 :)
30
+
31
+ <span id="rubygems_compatibility"/>
32
+
33
+ h1. Maintaining forward and backward compatibility with multiple RubyGems versions
34
+
35
+ * BACKWARD COMPATIBILITY UPDATE: As of July 2009, I had to give up on being able to keep the test suite running against all old versions of RubyGems back to 0.8.11, and I've had to drop test support for some of the old versions. The world moves on, and changes in Rspec, Rake, Hoe, Ruby, and Rubygems itself make it hard to make the tests themselves run under old versions of Rubygems. GemInstaller ITSELF should still run against these versions, I just can't run the automated tests for them.
36
+
37
+ * FORWARD COMPATIBILITY UPDATE: Between my move to git (no more automagically-updating svn:externals, just lame manual git submodules), and RubyGems' refusal to allow incrementing of the RubyGems trunk version ([refusal](http://rubyforge.org/pipermail/rubygems-developers/2009-June/004749.html), [explanation](http://rubyforge.org/pipermail/rubygems-developers/2009-June/004750.html)), maintaining automated tests and version checks against the latest RubyGems trunk presented some challenges. However, I have it working again, in an ugly Rube-Goldbergesque kind of way. More details later...
38
+
39
+ * I've put a lot of effort into ensuring that GemInstaller works with older versions of RubyGems, and run automated tests against several recent versions on Continuous Integration.
40
+
41
+ * This wasn't as hard as it seems. The hardest part was figuring out what was different between versions. Once you understand that, you can add switch statements to perform different logic or tests depending on the version (which is easy if you use rubygems built in Version Requirement support - see GemInstaller::RubygemsVersionChecker).
42
+
43
+ * To run against different RubyGems versions, I have a RUBYGEMS_VERSION environment variable which causes the correct version or RubyGems in spec/fixture/rubygems_dist to be put on the load path and used for the test run.
44
+
45
+ * RubyGems 0.9.5 was a major upgrade, the preview release for 1.0. It introduced support for platforms, auto-install of missing dependencies, and many other things that GemInstaller already did.
46
+
47
+ * Here are the main differences in GemInstaller when running on RubyGems >=0.9.5:
48
+ ** Don't use missing dependency finder (auto-install of missing dependencies is now built in)
49
+ ** Don't use valid platform selector, use --platform option (auto-selection of platform is now built in)
50
+ ** GemInteractionHandler should throw error on any interactive prompt (since dependencies and platforms are now handled automatically by default)
51
+ ** The "<code>prefer_binary_platform</code> config property":../documentation/documentation.html#prefer_binary_platform_config_property no longer applies, and has no effect.
52
+
53
+ <span id="release_process"/>
54
+
55
+ h1. Release Process
56
+
57
+ Yeah, it's too manual. Notes for improvement below.
58
+
59
+ * Add section and entries for new release to History.txt
60
+ * Add same history info to history section on homepage.
61
+ * Update version in geminstaller.rb (if it was not done immediately after last publish, which it should have been)
62
+ * rake update_manifest
63
+ * Run tests:
64
+ ** CI should be green for latest version and as many old versions as possible
65
+ ** install_smoketest.rb and autogem_smoketest.rb should pass for all RubyGems versions on mac/linux (if they are in a working state)
66
+ ** test_all, install_smoketest.rb and autogem_smoketest.rb should pass for latest RubyGems version on windows (if they are in a working state)
67
+ * Make sure everything is checked in
68
+ * Make a tag in git (git tag x.y.z; git push origin x.y.z)
69
+ * rake clean package
70
+ * Upload gem to rubyforge
71
+ * rake publish_website
72
+ * Bump the version in geminstaller.rb to the expected next release version.
73
+
74
+ Here's the improvements I need to make to the release process:
75
+
76
+ * Avoid duplication of history file to display on home page
77
+ * Make CI automatically build and tag a gem/package with next version + revision: x.y.z.<svn revision>
78
+ * Rake task to automatically tag and release latest build from CI and tag
79
+
80
+ <span id="rubygems_upgrade_process"/>
81
+
82
+ h1. Rubygems Upgrade Process
83
+
84
+ Process to test against new rubygems release.
85
+
86
+ <pre><code>
87
+ svn export http://rubygems.rubyforge.org/svn/tags/REL_X_Y_Z spec/fixture/rubygems_dist/rubygems-X.Y.Z
88
+ </code></pre>
89
+
90
+ * add spec/fixture/rubygems_dist/rubytems-X.Y.Z
91
+ * checkin
92
+ * Add project for that release on CI
@@ -0,0 +1,6 @@
1
+ index.html:
2
+ title: Rdoc
3
+ url: index.html
4
+ in_menu: true
5
+ sort_info: 30
6
+
@@ -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://thewoolleyweb.lighthouseapp.com/projects/11580-geminstaller/overview
10
+ * "Mailing Lists":http://rubyforge.org/mail/?group_id=1902
11
+
12
+ h2. Source Code
13
+
14
+ * Subversion Repository: "http://geminstaller.rubyforge.org/svn/trunk":http://geminstaller.rubyforge.org/svn/trunk
@@ -0,0 +1,11 @@
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
10
+ * RSpec
11
+ ** "RSpec Homepage":http://rspec.info/
@@ -0,0 +1,4 @@
1
+ rubyforge.html:
2
+ title: Rubyforge
3
+ url: http://rubyforge.org/projects/geminstaller
4
+ in_menu: true