carat 1.9.9.pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +24 -0
  5. data/CHANGELOG.md +2006 -0
  6. data/CODE_OF_CONDUCT.md +40 -0
  7. data/CONTRIBUTING.md +23 -0
  8. data/DEVELOPMENT.md +119 -0
  9. data/ISSUES.md +96 -0
  10. data/LICENSE.md +23 -0
  11. data/README.md +32 -0
  12. data/Rakefile +308 -0
  13. data/bin/carat +21 -0
  14. data/bin/carat_ruby +56 -0
  15. data/carat.gemspec +32 -0
  16. data/lib/carat.rb +446 -0
  17. data/lib/carat/anonymizable_uri.rb +32 -0
  18. data/lib/carat/capistrano.rb +16 -0
  19. data/lib/carat/cli.rb +407 -0
  20. data/lib/carat/cli/binstubs.rb +38 -0
  21. data/lib/carat/cli/cache.rb +35 -0
  22. data/lib/carat/cli/check.rb +35 -0
  23. data/lib/carat/cli/clean.rb +26 -0
  24. data/lib/carat/cli/common.rb +56 -0
  25. data/lib/carat/cli/config.rb +84 -0
  26. data/lib/carat/cli/console.rb +38 -0
  27. data/lib/carat/cli/exec.rb +44 -0
  28. data/lib/carat/cli/gem.rb +195 -0
  29. data/lib/carat/cli/init.rb +33 -0
  30. data/lib/carat/cli/inject.rb +33 -0
  31. data/lib/carat/cli/install.rb +156 -0
  32. data/lib/carat/cli/open.rb +23 -0
  33. data/lib/carat/cli/outdated.rb +80 -0
  34. data/lib/carat/cli/package.rb +45 -0
  35. data/lib/carat/cli/platform.rb +43 -0
  36. data/lib/carat/cli/show.rb +74 -0
  37. data/lib/carat/cli/update.rb +73 -0
  38. data/lib/carat/cli/viz.rb +27 -0
  39. data/lib/carat/constants.rb +5 -0
  40. data/lib/carat/current_ruby.rb +183 -0
  41. data/lib/carat/definition.rb +628 -0
  42. data/lib/carat/dep_proxy.rb +43 -0
  43. data/lib/carat/dependency.rb +110 -0
  44. data/lib/carat/deployment.rb +59 -0
  45. data/lib/carat/deprecate.rb +15 -0
  46. data/lib/carat/dsl.rb +331 -0
  47. data/lib/carat/endpoint_specification.rb +76 -0
  48. data/lib/carat/env.rb +75 -0
  49. data/lib/carat/environment.rb +42 -0
  50. data/lib/carat/fetcher.rb +423 -0
  51. data/lib/carat/friendly_errors.rb +85 -0
  52. data/lib/carat/gem_helper.rb +180 -0
  53. data/lib/carat/gem_helpers.rb +26 -0
  54. data/lib/carat/gem_installer.rb +9 -0
  55. data/lib/carat/gem_path_manipulation.rb +8 -0
  56. data/lib/carat/gem_tasks.rb +2 -0
  57. data/lib/carat/graph.rb +169 -0
  58. data/lib/carat/index.rb +197 -0
  59. data/lib/carat/injector.rb +64 -0
  60. data/lib/carat/installer.rb +339 -0
  61. data/lib/carat/lazy_specification.rb +83 -0
  62. data/lib/carat/lockfile_parser.rb +167 -0
  63. data/lib/carat/match_platform.rb +13 -0
  64. data/lib/carat/psyched_yaml.rb +26 -0
  65. data/lib/carat/remote_specification.rb +57 -0
  66. data/lib/carat/resolver.rb +334 -0
  67. data/lib/carat/retry.rb +60 -0
  68. data/lib/carat/ruby_dsl.rb +11 -0
  69. data/lib/carat/ruby_version.rb +117 -0
  70. data/lib/carat/rubygems_ext.rb +170 -0
  71. data/lib/carat/rubygems_integration.rb +619 -0
  72. data/lib/carat/runtime.rb +289 -0
  73. data/lib/carat/settings.rb +208 -0
  74. data/lib/carat/setup.rb +24 -0
  75. data/lib/carat/shared_helpers.rb +149 -0
  76. data/lib/carat/similarity_detector.rb +63 -0
  77. data/lib/carat/source.rb +46 -0
  78. data/lib/carat/source/git.rb +294 -0
  79. data/lib/carat/source/git/git_proxy.rb +162 -0
  80. data/lib/carat/source/path.rb +226 -0
  81. data/lib/carat/source/path/installer.rb +43 -0
  82. data/lib/carat/source/rubygems.rb +381 -0
  83. data/lib/carat/source_list.rb +101 -0
  84. data/lib/carat/spec_set.rb +154 -0
  85. data/lib/carat/ssl_certs/.document +1 -0
  86. data/lib/carat/ssl_certs/AddTrustExternalCARoot-2048.pem +25 -0
  87. data/lib/carat/ssl_certs/AddTrustExternalCARoot.pem +32 -0
  88. data/lib/carat/ssl_certs/Class3PublicPrimaryCertificationAuthority.pem +14 -0
  89. data/lib/carat/ssl_certs/DigiCertHighAssuranceEVRootCA.pem +23 -0
  90. data/lib/carat/ssl_certs/EntrustnetSecureServerCertificationAuthority.pem +28 -0
  91. data/lib/carat/ssl_certs/GeoTrustGlobalCA.pem +20 -0
  92. data/lib/carat/ssl_certs/certificate_manager.rb +66 -0
  93. data/lib/carat/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem +21 -0
  94. data/lib/carat/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem +23 -0
  95. data/lib/carat/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem +25 -0
  96. data/lib/carat/templates/Executable +16 -0
  97. data/lib/carat/templates/Executable.standalone +12 -0
  98. data/lib/carat/templates/Gemfile +4 -0
  99. data/lib/carat/templates/newgem/.travis.yml.tt +3 -0
  100. data/lib/carat/templates/newgem/CODE_OF_CONDUCT.md.tt +13 -0
  101. data/lib/carat/templates/newgem/Gemfile.tt +4 -0
  102. data/lib/carat/templates/newgem/LICENSE.txt.tt +21 -0
  103. data/lib/carat/templates/newgem/README.md.tt +39 -0
  104. data/lib/carat/templates/newgem/Rakefile.tt +25 -0
  105. data/lib/carat/templates/newgem/bin/console.tt +14 -0
  106. data/lib/carat/templates/newgem/bin/setup.tt +7 -0
  107. data/lib/carat/templates/newgem/exe/newgem.tt +3 -0
  108. data/lib/carat/templates/newgem/ext/newgem/extconf.rb.tt +3 -0
  109. data/lib/carat/templates/newgem/ext/newgem/newgem.c.tt +9 -0
  110. data/lib/carat/templates/newgem/ext/newgem/newgem.h.tt +6 -0
  111. data/lib/carat/templates/newgem/gitignore.tt +16 -0
  112. data/lib/carat/templates/newgem/lib/newgem.rb.tt +12 -0
  113. data/lib/carat/templates/newgem/lib/newgem/version.rb.tt +7 -0
  114. data/lib/carat/templates/newgem/newgem.gemspec.tt +43 -0
  115. data/lib/carat/templates/newgem/rspec.tt +2 -0
  116. data/lib/carat/templates/newgem/spec/newgem_spec.rb.tt +11 -0
  117. data/lib/carat/templates/newgem/spec/spec_helper.rb.tt +2 -0
  118. data/lib/carat/templates/newgem/test/minitest_helper.rb.tt +4 -0
  119. data/lib/carat/templates/newgem/test/test_newgem.rb.tt +11 -0
  120. data/lib/carat/ui.rb +7 -0
  121. data/lib/carat/ui/rg_proxy.rb +21 -0
  122. data/lib/carat/ui/shell.rb +103 -0
  123. data/lib/carat/ui/silent.rb +44 -0
  124. data/lib/carat/vendor/molinillo/lib/molinillo.rb +5 -0
  125. data/lib/carat/vendor/molinillo/lib/molinillo/dependency_graph.rb +266 -0
  126. data/lib/carat/vendor/molinillo/lib/molinillo/errors.rb +69 -0
  127. data/lib/carat/vendor/molinillo/lib/molinillo/gem_metadata.rb +3 -0
  128. data/lib/carat/vendor/molinillo/lib/molinillo/modules/specification_provider.rb +90 -0
  129. data/lib/carat/vendor/molinillo/lib/molinillo/modules/ui.rb +63 -0
  130. data/lib/carat/vendor/molinillo/lib/molinillo/resolution.rb +415 -0
  131. data/lib/carat/vendor/molinillo/lib/molinillo/resolver.rb +43 -0
  132. data/lib/carat/vendor/molinillo/lib/molinillo/state.rb +43 -0
  133. data/lib/carat/vendor/net/http/faster.rb +26 -0
  134. data/lib/carat/vendor/net/http/persistent.rb +1230 -0
  135. data/lib/carat/vendor/net/http/persistent/ssl_reuse.rb +128 -0
  136. data/lib/carat/vendor/thor/lib/thor.rb +484 -0
  137. data/lib/carat/vendor/thor/lib/thor/actions.rb +319 -0
  138. data/lib/carat/vendor/thor/lib/thor/actions/create_file.rb +103 -0
  139. data/lib/carat/vendor/thor/lib/thor/actions/create_link.rb +59 -0
  140. data/lib/carat/vendor/thor/lib/thor/actions/directory.rb +118 -0
  141. data/lib/carat/vendor/thor/lib/thor/actions/empty_directory.rb +135 -0
  142. data/lib/carat/vendor/thor/lib/thor/actions/file_manipulation.rb +316 -0
  143. data/lib/carat/vendor/thor/lib/thor/actions/inject_into_file.rb +107 -0
  144. data/lib/carat/vendor/thor/lib/thor/base.rb +656 -0
  145. data/lib/carat/vendor/thor/lib/thor/command.rb +133 -0
  146. data/lib/carat/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +77 -0
  147. data/lib/carat/vendor/thor/lib/thor/core_ext/io_binary_read.rb +10 -0
  148. data/lib/carat/vendor/thor/lib/thor/core_ext/ordered_hash.rb +98 -0
  149. data/lib/carat/vendor/thor/lib/thor/error.rb +32 -0
  150. data/lib/carat/vendor/thor/lib/thor/group.rb +281 -0
  151. data/lib/carat/vendor/thor/lib/thor/invocation.rb +178 -0
  152. data/lib/carat/vendor/thor/lib/thor/line_editor.rb +17 -0
  153. data/lib/carat/vendor/thor/lib/thor/line_editor/basic.rb +35 -0
  154. data/lib/carat/vendor/thor/lib/thor/line_editor/readline.rb +88 -0
  155. data/lib/carat/vendor/thor/lib/thor/parser.rb +4 -0
  156. data/lib/carat/vendor/thor/lib/thor/parser/argument.rb +73 -0
  157. data/lib/carat/vendor/thor/lib/thor/parser/arguments.rb +175 -0
  158. data/lib/carat/vendor/thor/lib/thor/parser/option.rb +125 -0
  159. data/lib/carat/vendor/thor/lib/thor/parser/options.rb +218 -0
  160. data/lib/carat/vendor/thor/lib/thor/rake_compat.rb +71 -0
  161. data/lib/carat/vendor/thor/lib/thor/runner.rb +322 -0
  162. data/lib/carat/vendor/thor/lib/thor/shell.rb +81 -0
  163. data/lib/carat/vendor/thor/lib/thor/shell/basic.rb +421 -0
  164. data/lib/carat/vendor/thor/lib/thor/shell/color.rb +149 -0
  165. data/lib/carat/vendor/thor/lib/thor/shell/html.rb +126 -0
  166. data/lib/carat/vendor/thor/lib/thor/util.rb +267 -0
  167. data/lib/carat/vendor/thor/lib/thor/version.rb +3 -0
  168. data/lib/carat/vendored_fileutils.rb +9 -0
  169. data/lib/carat/vendored_molinillo.rb +2 -0
  170. data/lib/carat/vendored_persistent.rb +11 -0
  171. data/lib/carat/vendored_thor.rb +3 -0
  172. data/lib/carat/version.rb +6 -0
  173. data/lib/carat/vlad.rb +11 -0
  174. data/lib/carat/worker.rb +73 -0
  175. data/man/carat-config.ronn +178 -0
  176. data/man/carat-exec.ronn +136 -0
  177. data/man/carat-install.ronn +383 -0
  178. data/man/carat-package.ronn +66 -0
  179. data/man/carat-platform.ronn +42 -0
  180. data/man/carat-update.ronn +188 -0
  181. data/man/carat.ronn +98 -0
  182. data/man/gemfile.5.ronn +473 -0
  183. data/man/index.txt +7 -0
  184. metadata +321 -0
