Class: Gem::Commands::SandboxCommand

Inherits:
Gem::Command
  • Object
show all
Defined in:
lib/rubygems/commands/sandbox_command.rb

Overview

Gem command to “sandbox” command-line tools into their own private gem repos but with a globally command-line wrapper.

Constant Summary collapse

VERSION =
"1.3.2"

Instance Method Summary collapse

Constructor Details

#initializeSandboxCommand

Returns a new instance of SandboxCommand.



24
25
26
27
28
29
30
31
32
33
34
# File 'lib/rubygems/commands/sandbox_command.rb', line 24

def initialize
  defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge(
    :document          => [],
    :format_executable => false,
    :version           => Gem::Requirement.default
  )

  super("sandbox", "Privately install a command-line tool", defaults)

  @scripts = []
end

Instance Method Details

#argumentsObject

:nodoc:



37
38
39
40
# File 'lib/rubygems/commands/sandbox_command.rb', line 37

def arguments # :nodoc:
  ['SUBCOMMAND    one of: list, install, help, etc... see help for full list',
   'GEMNAME       name of a gem to sandbox'].join "\n"
end

#cleanObject



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/rubygems/commands/sandbox_command.rb', line 181

def clean
  require 'rubygems/uninstaller'

  options[:args] = sandboxes if options[:args].empty?

  get_all_gem_names.each do |gem_name|
    dir = sandbox_dir gem_name

    # Forces reset of known installed gems so subsequent repeats work
    Gem.use_paths dir, dir # FUCK ME this is the worst code ever
    Gem.refresh

    primary_gems = {}

    Gem::Specification.each do |spec|
      if primary_gems[spec.name].nil? or
          primary_gems[spec.name].version < spec.version then
        primary_gems[spec.name] = spec
      end
    end

    gems_to_cleanup = Gem::Specification.to_a

    gems_to_cleanup = gems_to_cleanup.select { |spec|
      primary_gems[spec.name].version != spec.version
    }

    deplist = Gem::DependencyList.new
    gems_to_cleanup.uniq.each do |spec| deplist.add spec end

    deps = deplist.strongly_connected_components.flatten.reverse

    deps.each do |spec|
      options[:args] = [spec.name]

      uninstall_options = {
        :executables => false,
        :version => "= #{spec.version}",
      }

      uninstaller = Gem::Uninstaller.new spec.name, uninstall_options

      begin
        uninstaller.uninstall
      rescue Gem::DependencyRemovalException, Gem::InstallError,
        Gem::GemNotInHomeException, Gem::FilePermissionError => e
        say "Unable to uninstall #{spec.full_name}:"
        say "\t#{e.class}: #{e.message}"
      end
    end
  end
end

#descriptionObject



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/rubygems/commands/sandbox_command.rb', line 46

def description
  <<-EOF

`gem sandbox` helps you manage your command-line tools and their
dependencies. Sandboxed gems are installed in their own private
repositories with their dependencies. This means that you don't have
to have a rats nest of gems in your global repository in order to run
popular command-tools like rdoc, flog, flay, rcov, etc.

`gem sandbox` has the following sub-commands:

* list                               - list current sandboxes
* install   gem_name ...             - install 1 or more gems
* outdated  gem_name ...             - check 1 or more gems for outdated deps
* plugin    gem_name plugin_name ... - install a gem and plugins for it
* uninstall gem_name ...             - uninstall 1 or more gems
* cleanup   gem_name ...             - remove older gem deps
* update    gem_name ...             - update 1 or more gems
* help                               - show this output

Once you install `gem sandbox` will output something like:

  Copy the following scripts to any directory in your path to use them:

  cp /Users/USER/.gem/sandboxes/TOOL/bin/TOOL _in_your_$PATH_

Copy the scripts to a directory in your path (eg ~/bin or /usr/bin)
and you're good to go.
  EOF
end

#executeObject



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/rubygems/commands/sandbox_command.rb', line 77

def execute
  cmd = options[:args].shift

  case cmd
  when "list" then
    list
  when "install" then
    install
  when "outdated" then
    outdated
  when "update" then
    update
  when "clean", "cleanup" then
    clean
  when "plugin" then
    plugin
  when "uninstall" then
    uninstall
  when "help", "usage" then
    show_help
    abort
  else
    alert_error "Unknown sandbox subcommand: #{cmd}"
    show_help
    abort
  end
end

#installObject



122
123
124
125
126
127
128
# File 'lib/rubygems/commands/sandbox_command.rb', line 122

def install
  get_all_gem_names.each do |gem_name|
    install_gem gem_name
  end

  list_scripts
end

#install_gem(gem_name, dir = sandbox_dir(gem_name)) ⇒ Object



265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/rubygems/commands/sandbox_command.rb', line 265

def install_gem gem_name, dir = sandbox_dir(gem_name)
  # Forces reset of known installed gems so subsequent repeats work
  Gem.use_paths nil, nil

  inst = Gem::DependencyInstaller.new options.merge(:install_dir => dir)
  inst.install gem_name, options[:version]

  spec = inst.installed_gems.find { |s| s.name == gem_name }
  rewrite_executables dir, spec

  say "Successfully installed #{gem_name}"
