geminstaller 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/COPYING +1 -0
  2. data/History.txt +4 -0
  3. data/LICENSE +21 -0
  4. data/Manifest.txt +62 -0
  5. data/README.txt +60 -0
  6. data/Rakefile +92 -0
  7. data/TODO.txt +88 -0
  8. data/bin/geminstaller +30 -0
  9. data/geminstaller.yml +34 -0
  10. data/lib/geminstaller/application.rb +105 -0
  11. data/lib/geminstaller/arg_parser.rb +162 -0
  12. data/lib/geminstaller/autogem.rb +43 -0
  13. data/lib/geminstaller/config.rb +69 -0
  14. data/lib/geminstaller/config_builder.rb +40 -0
  15. data/lib/geminstaller/dependency_injector.rb +129 -0
  16. data/lib/geminstaller/enhanced_stream_ui.rb +57 -0
  17. data/lib/geminstaller/exact_match_list_command.rb +16 -0
  18. data/lib/geminstaller/file_reader.rb +31 -0
  19. data/lib/geminstaller/gem_arg_processor.rb +44 -0
  20. data/lib/geminstaller/gem_command_line_proxy.rb +32 -0
  21. data/lib/geminstaller/gem_command_manager.rb +62 -0
  22. data/lib/geminstaller/gem_interaction_handler.rb +30 -0
  23. data/lib/geminstaller/gem_list_checker.rb +55 -0
  24. data/lib/geminstaller/gem_runner_proxy.rb +43 -0
  25. data/lib/geminstaller/gem_source_index_proxy.rb +21 -0
  26. data/lib/geminstaller/gem_spec_manager.rb +42 -0
  27. data/lib/geminstaller/geminstaller_error.rb +13 -0
  28. data/lib/geminstaller/hoe_extensions.rb +5 -0
  29. data/lib/geminstaller/install_processor.rb +56 -0
  30. data/lib/geminstaller/missing_dependency_finder.rb +41 -0
  31. data/lib/geminstaller/missing_file_error.rb +4 -0
  32. data/lib/geminstaller/noninteractive_chooser.rb +107 -0
  33. data/lib/geminstaller/output_filter.rb +49 -0
  34. data/lib/geminstaller/output_listener.rb +33 -0
  35. data/lib/geminstaller/output_observer.rb +32 -0
  36. data/lib/geminstaller/output_proxy.rb +36 -0
  37. data/lib/geminstaller/requires.rb +59 -0
  38. data/lib/geminstaller/rogue_gem_finder.rb +195 -0
  39. data/lib/geminstaller/ruby_gem.rb +44 -0
  40. data/lib/geminstaller/rubygems_exit.rb +5 -0
  41. data/lib/geminstaller/rubygems_extensions.rb +5 -0
  42. data/lib/geminstaller/unauthorized_dependency_prompt_error.rb +4 -0
  43. data/lib/geminstaller/unexpected_prompt_error.rb +4 -0
  44. data/lib/geminstaller/valid_platform_selector.rb +44 -0
  45. data/lib/geminstaller/version_specifier.rb +24 -0
  46. data/lib/geminstaller/yaml_loader.rb +22 -0
  47. data/lib/geminstaller.rb +102 -0
  48. data/start_local_gem_server.sh +1 -0
  49. data/test/test_all.rb +31 -0
  50. data/website/config.yaml +2 -0
  51. data/website/metainfo.yaml +72 -0
  52. data/website/src/code/index.page +25 -0
  53. data/website/src/community/index.page +14 -0
  54. data/website/src/community/links.page +9 -0
  55. data/website/src/default.css +168 -0
  56. data/website/src/default.template +41 -0
  57. data/website/src/documentation/documentation.page +417 -0
  58. data/website/src/documentation/index.page +88 -0
  59. data/website/src/documentation/tutorials.page +94 -0
  60. data/website/src/download.page +12 -0
  61. data/website/src/faq.page +7 -0
  62. data/website/src/index.page +38 -0
  63. 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,5 @@
1
+ module GemInstaller
2
+ class RubyGemsExit < RuntimeError
3
+ # This is an exception which helps override the normal (no error) system 'exit' call in RubyGems' terminate_interaction
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Gem
2
+ class GemRunner
3
+ public :do_configuration
4
+ end
5
+ end
@@ -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,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
@@ -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
@@ -0,0 +1,2 @@
1
+ # Configuration file for webgen
2
+ # Used to set the parameters of the plugins
@@ -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">&copy; 2007 Chad Woolley | Generated by <a href="http://webgen.rubyforge.org">webgen</a></div>
40
+ </body>
41
+ </html>