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,38 @@
1
+ require "carat/cli/common"
2
+
3
+ module Carat
4
+ class CLI::Binstubs
5
+ attr_reader :options, :gems
6
+ def initialize(options, gems)
7
+ @options = options
8
+ @gems = gems
9
+ end
10
+
11
+ def run
12
+ Carat.definition.validate_ruby!
13
+ Carat.settings[:bin] = options["path"] if options["path"]
14
+ Carat.settings[:bin] = nil if options["path"] && options["path"].empty?
15
+ installer = Installer.new(Carat.root, Carat.definition)
16
+
17
+ if gems.empty?
18
+ Carat.ui.error "`carat binstubs` needs at least one gem to run."
19
+ exit 1
20
+ end
21
+
22
+ gems.each do |gem_name|
23
+ spec = installer.specs.find{|s| s.name == gem_name }
24
+ unless spec
25
+ raise GemNotFound, Carat::CLI::Common.gem_not_found_message(
26
+ gem_name, Carat.definition.specs)
27
+ end
28
+
29
+ if spec.name == "carat"
30
+ Carat.ui.warn "Sorry, Carat can only be run via Rubygems."
31
+ else
32
+ installer.generate_carat_executable_stubs(spec, :force => options[:force], :binstubs_cmd => true)
33
+ end
34
+ end
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,35 @@
1
+ module Carat
2
+ class CLI::Cache
3
+ attr_reader :options
4
+ def initialize(options)
5
+ @options = options
6
+ end
7
+
8
+ def run
9
+ Carat.definition.validate_ruby!
10
+ Carat.definition.resolve_with_cache!
11
+ setup_cache_all
12
+ Carat.settings[:cache_all_platforms] = options["all-platforms"] if options.key?("all-platforms")
13
+ Carat.load.cache
14
+ Carat.settings[:no_prune] = true if options["no-prune"]
15
+ Carat.load.lock
16
+ rescue GemNotFound => e
17
+ Carat.ui.error(e.message)
18
+ Carat.ui.warn "Run `carat install` to install missing gems."
19
+ exit 1
20
+ end
21
+
22
+ private
23
+
24
+ def setup_cache_all
25
+ Carat.settings[:cache_all] = options[:all] if options.key?("all")
26
+
27
+ if Carat.definition.has_local_dependencies? && !Carat.settings[:cache_all]
28
+ Carat.ui.warn "Your Gemfile contains path and git dependencies. If you want " \
29
+ "to package them as well, please pass the --all flag. This will be the default " \
30
+ "on Carat 2.0."
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,35 @@
1
+ module Carat
2
+ class CLI::Check
3
+ attr_reader :options
4
+ def initialize(options)
5
+ @options = options
6
+ end
7
+
8
+ def run
9
+ Carat.settings[:path] = File.expand_path(options[:path]) if options[:path]
10
+ begin
11
+ definition = Carat.definition
12
+ definition.validate_ruby!
13
+ not_installed = definition.missing_specs
14
+ rescue GemNotFound, VersionConflict
15
+ Carat.ui.error "Carat can't satisfy your Gemfile's dependencies."
16
+ Carat.ui.warn "Install missing gems with `carat install`."
17
+ exit 1
18
+ end
19
+
20
+ if not_installed.any?
21
+ Carat.ui.error "The following gems are missing"
22
+ not_installed.each { |s| Carat.ui.error " * #{s.name} (#{s.version})" }
23
+ Carat.ui.warn "Install missing gems with `carat install`"
24
+ exit 1
25
+ elsif !Carat.default_lockfile.exist? && Carat.settings[:frozen]
26
+ Carat.ui.error "This bundle has been frozen, but there is no Gemfile.lock present"
27
+ exit 1
28
+ else
29
+ Carat.load.lock unless options[:"dry-run"]
30
+ Carat.ui.info "The Gemfile's dependencies are satisfied"
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,26 @@
1
+ module Carat
2
+ class CLI::Clean
3
+ attr_reader :options
4
+
5
+ def initialize(options)
6
+ @options = options
7
+ end
8
+
9
+ def run
10
+ require_path_or_force
11
+ Carat.load.clean(options[:"dry-run"])
12
+ end
13
+
14
+ protected
15
+
16
+ def require_path_or_force
17
+ if !Carat.settings[:path] && !options[:force]
18
+ Carat.ui.error "Cleaning all the gems on your system is dangerous! " \
19
+ "If you're sure you want to remove every system gem not in this " \
20
+ "bundle, run `carat clean --force`."
21
+ exit 1
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,56 @@
1
+ module Carat
2
+ module CLI::Common
3
+ def self.without_groups_message
4
+ groups = Carat.settings.without
5
+ group_list = [groups[0...-1].join(", "), groups[-1..-1]].
6
+ reject{|s| s.to_s.empty? }.join(" and ")
7
+ group_str = (groups.size == 1) ? "group" : "groups"
8
+ "Gems in the #{group_str} #{group_list} were not installed."
9
+ end
10
+
11
+ def self.select_spec(name, regex_match = nil)
12
+ specs = []
13
+ regexp = Regexp.new(name) if regex_match
14
+
15
+ Carat.definition.specs.each do |spec|
16
+ return spec if spec.name == name
17
+ specs << spec if regexp && spec.name =~ regexp
18
+ end
19
+
20
+ case specs.count
21
+ when 0
22
+ raise GemNotFound, gem_not_found_message(name, Carat.definition.dependencies)
23
+ when 1
24
+ specs.first
25
+ else
26
+ ask_for_spec_from(specs)
27
+ end
28
+ rescue RegexpError
29
+ raise GemNotFound, gem_not_found_message(name, Carat.definition.dependencies)
30
+ end
31
+
32
+ def self.ask_for_spec_from(specs)
33
+ if !$stdout.tty? && ENV['BUNDLE_SPEC_RUN'].nil?
34
+ raise GemNotFound, gem_not_found_message(name, Carat.definition.dependencies)
35
+ end
36
+
37
+ specs.each_with_index do |spec, index|
38
+ Carat.ui.info "#{index.succ} : #{spec.name}", true
39
+ end
40
+ Carat.ui.info '0 : - exit -', true
41
+
42
+ num = Carat.ui.ask('> ').to_i
43
+ num > 0 ? specs[num - 1] : nil
44
+ end
45
+
46
+ def self.gem_not_found_message(missing_gem_name, alternatives)
47
+ require 'carat/similarity_detector'
48
+ message = "Could not find gem '#{missing_gem_name}'."
49
+ alternate_names = alternatives.map { |a| a.respond_to?(:name) ? a.name : a }
50
+ suggestions = SimilarityDetector.new(alternate_names).similar_word_list(missing_gem_name)
51
+ message += "\nDid you mean #{suggestions}?" if suggestions
52
+ message
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,84 @@
1
+ module Carat
2
+ class CLI::Config
3
+ attr_reader :options, :thor
4
+ attr_accessor :args
5
+
6
+ def initialize(options, args, thor)
7
+ @options = options
8
+ @args = args
9
+ @thor = thor
10
+ end
11
+
12
+ def run
13
+ peek = args.shift
14
+
15
+ if peek && peek =~ /^\-\-/
16
+ name, scope = args.shift, $'
17
+ else
18
+ name, scope = peek, "global"
19
+ end
20
+
21
+ unless name
22
+ Carat.ui.confirm "Settings are listed in order of priority. The top value will be used.\n"
23
+
24
+ Carat.settings.all.each do |setting|
25
+ Carat.ui.confirm "#{setting}"
26
+ thor.with_padding do
27
+ Carat.settings.pretty_values_for(setting).each do |line|
28
+ Carat.ui.info line
29
+ end
30
+ end
31
+ Carat.ui.confirm ""
32
+ end
33
+ return
34
+ end
35
+
36
+ case scope
37
+ when "delete"
38
+ Carat.settings.set_local(name, nil)
39
+ Carat.settings.set_global(name, nil)
40
+ when "local", "global"
41
+ if args.empty?
42
+ Carat.ui.confirm "Settings for `#{name}` in order of priority. The top value will be used"
43
+ thor.with_padding do
44
+ Carat.settings.pretty_values_for(name).each { |line| Carat.ui.info line }
45
+ end
46
+ return
47
+ end
48
+
49
+ locations = Carat.settings.locations(name)
50
+
51
+ if scope == "global"
52
+ if local = locations[:local]
53
+ Carat.ui.info "Your application has set #{name} to #{local.inspect}. This will override the " \
54
+ "global value you are currently setting"
55
+ end
56
+
57
+ if env = locations[:env]
58
+ Carat.ui.info "You have a carat environment variable for #{name} set to #{env.inspect}. " \
59
+ "This will take precedence over the global value you are setting"
60
+ end
61
+
62
+ if global = locations[:global]
63
+ Carat.ui.info "You are replacing the current global value of #{name}, which is currently #{global.inspect}"
64
+ end
65
+ end
66
+
67
+ if scope == "local" && local = locations[:local]
68
+ Carat.ui.info "You are replacing the current local value of #{name}, which is currently #{local.inspect}"
69
+ end
70
+
71
+ if name.match(/\Alocal\./)
72
+ pathname = Pathname.new(args.join(" "))
73
+ self.args = [pathname.expand_path.to_s] if pathname.directory?
74
+ end
75
+
76
+ Carat.settings.send("set_#{scope}", name, args.join(" "))
77
+ else
78
+ Carat.ui.error "Invalid scope --#{scope} given. Please use --local or --global."
79
+ exit 1
80
+ end
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,38 @@
1
+ module Carat
2
+ class CLI::Console
3
+ attr_reader :options, :group
4
+ def initialize(options, group)
5
+ @options = options
6
+ @group = group
7
+ end
8
+
9
+ def run
10
+ group ? Carat.require(:default, *(group.split.map! {|g| g.to_sym })) : Carat.require
11
+ ARGV.clear
12
+
13
+ console = get_console(Carat.settings[:console] || 'irb')
14
+ console.start
15
+ end
16
+
17
+ def get_console(name)
18
+ require name
19
+ get_constant(name)
20
+ rescue LoadError
21
+ Carat.ui.error "Couldn't load console #{name}"
22
+ get_constant('irb')
23
+ end
24
+
25
+ def get_constant(name)
26
+ const_name = {
27
+ 'pry' => :Pry,
28
+ 'ripl' => :Ripl,
29
+ 'irb' => :IRB,
30
+ }[name]
31
+ Object.const_get(const_name)
32
+ rescue NameError
33
+ Carat.ui.error "Could not find constant #{const_name}"
34
+ exit 1
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,44 @@
1
+ module Carat
2
+ class CLI::Exec
3
+ attr_reader :options, :args, :cmd
4
+
5
+ def initialize(options, args)
6
+ @options = options
7
+ @cmd = args.shift
8
+ @args = args
9
+
10
+ if RUBY_VERSION >= "2.0"
11
+ @args << { :close_others => !options.keep_file_descriptors? }
12
+ elsif options.keep_file_descriptors?
13
+ Carat.ui.warn "Ruby version #{RUBY_VERSION} defaults to keeping non-standard file descriptors on Kernel#exec."
14
+ end
15
+ end
16
+
17
+ def run
18
+ raise ArgumentError if cmd.nil?
19
+
20
+ # First, try to exec directly to something in PATH
21
+ SharedHelpers.set_bundle_environment
22
+ bin_path = Carat.which(@cmd)
23
+ if bin_path
24
+ Kernel.exec(bin_path, *args)
25
+ end
26
+
27
+ # If that didn't work, set up the whole bundle
28
+ Carat.definition.validate_ruby!
29
+ Carat.load.setup_environment
30
+ Kernel.exec(@cmd, *args)
31
+ rescue Errno::EACCES
32
+ Carat.ui.error "carat: not executable: #{cmd}"
33
+ exit 126
34
+ rescue Errno::ENOENT
35
+ Carat.ui.error "carat: command not found: #{cmd}"
36
+ Carat.ui.warn "Install missing gem executables with `carat install`"
37
+ exit 127
38
+ rescue ArgumentError
39
+ Carat.ui.error "carat: exec needs a command to run"
40
+ exit 128
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,195 @@
1
+ require 'pathname'
2
+
3
+ module Carat
4
+ class CLI::Gem
5
+ attr_reader :options, :gem_name, :thor, :name, :target
6
+
7
+ def initialize(options, gem_name, thor)
8
+ @options = options
9
+ @gem_name = resolve_name(gem_name)
10
+ @thor = thor
11
+
12
+ @name = @gem_name
13
+ @target = Pathname.pwd.join(gem_name)
14
+
15
+ validate_ext_name if options[:ext]
16
+ end
17
+
18
+ def run
19
+ Carat.ui.confirm "Creating gem '#{name}'..."
20
+
21
+ underscored_name = name.tr('-', '_')
22
+ namespaced_path = name.tr('-', '/')
23
+ constant_name = name.gsub(/-[_-]*(?![_-]|$)/){ '::' }.gsub(/([_-]+|(::)|^)(.|$)/){ $2.to_s + $3.upcase }
24
+ constant_array = constant_name.split('::')
25
+
26
+ git_user_name = `git config user.name`.chomp
27
+ git_user_email = `git config user.email`.chomp
28
+
29
+ config = {
30
+ :name => name,
31
+ :underscored_name => underscored_name,
32
+ :namespaced_path => namespaced_path,
33
+ :makefile_path => "#{underscored_name}/#{underscored_name}",
34
+ :constant_name => constant_name,
35
+ :constant_array => constant_array,
36
+ :author => git_user_name.empty? ? "TODO: Write your name" : git_user_name,
37
+ :email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email,
38
+ :test => options[:test],
39
+ :ext => options[:ext],
40
+ :bin => options[:bin],
41
+ :carat_version => carat_dependency_version
42
+ }
43
+ ensure_safe_gem_name(name, constant_array)
44
+
45
+ templates = {
46
+ "Gemfile.tt" => "Gemfile",
47
+ "gitignore.tt" => ".gitignore",
48
+ "lib/newgem.rb.tt" => "lib/#{namespaced_path}.rb",
49
+ "lib/newgem/version.rb.tt" => "lib/#{namespaced_path}/version.rb",
50
+ "newgem.gemspec.tt" => "#{name}.gemspec",
51
+ "Rakefile.tt" => "Rakefile",
52
+ "README.md.tt" => "README.md",
53
+ "bin/console.tt" => "bin/console",
54
+ "bin/setup.tt" => "bin/setup"
55
+ }
56
+
57
+ executables = %w[
58
+ bin/console
59
+ bin/setup
60
+ ]
61
+
62
+ if ask_and_set(:coc, "Do you want to include a code of conduct in gems you generate?",
63
+ "Codes of conduct can increase contributions to your project by contributors who " \
64
+ "prefer collaborative, safe spaces. You can read more about the code of conduct at " \
65
+ "contributor-covenant.org. Having a code of conduct means agreeing to the responsibility " \
66
+ "of enforcing it, so be sure that you are prepared to do that. For suggestions about " \
67
+ "how to enforce codes of conduct, see bit.ly/coc-enforcement."
68
+ )
69
+ templates.merge!("CODE_OF_CONDUCT.md.tt" => "CODE_OF_CONDUCT.md")
70
+ end
71
+
72
+ if ask_and_set(:mit, "Do you want to license your code permissively under the MIT license?",
73
+ "This means that any other developer or company will be legally allowed to use your code " \
74
+ "for free as long as they admit you created it. You can read more about the MIT license " \
75
+ "at choosealicense.com/licenses/mit."
76
+ )
77
+ config[:mit] = true
78
+ templates.merge!("LICENSE.txt.tt" => "LICENSE.txt")
79
+ end
80
+
81
+ if test_framework = ask_and_set_test_framework
82
+ templates.merge!(".travis.yml.tt" => ".travis.yml")
83
+
84
+ case test_framework
85
+ when 'rspec'
86
+ templates.merge!(
87
+ "rspec.tt" => ".rspec",
88
+ "spec/spec_helper.rb.tt" => "spec/spec_helper.rb",
89
+ "spec/newgem_spec.rb.tt" => "spec/#{namespaced_path}_spec.rb"
90
+ )
91
+ when 'minitest'
92
+ templates.merge!(
93
+ "test/minitest_helper.rb.tt" => "test/minitest_helper.rb",
94
+ "test/test_newgem.rb.tt" => "test/test_#{namespaced_path}.rb"
95
+ )
96
+ end
97
+ end
98
+
99
+ templates.merge!("exe/newgem.tt" => "exe/#{name}") if options[:bin]
100
+
101
+ if options[:ext]
102
+ templates.merge!(
103
+ "ext/newgem/extconf.rb.tt" => "ext/#{name}/extconf.rb",
104
+ "ext/newgem/newgem.h.tt" => "ext/#{name}/#{underscored_name}.h",
105
+ "ext/newgem/newgem.c.tt" => "ext/#{name}/#{underscored_name}.c"
106
+ )
107
+ end
108
+
109
+ templates.each do |src, dst|
110
+ thor.template("newgem/#{src}", target.join(dst), config)
111
+ end
112
+
113
+ executables.each do |file|
114
+ path = target.join(file)
115
+ executable = (path.stat.mode | 0111)
116
+ path.chmod(executable)
117
+ end
118
+
119
+ Carat.ui.info "Initializing git repo in #{target}"
120
+ Dir.chdir(target) { `git init`; `git add .` }
121
+
122
+ if options[:edit]
123
+ # Open gemspec in editor
124
+ thor.run("#{options["edit"]} \"#{target.join("#{name}.gemspec")}\"")
125
+ end
126
+ end
127
+
128
+ private
129
+
130
+ def resolve_name(name)
131
+ Pathname.pwd.join(name).basename.to_s
132
+ end
133
+
134
+ def ask_and_set(key, header, message)
135
+ choice = options[key] || Carat.settings["gem.#{key}"]
136
+
137
+ if choice.nil?
138
+ Carat.ui.confirm header
139
+ choice = Carat.ui.yes? "#{message} y/(n):"
140
+ Carat.settings.set_global("gem.#{key}", choice)
141
+ end
142
+
143
+ choice
144
+ end
145
+
146
+ def validate_ext_name
147
+ return unless gem_name.index('-')
148
+
149
+ Carat.ui.error "You have specified a gem name which does not conform to the \n" \
150
+ "naming guidelines for C extensions. For more information, \n" \
151
+ "see the 'Extension Naming' section at the following URL:\n" \
152
+ "http://guides.rubygems.org/gems-with-extensions/\n"
153
+ exit 1
154
+ end
155
+
156
+ def ask_and_set_test_framework
157
+ test_framework = options[:test] || Carat.settings["gem.test"]
158
+
159
+ if test_framework.nil?
160
+ Carat.ui.confirm "Do you want to generate tests with your gem?"
161
+ result = Carat.ui.ask "Type 'rspec' or 'minitest' to generate those test files now and " \
162
+ "in the future. rspec/minitest/(none):"
163
+ if result =~ /rspec|minitest/
164
+ test_framework = result
165
+ else
166
+ test_framework = false
167
+ end
168
+ end
169
+
170
+ if Carat.settings["gem.test"].nil?
171
+ Carat.settings.set_global("gem.test", test_framework)
172
+ end
173
+
174
+ test_framework
175
+ end
176
+
177
+ def carat_dependency_version
178
+ v = Gem::Version.new(Carat::VERSION)
179
+ req = v.segments[0..1]
180
+ req << 'a' if v.prerelease?
181
+ req.join(".")
182
+ end
183
+
184
+ def ensure_safe_gem_name name, constant_array
185
+ if name =~ /^\d/
186
+ Carat.ui.error "Invalid gem name #{name} Please give a name which does not start with numbers."
187
+ exit 1
188
+ elsif constant_array.inject(Object) {|c, s| (c.const_defined?(s) && c.const_get(s)) || break }
189
+ Carat.ui.error "Invalid gem name #{name} constant #{constant_array.join("::")} is already in use. Please choose another gem name."
190
+ exit 1
191
+ end
192
+ end
193
+
194
+ end
195
+ end