end

#listObject



113
114
115
116
117
118
119
120
# File 'lib/rubygems/commands/sandbox_command.rb', line 113

def list
  boxes = self.sandboxes
  if boxes then
    say boxes.join "\n"
  else
    say "No sandboxes installed."
  end
end

#list_scriptsObject



248
249
250
251
252
253
# File 'lib/rubygems/commands/sandbox_command.rb', line 248

def list_scripts
  say ""
  say "Copy the following scripts to any directory in your path to use them:"
  say ""
  say "cp #{@scripts.join ' '} _in_your_$PATH_"
end

#outdatedObject



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/rubygems/commands/sandbox_command.rb', line 139

def outdated
  get_all_gem_names.each do |gem_name|
    dir = sandbox_dir(gem_name)

    # Forces reset of known installed gems so subsequent repeats work
    Gem.use_paths dir, dir
    Gem.refresh

    Gem::Specification.outdated.sort.each do |name|
      local      = Gem::Specification.find_all_by_name(name).max
      dep        = Gem::Dependency.new local.name, ">= #{local.version}"
      remotes, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dep

      next if remotes.empty?

      remote = remotes.last.first
      say "#{local.name} (#{local.version} < #{remote.version})"
    end
  end
end

#pluginObject



234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/rubygems/commands/sandbox_command.rb', line 234

def plugin
  gem_name, *plugins = options[:args]
  dir                = sandbox_dir gem_name

  install_gem gem_name, dir

  plugins.each do |plugin_name|
    inst = Gem::DependencyInstaller.new options.merge(:install_dir => dir)
    inst.install plugin_name, options[:version]
  end

  list_scripts
end

#rewrite_executables(dir, spec) ⇒ Object



287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/rubygems/commands/sandbox_command.rb', line 287

def rewrite_executables dir, spec
  spec.executables.each do |filename|
    path = File.join dir, "bin", filename
    env  = "ENV['GEM_HOME'] = ENV['GEM_PATH'] = #{dir.inspect}"
    env = <<-"EOM".gsub(/^ +/, '')
      ENV['GEM_HOME'] = #{dir.inspect}
      Gem.paths = ENV
    EOM

    script = File.read path
    script.sub!(/require 'rubygems'/, "#{env}\n\\&")

    File.open path, "w" do |f|
      f.write script
    end

    @scripts << path
  end
end

#sandbox_dir(gem_name) ⇒ Object



259
260
261
262
263
# File 'lib/rubygems/commands/sandbox_command.rb', line 259

def sandbox_dir gem_name
  dir = File.join sandbox_home, gem_name
  warn "WARNING: #{gem_name} might not be a sandbox" unless File.exist? dir
  dir
end

#sandbox_homeObject



255
256
257
# File 'lib/rubygems/commands/sandbox_command.rb', line 255

def sandbox_home
  File.join Gem.user_home, '.gem', "sandboxes"
end

#sandboxesObject



105
106
107
108
109
110
111
# File 'lib/rubygems/commands/sandbox_command.rb', line 105

def sandboxes
  if File.directory? sandbox_home then
    Dir.chdir sandbox_home do
      Dir["*"]
    end
  end
end

#uninstallObject



130
131
132
133
134
135
136
137
# File 'lib/rubygems/commands/sandbox_command.rb', line 130

def uninstall
  get_all_gem_names.each do |gem_name|
    uninstall_gem gem_name
  end

  say ""
  say "You will need to manually clean up any script wrappers you copied."
end

#uninstall_gem(gem_name, dir = sandbox_dir(gem_name)) ⇒ Object



278
279
280
281
282
283
284
285
# File 'lib/rubygems/commands/sandbox_command.rb', line 278

def uninstall_gem gem_name, dir = sandbox_dir(gem_name)
  # Forces reset of known installed gems so subsequent repeats work
  Gem.use_paths nil, nil

  require "fileutils"

  FileUtils.rm_rf dir, :verbose => true
end

#updateObject



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/rubygems/commands/sandbox_command.rb', line 160

def update
  require 'rubygems/commands/update_command'

  get_all_gem_names.each do |gem_name|
    dir = sandbox_dir gem_name

    # Forces reset of known installed gems so subsequent repeats work
    Gem.use_paths dir, dir
    Gem.refresh

    updater = Gem::Commands::UpdateCommand.new
    updater.updated = [] # HACK: this is stupid
    updater.installer =
      Gem::DependencyInstaller.new(options.merge(:install_dir => dir))

    Gem::Specification.outdated.sort.each do |name|
      updater.update_gem name
    end
  end
end

#usageObject

:nodoc:



42
43
44
# File 'lib/rubygems/commands/sandbox_command.rb', line 42

def usage # :nodoc:
  "#{program_name} SUBCOMMAND GEMNAME(S)"
end