@@ -0,0 +1,85 @@
1
+ # encoding: utf-8
2
+ require "cgi"
3
+ require "carat/vendored_thor"
4
+
5
+ module Carat
6
+ def self.with_friendly_errors
7
+ yield
8
+ rescue Carat::CaratError => e
9
+ Carat.ui.error e.message, :wrap => true
10
+ Carat.ui.trace e
11
+ exit e.status_code
12
+ rescue Thor::AmbiguousTaskError => e
13
+ Carat.ui.error e.message
14
+ exit 15
15
+ rescue Thor::UndefinedTaskError => e
16
+ Carat.ui.error e.message
17
+ exit 15
18
+ rescue Thor::Error => e
19
+ Carat.ui.error e.message
20
+ exit 1
21
+ rescue LoadError => e
22
+ raise e unless e.message =~ /cannot load such file -- openssl|openssl.so|libcrypto.so/
23
+ Carat.ui.error "\nCould not load OpenSSL."
24
+ Carat.ui.warn <<-WARN, :wrap => true
25
+ You must recompile Ruby with OpenSSL support or change the sources in your \
26
+ Gemfile from 'https' to 'http'. Instructions for compiling with OpenSSL \
27
+ using RVM are available at http://rvm.io/packages/openssl.
28
+ WARN
29
+ Carat.ui.trace e
30
+ exit 1
31
+ rescue Interrupt => e
32
+ Carat.ui.error "\nQuitting..."
33
+ Carat.ui.trace e
34
+ exit 1
35
+ rescue SystemExit => e
36
+ exit e.status
37
+ rescue Exception => e
38
+ request_issue_report_for(e)
39
+ exit 1
40
+ end
41
+
42
+ def self.request_issue_report_for(e)
43
+ Carat.ui.info <<-EOS.gsub(/^ {6}/, '')
44
+ #{'--- ERROR REPORT TEMPLATE -------------------------------------------------------'}
45
+ - What did you do?
46
+
47
+ I ran the command `#{$PROGRAM_NAME} #{ARGV.join(' ')}`
48
+
49
+ - What did you expect to happen?
50
+
51
+ I expected Carat to...
52
+
53
+ - What happened instead?
54
+
55
+ Instead, what actually happened was...
56
+
57
+
58
+ Error details
59
+
60
+ #{e.class}: #{e.message}
61
+ #{e.backtrace.join("\n ")}
62
+
63
+ #{Carat::Env.new.report(:print_gemfile => false).gsub(/\n/, "\n ").strip}
64
+ #{'--- TEMPLATE END ----------------------------------------------------------------'}
65
+
66
+ EOS
67
+
68
+ Carat.ui.error "Unfortunately, an unexpected error occurred, and Carat cannot continue."
69
+
70
+ Carat.ui.warn <<-EOS.gsub(/^ {6}/, '')
71
+
72
+ First, try this link to see if there are any existing issue reports for this error:
73
+ #{issues_url(e)}
74
+
75
+ If there aren't any reports for this error yet, please create copy and paste the report template above into a new issue. Don't forget to anonymize any private data! The new issue form is located at:
76
+ https://github.com/caratrb/carat/issues/new
77
+ EOS
78
+ end
79
+
80
+ def self.issues_url(exception)
81
+ 'https://github.com/caratrb/carat/search?q=' \
82
+ "#{CGI.escape(exception.message.lines.first.chomp)}&type=Issues"
83
+ end
84
+
85
+ end
@@ -0,0 +1,180 @@
1
+ require 'carat/vendored_thor' unless defined?(Thor)
2
+ require 'carat'
3
+
4
+ module Carat
5
+ class GemHelper
6
+ include Rake::DSL if defined? Rake::DSL
7
+
8
+ class << self
9
+ # set when install'd.
10
+ attr_accessor :instance
11
+
12
+ def install_tasks(opts = {})
13
+ new(opts[:dir], opts[:name]).install
14
+ end
15
+
16
+ def gemspec(&block)
17
+ gemspec = instance.gemspec
18
+ block.call(gemspec) if block
19
+ gemspec
20
+ end
21
+ end
22
+
23
+ attr_reader :spec_path, :base, :gemspec
24
+
25
+ def initialize(base = nil, name = nil)
26
+ Carat.ui = UI::Shell.new
27
+ @base = (base ||= SharedHelpers.pwd)
28
+ gemspecs = name ? [File.join(base, "#{name}.gemspec")] : Dir[File.join(base, "{,*}.gemspec")]
29
+ raise "Unable to determine name from existing gemspec. Use :name => 'gemname' in #install_tasks to manually set it." unless gemspecs.size == 1
30
+ @spec_path = gemspecs.first
31
+ @gemspec = Carat.load_gemspec(@spec_path)
32
+ end
33
+
34
+ def install
35
+ built_gem_path = nil
36
+
37
+ desc "Build #{name}-#{version}.gem into the pkg directory."
38
+ task 'build' do
39
+ built_gem_path = build_gem
40
+ end
41
+
42
+ desc "Build and install #{name}-#{version}.gem into system gems."
43
+ task 'install' => 'build' do
44
+ install_gem(built_gem_path)
45
+ end
46
+
47
+ desc "Build and install #{name}-#{version}.gem into system gems without network access."
48
+ task 'install:local' => 'build' do
49
+ install_gem(built_gem_path, :local)
50
+ end
51
+
52
+ desc "Create tag #{version_tag} and build and push #{name}-#{version}.gem to Rubygems\n" \
53
+ "To prevent publishing in Rubygems use `gem_push=no rake release`"
54
+ task 'release' => ['build', 'release:guard_clean',
55
+ 'release:source_control_push', 'release:rubygem_push'] do
56
+ end
57
+
58
+ task 'release:guard_clean' do
59
+ guard_clean
60
+ end
61
+
62
+ task 'release:source_control_push' do
63
+ tag_version { git_push } unless already_tagged?
64
+ end
65
+
66
+ task 'release:rubygem_push' do
67
+ rubygem_push(built_gem_path) if gem_push?
68
+ end
69
+
70
+ GemHelper.instance = self
71
+ end
72
+
73
+ def build_gem
74
+ file_name = nil
75
+ sh("gem build -V '#{spec_path}'") { |out, code|
76
+ file_name = File.basename(built_gem_path)
77
+ FileUtils.mkdir_p(File.join(base, 'pkg'))
78
+ FileUtils.mv(built_gem_path, 'pkg')
79
+ Carat.ui.confirm "#{name} #{version} built to pkg/#{file_name}."
80
+ }
81
+ File.join(base, 'pkg', file_name)
82
+ end
83
+
84
+ def install_gem(built_gem_path = nil, local = false)
85
+ built_gem_path ||= build_gem
86
+ out, _ = sh_with_code("gem install '#{built_gem_path}'#{' --local' if local}")
87
+ raise "Couldn't install gem, run `gem install #{built_gem_path}' for more detailed output" unless out[/Successfully installed/]
88
+ Carat.ui.confirm "#{name} (#{version}) installed."
89
+ end
90
+
91
+ protected
92
+ def rubygem_push(path)
93
+ if Pathname.new("~/.gem/credentials").expand_path.exist?
94
+ sh("gem push '#{path}'")
95
+ Carat.ui.confirm "Pushed #{name} #{version} to rubygems.org."
96
+ else
97
+ raise "Your rubygems.org credentials aren't set. Run `gem push` to set them."
98
+ end
99
+ end
100
+
101
+ def built_gem_path
102
+ Dir[File.join(base, "#{name}-*.gem")].sort_by{|f| File.mtime(f)}.last
103
+ end
104
+
105
+ def git_push
106
+ perform_git_push
107
+ perform_git_push ' --tags'
108
+ Carat.ui.confirm "Pushed git commits and tags."
109
+ end
110
+
111
+ def perform_git_push(options = '')
112
+ cmd = "git push #{options}"
113
+ out, code = sh_with_code(cmd)
114
+ raise "Couldn't git push. `#{cmd}' failed with the following output:\n\n#{out}\n" unless code == 0
115
+ end
116
+
117
+ def already_tagged?
118
+ if sh('git tag').split(/\n/).include?(version_tag)
119
+ Carat.ui.confirm "Tag #{version_tag} has already been created."
120
+ true
121
+ end
122
+ end
123
+
124
+ def guard_clean
125
+ clean? && committed? or raise("There are files that need to be committed first.")
126
+ end
127
+
128
+ def clean?
129
+ sh_with_code("git diff --exit-code")[1] == 0
130
+ end
131
+
132
+ def committed?
133
+ sh_with_code("git diff-index --quiet --cached HEAD")[1] == 0
134
+ end
135
+
136
+ def tag_version
137
+ sh "git tag -a -m \"Version #{version}\" #{version_tag}"
138
+ Carat.ui.confirm "Tagged #{version_tag}."
139
+ yield if block_given?
140
+ rescue
141
+ Carat.ui.error "Untagging #{version_tag} due to error."
142
+ sh_with_code "git tag -d #{version_tag}"
143
+ raise
144
+ end
145
+
146
+ def version
147
+ gemspec.version
148
+ end
149
+
150
+ def version_tag
151
+ "v#{version}"
152
+ end
153
+
154
+ def name
155
+ gemspec.name
156
+ end
157
+
158
+ def sh(cmd, &block)
159
+ out, code = sh_with_code(cmd, &block)
160
+ code == 0 ? out : raise(out.empty? ? "Running `#{cmd}' failed. Run this command directly for more detailed output." : out)
161
+ end
162
+
163
+ def sh_with_code(cmd, &block)
164
+ cmd << " 2>&1"
165
+ outbuf = ''
166
+ Carat.ui.debug(cmd)
167
+ SharedHelpers.chdir(base) {
168
+ outbuf = `#{cmd}`
169
+ if $? == 0
170
+ block.call(outbuf) if block
171
+ end
172
+ }
173
+ [outbuf, $?]
174
+ end
175
+
176
+ def gem_push?
177
+ ! %w{n no nil false off 0}.include?(ENV['gem_push'].to_s.downcase)
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,26 @@
1
+ module Carat
2
+ module GemHelpers
3
+
4
+ GENERIC_CACHE = {}
5
+ GENERICS = [
6
+ [Gem::Platform.new('java'), Gem::Platform.new('java')],
7
+ [Gem::Platform.new('mswin32'), Gem::Platform.new('mswin32')],
8
+ [Gem::Platform.new('mswin64'), Gem::Platform.new('mswin64')],
9
+ [Gem::Platform.new('universal-mingw32'), Gem::Platform.new('universal-mingw32')],
10
+ [Gem::Platform.new('x64-mingw32'), Gem::Platform.new('x64-mingw32')],
11
+ [Gem::Platform.new('x86_64-mingw32'), Gem::Platform.new('x64-mingw32')],
12
+ [Gem::Platform.new('mingw32'), Gem::Platform.new('x86-mingw32')]
13
+ ]
14
+
15
+ def generic(p)
16
+ return p if p == Gem::Platform::RUBY
17
+
18
+ GENERIC_CACHE[p] ||= begin
19
+ _, found = GENERICS.find do |match, _generic|
20
+ p.os == match.os && (!match.cpu || p.cpu == match.cpu)
21
+ end
22
+ found || Gem::Platform::RUBY
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ require 'rubygems/installer'
2
+
3
+ module Carat
4
+ class GemInstaller < Gem::Installer
5
+ def check_executable_overwrite(filename)
6
+ # Carat needs to install gems regardless of binstub overwriting
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ module Carat
2
+ def self.preserve_gem_path
3
+ original_gem_path = ENV["_ORIGINAL_GEM_PATH"]
4
+ gem_path = ENV["GEM_PATH"]
5
+ ENV["_ORIGINAL_GEM_PATH"] = gem_path if original_gem_path.nil? || original_gem_path == ""
6
+ ENV["GEM_PATH"] = original_gem_path if gem_path.nil? || gem_path == ""
7
+ end
8
+ end
@@ -0,0 +1,2 @@
1
+ require 'carat/gem_helper'
2
+ Carat::GemHelper.install_tasks
@@ -0,0 +1,169 @@
1
+ require 'set'
2
+ module Carat
3
+ class Graph
4
+ GRAPH_NAME = :Gemfile
5
+
6
+ def initialize(env, output_file, show_version = false, show_requirements = false, output_format = "png", without = [])
7
+ @env = env
8
+ @output_file = output_file
9
+ @show_version = show_version
10
+ @show_requirements = show_requirements
11
+ @output_format = output_format
12
+ @without_groups = without.map(&:to_sym)
13
+
14
+ @groups = []
15
+ @relations = Hash.new {|h, k| h[k] = Set.new}
16
+ @node_options = {}
17
+ @edge_options = {}
18
+
19
+ _patching_gem_dependency_class
20
+ _populate_relations
21
+ end
22
+
23
+ attr_reader :groups, :relations, :node_options, :edge_options, :output_file, :output_format
24
+
25
+ def viz
26
+ GraphVizClient.new(self).run
27
+ end
28
+
29
+ private
30
+
31
+ def _populate_relations
32
+ parent_dependencies = _groups.values.to_set.flatten
33
+ while true
34
+ if parent_dependencies.empty?
35
+ break
36
+ else
37
+ tmp = Set.new
38
+ parent_dependencies.each do |dependency|
39
+ child_dependencies = dependency.to_spec.runtime_dependencies.to_set
40
+ @relations[dependency.name] += child_dependencies.map(&:name).to_set
41
+ tmp += child_dependencies
42
+
43
+ @node_options[dependency.name] = _make_label(dependency, :node)
44
+ child_dependencies.each do |c_dependency|
45
+ @edge_options["#{dependency.name}_#{c_dependency.name}"] = _make_label(c_dependency, :edge)
46
+ end
47
+ end
48
+ parent_dependencies = tmp
49
+ end
50
+ end
51
+ end
52
+
53
+ def _groups
54
+ relations = Hash.new {|h, k| h[k] = Set.new}
55
+ @env.current_dependencies.each do |dependency|
56
+ dependency.groups.each do |group|
57
+ next if @without_groups.include?(group)
58
+
59
+ relations[group.to_s].add(dependency)
60
+ @relations[group.to_s].add(dependency.name)
61
+
62
+ @node_options[group.to_s] ||= _make_label(group, :node)
63
+ @edge_options["#{group}_#{dependency.name}"] = _make_label(dependency, :edge)
64
+ end
65
+ end
66
+ @groups = relations.keys
67
+ relations
68
+ end
69
+
70
+ def _make_label(symbol_or_string_or_dependency, element_type)
71
+ case element_type.to_sym
72
+ when :node
73
+ if symbol_or_string_or_dependency.is_a?(Gem::Dependency)
74
+ label = symbol_or_string_or_dependency.name.dup
75
+ label << "\n#{symbol_or_string_or_dependency.to_spec.version.to_s}" if @show_version
76
+ else
77
+ label = symbol_or_string_or_dependency.to_s
78
+ end
79
+ when :edge
80
+ label = nil
81
+ if symbol_or_string_or_dependency.respond_to?(:requirements_list) && @show_requirements
82
+ tmp = symbol_or_string_or_dependency.requirements_list.join(", ")
83
+ label = tmp if tmp != ">= 0"
84
+ end
85
+ else
86
+ raise ArgumentError, "2nd argument is invalid"
87
+ end
88
+ label.nil? ? {} : { :label => label }
89
+ end
90
+
91
+ def _patching_gem_dependency_class
92
+ # method borrow from rubygems/dependency.rb
93
+ # redefinition of matching_specs will also redefine to_spec and to_specs
94
+ Gem::Dependency.class_eval do
95
+ def matching_specs platform_only = false
96
+ matches = Carat.load.specs.select { |spec|
97
+ self.name == spec.name and
98
+ requirement.satisfied_by? spec.version
99
+ }
100
+
101
+ if platform_only
102
+ matches.reject! { |spec|
103
+ not Gem::Platform.match spec.platform
104
+ }
105
+ end
106
+
107
+ matches = matches.sort_by { |s| s.sort_obj } # HACK: shouldn't be needed
108
+ end
109
+ end
110
+ end
111
+
112
+ class GraphVizClient
113
+ def initialize(graph_instance)
114
+ @graph_name = graph_instance.class::GRAPH_NAME
115
+ @groups = graph_instance.groups
116
+ @relations = graph_instance.relations
117
+ @node_options = graph_instance.node_options
118
+ @edge_options = graph_instance.edge_options
119
+ @output_file = graph_instance.output_file
120
+ @output_format = graph_instance.output_format
121
+ end
122
+
123
+ def g
124
+ @g ||= ::GraphViz.digraph(@graph_name, {:concentrate => true, :normalize => true, :nodesep => 0.55}) do |g|
125
+ g.edge[:weight] = 2
126
+ g.edge[:fontname] = g.node[:fontname] = 'Arial, Helvetica, SansSerif'
127
+ g.edge[:fontsize] = 12
128
+ end
129
+ end
130
+
131
+ def run
132
+ @groups.each do |group|
133
+ g.add_nodes(
134
+ group,
135
+ {:style => 'filled',
136
+ :fillcolor => '#B9B9D5',
137
+ :shape => "box3d",
138
+ :fontsize => 16}.merge(@node_options[group])
139
+ )
140
+ end
141
+
142
+ @relations.each do |parent, children|
143
+ children.each do |child|
144
+ if @groups.include?(parent)
145
+ g.add_nodes(child, {:style => 'filled', :fillcolor => '#B9B9D5'}.merge(@node_options[child]))
146
+ g.add_edges(parent, child, {:constraint => false}.merge(@edge_options["#{parent}_#{child}"]))
147
+ else
148
+ g.add_nodes(child, @node_options[child])
149
+ g.add_edges(parent, child, @edge_options["#{parent}_#{child}"])
150
+ end
151
+ end
152
+ end
153
+
154
+ if @output_format.to_s == "debug"
155
+ $stdout.puts g.output :none => String
156
+ Carat.ui.info "debugging carat viz..."
157
+ else
158
+ begin
159
+ g.output @output_format.to_sym => "#{@output_file}.#{@output_format}"
160
+ Carat.ui.info "#{@output_file}.#{@output_format}"
161
+ rescue ArgumentError => e
162
+ $stderr.puts "Unsupported output format. See Ruby-Graphviz/lib/graphviz/constants.rb"
163
+ raise e
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end